From f5f76ea75dce553631ffb08abc44dcecb68e74d4 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 11 Jan 2016 15:17:23 +0000 Subject: ASoC: qcom: use correct device pointer in dma allocation dev pointer in struct snd_soc_pcm_runtime does not have dma_ops set. In v4.4 kernel dma_ops would end up pointing to dummy_dma_ops in such cases. So attempting to use such device in allocating coherent memory on aarch64 would fail. According to commit 1dccb598df549d892b6450c261da54cdd7af44b4 ("arm64: simplify dma_get_ops") The current behavior of dma_get_ops is to fall back to the global dma_ops when a device has not set its own dma_ops, but only for DT based systems. So, this patch fixes the driver to use correct device pointer while allocating coherent memory, and also deletes un-necessary dma_mask setup on soc_runtime->dev. Without this patch lpass driver would fail with below log: ... [ 6.541542] ADV7533: lpass_platform_alloc_buffer: Could not allocate DMA buffer [ 6.541914] apq8016-lpass-cpu 7708000.lpass-cpu: ASoC: pcm constructor failed: -12 [ 6.548216] qcom-apq8016-sbc 7702000.sound: ASoC: can't create pcm ADV7533 :-12 [ 6.555581] qcom-apq8016-sbc 7702000.sound: ASoC: failed to instantiate card -12 [ 6.566072] qcom-apq8016-sbc: probe of 7702000.sound failed with error -12 ... Signed-off-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 79688aa1941a..4aeb8e1a7160 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -440,18 +440,18 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) } static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *soc_runtime) + struct snd_soc_pcm_runtime *rt) { struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = soc_runtime->dev; + buf->dev.dev = rt->platform->dev; buf->private_data = NULL; - buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, + buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr, GFP_KERNEL); if (!buf->area) { - dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", + dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n", __func__); return -ENOMEM; } @@ -461,12 +461,12 @@ static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, } static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *soc_runtime) + struct snd_soc_pcm_runtime *rt) { struct snd_dma_buffer *buf = &substream->dma_buffer; if (buf->area) { - dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, + dma_free_coherent(rt->dev, buf->bytes, buf->area, buf->addr); } buf->area = NULL; @@ -499,9 +499,6 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) snd_soc_pcm_set_drvdata(soc_runtime, data); - soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); - soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; - ret = lpass_platform_alloc_buffer(substream, soc_runtime); if (ret) return ret; -- cgit v1.2.3 From cde6bcd584b1b910d6ee8d6eb968ea5d20815444 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 13 Jan 2016 15:20:02 +0300 Subject: ASoC: AMD: free memory on error Static checkers complain if we don't free "adata" before returning. Fixes: 7c31335a03b6 ('ASoC: AMD: add AMD ASoC ACP 2.x DMA driver') Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 3191e0a7d273..d1fb035f44db 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -635,6 +635,7 @@ static int acp_dma_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(prtd->platform->dev, "set integer constraint failed\n"); + kfree(adata); return ret; } -- cgit v1.2.3 From 1ca2cf8c4167c2016d9716998b4f89c4e79d1f89 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jan 2016 15:55:17 +0800 Subject: ASoC: rt5659: Fix irq leak Use devm_request_threaded_irq to ensure the irq is freed when unload the module. The rt5659->i2c is no longer used after this conversion, thus remove it as well. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5659.c | 16 ++++------------ sound/soc/codecs/rt5659.h | 1 - 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 820d8fa62b5e..c166d9394c69 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3985,7 +3985,6 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, if (rt5659 == NULL) return -ENOMEM; - rt5659->i2c = i2c; i2c_set_clientdata(i2c, rt5659); if (pdata) @@ -4157,24 +4156,17 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, INIT_DELAYED_WORK(&rt5659->jack_detect_work, rt5659_jack_detect_work); - if (rt5659->i2c->irq) { - ret = request_threaded_irq(rt5659->i2c->irq, NULL, rt5659_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5659", rt5659); if (ret) dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659, + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659, rt5659_dai, ARRAY_SIZE(rt5659_dai)); - - if (ret) { - if (rt5659->i2c->irq) - free_irq(rt5659->i2c->irq, rt5659); - } - - return 0; } static int rt5659_i2c_remove(struct i2c_client *i2c) diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h index 8f07ee903eaa..d31c9e5bcec8 100644 --- a/sound/soc/codecs/rt5659.h +++ b/sound/soc/codecs/rt5659.h @@ -1792,7 +1792,6 @@ struct rt5659_priv { struct snd_soc_codec *codec; struct rt5659_platform_data pdata; struct regmap *regmap; - struct i2c_client *i2c; struct gpio_desc *gpiod_ldo1_en; struct gpio_desc *gpiod_reset; struct snd_soc_jack *hs_jack; -- cgit v1.2.3 From ec3995da27e782cc407ce48101c98c19c9ce738d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Jan 2016 23:14:54 +0100 Subject: ASoC: mediatek: add i2c dependency The newly added mediatek drivers for mt8173 select codes that depend on I2C, which cuases a build failure if I2C is disabled: warning: (SND_SOC_ADAU1761_I2C && SND_SOC_ADAU1781_I2C && SND_SOC_ADAU1977_I2C && SND_SOC_RT5677 && EXTCON_MAX14577 && EXTCON_MAX77693 && EXTCON_MAX77843 && BMC150_ACCEL_I2C && BMG160_I2C) selects REGMAP_I2C which has unmet direct dependencies (I2C) codecs/rt5645.c:3854:1: warning: data definition has no type or storage class codecs/rt5645.c:3854:1: error: type defaults to 'int' in declaration of 'module_i2c_driver' [-Werror=implicit-int] codecs/rt5677.c:5270:1: warning: data definition has no type or storage class 77_i2c_driver); codecs/rt5677.c:5270:1: error: type defaults to 'int' in declaration of 'module_i2c_driver' [-Werror=implicit-int] This adds an explicit dependency. Signed-off-by: Arnd Bergmann Acked-by: Koro Chen Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 15c04e2eae34..976967675387 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -9,7 +9,7 @@ config SND_SOC_MEDIATEK config SND_SOC_MT8173_MAX98090 tristate "ASoC Audio driver for MT8173 with MAX98090 codec" - depends on SND_SOC_MEDIATEK + depends on SND_SOC_MEDIATEK && I2C select SND_SOC_MAX98090 help This adds ASoC driver for Mediatek MT8173 boards @@ -19,7 +19,7 @@ config SND_SOC_MT8173_MAX98090 config SND_SOC_MT8173_RT5650_RT5676 tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" - depends on SND_SOC_MEDIATEK + depends on SND_SOC_MEDIATEK && I2C select SND_SOC_RT5645 select SND_SOC_RT5677 help -- cgit v1.2.3 From 2935bf43ef12a8d68b96776ec11155cfa120cb0d Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 15 Jan 2016 13:33:08 +0800 Subject: ASoC: fsl: document DT compatible string "fsl,imx-audio-wm8960" The devicetree compatible string "fsl,imx-audio-wm8960" for fsl-asoc-card is missing. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index ce55c0a6f757..4da41bf1888e 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -30,6 +30,8 @@ The compatible list for this generic sound card currently: "fsl,imx-audio-sgtl5000" (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt) + "fsl,imx-audio-wm8960" + Required properties: - compatible : Contains one of entries in the compatible list. -- cgit v1.2.3 From 6d514c720219a4c0e1c2612c1d830592bfaf5a03 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 21 Jan 2016 14:10:48 +0800 Subject: ASoC: rt286: fix capture doesn't work at some cases RT286_CBJ_CTRL1(0x4f) bit 10 is needed for headset capture. It will be turned off when "VREF" widget is on and be turned on when bias level is ON. It is odd. And if "VREF" is turned on in bias level is ON, RT286_CBJ_CTRL1(0x4f) bit 10 will be turned off. This patch move the bit control from rt286_set_bias_level and rt298_vref_event to rt286_jack_detect. So it will be turned on once a jack is plugged in. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt286.c | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index af2ed774b552..af30b062f57a 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -266,6 +266,8 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) } else { *mic = false; regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20); + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0x0400, 0x0000); } } else { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); @@ -470,24 +472,6 @@ static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w, return 0; } -static int rt286_vref_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0000); - mdelay(50); - break; - default: - return 0; - } - - return 0; -} - static int rt286_ldo2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -536,7 +520,7 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1, 12, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1, - 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU), + 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1, @@ -910,8 +894,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: mdelay(10); - snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0400); snd_soc_update_bits(codec, RT286_DC_GAIN, 0x200, 0x0); @@ -920,8 +902,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D3); - snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0000); break; default: -- cgit v1.2.3 From b28785fa9cede0d4f47310ca0dd2a4e1d50478b5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 21 Jan 2016 13:13:40 +0800 Subject: ASoC: rt5645: fix the shift bit of IN1 boost The shift bit of IN1 boost gain control is 12. Signed-off-by: Bard Liao Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 28132375e427..c916c3881259 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -600,7 +600,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1, - RT5645_BST_SFT1, 8, 0, bst_tlv), + RT5645_BST_SFT1, 12, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL, RT5645_BST_SFT2, 8, 0, bst_tlv), -- cgit v1.2.3 From 2256b8d2ff6c8e994161ab15b6e6d0314d3174ae Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2016 12:46:24 +0100 Subject: ASoC: rt5659: avoid unused variable warning for rt5659_acpi_match The newly added rt5659 codec driver unconditionally defines an ACPI device match table but then uses ACPI_PTR() to remove the only reference to it, so we get a harmless build warning: sound/soc/codecs/rt5659.c:4200:30: warning: 'rt5659_acpi_match' defined but not used [-Wunused-variable] static struct acpi_device_id rt5659_acpi_match[] = { This changes both the OF match table and the ACPI match table to follow the same style, using ACPI_PTR/of_match_ptr to make the reference conditional, and using an #ifdef to hide the table. This also adds the missing MODULE_DEVICE_TABLE for the OF case and adapts the formatting to the same style. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/codecs/rt5659.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index c166d9394c69..fb8ea05c0de1 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4183,24 +4183,29 @@ void rt5659_i2c_shutdown(struct i2c_client *client) regmap_write(rt5659->regmap, RT5659_RESET, 0); } +#ifdef CONFIG_OF static const struct of_device_id rt5659_of_match[] = { { .compatible = "realtek,rt5658", }, { .compatible = "realtek,rt5659", }, - {}, + { }, }; +MODULE_DEVICE_TABLE(of, rt5659_of_match); +#endif +#ifdef CONFIG_ACPI static struct acpi_device_id rt5659_acpi_match[] = { - { "10EC5658", 0}, - { "10EC5659", 0}, - { }, + { "10EC5658", 0, }, + { "10EC5659", 0, }, + { }, }; MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match); +#endif struct i2c_driver rt5659_i2c_driver = { .driver = { .name = "rt5659", .owner = THIS_MODULE, - .of_match_table = rt5659_of_match, + .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, .probe = rt5659_i2c_probe, -- cgit v1.2.3 From cbfe74a753e877b49dc54e9b04d5d42230ca0aed Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 8 Jan 2016 12:29:10 +0530 Subject: ARCv2: STAR 9000950267: Handle return from intr to Delay Slot #2 Returning to delay slot, riding an interrupti, had one loose end. AUX_USER_SP used for restoring user mode SP upon RTIE was not being setup from orig task's saved value, causing task to use wrong SP, leading to ProtV errors. The reason being: - INTERRUPT_EPILOGUE returns to a kernel trampoline, thus not expected to restore it - EXCEPTION_EPILOGUE is not used at all Fix that by restoring AUX_USER_SP explicitly in the trampoline. This was broken in the original workaround, but the error scenarios got reduced considerably since v3.14 due to following: 1. The Linuxthreads.old based userspace at the time caused many more exceptions in delay slot than the current NPTL based one. Infact with current userspace the error doesn't happen at all. 2. Return from interrupt (delay slot or otherwise) doesn't get exercised much after commit 4de0e52867d8 ("Really Re-enable interrupts to avoid deadlocks") since IRQ_ACTIVE.active being clear means most returns are as if from pure kernel (even for active interrupts) Infact the issue only happened in an experimental branch where I was tinkering with reverted 4de0e52867d8 Cc: stable@kernel.org # v4.2+ Fixes: 4255b07f2c9c ("ARCv2: STAR 9000793984: Handle return from intr to Delay Slot") Signed-off-by: Vineet Gupta --- arch/arc/kernel/entry-arcv2.S | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/arc/kernel/entry-arcv2.S b/arch/arc/kernel/entry-arcv2.S index cbfec79137bf..b17830294706 100644 --- a/arch/arc/kernel/entry-arcv2.S +++ b/arch/arc/kernel/entry-arcv2.S @@ -211,7 +211,11 @@ debug_marker_syscall: ; (since IRQ NOT allowed in DS in ARCv2, this can only happen if orig ; entry was via Exception in DS which got preempted in kernel). ; -; IRQ RTIE won't reliably restore DE bit and/or BTA, needs handling +; IRQ RTIE won't reliably restore DE bit and/or BTA, needs workaround +; +; Solution is return from Intr w/o any delay slot quirks into a kernel trampoline +; and from pure kernel mode return to delay slot which handles DS bit/BTA correctly + .Lintr_ret_to_delay_slot: debug_marker_ds: @@ -222,18 +226,23 @@ debug_marker_ds: ld r2, [sp, PT_ret] ld r3, [sp, PT_status32] + ; STAT32 for Int return created from scratch + ; (No delay dlot, disable Further intr in trampoline) + bic r0, r3, STATUS_U_MASK|STATUS_DE_MASK|STATUS_IE_MASK|STATUS_L_MASK st r0, [sp, PT_status32] mov r1, .Lintr_ret_to_delay_slot_2 st r1, [sp, PT_ret] + ; Orig exception PC/STAT32 safekept @orig_r0 and @event stack slots st r2, [sp, 0] st r3, [sp, 4] b .Lisr_ret_fast_path .Lintr_ret_to_delay_slot_2: + ; Trampoline to restore orig exception PC/STAT32/BTA/AUX_USER_SP sub sp, sp, SZ_PT_REGS st r9, [sp, -4] @@ -243,11 +252,19 @@ debug_marker_ds: ld r9, [sp, 4] sr r9, [erstatus] + ; restore AUX_USER_SP if returning to U mode + bbit0 r9, STATUS_U_BIT, 1f + ld r9, [sp, PT_sp] + sr r9, [AUX_USER_SP] + +1: ld r9, [sp, 8] sr r9, [erbta] ld r9, [sp, -4] add sp, sp, SZ_PT_REGS + + ; return from pure kernel mode to delay slot rtie END(ret_from_exception) -- cgit v1.2.3 From e93ad19d05648397ef3bcb838d26aec06c245dc0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 19 Jan 2016 12:18:41 -0500 Subject: cpuset: make mm migration asynchronous If "cpuset.memory_migrate" is set, when a process is moved from one cpuset to another with a different memory node mask, pages in used by the process are migrated to the new set of nodes. This was performed synchronously in the ->attach() callback, which is synchronized against process management. Recently, the synchronization was changed from per-process rwsem to global percpu rwsem for simplicity and optimization. Combined with the synchronous mm migration, this led to deadlocks because mm migration could schedule a work item which may in turn try to create a new worker blocking on the process management lock held from cgroup process migration path. This heavy an operation shouldn't be performed synchronously from that deep inside cgroup migration in the first place. This patch punts the actual migration to an ordered workqueue and updates cgroup process migration and cpuset config update paths to flush the workqueue after all locks are released. This way, the operations still seem synchronous to userland without entangling mm migration with process management synchronization. CPU hotplug can also invoke mm migration but there's no reason for it to wait for mm migrations and thus doesn't synchronize against their completions. Signed-off-by: Tejun Heo Reported-and-tested-by: Christian Borntraeger Cc: stable@vger.kernel.org # v4.4+ --- include/linux/cpuset.h | 6 +++++ kernel/cgroup.c | 2 ++ kernel/cpuset.c | 71 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 85a868ccb493..fea160ee5803 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -137,6 +137,8 @@ static inline void set_mems_allowed(nodemask_t nodemask) task_unlock(current); } +extern void cpuset_post_attach_flush(void); + #else /* !CONFIG_CPUSETS */ static inline bool cpusets_enabled(void) { return false; } @@ -243,6 +245,10 @@ static inline bool read_mems_allowed_retry(unsigned int seq) return false; } +static inline void cpuset_post_attach_flush(void) +{ +} + #endif /* !CONFIG_CPUSETS */ #endif /* _LINUX_CPUSET_H */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index c03a640ef6da..88abd4d076d8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -58,6 +58,7 @@ #include #include #include +#include #include /* @@ -2739,6 +2740,7 @@ out_unlock_rcu: out_unlock_threadgroup: percpu_up_write(&cgroup_threadgroup_rwsem); cgroup_kn_unlock(of->kn); + cpuset_post_attach_flush(); return ret ?: nbytes; } diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 3e945fcd8179..41989ab4db57 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -287,6 +287,8 @@ static struct cpuset top_cpuset = { static DEFINE_MUTEX(cpuset_mutex); static DEFINE_SPINLOCK(callback_lock); +static struct workqueue_struct *cpuset_migrate_mm_wq; + /* * CPU / memory hotplug is handled asynchronously. */ @@ -972,31 +974,51 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, } /* - * cpuset_migrate_mm - * - * Migrate memory region from one set of nodes to another. - * - * Temporarilly set tasks mems_allowed to target nodes of migration, - * so that the migration code can allocate pages on these nodes. - * - * While the mm_struct we are migrating is typically from some - * other task, the task_struct mems_allowed that we are hacking - * is for our current task, which must allocate new pages for that - * migrating memory region. + * Migrate memory region from one set of nodes to another. This is + * performed asynchronously as it can be called from process migration path + * holding locks involved in process management. All mm migrations are + * performed in the queued order and can be waited for by flushing + * cpuset_migrate_mm_wq. */ +struct cpuset_migrate_mm_work { + struct work_struct work; + struct mm_struct *mm; + nodemask_t from; + nodemask_t to; +}; + +static void cpuset_migrate_mm_workfn(struct work_struct *work) +{ + struct cpuset_migrate_mm_work *mwork = + container_of(work, struct cpuset_migrate_mm_work, work); + + /* on a wq worker, no need to worry about %current's mems_allowed */ + do_migrate_pages(mwork->mm, &mwork->from, &mwork->to, MPOL_MF_MOVE_ALL); + mmput(mwork->mm); + kfree(mwork); +} + static void cpuset_migrate_mm(struct mm_struct *mm, const nodemask_t *from, const nodemask_t *to) { - struct task_struct *tsk = current; - - tsk->mems_allowed = *to; + struct cpuset_migrate_mm_work *mwork; - do_migrate_pages(mm, from, to, MPOL_MF_MOVE_ALL); + mwork = kzalloc(sizeof(*mwork), GFP_KERNEL); + if (mwork) { + mwork->mm = mm; + mwork->from = *from; + mwork->to = *to; + INIT_WORK(&mwork->work, cpuset_migrate_mm_workfn); + queue_work(cpuset_migrate_mm_wq, &mwork->work); + } else { + mmput(mm); + } +} - rcu_read_lock(); - guarantee_online_mems(task_cs(tsk), &tsk->mems_allowed); - rcu_read_unlock(); +void cpuset_post_attach_flush(void) +{ + flush_workqueue(cpuset_migrate_mm_wq); } /* @@ -1097,7 +1119,8 @@ static void update_tasks_nodemask(struct cpuset *cs) mpol_rebind_mm(mm, &cs->mems_allowed); if (migrate) cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems); - mmput(mm); + else + mmput(mm); } css_task_iter_end(&it); @@ -1545,11 +1568,11 @@ static void cpuset_attach(struct cgroup_taskset *tset) * @old_mems_allowed is the right nodesets that we * migrate mm from. */ - if (is_memory_migrate(cs)) { + if (is_memory_migrate(cs)) cpuset_migrate_mm(mm, &oldcs->old_mems_allowed, &cpuset_attach_nodemask_to); - } - mmput(mm); + else + mmput(mm); } } @@ -1714,6 +1737,7 @@ out_unlock: mutex_unlock(&cpuset_mutex); kernfs_unbreak_active_protection(of->kn); css_put(&cs->css); + flush_workqueue(cpuset_migrate_mm_wq); return retval ?: nbytes; } @@ -2359,6 +2383,9 @@ void __init cpuset_init_smp(void) top_cpuset.effective_mems = node_states[N_MEMORY]; register_hotmemory_notifier(&cpuset_track_online_nodes_nb); + + cpuset_migrate_mm_wq = alloc_ordered_workqueue("cpuset_migrate_mm", 0); + BUG_ON(!cpuset_migrate_mm_wq); } /** -- cgit v1.2.3 From aa226ff4a1ce79f229c6b7a4c0a14e17fececd01 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 21 Jan 2016 15:31:11 -0500 Subject: cgroup: make sure a parent css isn't offlined before its children There are three subsystem callbacks in css shutdown path - css_offline(), css_released() and css_free(). Except for css_released(), cgroup core didn't guarantee the order of invocation. css_offline() or css_free() could be called on a parent css before its children. This behavior is unexpected and led to bugs in cpu and memory controller. This patch updates offline path so that a parent css is never offlined before its children. Each css keeps online_cnt which reaches zero iff itself and all its children are offline and offline_css() is invoked only after online_cnt reaches zero. This fixes the memory controller bug and allows the fix for cpu controller. Signed-off-by: Tejun Heo Reported-and-tested-by: Christian Borntraeger Reported-by: Brian Christiansen Link: http://lkml.kernel.org/g/5698A023.9070703@de.ibm.com Link: http://lkml.kernel.org/g/CAKB58ikDkzc8REt31WBkD99+hxNzjK4+FBmhkgS+NVrC9vjMSg@mail.gmail.com Cc: Heiko Carstens Cc: Peter Zijlstra Cc: stable@vger.kernel.org --- include/linux/cgroup-defs.h | 6 ++++++ kernel/cgroup.c | 22 +++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 7f540f7f588d..789471dba6fb 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -127,6 +127,12 @@ struct cgroup_subsys_state { */ u64 serial_nr; + /* + * Incremented by online self and children. Used to guarantee that + * parents are not offlined before their children. + */ + atomic_t online_cnt; + /* percpu_ref killing and RCU release */ struct rcu_head rcu_head; struct work_struct destroy_work; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 88abd4d076d8..d01587793865 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4760,6 +4760,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css, INIT_LIST_HEAD(&css->sibling); INIT_LIST_HEAD(&css->children); css->serial_nr = css_serial_nr_next++; + atomic_set(&css->online_cnt, 0); if (cgroup_parent(cgrp)) { css->parent = cgroup_css(cgroup_parent(cgrp), ss); @@ -4782,6 +4783,10 @@ static int online_css(struct cgroup_subsys_state *css) if (!ret) { css->flags |= CSS_ONLINE; rcu_assign_pointer(css->cgroup->subsys[ss->id], css); + + atomic_inc(&css->online_cnt); + if (css->parent) + atomic_inc(&css->parent->online_cnt); } return ret; } @@ -5019,10 +5024,15 @@ static void css_killed_work_fn(struct work_struct *work) container_of(work, struct cgroup_subsys_state, destroy_work); mutex_lock(&cgroup_mutex); - offline_css(css); - mutex_unlock(&cgroup_mutex); - css_put(css); + do { + offline_css(css); + css_put(css); + /* @css can't go away while we're holding cgroup_mutex */ + css = css->parent; + } while (css && atomic_dec_and_test(&css->online_cnt)); + + mutex_unlock(&cgroup_mutex); } /* css kill confirmation processing requires process context, bounce */ @@ -5031,8 +5041,10 @@ static void css_killed_ref_fn(struct percpu_ref *ref) struct cgroup_subsys_state *css = container_of(ref, struct cgroup_subsys_state, refcnt); - INIT_WORK(&css->destroy_work, css_killed_work_fn); - queue_work(cgroup_destroy_wq, &css->destroy_work); + if (atomic_dec_and_test(&css->online_cnt)) { + INIT_WORK(&css->destroy_work, css_killed_work_fn); + queue_work(cgroup_destroy_wq, &css->destroy_work); + } } /** -- cgit v1.2.3 From 8bb5ef79bc0f4016ecf79e8dce6096a3c63603e4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 21 Jan 2016 15:32:15 -0500 Subject: cgroup: make sure a parent css isn't freed before its children There are three subsystem callbacks in css shutdown path - css_offline(), css_released() and css_free(). Except for css_released(), cgroup core didn't guarantee the order of invocation. css_offline() or css_free() could be called on a parent css before its children. This behavior is unexpected and led to bugs in cpu and memory controller. The previous patch updated ordering for css_offline() which fixes the cpu controller issue. While there currently isn't a known bug caused by misordering of css_free() invocations, let's fix it too for consistency. css_free() ordering can be trivially fixed by moving putting of the parent css below css_free() invocation. Signed-off-by: Tejun Heo Cc: Peter Zijlstra --- kernel/cgroup.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d01587793865..d27904c193da 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4657,14 +4657,15 @@ static void css_free_work_fn(struct work_struct *work) if (ss) { /* css free path */ + struct cgroup_subsys_state *parent = css->parent; int id = css->id; - if (css->parent) - css_put(css->parent); - ss->css_free(css); cgroup_idr_remove(&ss->css_idr, id); cgroup_put(cgrp); + + if (parent) + css_put(parent); } else { /* cgroup free path */ atomic_dec(&cgrp->root->nr_cgrps); -- cgit v1.2.3 From c14a82c781f8df50c4c5215ab92affdc60d72c01 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 21 Jan 2016 17:27:59 +0530 Subject: ASoC: Intel: Skylake: Fix memory leak If snd_soc_tplg_component_load() fails we just printed an error message and returned the error code but we missed releasing the firmware. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 5315b7422b98..c7816d52ad08 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1511,6 +1511,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) release_firmware(fw); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); + release_firmware(fw); return -EINVAL; } -- cgit v1.2.3 From f5ede8dcc3ec1fe5344f0d30717931a44e630631 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 21 Jan 2016 14:41:12 +0000 Subject: ASoC: wm5110: Unregister compressed platform when driver is removed The driver was not unregistering the compressed platform in wm5110_remove(). If the codec is built as a module, this would lead to a NULL pointer deref if the module was unloaded and then re-probed. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index c36409601835..cd1b3080a497 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2358,6 +2358,7 @@ error: static int wm5110_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 95826a37991de87659e21b3649f265a049724aa2 Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Tue, 19 Jan 2016 13:09:08 +0000 Subject: ASoC: wm8960: Fix input boost mixer left/right naming INBMIX1 controls LINPUTs and INBMIX2 controls RINPUTs, so fix the naming accordingly. Signed-off-by: Stuart Henderson Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 5380798883b5..66057f853fae 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -231,13 +231,13 @@ SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 7, 1, 1), -SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", WM8960_RINPATH, 4, 3, 0, micboost_tlv), -- cgit v1.2.3 From 6bb7451429084cefcb3a18fff809f7992595d2af Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Tue, 19 Jan 2016 13:09:09 +0000 Subject: ASoC: wm8960: Fix WM8960_SYSCLK_PLL mode With the introduction of WM8960_SYSCLK_AUTO mode, WM8960_SYSCLK_PLL mode was made unusable. Ensure we're not PLL mode before trying to use MCLK. Fixes: 3176bf2d7ccd ("ASoC: wm8960: update pll and clock setting function") Signed-off-by: Stuart Henderson Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 66057f853fae..4b4401329591 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -631,29 +631,31 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) return -EINVAL; } - /* check if the sysclk frequency is available. */ - for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { - if (sysclk_divs[i] == -1) - continue; - sysclk = freq_out / sysclk_divs[i]; - for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { - if (sysclk == dac_divs[j] * lrclk) { + if (wm8960->clk_id != WM8960_SYSCLK_PLL) { + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = freq_out / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk != dac_divs[j] * lrclk) + continue; for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) if (sysclk == bclk * bclk_divs[k] / 10) break; if (k != ARRAY_SIZE(bclk_divs)) break; } + if (j != ARRAY_SIZE(dac_divs)) + break; } - if (j != ARRAY_SIZE(dac_divs)) - break; - } - if (i != ARRAY_SIZE(sysclk_divs)) { - goto configure_clock; - } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { - dev_err(codec->dev, "failed to configure clock\n"); - return -EINVAL; + if (i != ARRAY_SIZE(sysclk_divs)) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; + } } /* get a available pll out frequency and set pll */ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { -- cgit v1.2.3 From 8fc153cda9c9e6aed2a4a7235970dd6c6cb6e954 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Jan 2016 18:29:19 +0000 Subject: arm64: KVM: Fix AArch64 guest userspace exception injection At the moment, our fault injection is pretty limited. We always generate a SYNC exception into EL1, as if the fault was actually from EL1h, no matter how it was generated. This is obviously wrong, as EL0 can generate faults of its own (not to mention the pretty-much unused EL1t mode). This patch fixes it by implementing section D1.10.2 of the ARMv8 ARM, and in particular table D1-7 ("Vector offsets from vector table base address"), which describes which vector to use depending on the source exception level and type (synchronous, IRQ, FIQ or SError). Reviewed-by: Christoffer Dall Tested-by: Shannon Zhao Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 648112e90ed5..4d1ac81870d2 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -27,7 +27,11 @@ #define PSTATE_FAULT_BITS_64 (PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | \ PSR_I_BIT | PSR_D_BIT) -#define EL1_EXCEPT_SYNC_OFFSET 0x200 + +#define CURRENT_EL_SP_EL0_VECTOR 0x0 +#define CURRENT_EL_SP_ELx_VECTOR 0x200 +#define LOWER_EL_AArch64_VECTOR 0x400 +#define LOWER_EL_AArch32_VECTOR 0x600 static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) { @@ -97,6 +101,34 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, *fsr = 0x14; } +enum exception_type { + except_type_sync = 0, + except_type_irq = 0x80, + except_type_fiq = 0x100, + except_type_serror = 0x180, +}; + +static u64 get_except_vector(struct kvm_vcpu *vcpu, enum exception_type type) +{ + u64 exc_offset; + + switch (*vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT)) { + case PSR_MODE_EL1t: + exc_offset = CURRENT_EL_SP_EL0_VECTOR; + break; + case PSR_MODE_EL1h: + exc_offset = CURRENT_EL_SP_ELx_VECTOR; + break; + case PSR_MODE_EL0t: + exc_offset = LOWER_EL_AArch64_VECTOR; + break; + default: + exc_offset = LOWER_EL_AArch32_VECTOR; + } + + return vcpu_sys_reg(vcpu, VBAR_EL1) + exc_offset + type; +} + static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) { unsigned long cpsr = *vcpu_cpsr(vcpu); @@ -108,8 +140,8 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr *vcpu_spsr(vcpu) = cpsr; *vcpu_elr_el1(vcpu) = *vcpu_pc(vcpu); + *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; - *vcpu_pc(vcpu) = vcpu_sys_reg(vcpu, VBAR_EL1) + EL1_EXCEPT_SYNC_OFFSET; vcpu_sys_reg(vcpu, FAR_EL1) = addr; @@ -143,8 +175,8 @@ static void inject_undef64(struct kvm_vcpu *vcpu) *vcpu_spsr(vcpu) = cpsr; *vcpu_elr_el1(vcpu) = *vcpu_pc(vcpu); + *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; - *vcpu_pc(vcpu) = vcpu_sys_reg(vcpu, VBAR_EL1) + EL1_EXCEPT_SYNC_OFFSET; /* * Build an unknown exception, depending on the instruction -- cgit v1.2.3 From a7e0ac295d964086af3bf98352614f33c381213e Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 19 Jan 2016 16:20:18 +0000 Subject: arm64: KVM: Obey RES0/1 reserved bits when setting CPTR_EL2 Some bits in CPTR are defined as RES1 in the architecture. Setting these bits to zero may unintentionally enable future architecture extensions, allowing guests to use them without supervision by the host. This would be bad: for forwards compatibility, this patch makes sure the affected bits are always written with 1, not 0. This patch only addresses CPTR_EL2. Initialisation of other system registers may still need review. Reviewed-by: Marc Zyngier Signed-off-by: Dave Martin Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_arm.h | 1 + arch/arm64/kvm/hyp/switch.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 738a95f93e49..bef6e9243c63 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -182,6 +182,7 @@ #define CPTR_EL2_TCPAC (1 << 31) #define CPTR_EL2_TTA (1 << 20) #define CPTR_EL2_TFP (1 << CPTR_EL2_TFP_SHIFT) +#define CPTR_EL2_DEFAULT 0x000033ff /* Hyp Debug Configuration Register bits */ #define MDCR_EL2_TDRA (1 << 11) diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index ca8f5a5e2f96..f0e7bdfae134 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -36,7 +36,11 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu) write_sysreg(val, hcr_el2); /* Trap on AArch32 cp15 c15 accesses (EL1 or EL0) */ write_sysreg(1 << 15, hstr_el2); - write_sysreg(CPTR_EL2_TTA | CPTR_EL2_TFP, cptr_el2); + + val = CPTR_EL2_DEFAULT; + val |= CPTR_EL2_TTA | CPTR_EL2_TFP; + write_sysreg(val, cptr_el2); + write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); } @@ -45,7 +49,7 @@ static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu) write_sysreg(HCR_RW, hcr_el2); write_sysreg(0, hstr_el2); write_sysreg(read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK, mdcr_el2); - write_sysreg(0, cptr_el2); + write_sysreg(CPTR_EL2_DEFAULT, cptr_el2); } static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu) -- cgit v1.2.3 From 9586a2ea6806599c819a9e800581c2a698ef7467 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Wed, 13 Jan 2016 17:16:39 +0800 Subject: arm64: KVM: Fix wrong use of the CPSR MODE mask for 32bit guests The values of CPSR MODE mask are different between aarch32 and aarch64. It should use the right one according to the execution state. Reviewed-by: Marc Zyngier Signed-off-by: Shannon Zhao Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_emulate.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 3066328cd86b..779a5872a2c5 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -127,10 +127,14 @@ static inline unsigned long *vcpu_spsr(const struct kvm_vcpu *vcpu) static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu) { - u32 mode = *vcpu_cpsr(vcpu) & PSR_MODE_MASK; + u32 mode; - if (vcpu_mode_is_32bit(vcpu)) + if (vcpu_mode_is_32bit(vcpu)) { + mode = *vcpu_cpsr(vcpu) & COMPAT_PSR_MODE_MASK; return mode > COMPAT_PSR_MODE_USR; + } + + mode = *vcpu_cpsr(vcpu) & PSR_MODE_MASK; return mode != PSR_MODE_EL0t; } -- cgit v1.2.3 From 7769db905bd2df08e844b645437f4729fc1c4d20 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Wed, 13 Jan 2016 17:16:40 +0800 Subject: arm64: KVM: Fix comments of the CP handler Make sure the documentation reflects the actual name of the functions. Acked-by: Marc Zyngier Signed-off-by: Shannon Zhao Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index eec3598b4184..4048934bb3a5 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1043,7 +1043,7 @@ static void unhandled_cp_access(struct kvm_vcpu *vcpu, } /** - * kvm_handle_cp_64 -- handles a mrrc/mcrr trap on a guest CP15 access + * kvm_handle_cp_64 -- handles a mrrc/mcrr trap on a guest CP14/CP15 access * @vcpu: The VCPU pointer * @run: The kvm_run struct */ @@ -1095,7 +1095,7 @@ out: } /** - * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access + * kvm_handle_cp_32 -- handles a mrc/mcr trap on a guest CP14/CP15 access * @vcpu: The VCPU pointer * @run: The kvm_run struct */ -- cgit v1.2.3 From 6327f35a2010c06a3bc2bfb14202a38764fb9920 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Wed, 13 Jan 2016 17:16:41 +0800 Subject: arm64: KVM: Fix guest dead loop when register accessor returns false Currently emulate_cp will return 0 (Handled) no matter what the accessor returns. If register accessor returns false, it will not skip current PC while emulate_cp return handled. Then guest will stuck in a dead loop. Reviewed-by: Marc Zyngier Signed-off-by: Shannon Zhao Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 4048934bb3a5..2e90371cfb37 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1007,10 +1007,9 @@ static int emulate_cp(struct kvm_vcpu *vcpu, if (likely(r->access(vcpu, params, r))) { /* Skip instruction, since it was emulated */ kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); + /* Handled */ + return 0; } - - /* Handled */ - return 0; } /* Not handled */ -- cgit v1.2.3 From 5c408fee254633a5be69505bc86c6b034f871ab4 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 18 Jan 2016 20:07:44 +0100 Subject: ASoC: fsl_ssi: remove explicit register defaults There is no guarantee that on fsl_ssi module load SSI registers will have their power-on-reset values. In fact, if the driver is reloaded the values in registers will be whatever they were set to previously. However, the cache needs to be fully populated at probe time to avoid non-atomic allocations during register access. Special case here is imx21-class SSI, since according to datasheet it don't have SACC{ST,EN,DIS} regs. This fixes hard lockup on fsl_ssi module reload, at least in AC'97 mode. Fixes: 05cf237972fe ("ASoC: fsl_ssi: Add driver suspend and resume to support MEGA Fast") Signed-off-by: Maciej S. Szmigiero Tested-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 40dfd8a36484..ed8de1035cda 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val tx; }; -static const struct reg_default fsl_ssi_reg_defaults[] = { - {CCSR_SSI_SCR, 0x00000000}, - {CCSR_SSI_SIER, 0x00003003}, - {CCSR_SSI_STCR, 0x00000200}, - {CCSR_SSI_SRCR, 0x00000200}, - {CCSR_SSI_STCCR, 0x00040000}, - {CCSR_SSI_SRCCR, 0x00040000}, - {CCSR_SSI_SACNT, 0x00000000}, - {CCSR_SSI_STMSK, 0x00000000}, - {CCSR_SSI_SRMSK, 0x00000000}, - {CCSR_SSI_SACCEN, 0x00000000}, - {CCSR_SSI_SACCDIS, 0x00000000}, -}; - static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -190,8 +176,7 @@ static const struct regmap_config fsl_ssi_regconfig = { .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .reg_defaults = fsl_ssi_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1, .readable_reg = fsl_ssi_readable_reg, .volatile_reg = fsl_ssi_volatile_reg, .precious_reg = fsl_ssi_precious_reg, @@ -201,6 +186,7 @@ static const struct regmap_config fsl_ssi_regconfig = { struct fsl_ssi_soc_data { bool imx; + bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */ bool offline_config; u32 sisr_write_mask; }; @@ -303,6 +289,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { static struct fsl_ssi_soc_data fsl_ssi_imx21 = { .imx = true, + .imx21regs = true, .offline_config = true, .sisr_write_mask = 0, }; @@ -586,8 +573,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) */ regmap_write(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV); - regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); - regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + + /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ + if (!ssi_private->soc->imx21regs) { + regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); + regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + } /* * Enable SSI, Transmit and Receive. AC97 has to communicate with the @@ -1397,6 +1388,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource *res; void __iomem *iomem; char name[64]; + struct regmap_config regconfig = fsl_ssi_regconfig; of_id = of_match_device(fsl_ssi_ids, &pdev->dev); if (!of_id || !of_id->data) @@ -1444,15 +1436,25 @@ static int fsl_ssi_probe(struct platform_device *pdev) return PTR_ERR(iomem); ssi_private->ssi_phys = res->start; + if (ssi_private->soc->imx21regs) { + /* + * According to datasheet imx21-class SSI + * don't have SACC{ST,EN,DIS} regs. + */ + regconfig.max_register = CCSR_SSI_SRMSK; + regconfig.num_reg_defaults_raw = + CCSR_SSI_SRMSK / sizeof(uint32_t) + 1; + } + ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { ssi_private->has_ipg_clk_name = false; ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, - &fsl_ssi_regconfig); + ®config); } else { ssi_private->has_ipg_clk_name = true; ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, - "ipg", iomem, &fsl_ssi_regconfig); + "ipg", iomem, ®config); } if (IS_ERR(ssi_private->regs)) { dev_err(&pdev->dev, "Failed to init register map\n"); -- cgit v1.2.3 From 4ae2182b1e3407de369f8c5d799543b7db74221b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jan 2016 10:08:00 -0600 Subject: PCI/AER: Flush workqueue on device remove to avoid use-after-free A Root Port's AER structure (rpc) contains a queue of events. aer_irq() enqueues AER status information and schedules aer_isr() to dequeue and process it. When we remove a device, aer_remove() waits for the queue to be empty, then frees the rpc struct. But aer_isr() references the rpc struct after dequeueing and possibly emptying the queue, which can cause a use-after-free error as in the following scenario with two threads, aer_isr() on the left and a concurrent aer_remove() on the right: Thread A Thread B -------- -------- aer_irq(): rpc->prod_idx++ aer_remove(): wait_event(rpc->prod_idx == rpc->cons_idx) # now blocked until queue becomes empty aer_isr(): # ... rpc->cons_idx++ # unblocked because queue is now empty ... kfree(rpc) mutex_unlock(&rpc->rpc_mutex) To prevent this problem, use flush_work() to wait until the last scheduled instance of aer_isr() has completed before freeing the rpc struct in aer_remove(). I reproduced this use-after-free by flashing a device FPGA and re-enumerating the bus to find the new device. With SLUB debug, this crashes with 0x6b bytes (POISON_FREE, the use-after-free magic number) in GPR25: pcieport 0000:00:00.0: AER: Multiple Corrected error received: id=0000 Unable to handle kernel paging request for data at address 0x27ef9e3e Workqueue: events aer_isr GPR24: dd6aa000 6b6b6b6b 605f8378 605f8360 d99b12c0 604fc674 606b1704 d99b12c0 NIP [602f5328] pci_walk_bus+0xd4/0x104 [bhelgaas: changelog, stable tag] Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org --- drivers/pci/pcie/aer/aerdrv.c | 4 +--- drivers/pci/pcie/aer/aerdrv.h | 1 - drivers/pci/pcie/aer/aerdrv_core.c | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 0bf82a20a0fb..48d21e0edd56 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -262,7 +262,6 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) rpc->rpd = dev; INIT_WORK(&rpc->dpc_handler, aer_isr); mutex_init(&rpc->rpc_mutex); - init_waitqueue_head(&rpc->wait_release); /* Use PCIe bus function to store rpc into PCIe device */ set_service_data(dev, rpc); @@ -285,8 +284,7 @@ static void aer_remove(struct pcie_device *dev) if (rpc->isr) free_irq(dev->irq, dev); - wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); - + flush_work(&rpc->dpc_handler); aer_disable_rootport(rpc); kfree(rpc); set_service_data(dev, NULL); diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 84420b7c9456..945c939a86c5 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -72,7 +72,6 @@ struct aer_rpc { * recovery on the same * root port hierarchy */ - wait_queue_head_t wait_release; }; struct aer_broadcast_data { diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 712392504ed9..521e39c1b66d 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -811,8 +811,6 @@ void aer_isr(struct work_struct *work) while (get_e_source(rpc, &e_src)) aer_isr_one_error(p_device, &e_src); mutex_unlock(&rpc->rpc_mutex); - - wake_up(&rpc->wait_release); } /** -- cgit v1.2.3 From fb3296335500aaff61333df8eabbccf28761c79d Mon Sep 17 00:00:00 2001 From: Danesh Petigara Date: Mon, 11 Jan 2016 13:22:26 -0800 Subject: drivers: ata: wake port before DMA stop for ALPM The AHCI driver code stops and starts port DMA engines at will without considering the power state of the particular port. The AHCI specification isn't very clear on how to handle this scenario, leaving implementation open to interpretation. Broadcom's STB SATA host controller is unable to handle port DMA controller restarts when the port in question is in low power mode. When a port enters partial or slumber mode, its PHY is powered down. When a controller restart is requested, the controller's internal state machine expects the PHY to be brought back up by software which never happens in this case, resulting in failures. To avoid this situation, logic is added to manually wake up the port just before its DMA engine is stopped, if the port happens to be in a low power state. HBA initiated power management ensures that the port eventually returns to its configured low power state, when the link is idle (as per the conditions listed in the spec). A new host flag is also added to ensure this logic is only exercised for hosts with the above limitation. tj: Formatting changes. Signed-off-by: Danesh Petigara Reviewed-by: Markus Mayer Signed-off-by: Tejun Heo --- drivers/ata/ahci.h | 1 + drivers/ata/ahci_brcmstb.c | 1 + drivers/ata/libahci.c | 23 ++++++++++++++++++++++- include/linux/libata.h | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index a4faa438889c..a44c75d4c284 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -250,6 +250,7 @@ enum { AHCI_HFLAG_MULTI_MSI = 0, AHCI_HFLAG_MULTI_MSIX = 0, #endif + AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */ /* ap->flags bits */ diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c index b36cae2fd04b..e87bcec0fd7c 100644 --- a/drivers/ata/ahci_brcmstb.c +++ b/drivers/ata/ahci_brcmstb.c @@ -317,6 +317,7 @@ static int brcm_ahci_probe(struct platform_device *pdev) if (IS_ERR(hpriv)) return PTR_ERR(hpriv); hpriv->plat_data = priv; + hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP; brcm_sata_alpm_init(hpriv); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index d61740e78d6d..284a176076f5 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -593,8 +593,22 @@ EXPORT_SYMBOL_GPL(ahci_start_engine); int ahci_stop_engine(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; u32 tmp; + /* + * On some controllers, stopping a port's DMA engine while the port + * is in ALPM state (partial or slumber) results in failures on + * subsequent DMA engine starts. For those controllers, put the + * port back in active state before stopping its DMA engine. + */ + if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) && + (ap->link.lpm_policy > ATA_LPM_MAX_POWER) && + ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) { + dev_err(ap->host->dev, "Failed to wake up port before engine stop\n"); + return -EIO; + } + tmp = readl(port_mmio + PORT_CMD); /* check if the HBA is idle */ @@ -689,6 +703,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, void __iomem *port_mmio = ahci_port_base(ap); if (policy != ATA_LPM_MAX_POWER) { + /* wakeup flag only applies to the max power policy */ + hints &= ~ATA_LPM_WAKE_ONLY; + /* * Disable interrupts on Phy Ready. This keeps us from * getting woken up due to spurious phy ready @@ -704,7 +721,8 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, u32 cmd = readl(port_mmio + PORT_CMD); if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { - cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); + if (!(hints & ATA_LPM_WAKE_ONLY)) + cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); cmd |= PORT_CMD_ICC_ACTIVE; writel(cmd, port_mmio + PORT_CMD); @@ -712,6 +730,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, /* wait 10ms to be sure we've come out of LPM state */ ata_msleep(ap, 10); + + if (hints & ATA_LPM_WAKE_ONLY) + return 0; } else { cmd |= PORT_CMD_ALPE; if (policy == ATA_LPM_MIN_POWER) diff --git a/include/linux/libata.h b/include/linux/libata.h index 851821bfd553..bec2abbd7ab2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -526,6 +526,7 @@ enum ata_lpm_policy { enum ata_lpm_hints { ATA_LPM_EMPTY = (1 << 0), /* port empty/probing */ ATA_LPM_HIPM = (1 << 1), /* may use HIPM */ + ATA_LPM_WAKE_ONLY = (1 << 2), /* only wake up link */ }; /* forward declarations */ -- cgit v1.2.3 From b00622fc34e9d45069bd6e328dfd47ea2ef61468 Mon Sep 17 00:00:00 2001 From: Shikha Jain Date: Mon, 11 Jan 2016 14:55:45 -0800 Subject: libata: blacklist a Viking flash model for MWDMA corruption Viking flash model VRFDFC22048UCHC-TE causes data corruption in MWDMA mode. Cc: xe-kernel@external.cisco.com Signed-off-by: Shikha Jain Signed-off-by: Anil Veliyankara Madam Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index cbb74719d2c1..55e257c268dd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4125,6 +4125,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA }, { "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA }, { " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA }, + { "VRFDFC22048UCHC-TE*", NULL, ATA_HORKAGE_NODMA }, /* Odd clown on sil3726/4726 PMPs */ { "Config Disk", NULL, ATA_HORKAGE_DISABLE }, -- cgit v1.2.3 From 566d1827df2ef0cbe921d3d6946ac3007b1a6938 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 15 Jan 2016 15:13:05 -0500 Subject: libata: disable forced PORTS_IMPL for >= AHCI 1.3 Some early controllers incorrectly reported zero ports in PORTS_IMPL register and the ahci driver fabricates PORTS_IMPL from the number of ports in those cases. This hasn't mattered but with the new nvme controllers there are cases where zero PORTS_IMPL is valid and should be honored. Disable the workaround for >= AHCI 1.3. Signed-off-by: Tejun Heo Reported-by: Andy Lutomirski Link: http://lkml.kernel.org/g/CALCETrU7yMvXEDhjAUShoHEhDwifJGapdw--BKxsP0jmjKGmRw@mail.gmail.com Cc: Sergei Shtylyov Cc: stable@vger.kernel.org --- drivers/ata/libahci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 284a176076f5..402967902cbe 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -496,8 +496,8 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) } } - /* fabricate port_map from cap.nr_ports */ - if (!port_map) { + /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */ + if (!port_map && vers < 0x10300) { port_map = (1 << ahci_nr_ports(cap)) - 1; dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map); -- cgit v1.2.3 From 9a4e7849b5e4e8742d71fa90fbf0722dd0710a56 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 26 Jan 2016 14:49:22 +0000 Subject: component: fix crash on x86_64 with hda audio drivers Maarten reports that the addition of releasing match data to the component helper results in a general protection fault on x86_64. This is caused by the devm resources being freed in reverse order to their allocation, which caused a use-after-free of the match array. Switch the match array to be a more conventional kmalloc/kfree() affair, explicitly freeing it along with the parent match data structure. Reported-by: Maarten Lankhorst Fixes: ce657b1cddf1 ("component: add support for releasing match data") Signed-off-by: Russell King --- drivers/base/component.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index 89f5cf68d80a..05cd26c02449 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -206,6 +206,8 @@ static void component_match_release(struct device *master, if (mc->release) mc->release(master, mc->data); } + + kfree(match->compare); } static void devm_component_match_release(struct device *dev, void *res) @@ -221,14 +223,14 @@ static int component_match_realloc(struct device *dev, if (match->alloc == num) return 0; - new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL); + new = kmalloc_array(num, sizeof(*new), GFP_KERNEL); if (!new) return -ENOMEM; if (match->compare) { memcpy(new, match->compare, sizeof(*new) * min(match->num, num)); - devm_kfree(dev, match->compare); + kfree(match->compare); } match->compare = new; match->alloc = num; -- cgit v1.2.3 From 0f92db53c10ab608874eea8bbe59491cea2f9a46 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 26 Jan 2016 19:59:12 +0800 Subject: hisi_sas: add dependency for HAS_IOMEM Not every arch has io, so fix build by adding necessary dependency. Signed-off-by: John Garry Suggested-by: Richard Weinberger Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/Kconfig b/drivers/scsi/hisi_sas/Kconfig index b67661836c9f..d1dd1616f983 100644 --- a/drivers/scsi/hisi_sas/Kconfig +++ b/drivers/scsi/hisi_sas/Kconfig @@ -1,6 +1,6 @@ config SCSI_HISI_SAS tristate "HiSilicon SAS" - depends on HAS_DMA + depends on HAS_DMA && HAS_IOMEM depends on ARM64 || COMPILE_TEST select SCSI_SAS_LIBSAS select BLK_DEV_INTEGRITY -- cgit v1.2.3 From 0f28ede906a8ad44158a6ef9471a5a412343cced Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 27 Jan 2016 01:31:17 +0800 Subject: hisi_sas: fix v1 hw check for slot error Completion header bit CMPLT_HDR_RSPNS_XFRD flags whether the response frame is received into host memory, and not whether the response frame has an error. As such, change the decision on whether a slot has an error. Also redundant check on CMPLT_HDR_CMD_CMPLT_MSK is removed. Fixes: 27a3f229 ("hisi_sas: Add cq interrupt handler") Signed-off-by: John Garry Tested-by: Ricardo Salveti Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index d54381149c0d..d04808ef6b2b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1293,13 +1293,10 @@ static int slot_complete_v1_hw(struct hisi_hba *hisi_hba, goto out; } - if (cmplt_hdr_data & CMPLT_HDR_ERR_RCRD_XFRD_MSK) { - if (!(cmplt_hdr_data & CMPLT_HDR_CMD_CMPLT_MSK) || - !(cmplt_hdr_data & CMPLT_HDR_RSPNS_XFRD_MSK)) - ts->stat = SAS_DATA_OVERRUN; - else - slot_err_v1_hw(hisi_hba, task, slot); + if (cmplt_hdr_data & CMPLT_HDR_ERR_RCRD_XFRD_MSK && + !(cmplt_hdr_data & CMPLT_HDR_RSPNS_XFRD_MSK)) { + slot_err_v1_hw(hisi_hba, task, slot); goto out; } -- cgit v1.2.3 From 73c13c83491142e2ee7850159fc73b5e0967806f Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Fri, 15 Jan 2016 11:32:22 +0800 Subject: gpio: gpio-altera: Remove gpiochip on probe failure. On failure to setup the irq altera_gpio_probe would return an error but not go to cleanup. This resulted in kernel fault "Unable to handle kernel paging request at virtual address xxxxxxxx" later on in of_gpiochip_find_and_xlate. Signed-off-by: Phil Reid Signed-off-by: Linus Walleij --- drivers/gpio/gpio-altera.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 2aeaebd1c6e7..3f87a03abc22 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -312,8 +312,8 @@ static int altera_gpio_probe(struct platform_device *pdev) handle_simple_irq, IRQ_TYPE_NONE); if (ret) { - dev_info(&pdev->dev, "could not add irqchip\n"); - return ret; + dev_err(&pdev->dev, "could not add irqchip\n"); + goto teardown; } gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc, @@ -326,6 +326,7 @@ static int altera_gpio_probe(struct platform_device *pdev) skip_irq: return 0; teardown: + of_mm_gpiochip_remove(&altera_gc->mmchip); pr_err("%s: registration failed with status %d\n", node->full_name, ret); -- cgit v1.2.3 From 9954859185c6e8359e71121037b627f1e294057d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 26 Jan 2016 13:54:15 +0100 Subject: ASoC: imx-spdif: Fix crash on suspend When registering a ASoC card the driver data of the parent device is set to point to the card. This driver data is used in the snd_soc_suspend()/resume() callbacks. The imx-spdif driver overwrites the driver data with custom data which causes snd_soc_suspend() to crash. Since the custom driver is not used anywhere simply deleting the line which sets the custom driver data fixes the issue. Fixes: 43ac946922b3 ("ASoC: imx-spdif: add snd_soc_pm_ops for spdif machine driver") Tested-by: Fabio Estevam Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/fsl/imx-spdif.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index a407e833c612..fb896b2c9ba3 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -72,8 +72,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; } - platform_set_drvdata(pdev, data); - end: of_node_put(spdif_np); -- cgit v1.2.3 From f212c6d8c2b21c1e1d0158d38a7c37f4427f3848 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Thu, 21 Jan 2016 14:55:56 +0000 Subject: ASoC: mxs-saif: fix clk_prepare() without matching clk_unprepare() The clk_prepare() call in hw_params() has no matching clk_unprepare(), leaving the clk with an ever-increasing prepare count. Moreover, hw_params() can be called multiple times which would again leave us with a runaway prepare count. Fix this by moving the clk_prepare() call to the startup() function and adding a shutdown() function with a matching clk_unprepare() as these operations are already correctly bracketed by soc-core. Signed-off-by: Mans Rullgard Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index c866ade28ad0..a6c7b8d87cd2 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -381,9 +381,19 @@ static int mxs_saif_startup(struct snd_pcm_substream *substream, __raw_writel(BM_SAIF_CTRL_CLKGATE, saif->base + SAIF_CTRL + MXS_CLR_ADDR); + clk_prepare(saif->clk); + return 0; } +static void mxs_saif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + clk_unprepare(saif->clk); +} + /* * Should only be called when port is inactive. * although can be called multiple times by upper layers. @@ -424,8 +434,6 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, return ret; } - /* prepare clk in hw_param, enable in trigger */ - clk_prepare(saif->clk); if (saif != master_saif) { /* * Set an initial clock rate for the saif internal logic to work @@ -611,6 +619,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops mxs_saif_dai_ops = { .startup = mxs_saif_startup, + .shutdown = mxs_saif_shutdown, .trigger = mxs_saif_trigger, .prepare = mxs_saif_prepare, .hw_params = mxs_saif_hw_params, -- cgit v1.2.3 From 57480484f9f7631738ef28b952eca3c9081c4291 Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Tue, 26 Jan 2016 17:59:13 +0000 Subject: component: Detach components when deleting master struct component_master_add_with_match calls find_components which, if any components already exist, it attaches to the master struct. However, if we later encounter an error the master struct is deleted, leaving components with a dangling pointer to it. If the error was a temporary one, e.g. for probe deferral, then when the master device is re-probed, it will fail to find the required components as they appear to already be attached to a master. Fix this by nulling components pointers to the master struct when it is deleted. This code is factored out into a separate function so it can be shared with component_master_del. Signed-off-by: Jon Medhurst Signed-off-by: Russell King --- drivers/base/component.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index 05cd26c02449..2738039aae9e 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -285,6 +285,24 @@ void component_match_add_release(struct device *master, } EXPORT_SYMBOL(component_match_add_release); +static void free_master(struct master *master) +{ + struct component_match *match = master->match; + int i; + + list_del(&master->node); + + if (match) { + for (i = 0; i < match->num; i++) { + struct component *c = match->compare[i].component; + if (c) + c->master = NULL; + } + } + + kfree(master); +} + int component_master_add_with_match(struct device *dev, const struct component_master_ops *ops, struct component_match *match) @@ -311,11 +329,9 @@ int component_master_add_with_match(struct device *dev, ret = try_to_bring_up_master(master, NULL); - if (ret < 0) { - /* Delete off the list if we weren't successful */ - list_del(&master->node); - kfree(master); - } + if (ret < 0) + free_master(master); + mutex_unlock(&component_mutex); return ret < 0 ? ret : 0; @@ -326,25 +342,12 @@ void component_master_del(struct device *dev, const struct component_master_ops *ops) { struct master *master; - int i; mutex_lock(&component_mutex); master = __master_find(dev, ops); if (master) { - struct component_match *match = master->match; - take_down_master(master); - - list_del(&master->node); - - if (match) { - for (i = 0; i < match->num; i++) { - struct component *c = match->compare[i].component; - if (c) - c->master = NULL; - } - } - kfree(master); + free_master(master); } mutex_unlock(&component_mutex); } -- cgit v1.2.3 From ee43a1a0cd2a8f33cddfa1323a60b5cfcf865ba0 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 24 Jan 2016 00:36:40 +0200 Subject: ASoC: simple-card: don't fail if sysclk setting is not supported Commit e22579713ae1 ("ASoC: simple card: set cpu-dai sysclk with mclk-fs") added sysclk / SND_SOC_CLOCK_OUT setting, that makes asoc_simple_card_hw_params fail if the operation is not supported, although the intention clearly was to ignore ENOTSUPP. Fix it. The patch fixes audio playback on Kirkwood / OpenRD client, where the following errors are seen: asoc-simple-card sound: ASoC: machine hw_params failed: -524 alsa-lib: /alsa-lib-1.0.28/src/pcm/pcm_hw.c:327:(snd_pcm_hw_hw_params) SNDRV_PCM_IOCTL_HW_PARAMS failed (-524): Unknown error 524 Fixes: e22579713ae1 ("ASoC: simple card: set cpu-dai sysclk with mclk-fs") Signed-off-by: Aaro Koskinen Reviewed-by: Andrew Lunn Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 1ded8811598e..2389ab47e25f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -99,7 +99,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, if (ret && ret != -ENOTSUPP) goto err; } - + return 0; err: return ret; } -- cgit v1.2.3 From 46560388c476c8471fde7712c10f9fad8d0d1875 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Wed, 27 Jan 2016 16:52:24 -0600 Subject: PCI: iproc: Allow multiple devices except on PAXC Commit 943ebae781f5 ("PCI: iproc: Add PAXC interface support") only allowed device 0, which is a regression on BCMA-based platforms. All systems support only one device, a Root Port at 00:00.0, on the root bus. PAXC-based systems support only the Root Port (00:00.0) and a single device (with multiple functions) below it, e.g., 01:00.0, 01:00.1, etc. Non-PAXC systems support arbitrary devices below the Root Port. [bhelgaas: changelog, fold in removal of MAX_NUM_PAXC_PF check] Fixes: 943ebae781f5 ("PCI: iproc: Add PAXC interface support") Reported-by: Rafal Milecki Signed-off-by: Ray Jui Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-iproc.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 5816bceddb65..a576aeeb22da 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -64,7 +64,6 @@ #define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT) #define MAX_NUM_OB_WINDOWS 2 -#define MAX_NUM_PAXC_PF 4 #define IPROC_PCIE_REG_INVALID 0xffff @@ -170,20 +169,6 @@ static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie, writel(val, pcie->base + offset + (window * 8)); } -static inline bool iproc_pcie_device_is_valid(struct iproc_pcie *pcie, - unsigned int slot, - unsigned int fn) -{ - if (slot > 0) - return false; - - /* PAXC can only support limited number of functions */ - if (pcie->type == IPROC_PCIE_PAXC && fn >= MAX_NUM_PAXC_PF) - return false; - - return true; -} - /** * Note access to the configuration registers are protected at the higher layer * by 'pci_lock' in drivers/pci/access.c @@ -199,11 +184,11 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus, u32 val; u16 offset; - if (!iproc_pcie_device_is_valid(pcie, slot, fn)) - return NULL; - /* root complex access */ if (busno == 0) { + if (slot > 0 || fn > 0) + return NULL; + iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR, where & CFG_IND_ADDR_MASK); offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA); @@ -213,6 +198,14 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus, return (pcie->base + offset); } + /* + * PAXC is connected to an internally emulated EP within the SoC. It + * allows only one device. + */ + if (pcie->type == IPROC_PCIE_PAXC) + if (slot > 0) + return NULL; + /* EP device access */ val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | (slot << CFG_ADDR_DEV_NUM_SHIFT) | -- cgit v1.2.3 From a6ed4a18ba6a6f5a01e024b9d221d6439bf6ca4c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 27 Jan 2016 15:42:25 -0800 Subject: Input: xpad - remove unused function There are two definitions of xpad_identify_controller(), one is used when CONFIG_JOYSTICK_XPAD_LEDS is set, but the other one is empty and never used, and we get a gcc warning about it: drivers/input/joystick/xpad.c:1210:13: warning: 'xpad_identify_controller' defined but not used [-Wunused-function] This removes the second definition. Signed-off-by: Arnd Bergmann Fixes: cae705baa40b ("Input: xpad - re-send LED command on present event") Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 6727954ab74b..e8a84d12b7ff 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1207,7 +1207,6 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) #else static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } static void xpad_led_disconnect(struct usb_xpad *xpad) { } -static void xpad_identify_controller(struct usb_xpad *xpad) { } #endif static int xpad_start_input(struct usb_xpad *xpad) -- cgit v1.2.3 From af6e94634d0a77aa42a9fdee35abd00a95b4ca54 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 27 Jan 2016 15:43:32 -0800 Subject: Input: sirfsoc-onkey - allow modular build CONFIG_INPUT may itself be a loadable module, but the sirf power key driver is listed as 'bool', which makes it possible to select a broken configuration with the driver built-in but the subsystem not loaded. In this configuration, we get a link error: drivers/input/built-in.o: In function `sirfsoc_pwrc_isr': drivers/input/misc/sirfsoc-onkey.c:63: undefined reference to `input_event' drivers/input/built-in.o: In function `sirfsoc_pwrc_isr': include/linux/input.h:414: undefined reference to `input_event' drivers/input/built-in.o: In function `sirfsoc_pwrc_probe': drivers/input/misc/sirfsoc-onkey.c:132: undefined reference to `devm_input_allocate_device' drivers/input/misc/sirfsoc-onkey.c:139: undefined reference to `input_set_capability' drivers/input/misc/sirfsoc-onkey.c:161: undefined reference to `input_register_device' drivers/input/built-in.o: In function `sirfsoc_pwrc_report_event': drivers/input/misc/sirfsoc-onkey.c:48: undefined reference to `input_event' drivers/input/built-in.o: In function `sirfsoc_pwrc_report_event': include/linux/input.h:414: undefined reference to `input_event' drivers/input/built-in.o:(.debug_addr+0x24): undefined reference to `input_event' drivers/input/built-in.o:(.debug_addr+0xbc): undefined reference to `devm_input_allocate_device' drivers/input/built-in.o:(.debug_addr+0x104): undefined reference to `input_set_capability' drivers/input/built-in.o:(.debug_addr+0x128): undefined reference to `input_register_device' This marks the driver as 'tristate' so it becomes possible to have it in a loadable module, mainly to help with randconfig builds. We also have to add a missing semicolon here, which ended up not being needed in built-in mode because the following MODULE_DEVICE_TABLE is an empty macro followed by another semicolon then. Signed-off-by: Arnd Bergmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 2 +- drivers/input/misc/sirfsoc-onkey.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index d6d16fa78281..1f2337abcf2f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -733,7 +733,7 @@ config INPUT_XEN_KBDDEV_FRONTEND module will be called xen-kbdfront. config INPUT_SIRFSOC_ONKEY - bool "CSR SiRFSoC power on/off/suspend key support" + tristate "CSR SiRFSoC power on/off/suspend key support" depends on ARCH_SIRF && OF default y help diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 9d5b89befe6f..ed7237f19539 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -101,7 +101,7 @@ static void sirfsoc_pwrc_close(struct input_dev *input) static const struct of_device_id sirfsoc_pwrc_of_match[] = { { .compatible = "sirf,prima2-pwrc" }, {}, -} +}; MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match); static int sirfsoc_pwrc_probe(struct platform_device *pdev) -- cgit v1.2.3 From 497e1b3f6b6d3cf2206d15687eb7bfa0ab0e968d Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Wed, 27 Jan 2016 15:44:09 -0800 Subject: Input: cap11xx - add missing of_node_put for_each_child_of_node performs an of_node_get on each iteration, so to break out of the loop an of_node_put is required. Found using Coccinelle. Signed-off-by: Amitoj Kaur Chawla Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 378db10001df..4401be225d64 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -304,8 +304,10 @@ static int cap11xx_init_leds(struct device *dev, led->cdev.brightness = LED_OFF; error = of_property_read_u32(child, "reg", ®); - if (error != 0 || reg >= num_leds) + if (error != 0 || reg >= num_leds) { + of_node_put(child); return -EINVAL; + } led->reg = reg; led->priv = priv; @@ -313,8 +315,10 @@ static int cap11xx_init_leds(struct device *dev, INIT_WORK(&led->work, cap11xx_led_work); error = devm_led_classdev_register(dev, &led->cdev); - if (error) + if (error) { + of_node_put(child); return error; + } priv->num_leds++; led++; -- cgit v1.2.3 From 015bb5e134544492d840cee17d7442194cfb0fe4 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Sat, 16 Jan 2016 10:35:47 -0800 Subject: Input: serio - drop warnings in case of EPROBE_DEFER from serio_find_driver() Now serio_find_driver() will print warnings in case device_attach() returns -EPROBE_DEFER. Those warnings are obsolete, in genral, because: - DD core can report the same if required - since commit 013c074f8642 ("PM / sleep: prohibit devices probing during suspend/hibernation") the devices probing is prohibited during System suspend and deferred device will be carefully reprobed once Resume is finished. Hence, drop warnings in case of EPROBE_DEFER from serio_find_driver(). Signed-off-by: Grygorii Strashko Acked-by: Rafael J. Wysocki Signed-off-by: Dmitry Torokhov --- drivers/input/serio/serio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 8f828975ab10..1ca7f551e2da 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -134,7 +134,7 @@ static void serio_find_driver(struct serio *serio) int error; error = device_attach(&serio->dev); - if (error < 0) + if (error < 0 && error != -EPROBE_DEFER) dev_warn(&serio->dev, "device_attach() failed for %s (%s), error: %d\n", serio->phys, serio->name, error); -- cgit v1.2.3 From d4f1b06d685d11ebdaccf11c0db1cb3c78736862 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 16 Jan 2016 10:04:49 -0800 Subject: Input: vmmouse - fix absolute device registration We should set device's capabilities first, and then register it, otherwise various handlers already present in the kernel will not be able to connect to the device. Reported-by: Lauri Kasanen Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/vmmouse.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/vmmouse.c b/drivers/input/mouse/vmmouse.c index e272f06258ce..a3f0f5a47490 100644 --- a/drivers/input/mouse/vmmouse.c +++ b/drivers/input/mouse/vmmouse.c @@ -458,8 +458,6 @@ int vmmouse_init(struct psmouse *psmouse) priv->abs_dev = abs_dev; psmouse->private = priv; - input_set_capability(rel_dev, EV_REL, REL_WHEEL); - /* Set up and register absolute device */ snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); @@ -475,10 +473,6 @@ int vmmouse_init(struct psmouse *psmouse) abs_dev->id.version = psmouse->model; abs_dev->dev.parent = &psmouse->ps2dev.serio->dev; - error = input_register_device(priv->abs_dev); - if (error) - goto init_fail; - /* Set absolute device capabilities */ input_set_capability(abs_dev, EV_KEY, BTN_LEFT); input_set_capability(abs_dev, EV_KEY, BTN_RIGHT); @@ -488,6 +482,13 @@ int vmmouse_init(struct psmouse *psmouse) input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0); input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0); + error = input_register_device(priv->abs_dev); + if (error) + goto init_fail; + + /* Add wheel capability to the relative device */ + input_set_capability(rel_dev, EV_REL, REL_WHEEL); + psmouse->protocol_handler = vmmouse_process_byte; psmouse->disconnect = vmmouse_disconnect; psmouse->reconnect = vmmouse_reconnect; -- cgit v1.2.3 From d791a8c60e9cbd1c4fecbc5c86ff59c7ffe137e8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 27 Jan 2016 18:22:44 -0800 Subject: storvsc: Install the storvsc specific timeout handler for FC devices The default timeout routine used for FC transport is not suitable for FC devices managed by storvsc since FC devices managed by storvsc driver do not have an rport associated with them. Use the time out handler used for SCSI devices for FC devices as well. Signed-off-by: K. Y. Srinivasan Reviewed-by: Alex Ng Tested-by: Vivek Yadav Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 41c115c230d9..622f64a98a9e 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -42,6 +42,7 @@ #include #include #include +#include /* * All wire protocol details (storage protocol between the guest and the host) @@ -1770,6 +1771,11 @@ static int __init storvsc_drv_init(void) fc_transport_template = fc_attach_transport(&fc_transport_functions); if (!fc_transport_template) return -ENODEV; + + /* + * Install Hyper-V specific timeout handler. + */ + fc_transport_template->eh_timed_out = storvsc_eh_timed_out; #endif ret = vmbus_driver_register(&storvsc_drv); -- cgit v1.2.3 From 98441221708a092eb6d3d05142cf842a95aba152 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 27 Jan 2016 18:22:45 -0800 Subject: storvsc: Use the specified target ID in device lookup The current code assumes that there is only one target in device lookup. Fix this bug. This will alow us to correctly handle hot reomoval of LUNs. Signed-off-by: K. Y. Srinivasan Reviewed-by: Alex Ng Tested-by: Vivek Yadav Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 622f64a98a9e..132b16861954 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -478,19 +478,18 @@ struct hv_host_device { struct storvsc_scan_work { struct work_struct work; struct Scsi_Host *host; - uint lun; + u8 lun; + u8 tgt_id; }; static void storvsc_device_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; - uint lun; struct scsi_device *sdev; wrk = container_of(work, struct storvsc_scan_work, work); - lun = wrk->lun; - sdev = scsi_device_lookup(wrk->host, 0, 0, lun); + sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun); if (!sdev) goto done; scsi_rescan_device(&sdev->sdev_gendev); @@ -541,7 +540,7 @@ static void storvsc_remove_lun(struct work_struct *work) if (!scsi_host_get(wrk->host)) goto done; - sdev = scsi_device_lookup(wrk->host, 0, 0, wrk->lun); + sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun); if (sdev) { scsi_remove_device(sdev); @@ -941,6 +940,7 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, wrk->host = host; wrk->lun = vm_srb->lun; + wrk->tgt_id = vm_srb->target_id; INIT_WORK(&wrk->work, process_err_fn); schedule_work(&wrk->work); } -- cgit v1.2.3 From bd2f348db5033b88b8b81caf58e7bdabf0b6961d Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 6 Jan 2016 14:04:13 +0000 Subject: goldfish: refactor goldfish platform configs On new virtual devices, the goldfish virtual bus can be replaced with autoprobing infrastructure like Device Tree. Refactor the goldfish kernel configs to better accommodate this. Move the goldfish platform into a menuconfig in the style of the chrome platform, and separate the goldfish bus into its own config option. Signed-off-by: Greg Hackmann Signed-off-by: Jin Qian [Corrected a tristate to bool] Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/Kconfig | 3 +-- drivers/platform/goldfish/Kconfig | 18 ++++++++++++++++++ drivers/platform/goldfish/Makefile | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 0adccbf5c83f..c11db8bceea1 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -4,8 +4,7 @@ endif if MIPS source "drivers/platform/mips/Kconfig" endif -if GOLDFISH + source "drivers/platform/goldfish/Kconfig" -endif source "drivers/platform/chrome/Kconfig" diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig index 635ef25cc722..2be762743592 100644 --- a/drivers/platform/goldfish/Kconfig +++ b/drivers/platform/goldfish/Kconfig @@ -1,5 +1,23 @@ +menuconfig GOLDFISH + bool "Platform support for Goldfish virtual devices" + depends on X86_32 || X86_64 || ARM || ARM64 + ---help--- + Say Y here to get to see options for the Goldfish virtual platform. + This option alone does not add any kernel code. + + Unless you are building for the Android Goldfish emulator say N here. + +if GOLDFISH + +config GOLDFISH_BUS + bool "Goldfish platform bus" + ---help--- + This is a virtual bus to host Goldfish Android Virtual Devices. + config GOLDFISH_PIPE tristate "Goldfish virtual device for QEMU pipes" ---help--- This is a virtual device to drive the QEMU pipe interface used by the Goldfish Android Virtual Device. + +endif # GOLDFISH diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile index a0022395eee9..d3487125838c 100644 --- a/drivers/platform/goldfish/Makefile +++ b/drivers/platform/goldfish/Makefile @@ -1,5 +1,5 @@ # # Makefile for Goldfish platform specific drivers # -obj-$(CONFIG_GOLDFISH) += pdev_bus.o +obj-$(CONFIG_GOLDFISH_BUS) += pdev_bus.o obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o -- cgit v1.2.3 From 23c5ee21c46ba694681e2378e64a475b5c68f02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 6 Jan 2016 14:04:31 +0000 Subject: goldfish_pipe: don't be clever with #define offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It just makes it harder to figure out which commands are being used. Signed-off-by: Alex Bennée Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index e7a29e2750c6..0fb3a34f5991 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -90,12 +90,6 @@ #define CMD_WRITE_BUFFER 4 /* send a user buffer to the emulator */ #define CMD_WAKE_ON_WRITE 5 /* tell the emulator to wake us when writing is possible */ - -/* The following commands are related to read operations, they must be - * listed in the same order than the corresponding write ones, since we - * will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset - * in goldfish_pipe_read_write() below. - */ #define CMD_READ_BUFFER 6 /* receive a user buffer from the emulator */ #define CMD_WAKE_ON_READ 7 /* tell the emulator to wake us when reading * is possible */ @@ -272,8 +266,6 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, unsigned long irq_flags; struct goldfish_pipe *pipe = filp->private_data; struct goldfish_pipe_dev *dev = pipe->dev; - const int cmd_offset = is_write ? 0 - : (CMD_READ_BUFFER - CMD_WRITE_BUFFER); unsigned long address, address_end; int ret = 0; @@ -325,7 +317,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, /* Now, try to transfer the bytes in the current page */ spin_lock_irqsave(&dev->lock, irq_flags); - if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset, + if (access_with_param(dev, + is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, address, avail, pipe, &status)) { gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL, dev->base + PIPE_REG_CHANNEL_HIGH); @@ -333,7 +326,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, gf_write_ptr((void *)address, dev->base + PIPE_REG_ADDRESS, dev->base + PIPE_REG_ADDRESS_HIGH); - writel(CMD_WRITE_BUFFER + cmd_offset, + writel(is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, dev->base + PIPE_REG_COMMAND); status = readl(dev->base + PIPE_REG_STATUS); } @@ -370,7 +363,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, set_bit(wakeBit, &pipe->flags); /* Tell the emulator we're going to wait for a wake event */ - goldfish_cmd(pipe, CMD_WAKE_ON_WRITE + cmd_offset); + goldfish_cmd(pipe, + is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ); /* Unlock the pipe, then wait for the wake signal */ mutex_unlock(&pipe->lock); -- cgit v1.2.3 From 2f3be88237a301ab83b8ba4a0fdf0381bbb3f6ac Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Wed, 6 Jan 2016 14:05:15 +0000 Subject: goldfish_pipe: Pin pages to memory while copying and other cleanups The existing code had a troubling TODO statement concerning the fact that it just did a check if the page that the QEMU backend was going to read from / write to was there before the call to the QEMU backend and then relying on the fact that the page stayed around, even in a preemptible SMP kernel. Obviously the page could go away or be reassigned, and strange things may happen. Further, writes were not tracked, so any use of COW or KSM-like features would break completely. Probably that was never used by adbd (the only current active user of the pipe), but could prove much more dangerous for the GPU passthrough mechanism. Instead, use get_user_pages() as the comment suggested and cleanup the error path and add the set_page_dirt() call on a successful read operation. Also clarify the count used to return from successful read/write calls and use Linux style commentary in various places of the file. Note: The "just ignore error and return whatever we read so far" error handling is really quite horrific. I cannot change it without a more careful study of all user space ABIs reliance on this 'feature'. Signed-off-by: Christoffer Dall Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 129 +++++++++++++++++------------- 1 file changed, 72 insertions(+), 57 deletions(-) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 0fb3a34f5991..20a933726ac1 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -2,6 +2,7 @@ * Copyright (C) 2011 Google, Inc. * Copyright (C) 2012 Intel, Inc. * Copyright (C) 2013 Intel, Inc. + * Copyright (C) 2014 Linaro Limited * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -57,6 +58,7 @@ #include #include #include +#include /* * IMPORTANT: The following constants must match the ones used and defined @@ -257,17 +259,14 @@ static int access_with_param(struct goldfish_pipe_dev *dev, const int cmd, return 0; } -/* This function is used for both reading from and writing to a given - * pipe. - */ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, - size_t bufflen, int is_write) + size_t bufflen, int is_write) { unsigned long irq_flags; struct goldfish_pipe *pipe = filp->private_data; struct goldfish_pipe_dev *dev = pipe->dev; unsigned long address, address_end; - int ret = 0; + int count = 0, ret = -EINVAL; /* If the emulator already closed the pipe, no need to go further */ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) @@ -290,30 +289,23 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, address_end = address + bufflen; while (address < address_end) { - unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE; - unsigned long next = page_end < address_end ? page_end - : address_end; - unsigned long avail = next - address; + unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE; + unsigned long next = page_end < address_end ? page_end + : address_end; + unsigned long avail = next - address; int status, wakeBit; - - /* Ensure that the corresponding page is properly mapped */ - /* FIXME: this isn't safe or sufficient - use get_user_pages */ - if (is_write) { - char c; - /* Ensure that the page is mapped and readable */ - if (__get_user(c, (char __user *)address)) { - if (!ret) - ret = -EFAULT; - break; - } - } else { - /* Ensure that the page is mapped and writable */ - if (__put_user(0, (char __user *)address)) { - if (!ret) - ret = -EFAULT; - break; - } - } + struct page *page; + + /* + * We grab the pages on a page-by-page basis in case user + * space gives us a potentially huge buffer but the read only + * returns a small amount, then there's no need to pin that + * much memory to the process. + */ + ret = get_user_pages(current, current->mm, address, 1, + !is_write, 0, &page, NULL); + if (ret < 0) + return ret; /* Now, try to transfer the bytes in the current page */ spin_lock_irqsave(&dev->lock, irq_flags); @@ -332,33 +324,48 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, } spin_unlock_irqrestore(&dev->lock, irq_flags); + if (status > 0 && !is_write) + set_page_dirty(page); + put_page(page); + if (status > 0) { /* Correct transfer */ - ret += status; + count += status; address += status; continue; - } - - if (status == 0) /* EOF */ + } else if (status == 0) { /* EOF */ + ret = 0; break; - - /* An error occured. If we already transfered stuff, just - * return with its count. We expect the next call to return - * an error code */ - if (ret > 0) + } else if (status < 0 && count > 0) { + /* + * An error occurred and we already transferred + * something on one of the previous pages. + * Just return what we already copied and log this + * err. + * + * Note: This seems like an incorrect approach but + * cannot change it until we check if any user space + * ABI relies on this behavior. + */ + pr_info_ratelimited("android_pipe: backend returned error %d on %s\n", + status, is_write ? "write" : "read"); + ret = 0; break; + } - /* If the error is not PIPE_ERROR_AGAIN, or if we are not in - * non-blocking mode, just return the error code. - */ + /* + * If the error is not PIPE_ERROR_AGAIN, or if we are not in + * non-blocking mode, just return the error code. + */ if (status != PIPE_ERROR_AGAIN || (filp->f_flags & O_NONBLOCK) != 0) { ret = goldfish_pipe_error_convert(status); break; } - /* We will have to wait until more data/space is available. - * First, mark the pipe as waiting for a specific wake signal. - */ + /* + * The backend blocked the read/write, wait until the backend + * tells us it's ready to process more data. + */ wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; set_bit(wakeBit, &pipe->flags); @@ -372,22 +379,29 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, while (test_bit(wakeBit, &pipe->flags)) { if (wait_event_interruptible( pipe->wake_queue, - !test_bit(wakeBit, &pipe->flags))) - return -ERESTARTSYS; + !test_bit(wakeBit, &pipe->flags))) { + ret = -ERESTARTSYS; + break; + } - if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) - return -EIO; + if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) { + ret = -EIO; + break; + } } /* Try to re-acquire the lock */ - if (mutex_lock_interruptible(&pipe->lock)) - return -ERESTARTSYS; - - /* Try the transfer again */ - continue; + if (mutex_lock_interruptible(&pipe->lock)) { + ret = -ERESTARTSYS; + break; + } } mutex_unlock(&pipe->lock); - return ret; + + if (ret < 0) + return ret; + else + return count; } static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer, @@ -440,10 +454,11 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) unsigned long irq_flags; int count = 0; - /* We're going to read from the emulator a list of (channel,flags) - * pairs corresponding to the wake events that occured on each - * blocked pipe (i.e. channel). - */ + /* + * We're going to read from the emulator a list of (channel,flags) + * pairs corresponding to the wake events that occurred on each + * blocked pipe (i.e. channel). + */ spin_lock_irqsave(&dev->lock, irq_flags); for (;;) { /* First read the channel, 0 means the end of the list */ -- cgit v1.2.3 From 91a18a414185a2b937e2cc8bf0cc32de1863d11a Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 6 Jan 2016 14:05:36 +0000 Subject: platform: goldfish: pipe: add devicetree bindings Add bindings so we don't need to rely on goldfish virtual bus for probing any more, which means we don't need ARM and MIPS goldfish board code for instantiating the bus. In the long term we would like to move towards replacing the Android pipe with virtio-vsock that is currently under development. Signed-off-by: Greg Hackmann Signed-off-by: Jin Qian Acked-by: Rob Herring Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/goldfish/pipe.txt | 17 +++++++++++++++++ drivers/platform/goldfish/goldfish_pipe.c | 10 +++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/goldfish/pipe.txt diff --git a/Documentation/devicetree/bindings/goldfish/pipe.txt b/Documentation/devicetree/bindings/goldfish/pipe.txt new file mode 100644 index 000000000000..e417a31a1ee3 --- /dev/null +++ b/Documentation/devicetree/bindings/goldfish/pipe.txt @@ -0,0 +1,17 @@ +Android Goldfish QEMU Pipe + +Andorid pipe virtual device generated by android emulator. + +Required properties: + +- compatible : should contain "google,android-pipe" to match emulator +- reg : +- interrupts : + +Example: + + android_pipe@a010000 { + compatible = "google,android-pipe"; + reg = ; + interrupts = <0x12>; + }; diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 20a933726ac1..0b187ff7a35b 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -624,11 +624,19 @@ static int goldfish_pipe_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_pipe_of_match[] = { + { .compatible = "google,android-pipe", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match); + static struct platform_driver goldfish_pipe = { .probe = goldfish_pipe_probe, .remove = goldfish_pipe_remove, .driver = { - .name = "goldfish_pipe" + .name = "goldfish_pipe", + .owner = THIS_MODULE, + .of_match_table = goldfish_pipe_of_match, } }; -- cgit v1.2.3 From 25dd0f407307c54de5025250ca1dfbd4bdbb2fba Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 6 Jan 2016 14:05:55 +0000 Subject: platform: goldfish: pipe: don't log when dropping PIPE_ERROR_AGAIN On PIPE_ERROR_AGAIN, just stopping in the middle of a transfer and returning the number of bytes actually handled is the right behavior. Other errors should be returned on the next read() or write() call. Continue logging those until we confirm nothing actually relies on the existing (wrong) behavior of dropping errors on the floor. Signed-off-by: Greg Hackmann Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 0b187ff7a35b..7a56be9c9432 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -346,7 +346,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, * cannot change it until we check if any user space * ABI relies on this behavior. */ - pr_info_ratelimited("android_pipe: backend returned error %d on %s\n", + if (status != PIPE_ERROR_AGAIN) + pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n", status, is_write ? "write" : "read"); ret = 0; break; -- cgit v1.2.3 From 2e5fc89ac5a0b1460316f19b21c38284949daf38 Mon Sep 17 00:00:00 2001 From: Miodrag Dinic Date: Wed, 6 Jan 2016 14:06:08 +0000 Subject: Enable platform support for Goldfish virtual devices Enable CONFIG_GOLDFISH for MIPS platforms. Signed-off-by: Miodrag Dinic Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig index 2be762743592..50331e3e54f3 100644 --- a/drivers/platform/goldfish/Kconfig +++ b/drivers/platform/goldfish/Kconfig @@ -1,6 +1,6 @@ menuconfig GOLDFISH bool "Platform support for Goldfish virtual devices" - depends on X86_32 || X86_64 || ARM || ARM64 + depends on X86_32 || X86_64 || ARM || ARM64 || MIPS ---help--- Say Y here to get to see options for the Goldfish virtual platform. This option alone does not add any kernel code. -- cgit v1.2.3 From 4f42071c943977e91e7fda8230e4f85bc3ba117a Mon Sep 17 00:00:00 2001 From: Yu Ning Date: Wed, 6 Jan 2016 14:06:40 +0000 Subject: goldfish_pipe: Pass physical addresses to the device if supported For reading and writing guest user space buffers, currently the kernel sends the guest virtual address of the buffer to the pipe device. This virtual address has to be first converted to a guest physical address. Doing this translation on the QEMU side is inefficient and requires additional handling when KVM is enabled, whose implementation would either incur intrusive changes to QEMU's KVM support code or suffer from poor performance, see commit 08c7228c50f8 ("x86-kvm: only sync SREGS when doing address translation") of $AOSP/external/qemu for details, and thus should be avoided if possible. There is a TODO comment in hw/misc/android_pipe.c in the new Android emulator source tree ($AOSP/external/qemu-android) which requests that the translation be done on the kernel side and that physical addresses be passed to the device instead of virtual ones. Once the QEMU-side implementation is done, the kernel will need to support both the new paddr-based pipe device and the old vaddr-based one (which will continue to be used by the classic emulator). This patch achieves that by leveraging the device version register available in the new device. See https://android-review.googlesource.com/128280 for the QEMU-side patch. In addition, use the mmap semaphore (in read mode) to safeguard the call to get_user_pages(). Signed-off-by: Yu Ning Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 7a56be9c9432..c214434e8811 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -77,6 +77,7 @@ #define PIPE_REG_PARAMS_ADDR_LOW 0x18 /* read/write: batch data address */ #define PIPE_REG_PARAMS_ADDR_HIGH 0x1c /* read/write: batch data address */ #define PIPE_REG_ACCESS_PARAMS 0x20 /* write: batch access */ +#define PIPE_REG_VERSION 0x24 /* read: device version */ /* list of commands for PIPE_REG_COMMAND */ #define CMD_OPEN 1 /* open new channel */ @@ -126,6 +127,7 @@ struct goldfish_pipe_dev { unsigned char __iomem *base; struct access_params *aps; int irq; + u32 version; }; static struct goldfish_pipe_dev pipe_dev[1]; @@ -296,26 +298,43 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, int status, wakeBit; struct page *page; + /* Either vaddr or paddr depending on the device version */ + unsigned long xaddr; + /* * We grab the pages on a page-by-page basis in case user * space gives us a potentially huge buffer but the read only * returns a small amount, then there's no need to pin that * much memory to the process. */ + down_read(¤t->mm->mmap_sem); ret = get_user_pages(current, current->mm, address, 1, !is_write, 0, &page, NULL); + up_read(¤t->mm->mmap_sem); if (ret < 0) return ret; + if (dev->version) { + /* Device version 1 or newer (qemu-android) expects the + * physical address. + */ + xaddr = page_to_phys(page) | (address & ~PAGE_MASK); + } else { + /* Device version 0 (classic emulator) expects the + * virtual address. + */ + xaddr = address; + } + /* Now, try to transfer the bytes in the current page */ spin_lock_irqsave(&dev->lock, irq_flags); if (access_with_param(dev, is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, - address, avail, pipe, &status)) { + xaddr, avail, pipe, &status)) { gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL, dev->base + PIPE_REG_CHANNEL_HIGH); writel(avail, dev->base + PIPE_REG_SIZE); - gf_write_ptr((void *)address, + gf_write_ptr((void *)xaddr, dev->base + PIPE_REG_ADDRESS, dev->base + PIPE_REG_ADDRESS_HIGH); writel(is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, @@ -610,6 +629,12 @@ static int goldfish_pipe_probe(struct platform_device *pdev) goto error; } setup_access_params_addr(pdev, dev); + + /* Although the pipe device in the classic Android emulator does not + * recognize the 'version' register, it won't treat this as an error + * either and will simply return 0, which is fine. + */ + dev->version = readl(dev->base + PIPE_REG_VERSION); return 0; error: -- cgit v1.2.3 From d62f324b0ac80c3923ebbf897735c7c24ba887b8 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 6 Jan 2016 14:06:55 +0000 Subject: goldfish: Enable ACPI-based enumeration for android pipe Add ACPI binding to the android pipe driver Signed-off-by: Jason Hu Signed-off-by: Jin Qian Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index c214434e8811..e3fab9a1d9f7 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -59,6 +59,7 @@ #include #include #include +#include /* * IMPORTANT: The following constants must match the ones used and defined @@ -650,6 +651,12 @@ static int goldfish_pipe_remove(struct platform_device *pdev) return 0; } +static const struct acpi_device_id goldfish_pipe_acpi_match[] = { + { "GFSH0003", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match); + static const struct of_device_id goldfish_pipe_of_match[] = { { .compatible = "google,android-pipe", }, {}, @@ -663,6 +670,7 @@ static struct platform_driver goldfish_pipe = { .name = "goldfish_pipe", .owner = THIS_MODULE, .of_match_table = goldfish_pipe_of_match, + .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match), } }; -- cgit v1.2.3 From 3e2fbc7feec4426feba609b94aa89a147e02c67e Mon Sep 17 00:00:00 2001 From: Shraddha Barke Date: Thu, 21 Jan 2016 02:38:53 +0530 Subject: Staging: goldfish: goldfish_nand: Add DMA Support using dmam_alloc_coherent Function nand_setup_cmd_params has 2 goals- -Initialize the cmd_params field so that it can be used to send and read commands from the device. -Get a bus address for the allocated memory to transfer to the device. Replace the combination of devm_kzalloc and _pa() with dmam_alloc_coherent. Coherent mapping guarantees that the device and CPU are in sync. Signed-off-by: Shraddha Barke Signed-off-by: Greg Kroah-Hartman --- drivers/staging/goldfish/goldfish_nand.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c index 623353db5a08..f223b3a5a428 100644 --- a/drivers/staging/goldfish/goldfish_nand.c +++ b/drivers/staging/goldfish/goldfish_nand.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "goldfish_nand_reg.h" @@ -284,17 +285,18 @@ invalid_arg: static int nand_setup_cmd_params(struct platform_device *pdev, struct goldfish_nand *nand) { - u64 paddr; + dma_addr_t dma_handle; unsigned char __iomem *base = nand->base; - nand->cmd_params = devm_kzalloc(&pdev->dev, - sizeof(struct cmd_params), GFP_KERNEL); - if (!nand->cmd_params) - return -1; - - paddr = __pa(nand->cmd_params); - writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH); - writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW); + nand->cmd_params = dmam_alloc_coherent(&pdev->dev, + sizeof(struct cmd_params), + &dma_handle, GFP_KERNEL); + if (!nand->cmd_params) { + dev_err(&pdev->dev, "allocate buffer failed\n"); + return -ENOMEM; + } + writel((u32)((u64)dma_handle >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH); + writel((u32)dma_handle, base + NAND_CMD_PARAMS_ADDR_LOW); return 0; } -- cgit v1.2.3 From c84f6b8bce8723d8232547d6a3a3ae0725511adb Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 29 Jan 2016 12:08:24 +0200 Subject: ARM: omap2plus_defconfig: update display configs omapfb and omapdrm were recently made independent of each other, and this required Kconfig option changes. This patch changes the omap2plus_defconfig to enable display similarly as before: omapfb and panel & encoder drivers as modules. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren --- arch/arm/configs/omap2plus_defconfig | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index c5e1943e5427..b9581f1f0536 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -290,24 +290,23 @@ CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y -CONFIG_OMAP2_DSS=m -CONFIG_OMAP5_DSS_HDMI=y -CONFIG_OMAP2_DSS_SDI=y -CONFIG_OMAP2_DSS_DSI=y +CONFIG_FB_OMAP5_DSS_HDMI=y +CONFIG_FB_OMAP2_DSS_SDI=y +CONFIG_FB_OMAP2_DSS_DSI=y CONFIG_FB_OMAP2=m -CONFIG_DISPLAY_ENCODER_TFP410=m -CONFIG_DISPLAY_ENCODER_TPD12S015=m -CONFIG_DISPLAY_CONNECTOR_DVI=m -CONFIG_DISPLAY_CONNECTOR_HDMI=m -CONFIG_DISPLAY_CONNECTOR_ANALOG_TV=m -CONFIG_DISPLAY_PANEL_DPI=m -CONFIG_DISPLAY_PANEL_DSI_CM=m -CONFIG_DISPLAY_PANEL_SONY_ACX565AKM=m -CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02=m -CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01=m -CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1=m -CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1=m -CONFIG_DISPLAY_PANEL_NEC_NL8048HL11=m +CONFIG_FB_OMAP2_ENCODER_TFP410=m +CONFIG_FB_OMAP2_ENCODER_TPD12S015=m +CONFIG_FB_OMAP2_CONNECTOR_DVI=m +CONFIG_FB_OMAP2_CONNECTOR_HDMI=m +CONFIG_FB_OMAP2_CONNECTOR_ANALOG_TV=m +CONFIG_FB_OMAP2_PANEL_DPI=m +CONFIG_FB_OMAP2_PANEL_DSI_CM=m +CONFIG_FB_OMAP2_PANEL_SONY_ACX565AKM=m +CONFIG_FB_OMAP2_PANEL_LGPHILIPS_LB035Q02=m +CONFIG_FB_OMAP2_PANEL_SHARP_LS037V7DW01=m +CONFIG_FB_OMAP2_PANEL_TPO_TD028TTEC1=m +CONFIG_FB_OMAP2_PANEL_TPO_TD043MTEA1=m +CONFIG_FB_OMAP2_PANEL_NEC_NL8048HL11=m CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_PLATFORM=y -- cgit v1.2.3 From 9a2ddda572a002633a64b1ae5f4bc49cfcbf495f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 27 Jan 2016 13:01:52 -0800 Subject: Documentation: cgroup: Fix 'cgroup-legacy' -> 'cgroup-v1' This should have happened in 6255c46f (cgroup: rename cgroup documentations, 2016-01-11). Signed-off-by: W. Trevor King Signed-off-by: Tejun Heo --- Documentation/cgroup-v2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/cgroup-v2.txt b/Documentation/cgroup-v2.txt index 65b3eac8856c..9ae148ab1255 100644 --- a/Documentation/cgroup-v2.txt +++ b/Documentation/cgroup-v2.txt @@ -7,7 +7,7 @@ This is the authoritative documentation on the design, interface and conventions of cgroup v2. It describes all userland-visible aspects of cgroup including core and specific controller behaviors. All future changes must be reflected in this document. Documentation for -v1 is available under Documentation/cgroup-legacy/. +v1 is available under Documentation/cgroup-v1/. CONTENTS -- cgit v1.2.3 From d584f0fb041d86b9605fae1f0ed9e268f217daa9 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 22 Jan 2016 14:27:50 +0530 Subject: ARCv2: clocksource: Rename GRTC -> GFRC ... ... it is now called Global Free Running Counter Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 2 +- arch/arc/configs/vdk_hs38_smp_defconfig | 2 +- arch/arc/include/asm/mcip.h | 4 ++-- arch/arc/kernel/mcip.c | 10 +++++----- arch/arc/kernel/time.c | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 6312f607932f..bb15e8062b1f 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -413,7 +413,7 @@ config ARC_HAS_RTC default n depends on !SMP -config ARC_HAS_GRTC +config ARC_HAS_GFRC bool "SMP synchronized 64-bit cycle counter" default y depends on SMP diff --git a/arch/arc/configs/vdk_hs38_smp_defconfig b/arch/arc/configs/vdk_hs38_smp_defconfig index f36c047b33ca..735985974a31 100644 --- a/arch/arc/configs/vdk_hs38_smp_defconfig +++ b/arch/arc/configs/vdk_hs38_smp_defconfig @@ -16,7 +16,7 @@ CONFIG_ARC_PLAT_AXS10X=y CONFIG_AXS103=y CONFIG_ISA_ARCV2=y CONFIG_SMP=y -# CONFIG_ARC_HAS_GRTC is not set +# CONFIG_ARC_HAS_GFRC is not set CONFIG_ARC_UBOOT_SUPPORT=y CONFIG_ARC_BUILTIN_DTB_NAME="vdk_hs38_smp" CONFIG_PREEMPT=y diff --git a/arch/arc/include/asm/mcip.h b/arch/arc/include/asm/mcip.h index 46f4e5351b2a..847e3bbe387f 100644 --- a/arch/arc/include/asm/mcip.h +++ b/arch/arc/include/asm/mcip.h @@ -39,8 +39,8 @@ struct mcip_cmd { #define CMD_DEBUG_SET_MASK 0x34 #define CMD_DEBUG_SET_SELECT 0x36 -#define CMD_GRTC_READ_LO 0x42 -#define CMD_GRTC_READ_HI 0x43 +#define CMD_GFRC_READ_LO 0x42 +#define CMD_GFRC_READ_HI 0x43 #define CMD_IDU_ENABLE 0x71 #define CMD_IDU_DISABLE 0x72 diff --git a/arch/arc/kernel/mcip.c b/arch/arc/kernel/mcip.c index bd237acdf4f2..bc771f58fefb 100644 --- a/arch/arc/kernel/mcip.c +++ b/arch/arc/kernel/mcip.c @@ -96,13 +96,13 @@ static void mcip_probe_n_setup(void) #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int pad3:8, idu:1, llm:1, num_cores:6, - iocoh:1, grtc:1, dbg:1, pad2:1, + iocoh:1, gfrc:1, dbg:1, pad2:1, msg:1, sem:1, ipi:1, pad:1, ver:8; #else unsigned int ver:8, pad:1, ipi:1, sem:1, msg:1, - pad2:1, dbg:1, grtc:1, iocoh:1, + pad2:1, dbg:1, gfrc:1, iocoh:1, num_cores:6, llm:1, idu:1, pad3:8; #endif @@ -116,7 +116,7 @@ static void mcip_probe_n_setup(void) IS_AVAIL1(mp.ipi, "IPI "), IS_AVAIL1(mp.idu, "IDU "), IS_AVAIL1(mp.dbg, "DEBUG "), - IS_AVAIL1(mp.grtc, "GRTC")); + IS_AVAIL1(mp.gfrc, "GFRC")); idu_detected = mp.idu; @@ -125,8 +125,8 @@ static void mcip_probe_n_setup(void) __mcip_cmd_data(CMD_DEBUG_SET_MASK, 0xf, 0xf); } - if (IS_ENABLED(CONFIG_ARC_HAS_GRTC) && !mp.grtc) - panic("kernel trying to use non-existent GRTC\n"); + if (IS_ENABLED(CONFIG_ARC_HAS_GFRC) && !mp.gfrc) + panic("kernel trying to use non-existent GFRC\n"); } struct plat_smp_ops plat_smp_ops = { diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index dfad287f1db1..156d9833ff84 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -62,7 +62,7 @@ /********** Clock Source Device *********/ -#ifdef CONFIG_ARC_HAS_GRTC +#ifdef CONFIG_ARC_HAS_GFRC static int arc_counter_setup(void) { @@ -83,10 +83,10 @@ static cycle_t arc_counter_read(struct clocksource *cs) local_irq_save(flags); - __mcip_cmd(CMD_GRTC_READ_LO, 0); + __mcip_cmd(CMD_GFRC_READ_LO, 0); stamp.l = read_aux_reg(ARC_REG_MCIP_READBACK); - __mcip_cmd(CMD_GRTC_READ_HI, 0); + __mcip_cmd(CMD_GFRC_READ_HI, 0); stamp.h = read_aux_reg(ARC_REG_MCIP_READBACK); local_irq_restore(flags); @@ -95,7 +95,7 @@ static cycle_t arc_counter_read(struct clocksource *cs) } static struct clocksource arc_counter = { - .name = "ARConnect GRTC", + .name = "ARConnect GFRC", .rating = 400, .read = arc_counter_read, .mask = CLOCKSOURCE_MASK(64), -- cgit v1.2.3 From b89bd1f4fbaecaa842588a034f8a44f4a84597e4 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 22 Jan 2016 15:20:18 +0530 Subject: ARC: shrink cpuinfo by not saving full timer BCR Signed-off-by: Vineet Gupta --- arch/arc/include/asm/arcregs.h | 3 +-- arch/arc/kernel/setup.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 7fac7d85ed6a..fdc5be5b1029 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -349,14 +349,13 @@ struct cpuinfo_arc { struct cpuinfo_arc_bpu bpu; struct bcr_identity core; struct bcr_isa isa; - struct bcr_timer timers; unsigned int vec_base; struct cpuinfo_arc_ccm iccm, dccm; struct { unsigned int swap:1, norm:1, minmax:1, barrel:1, crc:1, pad1:3, fpu_sp:1, fpu_dp:1, pad2:6, debug:1, ap:1, smart:1, rtt:1, pad3:4, - pad4:8; + timer0:1, timer1:1, rtc:1, gfrc:1, pad4:4; } extn; struct bcr_mpy extn_mpy; struct bcr_extn_xymem extn_xymem; diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index e1b87444ea9a..7f0a3cb300a8 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -45,6 +45,7 @@ struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; static void read_arc_build_cfg_regs(void) { struct bcr_perip uncached_space; + struct bcr_timer timer; struct bcr_generic bcr; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; unsigned long perip_space; @@ -53,7 +54,11 @@ static void read_arc_build_cfg_regs(void) READ_BCR(AUX_IDENTITY, cpu->core); READ_BCR(ARC_REG_ISA_CFG_BCR, cpu->isa); - READ_BCR(ARC_REG_TIMERS_BCR, cpu->timers); + READ_BCR(ARC_REG_TIMERS_BCR, timer); + cpu->extn.timer0 = timer.t0; + cpu->extn.timer1 = timer.t1; + cpu->extn.rtc = timer.rtc; + cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); @@ -208,9 +213,9 @@ static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len) (unsigned int)(arc_get_core_freq() / 10000) % 100); n += scnprintf(buf + n, len - n, "Timers\t\t: %s%s%s%s\nISA Extn\t: ", - IS_AVAIL1(cpu->timers.t0, "Timer0 "), - IS_AVAIL1(cpu->timers.t1, "Timer1 "), - IS_AVAIL2(cpu->timers.rtc, "64-bit RTC ", + IS_AVAIL1(cpu->extn.timer0, "Timer0 "), + IS_AVAIL1(cpu->extn.timer1, "Timer1 "), + IS_AVAIL2(cpu->extn.rtc, "Local-64-bit-Ctr ", CONFIG_ARC_HAS_RTC)); n += i = scnprintf(buf + n, len - n, "%s%s%s%s%s", @@ -293,13 +298,13 @@ static void arc_chk_core_config(void) struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; int fpu_enabled; - if (!cpu->timers.t0) + if (!cpu->extn.timer0) panic("Timer0 is not present!\n"); - if (!cpu->timers.t1) + if (!cpu->extn.timer1) panic("Timer1 is not present!\n"); - if (IS_ENABLED(CONFIG_ARC_HAS_RTC) && !cpu->timers.rtc) + if (IS_ENABLED(CONFIG_ARC_HAS_RTC) && !cpu->extn.rtc) panic("RTC is not present\n"); #ifdef CONFIG_ARC_HAS_DCCM -- cgit v1.2.3 From 4d0cb15fccd1db9dac0c964b2ccf10874e69f5b8 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 29 Jan 2016 16:47:44 +0530 Subject: ARCv2: Check for LL-SC livelock only if LLSC is enabled Signed-off-by: Vineet Gupta --- arch/arc/kernel/setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 7f0a3cb300a8..a7edceba5f84 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -339,6 +339,7 @@ static void arc_chk_core_config(void) panic("FPU non-existent, disable CONFIG_ARC_FPU_SAVE_RESTORE\n"); if (is_isa_arcv2() && IS_ENABLED(CONFIG_SMP) && cpu->isa.atomic && + IS_ENABLED(CONFIG_ARC_HAS_LLSC) && !IS_ENABLED(CONFIG_ARC_STAR_9000923308)) panic("llock/scond livelock workaround missing\n"); } -- cgit v1.2.3 From 87df1b2a2103b9019433da99be43a472c16647ab Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 26 Jan 2016 15:48:04 +0100 Subject: ocfb: fix tgdel and tvdel timing parameters According to the ocfb documentation: Fix tgdel HW param should be left margin, not right. Fix tvdel HW param should upper margin, not lower. This seems to fix lock issues on certain monitors (tested on a slightly customized IP, but the FPGA guy said that it should be the same wrt this changes). Signed-off-by: Andrea Merello Acked-by: Stefan Kristiansson Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/ocfb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/ocfb.c b/drivers/video/fbdev/ocfb.c index c9293aea8ec3..a970edc2a6f8 100644 --- a/drivers/video/fbdev/ocfb.c +++ b/drivers/video/fbdev/ocfb.c @@ -123,11 +123,11 @@ static int ocfb_setupfb(struct ocfb_dev *fbdev) /* Horizontal timings */ ocfb_writereg(fbdev, OCFB_HTIM, (var->hsync_len - 1) << 24 | - (var->right_margin - 1) << 16 | (var->xres - 1)); + (var->left_margin - 1) << 16 | (var->xres - 1)); /* Vertical timings */ ocfb_writereg(fbdev, OCFB_VTIM, (var->vsync_len - 1) << 24 | - (var->lower_margin - 1) << 16 | (var->yres - 1)); + (var->upper_margin - 1) << 16 | (var->yres - 1)); /* Total length of frame */ hlen = var->left_margin + var->right_margin + var->hsync_len + -- cgit v1.2.3 From b54729b6cea7d1f46b1ed70cb7065c6bdefaa780 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 16:04:03 +0100 Subject: fbdev: s6e8ax0: avoid unused function warnings The s6e8ax0 suspend/resume functions are hidden inside of an #ifdef when CONFIG_PM is set to avoid unused function warnings, but they call some other functions that nothing else calls, and we get warnings about those: drivers/video/fbdev/exynos/s6e8ax0.c:449:13: error: 's6e8ax0_sleep_in' defined but not used [-Werror=unused-function] drivers/video/fbdev/exynos/s6e8ax0.c:485:13: error: 's6e8ax0_display_off' defined but not used [-Werror=unused-function] This marks the PM functions as __maybe_unused so the compiler can silently drop them when they are not referenced. Signed-off-by: Arnd Bergmann Reviewed-by: Krzysztof Kozlowski Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/exynos/s6e8ax0.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/video/fbdev/exynos/s6e8ax0.c b/drivers/video/fbdev/exynos/s6e8ax0.c index 95873f26e39c..de2f3e793786 100644 --- a/drivers/video/fbdev/exynos/s6e8ax0.c +++ b/drivers/video/fbdev/exynos/s6e8ax0.c @@ -829,8 +829,7 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) return 0; } -#ifdef CONFIG_PM -static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) +static int __maybe_unused s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) { struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); @@ -843,7 +842,7 @@ static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) return 0; } -static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) +static int __maybe_unused s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) { struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); @@ -855,10 +854,6 @@ static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) return 0; } -#else -#define s6e8ax0_suspend NULL -#define s6e8ax0_resume NULL -#endif static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { .name = "s6e8ax0", @@ -867,8 +862,8 @@ static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { .power_on = s6e8ax0_power_on, .set_sequence = s6e8ax0_set_sequence, .probe = s6e8ax0_probe, - .suspend = s6e8ax0_suspend, - .resume = s6e8ax0_resume, + .suspend = IS_ENABLED(CONFIG_PM) ? s6e8ax0_suspend : NULL, + .resume = IS_ENABLED(CONFIG_PM) ? s6e8ax0_resume : NULL, }; static int s6e8ax0_init(void) -- cgit v1.2.3 From ef88ee4e2c98c11dab56b2845ade9270acb8e68b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 16:05:18 +0100 Subject: fbdev: da8xx-fb: remove incorrect type cast The probe function correct passes a dma_addr_t pointer into dma_alloc_coherent(), but has a cast to resource_size_t, which might be different from dma_addr_t: drivers/video/fbdev/da8xx-fb.c: In function 'fb_probe': drivers/video/fbdev/da8xx-fb.c:1431:10: error: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type [-Werror=incompatible-pointer-types] This removes the cast, which avoids the warning. Signed-off-by: Arnd Bergmann Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/da8xx-fb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index 0081725c6b5b..6b2a06d09f2b 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -152,7 +152,7 @@ static void lcdc_write(unsigned int val, unsigned int addr) struct da8xx_fb_par { struct device *dev; - resource_size_t p_palette_base; + dma_addr_t p_palette_base; unsigned char *v_palette_base; dma_addr_t vram_phys; unsigned long vram_size; @@ -1428,7 +1428,7 @@ static int fb_probe(struct platform_device *device) par->vram_virt = dma_alloc_coherent(NULL, par->vram_size, - (resource_size_t *) &par->vram_phys, + &par->vram_phys, GFP_KERNEL | GFP_DMA); if (!par->vram_virt) { dev_err(&device->dev, @@ -1448,7 +1448,7 @@ static int fb_probe(struct platform_device *device) /* allocate palette buffer */ par->v_palette_base = dma_zalloc_coherent(NULL, PALETTE_SIZE, - (resource_size_t *)&par->p_palette_base, + &par->p_palette_base, GFP_KERNEL | GFP_DMA); if (!par->v_palette_base) { dev_err(&device->dev, -- cgit v1.2.3 From c3a2da26e6216033456c1d5375cd1d2629d1fd09 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 16:03:01 +0100 Subject: fbdev: mmp: print IRQ resource using %pR format string resource_size_t cannot be printed using the %x format string when we it is defined as u64: drivers/video/fbdev/mmp/hw/mmp_ctrl.c: In function 'mmphw_probe': drivers/video/fbdev/mmp/hw/mmp_ctrl.c:506:22: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t {aka long long unsigned int}' [-Werror=format=] dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__, ^ drivers/video/fbdev/mmp/hw/mmp_ctrl.c:506:22: error: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'resource_size_t {aka long long unsigned int}' [-Werror=format=] This changes the format string to %pR, which is interpreted by the printk implementation to pretty-print a resource structure. Signed-off-by: Arnd Bergmann Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/mmp/hw/mmp_ctrl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c index de54a4748065..b6f83d5df9fd 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c +++ b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c @@ -503,8 +503,7 @@ static int mmphw_probe(struct platform_device *pdev) ctrl->reg_base = devm_ioremap_nocache(ctrl->dev, res->start, resource_size(res)); if (ctrl->reg_base == NULL) { - dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__, - res->start, res->end); + dev_err(ctrl->dev, "%s: res %pR map failed\n", __func__, res); ret = -ENOMEM; goto failed; } -- cgit v1.2.3 From a588afc920bc50e894f6ae2874c4281c795e0979 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Jan 2016 07:06:53 -0500 Subject: libata-sff: use WARN instead of BUG on illegal host state machine state ata_sff_hsm_move() triggers BUG if it sees a host state machine state that it dind't expect. The risk for data corruption when the condition occurs is low as it's highly unlikely that it would lead to spurious completion of commands. The BUG occasionally triggered for subtle race conditions in the driver. Let's downgrade it to WARN so that it doesn't kill the machine unnecessarily. Signed-off-by: Tejun Heo Cc: Dmitry Vyukov --- drivers/ata/libata-sff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index cdf6215a9a22..608677df4f49 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1296,7 +1296,8 @@ fsm_start: break; default: poll_next = 0; - BUG(); + WARN(true, "ata%d: SFF host state machine in invalid state %d", + ap->print_id, ap->hsm_task_state); } return poll_next; -- cgit v1.2.3 From b82fe6ddd782f847332aeabf8cab980852f61629 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 19 Jan 2016 11:10:57 -0200 Subject: video: fbdev: imxfb: Provide a reset mechanism Currently when we boot the kernel on a mx25pdk the LCDC controller does not show the Linux logo on boot. This problem is well explained by Sascha Hauer: "Unfortunately this LCD controller does not have an enable bit. The controller starts directly when the clocks are enabled. If the clocks are enabled when the controller is not yet programmed with proper register values then it just goes into some undefined state. What I suspect is that the clocks already were enabled before driver probe, presumably by the bootloader, so the controller is already in undefined state when entering Linux. Now by dis/enabling the ipg clock you effectively reset the controller. Since you have programmed it with valid register values in the mean time it starts working after this reset." So do as suggested and force a reset of the LCDC hardware by enabling and disabling the IPG clock. With this change the Linux logo can be seen on boot on a mx25pdk. Signed-off-by: Fabio Estevam Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/imxfb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index cee88603efc9..bb2f1e866020 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c @@ -902,6 +902,21 @@ static int imxfb_probe(struct platform_device *pdev) goto failed_getclock; } + /* + * The LCDC controller does not have an enable bit. The + * controller starts directly when the clocks are enabled. + * If the clocks are enabled when the controller is not yet + * programmed with proper register values (enabled at the + * bootloader, for example) then it just goes into some undefined + * state. + * To avoid this issue, let's enable and disable LCDC IPG clock + * so that we force some kind of 'reset' to the LCDC block. + */ + ret = clk_prepare_enable(fbi->clk_ipg); + if (ret) + goto failed_getclock; + clk_disable_unprepare(fbi->clk_ipg); + fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(fbi->clk_ahb)) { ret = PTR_ERR(fbi->clk_ahb); -- cgit v1.2.3 From 23d11a58a9a60dcb52c8fc6494efce908b24c295 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Jan 2016 05:59:46 -0500 Subject: workqueue: skip flush dependency checks for legacy workqueues fca839c00a12 ("workqueue: warn if memory reclaim tries to flush !WQ_MEM_RECLAIM workqueue") implemented flush dependency warning which triggers if a PF_MEMALLOC task or WQ_MEM_RECLAIM workqueue tries to flush a !WQ_MEM_RECLAIM workquee. This assumes that workqueues marked with WQ_MEM_RECLAIM sit in memory reclaim path and making it depend on something which may need more memory to make forward progress can lead to deadlocks. Unfortunately, workqueues created with the legacy create*_workqueue() interface always have WQ_MEM_RECLAIM regardless of whether they are depended upon memory reclaim or not. These spurious WQ_MEM_RECLAIM markings cause spurious triggering of the flush dependency checks. WARNING: CPU: 0 PID: 6 at kernel/workqueue.c:2361 check_flush_dependency+0x138/0x144() workqueue: WQ_MEM_RECLAIM deferwq:deferred_probe_work_func is flushing !WQ_MEM_RECLAIM events:lru_add_drain_per_cpu ... Workqueue: deferwq deferred_probe_work_func [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x94/0xd4) [] (dump_stack) from [] (warn_slowpath_common+0x80/0xb0) [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) [] (warn_slowpath_fmt) from [] (check_flush_dependency+0x138/0x144) [] (check_flush_dependency) from [] (flush_work+0x50/0x15c) [] (flush_work) from [] (lru_add_drain_all+0x130/0x180) [] (lru_add_drain_all) from [] (migrate_prep+0x8/0x10) [] (migrate_prep) from [] (alloc_contig_range+0xd8/0x338) [] (alloc_contig_range) from [] (cma_alloc+0xe0/0x1ac) [] (cma_alloc) from [] (__alloc_from_contiguous+0x38/0xd8) [] (__alloc_from_contiguous) from [] (__dma_alloc+0x240/0x278) [] (__dma_alloc) from [] (arm_dma_alloc+0x54/0x5c) [] (arm_dma_alloc) from [] (dmam_alloc_coherent+0xc0/0xec) [] (dmam_alloc_coherent) from [] (ahci_port_start+0x150/0x1dc) [] (ahci_port_start) from [] (ata_host_start.part.3+0xc8/0x1c8) [] (ata_host_start.part.3) from [] (ata_host_activate+0x50/0x148) [] (ata_host_activate) from [] (ahci_host_activate+0x44/0x114) [] (ahci_host_activate) from [] (ahci_platform_init_host+0x1d8/0x3c8) [] (ahci_platform_init_host) from [] (tegra_ahci_probe+0x448/0x4e8) [] (tegra_ahci_probe) from [] (platform_drv_probe+0x50/0xac) [] (platform_drv_probe) from [] (driver_probe_device+0x214/0x2c0) [] (driver_probe_device) from [] (bus_for_each_drv+0x60/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+0x68/0x98) [] (deferred_probe_work_func) from [] (process_one_work+0x120/0x3f8) [] (process_one_work) from [] (worker_thread+0x38/0x55c) [] (worker_thread) from [] (kthread+0xdc/0xf4) [] (kthread) from [] (ret_from_fork+0x14/0x3c) Fix it by marking workqueues created via create*_workqueue() with __WQ_LEGACY and disabling flush dependency checks on them. Signed-off-by: Tejun Heo Reported-and-tested-by: Thierry Reding Link: http://lkml.kernel.org/g/20160126173843.GA11115@ulmo.nvidia.com Fixes: fca839c00a12 ("workqueue: warn if memory reclaim tries to flush !WQ_MEM_RECLAIM workqueue") --- include/linux/workqueue.h | 9 +++++---- kernel/workqueue.c | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 0e32bc71245e..ca73c503b92a 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -311,6 +311,7 @@ enum { __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ + __WQ_LEGACY = 1 << 18, /* internal: create*_workqueue() */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ @@ -411,12 +412,12 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args) #define create_workqueue(name) \ - alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, (name)) + alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name)) #define create_freezable_workqueue(name) \ - alloc_workqueue("%s", WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, \ - 1, (name)) + alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \ + WQ_MEM_RECLAIM, 1, (name)) #define create_singlethread_workqueue(name) \ - alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name) + alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name) extern void destroy_workqueue(struct workqueue_struct *wq); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 61a0264e28f9..dc7faad63646 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2355,7 +2355,8 @@ static void check_flush_dependency(struct workqueue_struct *target_wq, WARN_ONCE(current->flags & PF_MEMALLOC, "workqueue: PF_MEMALLOC task %d(%s) is flushing !WQ_MEM_RECLAIM %s:%pf", current->pid, current->comm, target_wq->name, target_func); - WARN_ONCE(worker && (worker->current_pwq->wq->flags & WQ_MEM_RECLAIM), + WARN_ONCE(worker && ((worker->current_pwq->wq->flags & + (WQ_MEM_RECLAIM | __WQ_LEGACY)) == WQ_MEM_RECLAIM), "workqueue: WQ_MEM_RECLAIM %s:%pf is flushing !WQ_MEM_RECLAIM %s:%pf", worker->current_pwq->wq->name, worker->current_func, target_wq->name, target_func); -- cgit v1.2.3 From 8a9ebe717a133ba7bc90b06047f43cc6b8bcb8b3 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 18 Jan 2016 14:09:27 -0600 Subject: target: Fix WRITE_SAME/DISCARD conversion to linux 512b sectors In a couple places we are not converting to/from the Linux block layer 512 bytes sectors. 1. The request queue values and what we do are a mismatch of things: max_discard_sectors - This is in linux block layer 512 byte sectors. We are just copying this to max_unmap_lba_count. discard_granularity - This is in bytes. We are converting it to Linux block layer 512 byte sectors. discard_alignment - This is in bytes. We are just copying this over. The problem is that the core LIO code exports these values in spc_emulate_evpd_b0 and we use them to test request arguments in sbc_execute_unmap, but we never convert to the block size we export to the initiator. If we are not using 512 byte sectors then we are exporting the wrong values or are checks are off. And, for the discard_alignment/bytes case we are just plain messed up. 2. blkdev_issue_discard's start and number of sector arguments are supposed to be in linux block layer 512 byte sectors. We are currently passing in the values we get from the initiator which might be based on some other sector size. There is a similar problem in iblock_execute_write_same where the bio functions want values in 512 byte sectors but we are passing in what we got from the initiator. Signed-off-by: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 44 +++++++++++++++++++++++++++ drivers/target/target_core_file.c | 29 ++++++------------ drivers/target/target_core_iblock.c | 58 +++++++++--------------------------- include/target/target_core_backend.h | 3 ++ 4 files changed, 70 insertions(+), 64 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index cacd97a8cbd0..da457e25717a 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -828,6 +828,50 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) return dev; } +/* + * Check if the underlying struct block_device request_queue supports + * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM + * in ATA and we need to set TPE=1 + */ +bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, + struct request_queue *q, int block_size) +{ + if (!blk_queue_discard(q)) + return false; + + attrib->max_unmap_lba_count = (q->limits.max_discard_sectors << 9) / + block_size; + /* + * Currently hardcoded to 1 in Linux/SCSI code.. + */ + attrib->max_unmap_block_desc_count = 1; + attrib->unmap_granularity = q->limits.discard_granularity / block_size; + attrib->unmap_granularity_alignment = q->limits.discard_alignment / + block_size; + attrib->unmap_zeroes_data = q->limits.discard_zeroes_data; + return true; +} +EXPORT_SYMBOL(target_configure_unmap_from_queue); + +/* + * Convert from blocksize advertised to the initiator to the 512 byte + * units unconditionally used by the Linux block layer. + */ +sector_t target_to_linux_sector(struct se_device *dev, sector_t lb) +{ + switch (dev->dev_attrib.block_size) { + case 4096: + return lb << 3; + case 2048: + return lb << 2; + case 1024: + return lb << 1; + default: + return lb; + } +} +EXPORT_SYMBOL(target_to_linux_sector); + int target_configure_device(struct se_device *dev) { struct se_hba *hba = dev->se_hba; diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index e3195700211a..75f0f08b2a34 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -160,25 +160,11 @@ static int fd_configure_device(struct se_device *dev) " block_device blocks: %llu logical_block_size: %d\n", dev_size, div_u64(dev_size, fd_dev->fd_block_size), fd_dev->fd_block_size); - /* - * Check if the underlying struct block_device request_queue supports - * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM - * in ATA and we need to set TPE=1 - */ - if (blk_queue_discard(q)) { - dev->dev_attrib.max_unmap_lba_count = - q->limits.max_discard_sectors; - /* - * Currently hardcoded to 1 in Linux/SCSI code.. - */ - dev->dev_attrib.max_unmap_block_desc_count = 1; - dev->dev_attrib.unmap_granularity = - q->limits.discard_granularity >> 9; - dev->dev_attrib.unmap_granularity_alignment = - q->limits.discard_alignment; + + if (target_configure_unmap_from_queue(&dev->dev_attrib, q, + fd_dev->fd_block_size)) pr_debug("IFILE: BLOCK Discard support available," - " disabled by default\n"); - } + " disabled by default\n"); /* * Enable write same emulation for IBLOCK and use 0xFFFF as * the smaller WRITE_SAME(10) only has a two-byte block count. @@ -490,9 +476,12 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) if (S_ISBLK(inode->i_mode)) { /* The backend is block device, use discard */ struct block_device *bdev = inode->i_bdev; + struct se_device *dev = cmd->se_dev; - ret = blkdev_issue_discard(bdev, lba, - nolb, GFP_KERNEL, 0); + ret = blkdev_issue_discard(bdev, + target_to_linux_sector(dev, lba), + target_to_linux_sector(dev, nolb), + GFP_KERNEL, 0); if (ret < 0) { pr_warn("FILEIO: blkdev_issue_discard() failed: %d\n", ret); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 5a2899f9f50b..abe4eb997a84 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -121,29 +121,11 @@ static int iblock_configure_device(struct se_device *dev) dev->dev_attrib.hw_max_sectors = queue_max_hw_sectors(q); dev->dev_attrib.hw_queue_depth = q->nr_requests; - /* - * Check if the underlying struct block_device request_queue supports - * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM - * in ATA and we need to set TPE=1 - */ - if (blk_queue_discard(q)) { - dev->dev_attrib.max_unmap_lba_count = - q->limits.max_discard_sectors; - - /* - * Currently hardcoded to 1 in Linux/SCSI code.. - */ - dev->dev_attrib.max_unmap_block_desc_count = 1; - dev->dev_attrib.unmap_granularity = - q->limits.discard_granularity >> 9; - dev->dev_attrib.unmap_granularity_alignment = - q->limits.discard_alignment; - dev->dev_attrib.unmap_zeroes_data = - q->limits.discard_zeroes_data; - + if (target_configure_unmap_from_queue(&dev->dev_attrib, q, + dev->dev_attrib.hw_block_size)) pr_debug("IBLOCK: BLOCK Discard support available," - " disabled by default\n"); - } + " disabled by default\n"); + /* * Enable write same emulation for IBLOCK and use 0xFFFF as * the smaller WRITE_SAME(10) only has a two-byte block count. @@ -415,9 +397,13 @@ static sense_reason_t iblock_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) { struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd; + struct se_device *dev = cmd->se_dev; int ret; - ret = blkdev_issue_discard(bdev, lba, nolb, GFP_KERNEL, 0); + ret = blkdev_issue_discard(bdev, + target_to_linux_sector(dev, lba), + target_to_linux_sector(dev, nolb), + GFP_KERNEL, 0); if (ret < 0) { pr_err("blkdev_issue_discard() failed: %d\n", ret); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -433,8 +419,10 @@ iblock_execute_write_same(struct se_cmd *cmd) struct scatterlist *sg; struct bio *bio; struct bio_list list; - sector_t block_lba = cmd->t_task_lba; - sector_t sectors = sbc_get_write_same_sectors(cmd); + struct se_device *dev = cmd->se_dev; + sector_t block_lba = target_to_linux_sector(dev, cmd->t_task_lba); + sector_t sectors = target_to_linux_sector(dev, + sbc_get_write_same_sectors(cmd)); if (cmd->prot_op) { pr_err("WRITE_SAME: Protection information with IBLOCK" @@ -648,12 +636,12 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, enum dma_data_direction data_direction) { struct se_device *dev = cmd->se_dev; + sector_t block_lba = target_to_linux_sector(dev, cmd->t_task_lba); struct iblock_req *ibr; struct bio *bio, *bio_start; struct bio_list list; struct scatterlist *sg; u32 sg_num = sgl_nents; - sector_t block_lba; unsigned bio_cnt; int rw = 0; int i; @@ -679,24 +667,6 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, rw = READ; } - /* - * Convert the blocksize advertised to the initiator to the 512 byte - * units unconditionally used by the Linux block layer. - */ - if (dev->dev_attrib.block_size == 4096) - block_lba = (cmd->t_task_lba << 3); - else if (dev->dev_attrib.block_size == 2048) - block_lba = (cmd->t_task_lba << 2); - else if (dev->dev_attrib.block_size == 1024) - block_lba = (cmd->t_task_lba << 1); - else if (dev->dev_attrib.block_size == 512) - block_lba = cmd->t_task_lba; - else { - pr_err("Unsupported SCSI -> BLOCK LBA conversion:" - " %u\n", dev->dev_attrib.block_size); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - ibr = kzalloc(sizeof(struct iblock_req), GFP_KERNEL); if (!ibr) goto fail; diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 56cf8e485ef2..28ee5c2e6bcd 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -94,5 +94,8 @@ sense_reason_t passthrough_parse_cdb(struct se_cmd *cmd, sense_reason_t (*exec_cmd)(struct se_cmd *cmd)); bool target_sense_desc_format(struct se_device *dev); +sector_t target_to_linux_sector(struct se_device *dev, sector_t lb); +bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, + struct request_queue *q, int block_size); #endif /* TARGET_CORE_BACKEND_H */ -- cgit v1.2.3 From dacb58221805bb72ec46a73826c9e59a587d7d68 Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Wed, 20 Jan 2016 15:42:58 -0800 Subject: qla2xxx: Fix warning reported by static checker This patch fixes following warning drivers/scsi/qla2xxx/qla_target.c:3587 qlt_do_ctio_completion() warn: impossible condition '(logged_out == 41) => (0-1 == 41)' drivers/scsi/qla2xxx/qla_target.c 3580 case CTIO_PORT_LOGGED_OUT: 3581 case CTIO_PORT_UNAVAILABLE: 3582 { 3583 bool logged_out = (status & 0xFFFF); 3584 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059, 3585 "qla_target(%d): CTIO with %s status %x " 3586 "received (state %x, se_cmd %p)\n", vha->vp_idx, 3587 (logged_out == CTIO_PORT_LOGGED_OUT) ? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Bool cannot equal 0x26. 3588 "PORT LOGGED OUT" : "PORT UNAVAILABLE", Reported-by: Dan Carpenter Signed-off-by: Himanshu Madhani Signed-off-by: Giridhar Malavali Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 8075a4cdb45c..2c71305d2563 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3580,12 +3580,13 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, case CTIO_PORT_LOGGED_OUT: case CTIO_PORT_UNAVAILABLE: { - int logged_out = (status & 0xFFFF); + int logged_out = + (status & 0xFFFF) == CTIO_PORT_LOGGED_OUT; + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059, "qla_target(%d): CTIO with %s status %x " "received (state %x, se_cmd %p)\n", vha->vp_idx, - (logged_out == CTIO_PORT_LOGGED_OUT) ? - "PORT LOGGED OUT" : "PORT UNAVAILABLE", + logged_out ? "PORT LOGGED OUT" : "PORT UNAVAILABLE", status, cmd->state, se_cmd); if (logged_out && cmd->sess) { -- cgit v1.2.3 From a07100e00ac42a4474530ce17b4978c9e06bde55 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Mon, 7 Dec 2015 19:48:57 -0500 Subject: qla2xxx: Fix TMR ABORT interaction issue between qla2xxx and TCM During lun reset, TMR thread from TCM would issue abort to qla driver. At abort time, each command is in different state. Depending on the state, qla will use the TMR thread to trigger a command free(cmd_kref--) if command is not down at firmware. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 55 +++++++++++++++++++--------- drivers/scsi/qla2xxx/qla_target.h | 59 +++++++++++++++++------------- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 73 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 2c71305d2563..74eb776d2faa 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -105,7 +105,7 @@ static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt); static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, int fn, void *iocb, int flags); static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd - *cmd, struct atio_from_isp *atio, int ha_locked); + *cmd, struct atio_from_isp *atio, int ha_locked, int ul_abort); static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, struct qla_tgt_srr_imm *imm, int ha_lock); static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, @@ -2665,7 +2665,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, /* no need to terminate. FW already freed exchange. */ qlt_abort_cmd_on_host_reset(cmd->vha, cmd); else - qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); return 0; } @@ -3173,7 +3173,8 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, } static void qlt_send_term_exchange(struct scsi_qla_host *vha, - struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked) + struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked, + int ul_abort) { unsigned long flags = 0; int rc; @@ -3193,8 +3194,7 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha, qlt_alloc_qfull_cmd(vha, atio, 0, 0); done: - if (cmd && (!cmd->aborted || - !cmd->cmd_sent_to_fw)) { + if (cmd && !ul_abort && !cmd->aborted) { if (cmd->sg_mapped) qlt_unmap_sg(vha, cmd); vha->hw->tgt.tgt_ops->free_cmd(cmd); @@ -3253,21 +3253,38 @@ static void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha) } -void qlt_abort_cmd(struct qla_tgt_cmd *cmd) +int qlt_abort_cmd(struct qla_tgt_cmd *cmd) { struct qla_tgt *tgt = cmd->tgt; struct scsi_qla_host *vha = tgt->vha; struct se_cmd *se_cmd = &cmd->se_cmd; + unsigned long flags; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, "qla_target(%d): terminating exchange for aborted cmd=%p " "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, &cmd->se_cmd, se_cmd->tag); + spin_lock_irqsave(&cmd->cmd_lock, flags); + if (cmd->aborted) { + spin_unlock_irqrestore(&cmd->cmd_lock, flags); + /* + * It's normal to see 2 calls in this path: + * 1) XFER Rdy completion + CMD_T_ABORT + * 2) TCM TMR - drain_state_list + */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xffff, + "multiple abort. %p transport_state %x, t_state %x," + " se_cmd_flags %x \n", cmd, cmd->se_cmd.transport_state, + cmd->se_cmd.t_state,cmd->se_cmd.se_cmd_flags); + return EIO; + } cmd->aborted = 1; cmd->cmd_flags |= BIT_6; + spin_unlock_irqrestore(&cmd->cmd_lock, flags); - qlt_send_term_exchange(vha, cmd, &cmd->atio, 0); + qlt_send_term_exchange(vha, cmd, &cmd->atio, 0, 1); + return 0; } EXPORT_SYMBOL(qlt_abort_cmd); @@ -3282,6 +3299,9 @@ void qlt_free_cmd(struct qla_tgt_cmd *cmd) BUG_ON(cmd->cmd_in_wq); + if (cmd->sg_mapped) + qlt_unmap_sg(cmd->vha, cmd); + if (!cmd->q_full) qlt_decr_num_pend_cmds(cmd->vha); @@ -3399,7 +3419,7 @@ static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio, term = 1; if (term) - qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1, 0); return term; } @@ -3755,6 +3775,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) goto out_term; } + spin_lock_init(&cmd->cmd_lock); cdb = &atio->u.isp24.fcp_cmnd.cdb[0]; cmd->se_cmd.tag = atio->u.isp24.exchange_addr; cmd->unpacked_lun = scsilun_to_int( @@ -3797,7 +3818,7 @@ out_term: */ cmd->cmd_flags |= BIT_2; spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_term_exchange(vha, NULL, &cmd->atio, 1); + qlt_send_term_exchange(vha, NULL, &cmd->atio, 1, 0); qlt_decr_num_pend_cmds(vha); percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag); @@ -3919,7 +3940,7 @@ static void qlt_create_sess_from_atio(struct work_struct *work) out_term: spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_term_exchange(vha, NULL, &op->atio, 1); + qlt_send_term_exchange(vha, NULL, &op->atio, 1, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); kfree(op); @@ -4772,7 +4793,7 @@ out_reject: dump_stack(); } else { cmd->cmd_flags |= BIT_9; - qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1, 0); } spin_unlock_irqrestore(&ha->hardware_lock, flags); } @@ -4951,7 +4972,7 @@ static void qlt_prepare_srr_imm(struct scsi_qla_host *vha, sctio, sctio->srr_id); list_del(&sctio->srr_list_entry); qlt_send_term_exchange(vha, sctio->cmd, - &sctio->cmd->atio, 1); + &sctio->cmd->atio, 1, 0); kfree(sctio); } } @@ -5124,7 +5145,7 @@ static int __qlt_send_busy(struct scsi_qla_host *vha, atio->u.isp24.fcp_hdr.s_id); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); if (!sess) { - qlt_send_term_exchange(vha, NULL, atio, 1); + qlt_send_term_exchange(vha, NULL, atio, 1, 0); return 0; } /* Sending marker isn't necessary, since we called from ISR */ @@ -5407,7 +5428,7 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, #if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ qlt_send_busy(vha, atio, SAM_STAT_BUSY); #else - qlt_send_term_exchange(vha, NULL, atio, 1); + qlt_send_term_exchange(vha, NULL, atio, 1, 0); #endif if (!ha_locked) @@ -5524,7 +5545,7 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) #if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ qlt_send_busy(vha, atio, 0); #else - qlt_send_term_exchange(vha, NULL, atio, 1); + qlt_send_term_exchange(vha, NULL, atio, 1, 0); #endif } else { if (tgt->tgt_stop) { @@ -5533,7 +5554,7 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) "command to target, sending TERM " "EXCHANGE for rsp\n"); qlt_send_term_exchange(vha, NULL, - atio, 1); + atio, 1, 0); } else { ql_dbg(ql_dbg_tgt, vha, 0xe060, "qla_target(%d): Unable to send " @@ -5961,7 +5982,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt, return; out_term: - qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 0); + qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1, 0); if (sess) ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 71b2865ba3c8..22a6a767fe07 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -943,6 +943,36 @@ struct qla_tgt_sess { qlt_plogi_ack_t *plogi_link[QLT_PLOGI_LINK_MAX]; }; +typedef enum { + /* + * BIT_0 - Atio Arrival / schedule to work + * BIT_1 - qlt_do_work + * BIT_2 - qlt_do work failed + * BIT_3 - xfer rdy/tcm_qla2xxx_write_pending + * BIT_4 - read respond/tcm_qla2xx_queue_data_in + * BIT_5 - status respond / tcm_qla2xx_queue_status + * BIT_6 - tcm request to abort/Term exchange. + * pre_xmit_response->qlt_send_term_exchange + * BIT_7 - SRR received (qlt_handle_srr->qlt_xmit_response) + * BIT_8 - SRR received (qlt_handle_srr->qlt_rdy_to_xfer) + * BIT_9 - SRR received (qla_handle_srr->qlt_send_term_exchange) + * BIT_10 - Data in - hanlde_data->tcm_qla2xxx_handle_data + + * BIT_12 - good completion - qlt_ctio_do_completion -->free_cmd + * BIT_13 - Bad completion - + * qlt_ctio_do_completion --> qlt_term_ctio_exchange + * BIT_14 - Back end data received/sent. + * BIT_15 - SRR prepare ctio + * BIT_16 - complete free + * BIT_17 - flush - qlt_abort_cmd_on_host_reset + * BIT_18 - completion w/abort status + * BIT_19 - completion w/unknown status + * BIT_20 - tcm_qla2xxx_free_cmd + */ + CMD_FLAG_DATA_WORK = BIT_11, + CMD_FLAG_DATA_WORK_FREE = BIT_21, +} cmd_flags_t; + struct qla_tgt_cmd { struct se_cmd se_cmd; struct qla_tgt_sess *sess; @@ -952,6 +982,7 @@ struct qla_tgt_cmd { /* Sense buffer that will be mapped into outgoing status */ unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER]; + spinlock_t cmd_lock; /* to save extra sess dereferences */ unsigned int conf_compl_supported:1; unsigned int sg_mapped:1; @@ -986,30 +1017,8 @@ struct qla_tgt_cmd { uint64_t jiffies_at_alloc; uint64_t jiffies_at_free; - /* BIT_0 - Atio Arrival / schedule to work - * BIT_1 - qlt_do_work - * BIT_2 - qlt_do work failed - * BIT_3 - xfer rdy/tcm_qla2xxx_write_pending - * BIT_4 - read respond/tcm_qla2xx_queue_data_in - * BIT_5 - status respond / tcm_qla2xx_queue_status - * BIT_6 - tcm request to abort/Term exchange. - * pre_xmit_response->qlt_send_term_exchange - * BIT_7 - SRR received (qlt_handle_srr->qlt_xmit_response) - * BIT_8 - SRR received (qlt_handle_srr->qlt_rdy_to_xfer) - * BIT_9 - SRR received (qla_handle_srr->qlt_send_term_exchange) - * BIT_10 - Data in - hanlde_data->tcm_qla2xxx_handle_data - * BIT_11 - Data actually going to TCM : tcm_qla2xx_handle_data_work - * BIT_12 - good completion - qlt_ctio_do_completion -->free_cmd - * BIT_13 - Bad completion - - * qlt_ctio_do_completion --> qlt_term_ctio_exchange - * BIT_14 - Back end data received/sent. - * BIT_15 - SRR prepare ctio - * BIT_16 - complete free - * BIT_17 - flush - qlt_abort_cmd_on_host_reset - * BIT_18 - completion w/abort status - * BIT_19 - completion w/unknown status - */ - uint32_t cmd_flags; + + cmd_flags_t cmd_flags; }; struct qla_tgt_sess_work_param { @@ -1148,7 +1157,7 @@ static inline void sid_to_portid(const uint8_t *s_id, port_id_t *p) extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); -extern void qlt_abort_cmd(struct qla_tgt_cmd *); +extern int qlt_abort_cmd(struct qla_tgt_cmd *); extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index faf0a126627f..3eecdd1bc6fb 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -298,6 +298,10 @@ static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) { cmd->vha->tgt_counters.core_qla_free_cmd++; cmd->cmd_in_wq = 1; + + BUG_ON(cmd->cmd_flags & BIT_20); + cmd->cmd_flags |= BIT_20; + INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free); queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); } @@ -374,6 +378,20 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); + + if (cmd->aborted) { + /* Cmd can loop during Q-full. tcm_qla2xxx_aborted_task + * can get ahead of this cmd. tcm_qla2xxx_aborted_task + * already kick start the free. + */ + pr_debug("write_pending aborted cmd[%p] refcount %d " + "transport_state %x, t_state %x, se_cmd_flags %x\n", + cmd,cmd->se_cmd.cmd_kref.refcount.counter, + cmd->se_cmd.transport_state, + cmd->se_cmd.t_state, + cmd->se_cmd.se_cmd_flags); + return 0; + } cmd->cmd_flags |= BIT_3; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); @@ -405,7 +423,7 @@ static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd) 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, - 3 * HZ); + 50); return 0; } spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); @@ -465,13 +483,25 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, static void tcm_qla2xxx_handle_data_work(struct work_struct *work) { struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + unsigned long flags; /* * Ensure that the complete FCP WRITE payload has been received. * Otherwise return an exception via CHECK_CONDITION status. */ cmd->cmd_in_wq = 0; - cmd->cmd_flags |= BIT_11; + + spin_lock_irqsave(&cmd->cmd_lock, flags); + cmd->cmd_flags |= CMD_FLAG_DATA_WORK; + if (cmd->aborted) { + cmd->cmd_flags |= CMD_FLAG_DATA_WORK_FREE; + spin_unlock_irqrestore(&cmd->cmd_lock, flags); + + tcm_qla2xxx_free_cmd(cmd); + return; + } + spin_unlock_irqrestore(&cmd->cmd_lock, flags); + cmd->vha->tgt_counters.qla_core_ret_ctio++; if (!cmd->write_data_transferred) { /* @@ -546,6 +576,20 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); + if (cmd->aborted) { + /* Cmd can loop during Q-full. tcm_qla2xxx_aborted_task + * can get ahead of this cmd. tcm_qla2xxx_aborted_task + * already kick start the free. + */ + pr_debug("queue_data_in aborted cmd[%p] refcount %d " + "transport_state %x, t_state %x, se_cmd_flags %x\n", + cmd,cmd->se_cmd.cmd_kref.refcount.counter, + cmd->se_cmd.transport_state, + cmd->se_cmd.t_state, + cmd->se_cmd.se_cmd_flags); + return 0; + } + cmd->cmd_flags |= BIT_4; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); @@ -637,11 +681,34 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) qlt_xmit_tm_rsp(mcmd); } + +#define DATA_WORK_NOT_FREE(_flags) \ + (( _flags & (CMD_FLAG_DATA_WORK|CMD_FLAG_DATA_WORK_FREE)) == \ + CMD_FLAG_DATA_WORK) static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - qlt_abort_cmd(cmd); + unsigned long flags; + + if (qlt_abort_cmd(cmd)) + return; + + spin_lock_irqsave(&cmd->cmd_lock, flags); + if ((cmd->state == QLA_TGT_STATE_NEW)|| + ((cmd->state == QLA_TGT_STATE_DATA_IN) && + DATA_WORK_NOT_FREE(cmd->cmd_flags)) ) { + + cmd->cmd_flags |= CMD_FLAG_DATA_WORK_FREE; + spin_unlock_irqrestore(&cmd->cmd_lock, flags); + /* Cmd have not reached firmware. + * Use this trigger to free it. */ + tcm_qla2xxx_free_cmd(cmd); + return; + } + spin_unlock_irqrestore(&cmd->cmd_lock, flags); + return; + } static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, -- cgit v1.2.3 From 1c319e781e0ecc48228081558f38044a11c7a76e Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 27 Jan 2016 21:35:00 +0800 Subject: intel-hid: fix incorrect entries in intel_hid_keymap intel_hid_keymap contains a duplicate entry for KEY_HOME and an incorrect HID index for KEY_PAGEDOWN Reported-by: Pavel Bludov Signed-off-by: Alex Hung --- drivers/platform/x86/intel-hid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 20f0ad9bb9f3..e20f23e04c24 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -41,8 +41,7 @@ static const struct key_entry intel_hid_keymap[] = { { KE_KEY, 4, { KEY_HOME } }, { KE_KEY, 5, { KEY_END } }, { KE_KEY, 6, { KEY_PAGEUP } }, - { KE_KEY, 4, { KEY_PAGEDOWN } }, - { KE_KEY, 4, { KEY_HOME } }, + { KE_KEY, 7, { KEY_PAGEDOWN } }, { KE_KEY, 8, { KEY_RFKILL } }, { KE_KEY, 9, { KEY_POWER } }, { KE_KEY, 11, { KEY_SLEEP } }, -- cgit v1.2.3 From b1d353ad3d5835b16724653b33c05124e1b5acf1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 26 Jan 2016 12:24:25 +0300 Subject: intel_scu_ipcutil: underflow in scu_reg_access() "count" is controlled by the user and it can be negative. Let's prevent that by making it unsigned. You have to have CAP_SYS_RAWIO to call this function so the bug is not as serious as it could be. Fixes: 5369c02d951a ('intel_scu_ipc: Utility driver for intel scu ipc') Signed-off-by: Dan Carpenter Cc: stable@vger.kernel.org Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipcutil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 02bc5a6343c3..aa454241489c 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -49,7 +49,7 @@ struct scu_ipc_data { static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) { - int count = data->count; + unsigned int count = data->count; if (count == 0 || count == 3 || count > 4) return -EINVAL; -- cgit v1.2.3 From ec183d22cc284a7a1e17f0341219d8ec8ca070cc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 26 Jan 2016 14:05:20 +0200 Subject: perf tools: tracepoint_error() can receive e=NULL, robustify it Fixes segmentation fault using, for instance: (gdb) run record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Starting program: /home/acme/bin/perf record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Missing separate debuginfos, use: dnf debuginfo-install glibc-2.22-7.fc23.x86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0 x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 (gdb) bt #0 0x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 #1 0x00000000004b9fc5 in add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:433 #2 0x00000000004ba334 in add_tracepoint_event (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:498 #3 0x00000000004bb699 in parse_events_add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys=0x19b1370 "sched", event=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:936 #4 0x00000000004f6eda in parse_events_parse (_data=0x7fffffffb8b0, scanner=0x19a49d0) at util/parse-events.y:391 #5 0x00000000004bc8e5 in parse_events__scanner (str=0x663ff2 "sched:sched_switch", data=0x7fffffffb8b0, start_token=258) at util/parse-events.c:1361 #6 0x00000000004bca57 in parse_events (evlist=0x19a5220, str=0x663ff2 "sched:sched_switch", err=0x0) at util/parse-events.c:1401 #7 0x0000000000518d5f in perf_evlist__can_select_event (evlist=0x19a3b90, str=0x663ff2 "sched:sched_switch") at util/record.c:253 #8 0x0000000000553c42 in intel_pt_track_switches (evlist=0x19a3b90) at arch/x86/util/intel-pt.c:364 #9 0x00000000005549d1 in intel_pt_recording_options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at arch/x86/util/intel-pt.c:664 #10 0x000000000051e076 in auxtrace_record__options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at util/auxtrace.c:539 #11 0x0000000000433368 in cmd_record (argc=1, argv=0x7fffffffde60, prefix=0x0) at builtin-record.c:1264 #12 0x000000000049bec2 in run_builtin (p=0x8fa2a8 , argc=5, argv=0x7fffffffde60) at perf.c:390 #13 0x000000000049c12a in handle_internal_command (argc=5, argv=0x7fffffffde60) at perf.c:451 #14 0x000000000049c278 in run_argv (argcp=0x7fffffffdcbc, argv=0x7fffffffdcb0) at perf.c:495 #15 0x000000000049c60a in main (argc=5, argv=0x7fffffffde60) at perf.c:618 (gdb) Intel PT attempts to find the sched:sched_switch tracepoint but that seg faults if tracefs is not readable, because the error reporting structure is null, as errors are not reported when automatically adding tracepoints. Fix by checking before using. Committer note: This doesn't take place in a kernel that supports perf_event_attr.context_switch, that is the default way that will be used for tracking context switches, only in older kernels, like 4.2, in a machine with Intel PT (e.g. Broadwell) for non-priviledged users. Further info from a similar patch by Wang: The error is in tracepoint_error: it assumes the 'e' parameter is valid. However, there are many situation a parse_event() can be called without parse_events_error. See result of $ grep 'parse_events(.*NULL)' ./tools/perf/ -r' Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Josh Poimboeuf Cc: Tong Zhang Cc: Wang Nan Cc: stable@vger.kernel.org # v4.4+ Fixes: 196581717d85 ("perf tools: Enhance parsing events tracepoint error output") Link: http://lkml.kernel.org/r/1453809921-24596-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4f7b0efdde2f..813d9b272c81 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -399,6 +399,9 @@ static void tracepoint_error(struct parse_events_error *e, int err, { char help[BUFSIZ]; + if (!e) + return; + /* * We get error directly from syscall errno ( > 0), * or from encoded pointer's error ( < 0). -- cgit v1.2.3 From 8eee1d3ed5b6fc8e14389567c9a6f53f82bb7224 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 1 Feb 2016 11:33:21 -0500 Subject: libata: fix sff host state machine locking while polling The bulk of ATA host state machine is implemented by ata_sff_hsm_move(). The function is called from either the interrupt handler or, if polling, a work item. Unlike from the interrupt path, the polling path calls the function without holding the host lock and ata_sff_hsm_move() selectively grabs the lock. This is completely broken. If an IRQ triggers while polling is in progress, the two can easily race and end up accessing the hardware and updating state machine state at the same time. This can put the state machine in an illegal state and lead to a crash like the following. kernel BUG at drivers/ata/libata-sff.c:1302! invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN Modules linked in: CPU: 1 PID: 10679 Comm: syz-executor Not tainted 4.5.0-rc1+ #300 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff88002bd00000 ti: ffff88002e048000 task.ti: ffff88002e048000 RIP: 0010:[] [] ata_sff_hsm_move+0x619/0x1c60 ... Call Trace: [] __ata_sff_port_intr+0x1e1/0x3a0 drivers/ata/libata-sff.c:1584 [] ata_bmdma_port_intr+0x71/0x400 drivers/ata/libata-sff.c:2877 [< inline >] __ata_sff_interrupt drivers/ata/libata-sff.c:1629 [] ata_bmdma_interrupt+0x253/0x580 drivers/ata/libata-sff.c:2902 [] handle_irq_event_percpu+0x108/0x7e0 kernel/irq/handle.c:157 [] handle_irq_event+0xa7/0x140 kernel/irq/handle.c:205 [] handle_edge_irq+0x1e3/0x8d0 kernel/irq/chip.c:623 [< inline >] generic_handle_irq_desc include/linux/irqdesc.h:146 [] handle_irq+0x10c/0x2a0 arch/x86/kernel/irq_64.c:78 [] do_IRQ+0x7d/0x1a0 arch/x86/kernel/irq.c:240 [] common_interrupt+0x8c/0x8c arch/x86/entry/entry_64.S:520 [< inline >] rcu_lock_acquire include/linux/rcupdate.h:490 [< inline >] rcu_read_lock include/linux/rcupdate.h:874 [] filemap_map_pages+0x131/0xba0 mm/filemap.c:2145 [< inline >] do_fault_around mm/memory.c:2943 [< inline >] do_read_fault mm/memory.c:2962 [< inline >] do_fault mm/memory.c:3133 [< inline >] handle_pte_fault mm/memory.c:3308 [< inline >] __handle_mm_fault mm/memory.c:3418 [] handle_mm_fault+0x2516/0x49a0 mm/memory.c:3447 [] __do_page_fault+0x376/0x960 arch/x86/mm/fault.c:1238 [] trace_do_page_fault+0xe8/0x420 arch/x86/mm/fault.c:1331 [] do_async_page_fault+0x14/0xd0 arch/x86/kernel/kvm.c:264 [] async_page_fault+0x28/0x30 arch/x86/entry/entry_64.S:986 Fix it by ensuring that the polling path is holding the host lock before entering ata_sff_hsm_move() so that all hardware accesses and state updates are performed under the host lock. Signed-off-by: Tejun Heo Reported-and-tested-by: Dmitry Vyukov Link: http://lkml.kernel.org/g/CACT4Y+b_JsOxJu2EZyEf+mOXORc_zid5V1-pLZSroJVxyWdSpw@mail.gmail.com Cc: stable@vger.kernel.org --- drivers/ata/libata-sff.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 608677df4f49..051b6158d1b7 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -997,12 +997,9 @@ static inline int ata_hsm_ok_in_wq(struct ata_port *ap, static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) { struct ata_port *ap = qc->ap; - unsigned long flags; if (ap->ops->error_handler) { if (in_wq) { - spin_lock_irqsave(ap->lock, flags); - /* EH might have kicked in while host lock is * released. */ @@ -1014,8 +1011,6 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) } else ata_port_freeze(ap); } - - spin_unlock_irqrestore(ap->lock, flags); } else { if (likely(!(qc->err_mask & AC_ERR_HSM))) ata_qc_complete(qc); @@ -1024,10 +1019,8 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) } } else { if (in_wq) { - spin_lock_irqsave(ap->lock, flags); ata_sff_irq_on(ap); ata_qc_complete(qc); - spin_unlock_irqrestore(ap->lock, flags); } else ata_qc_complete(qc); } @@ -1048,9 +1041,10 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, { struct ata_link *link = qc->dev->link; struct ata_eh_info *ehi = &link->eh_info; - unsigned long flags = 0; int poll_next; + lockdep_assert_held(ap->lock); + WARN_ON_ONCE((qc->flags & ATA_QCFLAG_ACTIVE) == 0); /* Make sure ata_sff_qc_issue() does not throw things @@ -1112,14 +1106,6 @@ fsm_start: } } - /* Send the CDB (atapi) or the first data block (ata pio out). - * During the state transition, interrupt handler shouldn't - * be invoked before the data transfer is complete and - * hsm_task_state is changed. Hence, the following locking. - */ - if (in_wq) - spin_lock_irqsave(ap->lock, flags); - if (qc->tf.protocol == ATA_PROT_PIO) { /* PIO data out protocol. * send first data block. @@ -1135,9 +1121,6 @@ fsm_start: /* send CDB */ atapi_send_cdb(ap, qc); - if (in_wq) - spin_unlock_irqrestore(ap->lock, flags); - /* if polling, ata_sff_pio_task() handles the rest. * otherwise, interrupt handler takes over from here. */ @@ -1362,12 +1345,14 @@ static void ata_sff_pio_task(struct work_struct *work) u8 status; int poll_next; + spin_lock_irq(ap->lock); + BUG_ON(ap->sff_pio_task_link == NULL); /* qc can be NULL if timeout occurred */ qc = ata_qc_from_tag(ap, link->active_tag); if (!qc) { ap->sff_pio_task_link = NULL; - return; + goto out_unlock; } fsm_start: @@ -1382,11 +1367,14 @@ fsm_start: */ status = ata_sff_busy_wait(ap, ATA_BUSY, 5); if (status & ATA_BUSY) { + spin_unlock_irq(ap->lock); ata_msleep(ap, 2); + spin_lock_irq(ap->lock); + status = ata_sff_busy_wait(ap, ATA_BUSY, 10); if (status & ATA_BUSY) { ata_sff_queue_pio_task(link, ATA_SHORT_PAUSE); - return; + goto out_unlock; } } @@ -1403,6 +1391,8 @@ fsm_start: */ if (poll_next) goto fsm_start; +out_unlock: + spin_unlock_irq(ap->lock); } /** -- cgit v1.2.3 From f4dce1ffd2e30fa31756876ef502ce6d2324be35 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 25 Jan 2016 20:32:03 +0000 Subject: MIPS: Fix buffer overflow in syscall_get_arguments() Since commit 4c21b8fd8f14 ("MIPS: seccomp: Handle indirect system calls (o32)"), syscall_get_arguments() attempts to handle o32 indirect syscall arguments by incrementing both the start argument number and the number of arguments to fetch. However only the start argument number needs to be incremented. The number of arguments does not change, they're just shifted up by one, and in fact the output array is provided by the caller and is likely only n entries long, so reading more arguments overflows the output buffer. In the case of seccomp, this results in it fetching 7 arguments starting at the 2nd one, which overflows the unsigned long args[6] in populate_seccomp_data(). This clobbers the $s0 register from syscall_trace_enter() which __seccomp_phase1_filter() saved onto the stack, into which syscall_trace_enter() had placed its syscall number argument. This caused Chromium to crash. Credit goes to Milko for tracking it down as far as $s0 being clobbered. Fixes: 4c21b8fd8f14 ("MIPS: seccomp: Handle indirect system calls (o32)") Reported-by: Milko Leporis Signed-off-by: James Hogan Cc: linux-mips@linux-mips.org Cc: # 3.15- Patchwork: https://patchwork.linux-mips.org/patch/12213/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/syscall.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index 6499d93ae68d..47bc45a67e9b 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -101,10 +101,8 @@ static inline void syscall_get_arguments(struct task_struct *task, /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */ if ((config_enabled(CONFIG_32BIT) || test_tsk_thread_flag(task, TIF_32BIT_REGS)) && - (regs->regs[2] == __NR_syscall)) { + (regs->regs[2] == __NR_syscall)) i++; - n++; - } while (n--) ret |= mips_get_syscall_arg(args++, task, regs, i++); -- cgit v1.2.3 From 76e5846d3bdf59eb1010d5607003da2dc3910bb1 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 1 Feb 2016 13:50:36 +0000 Subject: MIPS: Properly disable FPU in start_thread() start_thread() (called for execve(2)) clears the TIF_USEDFPU flag without atomically disabling the FPU. With a preemptive kernel, an unfortunately timed preemption after this could result in another task (or KVM guest) being scheduled in with the FPU still enabled, since lose_fpu_inatomic() only turns it off if TIF_USEDFPU is set. Use lose_fpu(0) instead of the separate FPU / MSA management, which should do the right thing (drop FPU properly and atomically without saving state) and will be more future proof. Signed-off-by: James Hogan Reviewed-by: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12302/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/process.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index f2975d4d1e44..eddd5fd6fdfa 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -65,12 +65,10 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|ST0_FR|KU_MASK); status |= KU_USER; regs->cp0_status = status; + lose_fpu(0); + clear_thread_flag(TIF_MSA_CTX_LIVE); clear_used_math(); - clear_fpu_owner(); init_dsp(); - clear_thread_flag(TIF_USEDMSA); - clear_thread_flag(TIF_MSA_CTX_LIVE); - disable_msa(); regs->cp0_epc = pc; regs->regs[29] = sp; } -- cgit v1.2.3 From 00fe56dca6a845d5f10ef0398eef26e559e8f98c Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 1 Feb 2016 13:50:37 +0000 Subject: MIPS: Fix FPU disable with preemption The FPU should not be left enabled after a task context switch. This isn't usually a problem as the FPU enable bit is updated before returning to userland, however it can potentially mask kernel bugs, and in fact KVM assumes it won't happen and won't clear the FPU enable bit before returning to the guest, which allows the guest to use stale FPU context. Interrupts and exceptions save and restore most bits of the CP0 Status register which contains the FPU enable bit (CU1). When the kernel needs to enable or disable the FPU (for example due to attempted FPU use by userland, or the scheduler being invoked) both the actual Status register and the saved value in the userland context are updated. However this doesn't work correctly with full kernel preemption enabled, since the FPU enable bit can be cleared from within an interrupt when the scheduler is invoked, and only the userland context is updated, not the interrupt context. For example: 1) Enter kernel with FPU already enabled, TIF_USEDFPU=1, Status.CU1=1 saved. 2) Take a timer interrupt while in kernel mode, Status.CU1=1 saved. 3) Timer interrupt invokes scheduler to preempt the task, which clears TIF_USEDFPU, disables the FPU in Status register (Status.CU1=0), and the value stored in user context from step (1), but not the interrupt context from step (2). 4) When the process is scheduled back in again Status.CU1=0. 5) The interrupt context from step (2) is restored, which sets Status.CU1=1. So from user context point of view, preemption has re-enabled FPU! 6) If the scheduler is invoked again (via preemption or voluntarily) before returning to userland, TIF_USEDFPU=0 so the FPU is not disabled before the task context switch. 7) The next task resumes from the context switch with FPU enabled! The restoring of the Status register on return from interrupt/exception is already selective about which bits to restore, leaving the interrupt mask bits alone so enabling/disabling of CPU interrupt lines can persist. Extend this to also leave both the CU1 bit (FPU enable) and the FR bit (which specifies the FPU mode and gets changed with CU1). This prevents a stale Status value being restored in step (5) above and persisting through subsequent context switches. Also switch to the use of definitions from asm/mipsregs.h while we're at it. Since this change also affects the restoration of Status register on the path back to userland, it increases the sensitivity of the kernel to the problem of the FPU being left enabled, allowing it to propagate to userland, therefore a warning is also added to lose_fpu_inatomic() to point out any future reoccurances before they do any damage. Signed-off-by: James Hogan Reviewed-by: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12303/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/fpu.h | 4 ++++ arch/mips/include/asm/stackframe.h | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h index 9cbf383b8834..f06f97bd62df 100644 --- a/arch/mips/include/asm/fpu.h +++ b/arch/mips/include/asm/fpu.h @@ -179,6 +179,10 @@ static inline void lose_fpu_inatomic(int save, struct task_struct *tsk) if (save) _save_fp(tsk); __disable_fpu(); + } else { + /* FPU should not have been left enabled with no owner */ + WARN(read_c0_status() & ST0_CU1, + "Orphaned FPU left enabled"); } KSTK_STATUS(tsk) &= ~ST0_CU1; clear_tsk_thread_flag(tsk, TIF_USEDFPU); diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h index a71da576883c..eebf39549606 100644 --- a/arch/mips/include/asm/stackframe.h +++ b/arch/mips/include/asm/stackframe.h @@ -289,7 +289,7 @@ .set reorder .set noat mfc0 a0, CP0_STATUS - li v1, 0xff00 + li v1, ST0_CU1 | ST0_IM ori a0, STATMASK xori a0, STATMASK mtc0 a0, CP0_STATUS @@ -330,7 +330,7 @@ ori a0, STATMASK xori a0, STATMASK mtc0 a0, CP0_STATUS - li v1, 0xff00 + li v1, ST0_CU1 | ST0_FR | ST0_IM and a0, v1 LONG_L v0, PT_STATUS(sp) nor v1, $0, v1 -- cgit v1.2.3 From 87bee0ecf01d2ed0d48bba1fb12c954f9476d243 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 31 Jan 2016 17:40:01 -0800 Subject: MAINTAINERS: Remove stale entry for BCM33xx chips Commit 70371cef114ca ("MAINTAINERS: Add entry for BMIPS multiplatform kernel") supersedes this entry for BCM33xx. Fixes: 70371cef114ca ("MAINTAINERS: Add entry for BMIPS multiplatform kernel") Signed-off-by: Florian Fainelli Cc: blogic@openwrt.org Cc: cernekee@gmail.com Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12301/ Signed-off-by: Ralf Baechle --- MAINTAINERS | 8 -------- 1 file changed, 8 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 30aca4aa5467..3361093cd799 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2357,14 +2357,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git S: Maintained N: bcm2835 -BROADCOM BCM33XX MIPS ARCHITECTURE -M: Kevin Cernekee -L: linux-mips@linux-mips.org -S: Maintained -F: arch/mips/bcm3384/* -F: arch/mips/include/asm/mach-bcm3384/* -F: arch/mips/kernel/*bmips* - BROADCOM BCM47XX MIPS ARCHITECTURE M: Hauke Mehrtens M: RafaÅ‚ MiÅ‚ecki -- cgit v1.2.3 From 10f6d99f0fb186bbca1e9e2905d0d3693f941396 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sat, 30 Jan 2016 09:08:16 +0000 Subject: MIPS: traps.c: Don't emulate RDHWR in the CpU #0 exception handler In the regular MIPS instruction set RDHWR is encoded with the SPECIAL3 (011111) major opcode. Therefore it cannot trigger the CpU (Coprocessor Unusable) exception, and certainly not for coprocessor 0, as the opcode does not overlap with any of the older ISA reservations, i.e. LWC0 (110000), SWC0 (111000), LDC0 (110100) or SDC0 (111100). The closest match might be SDC3 (111111), possibly causing a CpU #3 exception, however our code does not handle it anyway. A quick check with a MIPS I and a MIPS III processor: CPU0 revision is: 00000220 (R3000) CPU0 revision is: 00000440 (R4400SC) indeed indicates that the RI (Reserved Instruction) exception is triggered. It's only LL and SC that require emulation in the CpU #0 exception handler as they reuse the LWC0 and SWC0 opcodes respectively. In the microMIPS instruction set RDHWR is mandatory and triggering the RI exception is required on unimplemented or disabled register accesses. Therefore emulating the microMIPS instruction in the CpU #0 exception handler is not required either. Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12280/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/traps.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index bafcb7ad5c85..485b0d517177 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -1369,26 +1369,12 @@ asmlinkage void do_cpu(struct pt_regs *regs) if (unlikely(compute_return_epc(regs) < 0)) break; - if (get_isa16_mode(regs->cp0_epc)) { - unsigned short mmop[2] = { 0 }; - - if (unlikely(get_user(mmop[0], epc) < 0)) - status = SIGSEGV; - if (unlikely(get_user(mmop[1], epc) < 0)) - status = SIGSEGV; - opcode = (mmop[0] << 16) | mmop[1]; - - if (status < 0) - status = simulate_rdhwr_mm(regs, opcode); - } else { + if (!get_isa16_mode(regs->cp0_epc)) { if (unlikely(get_user(opcode, epc) < 0)) status = SIGSEGV; if (!cpu_has_llsc && status < 0) status = simulate_llsc(regs, opcode); - - if (status < 0) - status = simulate_rdhwr_normal(regs, opcode); } if (status < 0) -- cgit v1.2.3 From 7aa7047100113ec9f5e4e685f94223825bd74a7b Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sat, 30 Jan 2016 09:08:28 +0000 Subject: MIPS: traps.c: Correct microMIPS RDHWR emulation Fix the code to fetch and decode the whole 32-bit instruction. This only really matters with the `noulri' kernel parameter as all microMIPS processors are supposed to have all the hardware registers we support. Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12281/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/traps.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 485b0d517177..ae790c575d4f 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -663,7 +663,7 @@ static int simulate_rdhwr_normal(struct pt_regs *regs, unsigned int opcode) return -1; } -static int simulate_rdhwr_mm(struct pt_regs *regs, unsigned short opcode) +static int simulate_rdhwr_mm(struct pt_regs *regs, unsigned int opcode) { if ((opcode & MM_POOL32A_FUNC) == MM_RDHWR) { int rd = (opcode & MM_RS) >> 16; @@ -1119,11 +1119,12 @@ no_r2_instr: if (get_isa16_mode(regs->cp0_epc)) { unsigned short mmop[2] = { 0 }; - if (unlikely(get_user(mmop[0], epc) < 0)) + if (unlikely(get_user(mmop[0], (u16 __user *)epc + 0) < 0)) status = SIGSEGV; - if (unlikely(get_user(mmop[1], epc) < 0)) + if (unlikely(get_user(mmop[1], (u16 __user *)epc + 1) < 0)) status = SIGSEGV; - opcode = (mmop[0] << 16) | mmop[1]; + opcode = mmop[0]; + opcode = (opcode << 16) | mmop[1]; if (status < 0) status = simulate_rdhwr_mm(regs, opcode); -- cgit v1.2.3 From 41c89159a6ae5472d39ed8bded5b3b4e07a37944 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 29 Jan 2016 00:21:26 +0100 Subject: mmc: pxamci: fix again read-only gpio detection polarity The commit fixing the conversion of pxamci to slot-gpio API fixed the inverted the logic of the read-only gpio. Unfortunately, the commit was tested on a non-inverted gpio, and not on the inverted one. And the fix did work partially, by luck. This is the remaining missing part of the fix, trivial but still necessary. Fixes: Fixes: 26d49fe71953 ("mmc: pxamci: fix read-only gpio detection polarity") Reported-by: Andrea Adami Tested-by: Andrea Adami Signed-off-by: Robert Jarzmik Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index ce08896b9d69..28a057fae0a1 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -804,7 +804,7 @@ static int pxamci_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); goto out; } else { - mmc->caps |= host->pdata->gpio_card_ro_invert ? + mmc->caps2 |= host->pdata->gpio_card_ro_invert ? 0 : MMC_CAP2_RO_ACTIVE_HIGH; } -- cgit v1.2.3 From 2df9d58fcc0f20d812f390e0b97df69d70d3152f Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 2 Feb 2016 19:55:06 +0800 Subject: mmc: sdhci-of-at91: fix pm runtime unbalanced issue in error path The device power usage counter is increased by pm_runtime_get_noresume but isn't decreased in err_add_host error path. Fix this issue by calling pm_runtime_put_noidle() in the error path to restore the device's power usage counter. Signed-off-by: Jisheng Zhang Acked-by: Ludovic Desroches Fixes: f5f17813ae9b ("mmc: sdhci-of-at91: add PM support) Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-at91.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 7e7d8f0c9438..9cb86fb25976 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -217,6 +217,7 @@ static int sdhci_at91_probe(struct platform_device *pdev) pm_runtime_disable: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); clocks_disable_unprepare: clk_disable_unprepare(priv->gck); clk_disable_unprepare(priv->mainck); -- cgit v1.2.3 From 0f26922fe5dc5724b1adbbd54b21bad03590b4f3 Mon Sep 17 00:00:00 2001 From: zengtao Date: Tue, 2 Feb 2016 11:38:34 +0800 Subject: cputime: Prevent 32bit overflow in time[val|spec]_to_cputime() The datatype __kernel_time_t is u32 on 32bit platform, so its subject to overflows in the timeval/timespec to cputime conversion. Currently the following functions are affected: 1. setitimer() 2. timer_create/timer_settime() 3. sys_clock_nanosleep This can happen on MIPS32 and ARM32 with "Full dynticks CPU time accounting" enabled, which is required for CONFIG_NO_HZ_FULL. Enforce u64 conversion to prevent the overflow. Fixes: 31c1fc818715 ("ARM: Kconfig: allow full nohz CPU accounting") Signed-off-by: zengtao Reviewed-by: Arnd Bergmann Cc: Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1454384314-154784-1-git-send-email-prime.zeng@huawei.com Signed-off-by: Thomas Gleixner --- include/asm-generic/cputime_nsecs.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/asm-generic/cputime_nsecs.h b/include/asm-generic/cputime_nsecs.h index 0419485891f2..0f1c6f315cdc 100644 --- a/include/asm-generic/cputime_nsecs.h +++ b/include/asm-generic/cputime_nsecs.h @@ -75,7 +75,7 @@ typedef u64 __nocast cputime64_t; */ static inline cputime_t timespec_to_cputime(const struct timespec *val) { - u64 ret = val->tv_sec * NSEC_PER_SEC + val->tv_nsec; + u64 ret = (u64)val->tv_sec * NSEC_PER_SEC + val->tv_nsec; return (__force cputime_t) ret; } static inline void cputime_to_timespec(const cputime_t ct, struct timespec *val) @@ -91,7 +91,8 @@ static inline void cputime_to_timespec(const cputime_t ct, struct timespec *val) */ static inline cputime_t timeval_to_cputime(const struct timeval *val) { - u64 ret = val->tv_sec * NSEC_PER_SEC + val->tv_usec * NSEC_PER_USEC; + u64 ret = (u64)val->tv_sec * NSEC_PER_SEC + + val->tv_usec * NSEC_PER_USEC; return (__force cputime_t) ret; } static inline void cputime_to_timeval(const cputime_t ct, struct timeval *val) -- cgit v1.2.3 From 6235f0ecc4ed799169b80f7317c7f974f7415320 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 1 Feb 2016 17:39:20 +0000 Subject: irqchip/sun4i: Fix compilation outside of arch/arm The Allwinner sunxi specific interrupt controller cannot be compiled for any architecture except arm: drivers/irqchip/irq-sun4i.c:25:26: fatal error: asm/mach/irq.h: No such file or directory compilation terminated. It turns out that this header is actually not needed for the driver, so remove it and allow compilation for other architectures like arm64. Signed-off-by: Andre Przywara Acked-by: Arnd Bergmann Cc: linux-arm-kernel@lists.infradead.org Cc: Jason Cooper Cc: Marc Zyngier Cc: Maxime Ripard Cc: Chen-Yu Tsai Cc: linux-sunxi@googlegroups.com Link: http://lkml.kernel.org/r/1454348370-3816-2-git-send-email-andre.przywara@arm.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-sun4i.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 0704362f4c82..376b28074e0d 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -22,7 +22,6 @@ #include #include -#include #define SUN4I_IRQ_VECTOR_REG 0x00 #define SUN4I_IRQ_PROTECTION_REG 0x08 -- cgit v1.2.3 From 3a4acda1ecbd290973de08250d7dcdfaf5b2fe0f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Feb 2016 03:21:04 +0000 Subject: perf tools: Fix thread lifetime related segfaut in intel_pt intel_pt_process_auxtrace_info() creates a pt->unknown_thread thread that eventually needs to be freed by the last thread__put() on it, when its refcount hits zero, which may happen in intel_pt_process_auxtrace_info() error handling path and triggers the following segfault, which would happen as well at intel_pt_free, when tools using this intel_pt codebase frees up resources: # perf record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls 0 a anaconda-ks.cfg bin perf.data perf.data.old perf-f23-bringup.todo [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.217 MB perf.data ] # # perf script -F event,comm,pid,tid,time,addr,ip,sym,dso,iregs Samples for 'instructions:u' event do not have IREGS attribute set. Cannot print 'iregs' field. intel_pt_synth_events: failed to synthesize 'instructions' event type Segmentation fault (core dumped) # The problem is: there's a union in 'struct thread' combines a list_head and a rb_node. The standard life cycle of a thread is: init rb_node in the constructor, insert it into machine->threads rbtree using rb_node, move it to machine->dead_threads using list_head, clean in the last thread__put: list_del_init(&thread->node). In the above command, it clean a thread before adding it into list, causes the above segfault. Since pt->unknown_thread will never live in an rbtree, initialize its list node so that when list_del_init() is done on it we don't segfault. After this patch: # perf script -F event,comm,pid,tid,time,addr,ip,sym,dso,iregs Samples for 'instructions:u' event do not have IREGS attribute set. Cannot print 'iregs' field. intel_pt_synth_events: failed to synthesize 'instructions' event type 0x248 [0x88]: failed to process type: 70 # Reported-by: Tong Zhang Reported-by: Wang Nan Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Josh Poimboeuf Link: http://lkml.kernel.org/r/1454296865-19749-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 81a2eb77ba7f..05d815851be1 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -2068,6 +2068,15 @@ int intel_pt_process_auxtrace_info(union perf_event *event, err = -ENOMEM; goto err_free_queues; } + + /* + * Since this thread will not be kept in any rbtree not in a + * list, initialize its list node so that at thread__put() the + * current thread lifetime assuption is kept and we don't segfault + * at list_del_init(). + */ + INIT_LIST_HEAD(&pt->unknown_thread->node); + err = thread__set_comm(pt->unknown_thread, "unknown", 0); if (err) goto err_delete_thread; -- cgit v1.2.3 From 270bde1e76f400d81f8d0ab68905a18ee17fa2e8 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Tue, 2 Feb 2016 20:56:46 +0530 Subject: perf probe: Search both .eh_frame and .debug_frame sections for probe location 'perf probe' through debuginfo__find_probes() in util/probe-finder.c checks for the functions' frame descriptions in either .eh_frame section of an ELF or the .debug_frame. The check is based on whether either one of these sections is present. Depending on distro, toolchain defaults, architetcutre, build flags, etc., CFI might be found in either .eh_frame and/or .debug_frame. Sometimes, it may happen that, .eh_frame, even if present, may not be complete and may miss some descriptions. Therefore, to be sure, to find the CFI covering an address we will always have to investigate both if available. For e.g., in powerpc, this may happen: $ gcc -g bin.c -o bin $ objdump --dwarf ./bin <1><145>: Abbrev Number: 7 (DW_TAG_subprogram) <146> DW_AT_external : 1 <146> DW_AT_name : (indirect string, offset: 0x9e): main <14a> DW_AT_decl_file : 1 <14b> DW_AT_decl_line : 39 <14c> DW_AT_prototyped : 1 <14c> DW_AT_type : <0x57> <150> DW_AT_low_pc : 0x100007b8 If the .eh_frame and .debug_frame are checked for the same binary, we will find that, .eh_frame (although present) doesn't contain a description for "main" function. But, .debug_frame has a description: 000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838 DW_CFA_advance_loc: 16 to 100007c8 DW_CFA_def_cfa_offset: 144 DW_CFA_offset_extended_sf: r65 at cfa+16 ... Due to this (since, perf checks whether .eh_frame is present and goes on searching for that address inside that frame), perf is unable to process the probes: # perf probe -x ./bin main Failed to get call frame on 0x100007b8 Error: Failed to add events. To avoid this issue, we need to check both the sections (.eh_frame and .debug_frame), which is done in this patch. Note that, we can always force everything into both .eh_frame and .debug_frame by: $ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin Signed-off-by: Hemant Kumar Acked-by: Masami Hiramatsu Cc: linuxppc-dev@lists.ozlabs.org Cc: Mark Wielaard Cc: Naveen N. Rao Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 62 +++++++++++++++++++++++++----------------- tools/perf/util/probe-finder.h | 5 +++- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 2be10fb27172..4ce5c5e18f48 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -686,8 +686,9 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) pf->fb_ops = NULL; #if _ELFUTILS_PREREQ(0, 142) } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && - pf->cfi != NULL) { - if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || + (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { + if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && + (dwarf_cfi_addrframe(pf->cfi_dbg, pf->addr, &frame) != 0)) || dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { pr_warning("Failed to get call frame on 0x%jx\n", (uintmax_t)pf->addr); @@ -1015,8 +1016,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) return DWARF_CB_OK; } -/* Find probe points from debuginfo */ -static int debuginfo__find_probes(struct debuginfo *dbg, +static int debuginfo__find_probe_location(struct debuginfo *dbg, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; @@ -1025,27 +1025,6 @@ static int debuginfo__find_probes(struct debuginfo *dbg, Dwarf_Die *diep; int ret = 0; -#if _ELFUTILS_PREREQ(0, 142) - Elf *elf; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - - /* Get the call frame information from this dwarf */ - elf = dwarf_getelf(dbg->dbg); - if (elf == NULL) - return -EINVAL; - - if (gelf_getehdr(elf, &ehdr) == NULL) - return -EINVAL; - - if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && - shdr.sh_type == SHT_PROGBITS) { - pf->cfi = dwarf_getcfi_elf(elf); - } else { - pf->cfi = dwarf_getcfi(dbg->dbg); - } -#endif - off = 0; pf->lcache = intlist__new(NULL); if (!pf->lcache) @@ -1108,6 +1087,39 @@ found: return ret; } +/* Find probe points from debuginfo */ +static int debuginfo__find_probes(struct debuginfo *dbg, + struct probe_finder *pf) +{ + int ret = 0; + +#if _ELFUTILS_PREREQ(0, 142) + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + + if (pf->cfi_eh || pf->cfi_dbg) + return debuginfo__find_probe_location(dbg, pf); + + /* Get the call frame information from this dwarf */ + elf = dwarf_getelf(dbg->dbg); + if (elf == NULL) + return -EINVAL; + + if (gelf_getehdr(elf, &ehdr) == NULL) + return -EINVAL; + + if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && + shdr.sh_type == SHT_PROGBITS) + pf->cfi_eh = dwarf_getcfi_elf(elf); + + pf->cfi_dbg = dwarf_getcfi(dbg->dbg); +#endif + + ret = debuginfo__find_probe_location(dbg, pf); + return ret; +} + struct local_vars_finder { struct probe_finder *pf; struct perf_probe_arg *args; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bed82716e1b4..0aec7704e395 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -76,7 +76,10 @@ struct probe_finder { /* For variable searching */ #if _ELFUTILS_PREREQ(0, 142) - Dwarf_CFI *cfi; /* Call Frame Information */ + /* Call Frame Information from .eh_frame */ + Dwarf_CFI *cfi_eh; + /* Call Frame Information from .debug_frame */ + Dwarf_CFI *cfi_dbg; #endif Dwarf_Op *fb_ops; /* Frame base attribute */ struct perf_probe_arg *pvar; /* Current target variable */ -- cgit v1.2.3 From d2f916aaccaf7b3bc27df2fd6cfc00f6cda2f78d Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 1 Feb 2016 15:54:37 +0000 Subject: ASoC: dwc: Ensure i2s_reg_comp{1,2} is always initialised In the case that the driver is configured from device-tree i2s_reg_comp1 and i2s_reg_comp2 aren't initialised, breaking the driver. Fix this by unconditionally setting these values before checking for quirks. Fixes: a242cac1d3aa ("ASoC: dwc: add quirk to override COMP_PARAM_1 register") Signed-off-by: Jon Medhurst Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index ce664c239be3..bff258d7bcea 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -645,6 +645,8 @@ static int dw_i2s_probe(struct platform_device *pdev) dev->dev = &pdev->dev; + dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; + dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; if (pdata) { dev->capability = pdata->cap; clk_id = NULL; @@ -652,9 +654,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) { dev->i2s_reg_comp1 = pdata->i2s_reg_comp1; dev->i2s_reg_comp2 = pdata->i2s_reg_comp2; - } else { - dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; - dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; } ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { -- cgit v1.2.3 From 5e82d2be6ee53275c72e964507518d7964c82753 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 1 Feb 2016 22:26:40 +0530 Subject: ASoC: dpcm: fix the BE state on hw_free While performing hw_free, DPCM checks the BE state but leaves out the suspend state. The suspend state needs to be checked as well, as we might be suspended and then usermode closes rather than resuming the audio stream. This was found by a stress testing of system with playback in loop and killed after few seconds running in background and second script running suspend-resume test in loop Signed-off-by: Vinod Koul Acked-by: Liam Girdwood Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/soc-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e898b427be7e..1af4f23697a7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1810,7 +1810,8 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) continue; dev_dbg(be->dev, "ASoC: hw_free BE %s\n", -- cgit v1.2.3 From 292d4200a90715ac29f3763df27adb38a243868c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 2 Feb 2016 12:49:49 -0600 Subject: ASoC: Intel: Atom: fix regression on compress DAI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a106804 ("ASoC: compress: Fix compress device direction check") added a dependency on the compress-cpu-dai channel_min field which was removed earlier by commit 77095796 ("ASoC: Intel: Atom: clean-up compressed DAI definition") as part of the baytrail cleanups. The net result was a regression at probe on all Atom platforms with no sound card created. Fix by adding explicit initialization for channel_min to 1 for the compress-cpu-dai. Reported-by: Tobias Mädel Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 55c33dc76ce4..52ed434cbca6 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -528,6 +528,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", + .channels_min = 1, }, }, /* BE CPU Dais */ -- cgit v1.2.3 From d896910f381737a139686eb3fa9e1c7ce8f59e52 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 Jan 2016 14:40:24 +0100 Subject: phy: Restrict phy-hi6220-usb to HiSilicon arm64 The HiSilicon Hi6220 USB PHY is available in HiSilicon Hi6220 SoCs only. Restrict it to HiSilicon arm64, unless compile-testing. Signed-off-by: Geert Uytterhoeven Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e7e117d5dbbe..0124d17bd9fe 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -224,6 +224,7 @@ config PHY_MT65XX_USB3 config PHY_HI6220_USB tristate "hi6220 USB PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON help -- cgit v1.2.3 From 4355efbd80482a961cae849281a8ef866e53d55c Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 3 Feb 2016 16:55:26 +1030 Subject: modules: fix modparam async_probe request Commit f2411da746985 ("driver-core: add driver module asynchronous probe support") added async probe support, in two forms: * in-kernel driver specification annotation * generic async_probe module parameter (modprobe foo async_probe) To support the generic kernel parameter parse_args() was extended via commit ecc8617053e0 ("module: add extra argument for parse_params() callback") however commit failed to f2411da746985 failed to add the required argument. This causes a crash then whenever async_probe generic module parameter is used. This was overlooked when the form in which in-kernel async probe support was reworked a bit... Fix this as originally intended. Cc: Hannes Reinecke Cc: Dmitry Torokhov Cc: stable@vger.kernel.org (4.2+) Signed-off-by: Luis R. Rodriguez Signed-off-by: Rusty Russell [minimized] --- kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/module.c b/kernel/module.c index 8358f4697c0c..2149f7003e49 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3496,7 +3496,7 @@ static int load_module(struct load_info *info, const char __user *uargs, /* Module is ready to execute: parsing args may do that. */ after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, - -32768, 32767, NULL, + -32768, 32767, mod, unknown_module_param_cb); if (IS_ERR(after_dashes)) { err = PTR_ERR(after_dashes); -- cgit v1.2.3 From 2e7bac536106236104e9e339531ff0fcdb7b8147 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Feb 2016 16:55:26 +1030 Subject: module: wrapper for symbol name. This trivial wrapper adds clarity and makes the following patch smaller. Cc: stable@kernel.org Signed-off-by: Rusty Russell --- kernel/module.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 2149f7003e49..1e79d8157712 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3627,6 +3627,11 @@ static inline int is_arm_mapping_symbol(const char *str) && (str[2] == '\0' || str[2] == '.'); } +static const char *symname(struct module *mod, unsigned int symnum) +{ + return mod->strtab + mod->symtab[symnum].st_name; +} + static const char *get_ksymbol(struct module *mod, unsigned long addr, unsigned long *size, @@ -3649,15 +3654,15 @@ static const char *get_ksymbol(struct module *mod, /* We ignore unnamed symbols: they're uninformative * and inserted at a whim. */ + if (*symname(mod, i) == '\0' + || is_arm_mapping_symbol(symname(mod, i))) + continue; + if (mod->symtab[i].st_value <= addr - && mod->symtab[i].st_value > mod->symtab[best].st_value - && *(mod->strtab + mod->symtab[i].st_name) != '\0' - && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) + && mod->symtab[i].st_value > mod->symtab[best].st_value) best = i; if (mod->symtab[i].st_value > addr - && mod->symtab[i].st_value < nextval - && *(mod->strtab + mod->symtab[i].st_name) != '\0' - && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) + && mod->symtab[i].st_value < nextval) nextval = mod->symtab[i].st_value; } @@ -3668,7 +3673,7 @@ static const char *get_ksymbol(struct module *mod, *size = nextval - mod->symtab[best].st_value; if (offset) *offset = addr - mod->symtab[best].st_value; - return mod->strtab + mod->symtab[best].st_name; + return symname(mod, best); } /* For kallsyms to ask for address resolution. NULL means not found. Careful @@ -3763,8 +3768,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; *type = mod->symtab[symnum].st_info; - strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, - KSYM_NAME_LEN); + strlcpy(name, symname(mod, symnum), KSYM_NAME_LEN); strlcpy(module_name, mod->name, MODULE_NAME_LEN); *exported = is_exported(name, *value, mod); preempt_enable(); @@ -3781,7 +3785,7 @@ static unsigned long mod_find_symname(struct module *mod, const char *name) unsigned int i; for (i = 0; i < mod->num_symtab; i++) - if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 && + if (strcmp(name, symname(mod, i)) == 0 && mod->symtab[i].st_info != 'U') return mod->symtab[i].st_value; return 0; @@ -3825,7 +3829,7 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, if (mod->state == MODULE_STATE_UNFORMED) continue; for (i = 0; i < mod->num_symtab; i++) { - ret = fn(data, mod->strtab + mod->symtab[i].st_name, + ret = fn(data, symname(mod, i), mod, mod->symtab[i].st_value); if (ret != 0) return ret; -- cgit v1.2.3 From 8244062ef1e54502ef55f54cced659913f244c3e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Feb 2016 16:55:26 +1030 Subject: modules: fix longstanding /proc/kallsyms vs module insertion race. For CONFIG_KALLSYMS, we keep two symbol tables and two string tables. There's one full copy, marked SHF_ALLOC and laid out at the end of the module's init section. There's also a cut-down version that only contains core symbols and strings, and lives in the module's core section. After module init (and before we free the module memory), we switch the mod->symtab, mod->num_symtab and mod->strtab to point to the core versions. We do this under the module_mutex. However, kallsyms doesn't take the module_mutex: it uses preempt_disable() and rcu tricks to walk through the modules, because it's used in the oops path. It's also used in /proc/kallsyms. There's nothing atomic about the change of these variables, so we can get the old (larger!) num_symtab and the new symtab pointer; in fact this is what I saw when trying to reproduce. By grouping these variables together, we can use a carefully-dereferenced pointer to ensure we always get one or the other (the free of the module init section is already done in an RCU callback, so that's safe). We allocate the init one at the end of the module init section, and keep the core one inside the struct module itself (it could also have been allocated at the end of the module core, but that's probably overkill). Reported-by: Weilong Chen Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111541 Cc: stable@kernel.org Signed-off-by: Rusty Russell --- include/linux/module.h | 19 +++++---- kernel/module.c | 112 ++++++++++++++++++++++++++++++------------------- 2 files changed, 79 insertions(+), 52 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index 4560d8f1545d..2bb0c3085706 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -324,6 +324,12 @@ struct module_layout { #define __module_layout_align #endif +struct mod_kallsyms { + Elf_Sym *symtab; + unsigned int num_symtab; + char *strtab; +}; + struct module { enum module_state state; @@ -405,15 +411,10 @@ struct module { #endif #ifdef CONFIG_KALLSYMS - /* - * We keep the symbol and string tables for kallsyms. - * The core_* fields below are temporary, loader-only (they - * could really be discarded after module init). - */ - Elf_Sym *symtab, *core_symtab; - unsigned int num_symtab, core_num_syms; - char *strtab, *core_strtab; - + /* Protected by RCU and/or module_mutex: use rcu_dereference() */ + struct mod_kallsyms *kallsyms; + struct mod_kallsyms core_kallsyms; + /* Section attributes */ struct module_sect_attrs *sect_attrs; diff --git a/kernel/module.c b/kernel/module.c index 1e79d8157712..9537da37ce87 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -303,6 +303,9 @@ struct load_info { struct _ddebug *debug; unsigned int num_debug; bool sig_ok; +#ifdef CONFIG_KALLSYMS + unsigned long mod_kallsyms_init_off; +#endif struct { unsigned int sym, str, mod, vers, info, pcpu; } index; @@ -2480,10 +2483,21 @@ static void layout_symtab(struct module *mod, struct load_info *info) strsect->sh_flags |= SHF_ALLOC; strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, info->index.str) | INIT_OFFSET_MASK; - mod->init_layout.size = debug_align(mod->init_layout.size); pr_debug("\t%s\n", info->secstrings + strsect->sh_name); + + /* We'll tack temporary mod_kallsyms on the end. */ + mod->init_layout.size = ALIGN(mod->init_layout.size, + __alignof__(struct mod_kallsyms)); + info->mod_kallsyms_init_off = mod->init_layout.size; + mod->init_layout.size += sizeof(struct mod_kallsyms); + mod->init_layout.size = debug_align(mod->init_layout.size); } +/* + * We use the full symtab and strtab which layout_symtab arranged to + * be appended to the init section. Later we switch to the cut-down + * core-only ones. + */ static void add_kallsyms(struct module *mod, const struct load_info *info) { unsigned int i, ndst; @@ -2492,29 +2506,34 @@ static void add_kallsyms(struct module *mod, const struct load_info *info) char *s; Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; - mod->symtab = (void *)symsec->sh_addr; - mod->num_symtab = symsec->sh_size / sizeof(Elf_Sym); + /* Set up to point into init section. */ + mod->kallsyms = mod->init_layout.base + info->mod_kallsyms_init_off; + + mod->kallsyms->symtab = (void *)symsec->sh_addr; + mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym); /* Make sure we get permanent strtab: don't use info->strtab. */ - mod->strtab = (void *)info->sechdrs[info->index.str].sh_addr; + mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr; /* Set types up while we still have access to sections. */ - for (i = 0; i < mod->num_symtab; i++) - mod->symtab[i].st_info = elf_type(&mod->symtab[i], info); - - mod->core_symtab = dst = mod->core_layout.base + info->symoffs; - mod->core_strtab = s = mod->core_layout.base + info->stroffs; - src = mod->symtab; - for (ndst = i = 0; i < mod->num_symtab; i++) { + for (i = 0; i < mod->kallsyms->num_symtab; i++) + mod->kallsyms->symtab[i].st_info + = elf_type(&mod->kallsyms->symtab[i], info); + + /* Now populate the cut down core kallsyms for after init. */ + mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; + mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; + src = mod->kallsyms->symtab; + for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { if (i == 0 || is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, info->index.pcpu)) { dst[ndst] = src[i]; - dst[ndst++].st_name = s - mod->core_strtab; - s += strlcpy(s, &mod->strtab[src[i].st_name], + dst[ndst++].st_name = s - mod->core_kallsyms.strtab; + s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name], KSYM_NAME_LEN) + 1; } } - mod->core_num_syms = ndst; + mod->core_kallsyms.num_symtab = ndst; } #else static inline void layout_symtab(struct module *mod, struct load_info *info) @@ -3263,9 +3282,8 @@ static noinline int do_init_module(struct module *mod) module_put(mod); trim_init_extable(mod); #ifdef CONFIG_KALLSYMS - mod->num_symtab = mod->core_num_syms; - mod->symtab = mod->core_symtab; - mod->strtab = mod->core_strtab; + /* Switch to core kallsyms now init is done: kallsyms may be walking! */ + rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms); #endif mod_tree_remove_init(mod); disable_ro_nx(&mod->init_layout); @@ -3627,9 +3645,9 @@ static inline int is_arm_mapping_symbol(const char *str) && (str[2] == '\0' || str[2] == '.'); } -static const char *symname(struct module *mod, unsigned int symnum) +static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum) { - return mod->strtab + mod->symtab[symnum].st_name; + return kallsyms->strtab + kallsyms->symtab[symnum].st_name; } static const char *get_ksymbol(struct module *mod, @@ -3639,6 +3657,7 @@ static const char *get_ksymbol(struct module *mod, { unsigned int i, best = 0; unsigned long nextval; + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); /* At worse, next value is at end of module */ if (within_module_init(addr, mod)) @@ -3648,32 +3667,32 @@ static const char *get_ksymbol(struct module *mod, /* Scan for closest preceding symbol, and next symbol. (ELF starts real symbols at 1). */ - for (i = 1; i < mod->num_symtab; i++) { - if (mod->symtab[i].st_shndx == SHN_UNDEF) + for (i = 1; i < kallsyms->num_symtab; i++) { + if (kallsyms->symtab[i].st_shndx == SHN_UNDEF) continue; /* We ignore unnamed symbols: they're uninformative * and inserted at a whim. */ - if (*symname(mod, i) == '\0' - || is_arm_mapping_symbol(symname(mod, i))) + if (*symname(kallsyms, i) == '\0' + || is_arm_mapping_symbol(symname(kallsyms, i))) continue; - if (mod->symtab[i].st_value <= addr - && mod->symtab[i].st_value > mod->symtab[best].st_value) + if (kallsyms->symtab[i].st_value <= addr + && kallsyms->symtab[i].st_value > kallsyms->symtab[best].st_value) best = i; - if (mod->symtab[i].st_value > addr - && mod->symtab[i].st_value < nextval) - nextval = mod->symtab[i].st_value; + if (kallsyms->symtab[i].st_value > addr + && kallsyms->symtab[i].st_value < nextval) + nextval = kallsyms->symtab[i].st_value; } if (!best) return NULL; if (size) - *size = nextval - mod->symtab[best].st_value; + *size = nextval - kallsyms->symtab[best].st_value; if (offset) - *offset = addr - mod->symtab[best].st_value; - return symname(mod, best); + *offset = addr - kallsyms->symtab[best].st_value; + return symname(kallsyms, best); } /* For kallsyms to ask for address resolution. NULL means not found. Careful @@ -3763,18 +3782,21 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + struct mod_kallsyms *kallsyms; + if (mod->state == MODULE_STATE_UNFORMED) continue; - if (symnum < mod->num_symtab) { - *value = mod->symtab[symnum].st_value; - *type = mod->symtab[symnum].st_info; - strlcpy(name, symname(mod, symnum), KSYM_NAME_LEN); + kallsyms = rcu_dereference_sched(mod->kallsyms); + if (symnum < kallsyms->num_symtab) { + *value = kallsyms->symtab[symnum].st_value; + *type = kallsyms->symtab[symnum].st_info; + strlcpy(name, symname(kallsyms, symnum), KSYM_NAME_LEN); strlcpy(module_name, mod->name, MODULE_NAME_LEN); *exported = is_exported(name, *value, mod); preempt_enable(); return 0; } - symnum -= mod->num_symtab; + symnum -= kallsyms->num_symtab; } preempt_enable(); return -ERANGE; @@ -3783,11 +3805,12 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, static unsigned long mod_find_symname(struct module *mod, const char *name) { unsigned int i; + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); - for (i = 0; i < mod->num_symtab; i++) - if (strcmp(name, symname(mod, i)) == 0 && - mod->symtab[i].st_info != 'U') - return mod->symtab[i].st_value; + for (i = 0; i < kallsyms->num_symtab; i++) + if (strcmp(name, symname(kallsyms, i)) == 0 && + kallsyms->symtab[i].st_info != 'U') + return kallsyms->symtab[i].st_value; return 0; } @@ -3826,11 +3849,14 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, module_assert_mutex(); list_for_each_entry(mod, &modules, list) { + /* We hold module_mutex: no need for rcu_dereference_sched */ + struct mod_kallsyms *kallsyms = mod->kallsyms; + if (mod->state == MODULE_STATE_UNFORMED) continue; - for (i = 0; i < mod->num_symtab; i++) { - ret = fn(data, symname(mod, i), - mod, mod->symtab[i].st_value); + for (i = 0; i < kallsyms->num_symtab; i++) { + ret = fn(data, symname(kallsyms, i), + mod, kallsyms->symtab[i].st_value); if (ret != 0) return ret; } -- cgit v1.2.3 From 74c81ecdc0e37b917d7c6358ed72dc8337d8900f Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 3 Feb 2016 10:32:22 +0100 Subject: MIPS: R6000: Don't allow 64k pages for R6000. The R6000 does not support 64k pages. Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 57a945e832f4..74a3db92da1b 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2085,7 +2085,7 @@ config PAGE_SIZE_32KB config PAGE_SIZE_64KB bool "64kB" - depends on !CPU_R3000 && !CPU_TX39XX + depends on !CPU_R3000 && !CPU_TX39XX && !CPU_R6000 help Using 64kB page size will result in higher performance kernel at the price of higher memory consumption. This option is available on -- cgit v1.2.3 From e0bd70c67bf996b360f706b6c643000f2e384681 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 27 Jan 2016 10:20:58 +0000 Subject: Btrfs: fix invalid page accesses in extent_same (dedup) ioctl In the extent_same ioctl we are getting the pages for the source and target ranges and unlocking them immediately after, which is incorrect because later we attempt to map them (with kmap_atomic) and access their contents at btrfs_cmp_data(). When we do such access the pages might have been relocated or removed from memory, which leads to an invalid memory access. This issue is detected on a kernel with CONFIG_DEBUG_PAGEALLOC=y which produces a trace like the following: 186736.677437] general protection fault: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC [186736.680382] Modules linked in: btrfs dm_flakey dm_mod ppdev xor raid6_pq sha256_generic hmac drbg ansi_cprng acpi_cpufreq evdev sg aesni_intel aes_x86_64 parport_pc ablk_helper tpm_tis psmouse parport i2c_piix4 tpm cryptd i2c_core lrw processor button serio_raw pcspkr gf128mul glue_helper loop autofs4 ext4 crc16 mbcache jbd2 sd_mod sr_mod cdrom ata_generic virtio_scsi ata_piix libata virtio_pci virtio_ring crc32c_intel scsi_mod e1000 virtio floppy [last unloaded: btrfs] [186736.681319] CPU: 13 PID: 10222 Comm: duperemove Tainted: G W 4.4.0-rc6-btrfs-next-18+ #1 [186736.681319] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS by qemu-project.org 04/01/2014 [186736.681319] task: ffff880132600400 ti: ffff880362284000 task.ti: ffff880362284000 [186736.681319] RIP: 0010:[] [] memcmp+0xb/0x22 [186736.681319] RSP: 0018:ffff880362287d70 EFLAGS: 00010287 [186736.681319] RAX: 000002c002468acf RBX: 0000000012345678 RCX: 0000000000000000 [186736.681319] RDX: 0000000000001000 RSI: 0005d129c5cf9000 RDI: 0005d129c5cf9000 [186736.681319] RBP: ffff880362287d70 R08: 0000000000000000 R09: 0000000000001000 [186736.681319] R10: ffff880000000000 R11: 0000000000000476 R12: 0000000000001000 [186736.681319] R13: ffff8802f91d4c88 R14: ffff8801f2a77830 R15: ffff880352e83e40 [186736.681319] FS: 00007f27b37fe700(0000) GS:ffff88043dda0000(0000) knlGS:0000000000000000 [186736.681319] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [186736.681319] CR2: 00007f27a406a000 CR3: 0000000217421000 CR4: 00000000001406e0 [186736.681319] Stack: [186736.681319] ffff880362287ea0 ffffffffa048d0bd 000000000009f000 0000000000001000 [186736.681319] 0100000000000000 ffff8801f2a77850 ffff8802f91d49b0 ffff880132600400 [186736.681319] 00000000000004f8 ffff8801c1efbe41 0000000000000000 0000000000000038 [186736.681319] Call Trace: [186736.681319] [] btrfs_ioctl+0x24cb/0x2731 [btrfs] [186736.681319] [] ? arch_local_irq_save+0x9/0xc [186736.681319] [] ? rcu_read_unlock+0x3e/0x5d [186736.681319] [] do_vfs_ioctl+0x42b/0x4ea [186736.681319] [] ? __fget_light+0x62/0x71 [186736.681319] [] SyS_ioctl+0x57/0x79 [186736.681319] [] entry_SYSCALL_64_fastpath+0x12/0x6f [186736.681319] Code: 0a 3c 6e 74 0d 3c 79 74 04 3c 59 75 0c c6 06 01 eb 03 c6 06 00 31 c0 eb 05 b8 ea ff ff ff 5d c3 55 31 c9 48 89 e5 48 39 d1 74 13 <0f> b6 04 0f 44 0f b6 04 0e 48 ff c1 44 29 c0 74 ea eb 02 31 c0 (gdb) list *(btrfs_ioctl+0x24cb) 0x5e0e1 is in btrfs_ioctl (fs/btrfs/ioctl.c:2972). 2967 dst_addr = kmap_atomic(dst_page); 2968 2969 flush_dcache_page(src_page); 2970 flush_dcache_page(dst_page); 2971 2972 if (memcmp(addr, dst_addr, cmp_len)) 2973 ret = BTRFS_SAME_DATA_DIFFERS; 2974 2975 kunmap_atomic(addr); 2976 kunmap_atomic(dst_addr); So fix this by making sure we keep the pages locked and respect the same locking order as everywhere else: get and lock the pages first and then lock the range in the inode's io tree (like for example at __btrfs_buffered_write() and extent_readpages()). If an ordered extent is found after locking the range in the io tree, unlock the range, unlock the pages, wait for the ordered extent to complete and repeat the entire locking process until no overlapping ordered extents are found. Cc: stable@vger.kernel.org # 4.2+ Signed-off-by: Filipe Manana --- fs/btrfs/ioctl.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 83c9ad3f2621..1d6767c4c092 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2811,7 +2811,6 @@ static struct page *extent_same_get_page(struct inode *inode, pgoff_t index) return NULL; } } - unlock_page(page); return page; } @@ -2830,10 +2829,17 @@ static int gather_extent_pages(struct inode *inode, struct page **pages, return 0; } -static inline void lock_extent_range(struct inode *inode, u64 off, u64 len) +static int lock_extent_range(struct inode *inode, u64 off, u64 len, + bool retry_range_locking) { - /* do any pending delalloc/csum calc on src, one way or - another, and lock file content */ + /* + * Do any pending delalloc/csum calculations on inode, one way or + * another, and lock file content. + * The locking order is: + * + * 1) pages + * 2) range in the inode's io tree + */ while (1) { struct btrfs_ordered_extent *ordered; lock_extent(&BTRFS_I(inode)->io_tree, off, off + len - 1); @@ -2851,8 +2857,11 @@ static inline void lock_extent_range(struct inode *inode, u64 off, u64 len) unlock_extent(&BTRFS_I(inode)->io_tree, off, off + len - 1); if (ordered) btrfs_put_ordered_extent(ordered); + if (!retry_range_locking) + return -EAGAIN; btrfs_wait_ordered_range(inode, off, len); } + return 0; } static void btrfs_double_inode_unlock(struct inode *inode1, struct inode *inode2) @@ -2877,15 +2886,24 @@ static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, unlock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1); } -static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1, - struct inode *inode2, u64 loff2, u64 len) +static int btrfs_double_extent_lock(struct inode *inode1, u64 loff1, + struct inode *inode2, u64 loff2, u64 len, + bool retry_range_locking) { + int ret; + if (inode1 < inode2) { swap(inode1, inode2); swap(loff1, loff2); } - lock_extent_range(inode1, loff1, len); - lock_extent_range(inode2, loff2, len); + ret = lock_extent_range(inode1, loff1, len, retry_range_locking); + if (ret) + return ret; + ret = lock_extent_range(inode2, loff2, len, retry_range_locking); + if (ret) + unlock_extent(&BTRFS_I(inode1)->io_tree, loff1, + loff1 + len - 1); + return ret; } struct cmp_pages { @@ -2901,11 +2919,15 @@ static void btrfs_cmp_data_free(struct cmp_pages *cmp) for (i = 0; i < cmp->num_pages; i++) { pg = cmp->src_pages[i]; - if (pg) + if (pg) { + unlock_page(pg); page_cache_release(pg); + } pg = cmp->dst_pages[i]; - if (pg) + if (pg) { + unlock_page(pg); page_cache_release(pg); + } } kfree(cmp->src_pages); kfree(cmp->dst_pages); @@ -2966,6 +2988,8 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst, src_page = cmp->src_pages[i]; dst_page = cmp->dst_pages[i]; + ASSERT(PageLocked(src_page)); + ASSERT(PageLocked(dst_page)); addr = kmap_atomic(src_page); dst_addr = kmap_atomic(dst_page); @@ -3078,14 +3102,46 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, goto out_unlock; } +again: ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, &cmp); if (ret) goto out_unlock; if (same_inode) - lock_extent_range(src, same_lock_start, same_lock_len); + ret = lock_extent_range(src, same_lock_start, same_lock_len, + false); else - btrfs_double_extent_lock(src, loff, dst, dst_loff, len); + ret = btrfs_double_extent_lock(src, loff, dst, dst_loff, len, + false); + /* + * If one of the inodes has dirty pages in the respective range or + * ordered extents, we need to flush dellaloc and wait for all ordered + * extents in the range. We must unlock the pages and the ranges in the + * io trees to avoid deadlocks when flushing delalloc (requires locking + * pages) and when waiting for ordered extents to complete (they require + * range locking). + */ + if (ret == -EAGAIN) { + /* + * Ranges in the io trees already unlocked. Now unlock all + * pages before waiting for all IO to complete. + */ + btrfs_cmp_data_free(&cmp); + if (same_inode) { + btrfs_wait_ordered_range(src, same_lock_start, + same_lock_len); + } else { + btrfs_wait_ordered_range(src, loff, len); + btrfs_wait_ordered_range(dst, dst_loff, len); + } + goto again; + } + ASSERT(ret == 0); + if (WARN_ON(ret)) { + /* ranges in the io trees already unlocked */ + btrfs_cmp_data_free(&cmp); + return ret; + } /* pass original length for comparison so we stay within i_size */ ret = btrfs_cmp_data(src, loff, dst, dst_loff, olen, &cmp); @@ -3907,9 +3963,15 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, u64 lock_start = min_t(u64, off, destoff); u64 lock_len = max_t(u64, off, destoff) + len - lock_start; - lock_extent_range(src, lock_start, lock_len); + ret = lock_extent_range(src, lock_start, lock_len, true); } else { - btrfs_double_extent_lock(src, off, inode, destoff, len); + ret = btrfs_double_extent_lock(src, off, inode, destoff, len, + true); + } + ASSERT(ret == 0); + if (WARN_ON(ret)) { + /* ranges in the io trees already unlocked */ + goto out_unlock; } ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); -- cgit v1.2.3 From 313140023026ae542ad76e7e268c56a1eaa2c28e Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 27 Jan 2016 18:37:47 +0000 Subject: Btrfs: fix page reading in extent_same ioctl leading to csum errors In the extent_same ioctl, we were grabbing the pages (locked) and attempting to read them without bothering about any concurrent IO against them. That is, we were not checking for any ongoing ordered extents nor waiting for them to complete, which leads to a race where the extent_same() code gets a checksum verification error when it reads the pages, producing a message like the following in dmesg and making the operation fail to user space with -ENOMEM: [18990.161265] BTRFS warning (device sdc): csum failed ino 259 off 495616 csum 685204116 expected csum 1515870868 Fix this by using btrfs_readpage() for reading the pages instead of extent_read_full_page_nolock(), which waits for any concurrent ordered extents to complete and locks the io range. Also do better error handling and don't treat all failures as -ENOMEM, as that's clearly misleasing, becoming identical to the checks and operation of prepare_uptodate_page(). The use of extent_read_full_page_nolock() was required before commit f441460202cb ("btrfs: fix deadlock with extent-same and readpage"), as we had the range locked in an inode's io tree before attempting to read the pages. Fixes: f441460202cb ("btrfs: fix deadlock with extent-same and readpage") Cc: stable@vger.kernel.org # 4.2+ Signed-off-by: Filipe Manana --- fs/btrfs/ioctl.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 1d6767c4c092..561aa6292c1e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2794,21 +2794,27 @@ out: static struct page *extent_same_get_page(struct inode *inode, pgoff_t index) { struct page *page; - struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree; page = grab_cache_page(inode->i_mapping, index); if (!page) - return NULL; + return ERR_PTR(-ENOMEM); if (!PageUptodate(page)) { - if (extent_read_full_page_nolock(tree, page, btrfs_get_extent, - 0)) - return NULL; + int ret; + + ret = btrfs_readpage(NULL, page); + if (ret) + return ERR_PTR(ret); lock_page(page); if (!PageUptodate(page)) { unlock_page(page); page_cache_release(page); - return NULL; + return ERR_PTR(-EIO); + } + if (page->mapping != inode->i_mapping) { + unlock_page(page); + page_cache_release(page); + return ERR_PTR(-EAGAIN); } } @@ -2822,9 +2828,16 @@ static int gather_extent_pages(struct inode *inode, struct page **pages, pgoff_t index = off >> PAGE_CACHE_SHIFT; for (i = 0; i < num_pages; i++) { +again: pages[i] = extent_same_get_page(inode, index + i); - if (!pages[i]) - return -ENOMEM; + if (IS_ERR(pages[i])) { + int err = PTR_ERR(pages[i]); + + if (err == -EAGAIN) + goto again; + pages[i] = NULL; + return err; + } } return 0; } -- cgit v1.2.3 From 7f042a8370a5bb7e29a6a6372e8180a56d44aa5c Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 27 Jan 2016 19:17:20 +0000 Subject: Btrfs: remove no longer used function extent_read_full_page_nolock() Not needed after the previous patch named "Btrfs: fix page reading in extent_same ioctl leading to csum errors". Signed-off-by: Filipe Manana --- fs/btrfs/compression.c | 6 +----- fs/btrfs/extent_io.c | 45 +++++++++++---------------------------------- fs/btrfs/extent_io.h | 3 --- 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index c473c42d7d6c..3346cd8f9910 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -637,11 +637,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, faili = nr_pages - 1; cb->nr_pages = nr_pages; - /* In the parent-locked case, we only locked the range we are - * interested in. In all other cases, we can opportunistically - * cache decompressed data that goes beyond the requested range. */ - if (!(bio_flags & EXTENT_BIO_PARENT_LOCKED)) - add_ra_bio_pages(inode, em_start + em_len, cb); + add_ra_bio_pages(inode, em_start + em_len, cb); /* include any pages we added in add_ra-bio_pages */ uncompressed_len = bio->bi_vcnt * PAGE_CACHE_SIZE; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 2e7c97a3f344..392592dc7010 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2897,12 +2897,11 @@ static int __do_readpage(struct extent_io_tree *tree, struct block_device *bdev; int ret; int nr = 0; - int parent_locked = *bio_flags & EXTENT_BIO_PARENT_LOCKED; size_t pg_offset = 0; size_t iosize; size_t disk_io_size; size_t blocksize = inode->i_sb->s_blocksize; - unsigned long this_bio_flag = *bio_flags & EXTENT_BIO_PARENT_LOCKED; + unsigned long this_bio_flag = 0; set_page_extent_mapped(page); @@ -2942,18 +2941,16 @@ static int __do_readpage(struct extent_io_tree *tree, kunmap_atomic(userpage); set_extent_uptodate(tree, cur, cur + iosize - 1, &cached, GFP_NOFS); - if (!parent_locked) - unlock_extent_cached(tree, cur, - cur + iosize - 1, - &cached, GFP_NOFS); + unlock_extent_cached(tree, cur, + cur + iosize - 1, + &cached, GFP_NOFS); break; } em = __get_extent_map(inode, page, pg_offset, cur, end - cur + 1, get_extent, em_cached); if (IS_ERR_OR_NULL(em)) { SetPageError(page); - if (!parent_locked) - unlock_extent(tree, cur, end); + unlock_extent(tree, cur, end); break; } extent_offset = cur - em->start; @@ -3038,12 +3035,9 @@ static int __do_readpage(struct extent_io_tree *tree, set_extent_uptodate(tree, cur, cur + iosize - 1, &cached, GFP_NOFS); - if (parent_locked) - free_extent_state(cached); - else - unlock_extent_cached(tree, cur, - cur + iosize - 1, - &cached, GFP_NOFS); + unlock_extent_cached(tree, cur, + cur + iosize - 1, + &cached, GFP_NOFS); cur = cur + iosize; pg_offset += iosize; continue; @@ -3052,8 +3046,7 @@ static int __do_readpage(struct extent_io_tree *tree, if (test_range_bit(tree, cur, cur_end, EXTENT_UPTODATE, 1, NULL)) { check_page_uptodate(tree, page); - if (!parent_locked) - unlock_extent(tree, cur, cur + iosize - 1); + unlock_extent(tree, cur, cur + iosize - 1); cur = cur + iosize; pg_offset += iosize; continue; @@ -3063,8 +3056,7 @@ static int __do_readpage(struct extent_io_tree *tree, */ if (block_start == EXTENT_MAP_INLINE) { SetPageError(page); - if (!parent_locked) - unlock_extent(tree, cur, cur + iosize - 1); + unlock_extent(tree, cur, cur + iosize - 1); cur = cur + iosize; pg_offset += iosize; continue; @@ -3083,8 +3075,7 @@ static int __do_readpage(struct extent_io_tree *tree, *bio_flags = this_bio_flag; } else { SetPageError(page); - if (!parent_locked) - unlock_extent(tree, cur, cur + iosize - 1); + unlock_extent(tree, cur, cur + iosize - 1); } cur = cur + iosize; pg_offset += iosize; @@ -3213,20 +3204,6 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page, return ret; } -int extent_read_full_page_nolock(struct extent_io_tree *tree, struct page *page, - get_extent_t *get_extent, int mirror_num) -{ - struct bio *bio = NULL; - unsigned long bio_flags = EXTENT_BIO_PARENT_LOCKED; - int ret; - - ret = __do_readpage(tree, page, get_extent, NULL, &bio, mirror_num, - &bio_flags, READ, NULL); - if (bio) - ret = submit_one_bio(READ, bio, mirror_num, bio_flags); - return ret; -} - static noinline void update_nr_written(struct page *page, struct writeback_control *wbc, unsigned long nr_written) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 0377413bd4b9..880d5292e972 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -29,7 +29,6 @@ */ #define EXTENT_BIO_COMPRESSED 1 #define EXTENT_BIO_TREE_LOG 2 -#define EXTENT_BIO_PARENT_LOCKED 4 #define EXTENT_BIO_FLAG_SHIFT 16 /* these are bit numbers for test/set bit */ @@ -210,8 +209,6 @@ static inline int lock_extent(struct extent_io_tree *tree, u64 start, u64 end) int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end); int extent_read_full_page(struct extent_io_tree *tree, struct page *page, get_extent_t *get_extent, int mirror_num); -int extent_read_full_page_nolock(struct extent_io_tree *tree, struct page *page, - get_extent_t *get_extent, int mirror_num); int __init extent_io_init(void); void extent_io_exit(void); -- cgit v1.2.3 From febe562c20dfa8f33bee7d419c6b517986a5aa33 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 11 Jan 2016 21:31:09 -0800 Subject: target: Fix LUN_RESET active I/O handling for ACK_KREF This patch fixes a NULL pointer se_cmd->cmd_kref < 0 refcount bug during TMR LUN_RESET with active se_cmd I/O, that can be triggered during se_cmd descriptor shutdown + release via core_tmr_drain_state_list() code. To address this bug, add common __target_check_io_state() helper for ABORT_TASK + LUN_RESET w/ CMD_T_COMPLETE checking, and set CMD_T_ABORTED + obtain ->cmd_kref for both cases ahead of last target_put_sess_cmd() after TFO->aborted_task() -> transport_cmd_finish_abort() callback has completed. It also introduces SCF_ACK_KREF to determine when transport_cmd_finish_abort() needs to drop the second extra reference, ahead of calling target_put_sess_cmd() for the final kref_put(&se_cmd->cmd_kref). It also updates transport_cmd_check_stop() to avoid holding se_cmd->t_state_lock while dropping se_cmd device state via target_remove_from_state_list(), now that core_tmr_drain_state_list() is holding the se_device lock while checking se_cmd state from within TMR logic. Finally, move transport_put_cmd() release of SGL + TMR + extended CDB memory into target_free_cmd_mem() in order to avoid potential resource leaks in TMR ABORT_TASK + LUN_RESET code-paths. Also update target_release_cmd_kref() accordingly. Reviewed-by: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 69 ++++++++++++++++++++++------------ drivers/target/target_core_transport.c | 67 ++++++++++++++------------------- include/target/target_core_base.h | 1 + 3 files changed, 76 insertions(+), 61 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index fcdcb117c60d..fb3decc28589 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -107,6 +107,34 @@ static int target_check_cdb_and_preempt(struct list_head *list, return 1; } +static bool __target_check_io_state(struct se_cmd *se_cmd) +{ + struct se_session *sess = se_cmd->se_sess; + + assert_spin_locked(&sess->sess_cmd_lock); + WARN_ON_ONCE(!irqs_disabled()); + /* + * If command already reached CMD_T_COMPLETE state within + * target_complete_cmd(), this se_cmd has been passed to + * fabric driver and will not be aborted. + * + * Otherwise, obtain a local se_cmd->cmd_kref now for TMR + * ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as + * long as se_cmd->cmd_kref is still active unless zero. + */ + spin_lock(&se_cmd->t_state_lock); + if (se_cmd->transport_state & CMD_T_COMPLETE) { + pr_debug("Attempted to abort io tag: %llu already complete," + " skipping\n", se_cmd->tag); + spin_unlock(&se_cmd->t_state_lock); + return false; + } + se_cmd->transport_state |= CMD_T_ABORTED; + spin_unlock(&se_cmd->t_state_lock); + + return kref_get_unless_zero(&se_cmd->cmd_kref); +} + void core_tmr_abort_task( struct se_device *dev, struct se_tmr_req *tmr, @@ -130,34 +158,22 @@ void core_tmr_abort_task( if (tmr->ref_task_tag != ref_tag) continue; - if (!kref_get_unless_zero(&se_cmd->cmd_kref)) - continue; - printk("ABORT_TASK: Found referenced %s task_tag: %llu\n", se_cmd->se_tfo->get_fabric_name(), ref_tag); - spin_lock(&se_cmd->t_state_lock); - if (se_cmd->transport_state & CMD_T_COMPLETE) { - printk("ABORT_TASK: ref_tag: %llu already complete," - " skipping\n", ref_tag); - spin_unlock(&se_cmd->t_state_lock); + if (!__target_check_io_state(se_cmd)) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); - target_put_sess_cmd(se_cmd); - goto out; } - se_cmd->transport_state |= CMD_T_ABORTED; - spin_unlock(&se_cmd->t_state_lock); - list_del_init(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); cancel_work_sync(&se_cmd->work); transport_wait_for_tasks(se_cmd); - target_put_sess_cmd(se_cmd); transport_cmd_finish_abort(se_cmd, true); + target_put_sess_cmd(se_cmd); printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" " ref_tag: %llu\n", ref_tag); @@ -242,8 +258,10 @@ static void core_tmr_drain_state_list( struct list_head *preempt_and_abort_list) { LIST_HEAD(drain_task_list); + struct se_session *sess; struct se_cmd *cmd, *next; unsigned long flags; + int rc; /* * Complete outstanding commands with TASK_ABORTED SAM status. @@ -282,6 +300,16 @@ static void core_tmr_drain_state_list( if (prout_cmd == cmd) continue; + sess = cmd->se_sess; + if (WARN_ON_ONCE(!sess)) + continue; + + spin_lock(&sess->sess_cmd_lock); + rc = __target_check_io_state(cmd); + spin_unlock(&sess->sess_cmd_lock); + if (!rc) + continue; + list_move_tail(&cmd->state_list, &drain_task_list); cmd->state_active = false; } @@ -289,7 +317,7 @@ static void core_tmr_drain_state_list( while (!list_empty(&drain_task_list)) { cmd = list_entry(drain_task_list.next, struct se_cmd, state_list); - list_del(&cmd->state_list); + list_del_init(&cmd->state_list); pr_debug("LUN_RESET: %s cmd: %p" " ITT/CmdSN: 0x%08llx/0x%08x, i_state: %d, t_state: %d" @@ -313,16 +341,11 @@ static void core_tmr_drain_state_list( * loop above, but we do it down here given that * cancel_work_sync may block. */ - if (cmd->t_state == TRANSPORT_COMPLETE) - cancel_work_sync(&cmd->work); - - spin_lock_irqsave(&cmd->t_state_lock, flags); - target_stop_cmd(cmd, &flags); - - cmd->transport_state |= CMD_T_ABORTED; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + cancel_work_sync(&cmd->work); + transport_wait_for_tasks(cmd); core_tmr_handle_tas_abort(tmr_nacl, cmd, tas); + target_put_sess_cmd(cmd); } } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 9f3608e10f25..af52f8bd8954 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -534,9 +534,6 @@ void transport_deregister_session(struct se_session *se_sess) } EXPORT_SYMBOL(transport_deregister_session); -/* - * Called with cmd->t_state_lock held. - */ static void target_remove_from_state_list(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; @@ -561,10 +558,6 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists, { unsigned long flags; - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (write_pending) - cmd->t_state = TRANSPORT_WRITE_PENDING; - if (remove_from_lists) { target_remove_from_state_list(cmd); @@ -574,6 +567,10 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists, cmd->se_lun = NULL; } + spin_lock_irqsave(&cmd->t_state_lock, flags); + if (write_pending) + cmd->t_state = TRANSPORT_WRITE_PENDING; + /* * Determine if frontend context caller is requesting the stopping of * this command for frontend exceptions. @@ -627,6 +624,8 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { + bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF); + if (cmd->se_cmd_flags & SCF_SE_LUN_CMD) transport_lun_remove_cmd(cmd); /* @@ -638,7 +637,7 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) if (transport_cmd_check_stop_to_fabric(cmd)) return; - if (remove) + if (remove && ack_kref) transport_put_cmd(cmd); } @@ -706,7 +705,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) * Check for case where an explicit ABORT_TASK has been received * and transport_wait_for_tasks() will be waiting for completion.. */ - if (cmd->transport_state & CMD_T_ABORTED && + if (cmd->transport_state & CMD_T_ABORTED || cmd->transport_state & CMD_T_STOP) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); complete_all(&cmd->t_transport_stop_comp); @@ -2222,20 +2221,14 @@ static inline void transport_free_pages(struct se_cmd *cmd) } /** - * transport_release_cmd - free a command - * @cmd: command to free + * transport_put_cmd - release a reference to a command + * @cmd: command to release * - * This routine unconditionally frees a command, and reference counting - * or list removal must be done in the caller. + * This routine releases our reference to the command and frees it if possible. */ -static int transport_release_cmd(struct se_cmd *cmd) +static int transport_put_cmd(struct se_cmd *cmd) { BUG_ON(!cmd->se_tfo); - - if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) - core_tmr_release_req(cmd->se_tmr_req); - if (cmd->t_task_cdb != cmd->__t_task_cdb) - kfree(cmd->t_task_cdb); /* * If this cmd has been setup with target_get_sess_cmd(), drop * the kref and call ->release_cmd() in kref callback. @@ -2243,18 +2236,6 @@ static int transport_release_cmd(struct se_cmd *cmd) return target_put_sess_cmd(cmd); } -/** - * transport_put_cmd - release a reference to a command - * @cmd: command to release - * - * This routine releases our reference to the command and frees it if possible. - */ -static int transport_put_cmd(struct se_cmd *cmd) -{ - transport_free_pages(cmd); - return transport_release_cmd(cmd); -} - void *transport_kmap_data_sg(struct se_cmd *cmd) { struct scatterlist *sg = cmd->t_data_sg; @@ -2452,14 +2433,13 @@ static void transport_write_pending_qf(struct se_cmd *cmd) int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) { - unsigned long flags; int ret = 0; if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) { if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) - transport_wait_for_tasks(cmd); + transport_wait_for_tasks(cmd); - ret = transport_release_cmd(cmd); + ret = transport_put_cmd(cmd); } else { if (wait_for_tasks) transport_wait_for_tasks(cmd); @@ -2468,11 +2448,8 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) * has already added se_cmd to state_list, but fabric has * failed command before I/O submission. */ - if (cmd->state_active) { - spin_lock_irqsave(&cmd->t_state_lock, flags); + if (cmd->state_active) target_remove_from_state_list(cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - } if (cmd->se_lun) transport_lun_remove_cmd(cmd); @@ -2517,6 +2494,16 @@ out: } EXPORT_SYMBOL(target_get_sess_cmd); +static void target_free_cmd_mem(struct se_cmd *cmd) +{ + transport_free_pages(cmd); + + if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) + core_tmr_release_req(cmd->se_tmr_req); + if (cmd->t_task_cdb != cmd->__t_task_cdb) + kfree(cmd->t_task_cdb); +} + static void target_release_cmd_kref(struct kref *kref) { struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref); @@ -2526,17 +2513,20 @@ static void target_release_cmd_kref(struct kref *kref) spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (list_empty(&se_cmd->se_cmd_list)) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + target_free_cmd_mem(se_cmd); se_cmd->se_tfo->release_cmd(se_cmd); return; } if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + target_free_cmd_mem(se_cmd); complete(&se_cmd->cmd_wait_comp); return; } list_del(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + target_free_cmd_mem(se_cmd); se_cmd->se_tfo->release_cmd(se_cmd); } @@ -2548,6 +2538,7 @@ int target_put_sess_cmd(struct se_cmd *se_cmd) struct se_session *se_sess = se_cmd->se_sess; if (!se_sess) { + target_free_cmd_mem(se_cmd); se_cmd->se_tfo->release_cmd(se_cmd); return 1; } diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 5d82816cc4e3..1a76726c45a2 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -140,6 +140,7 @@ enum se_cmd_flags_table { SCF_COMPARE_AND_WRITE = 0x00080000, SCF_COMPARE_AND_WRITE_POST = 0x00100000, SCF_PASSTHROUGH_PROT_SG_TO_MEM_NOALLOC = 0x00200000, + SCF_ACK_KREF = 0x00400000, }; /* struct se_dev_entry->lun_flags and struct se_lun->lun_access */ -- cgit v1.2.3 From a6d9bb1c9605cd4f44e2d8290dc4d0e88f20292d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 11 Jan 2016 21:53:05 -0800 Subject: target: Fix LUN_RESET active TMR descriptor handling This patch fixes a NULL pointer se_cmd->cmd_kref < 0 refcount bug during TMR LUN_RESET with active TMRs, triggered during se_cmd + se_tmr_req descriptor shutdown + release via core_tmr_drain_tmr_list(). To address this bug, go ahead and obtain a local kref_get_unless_zero(&se_cmd->cmd_kref) for active I/O to set CMD_T_ABORTED, and transport_wait_for_tasks() followed by the final target_put_sess_cmd() to drop the local ->cmd_kref. Also add two new checks within target_tmr_work() to avoid CMD_T_ABORTED -> TFO->queue_tm_rsp() callbacks ahead of invoking the backend -> fabric put in transport_cmd_check_stop_to_fabric(). For good measure, also change core_tmr_release_req() to use list_del_init() ahead of se_tmr_req memory free. Reviewed-by: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 22 +++++++++++++++++++++- drivers/target/target_core_transport.c | 17 +++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index fb3decc28589..072af07bd2ff 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -68,7 +68,7 @@ void core_tmr_release_req(struct se_tmr_req *tmr) if (dev) { spin_lock_irqsave(&dev->se_tmr_lock, flags); - list_del(&tmr->tmr_list); + list_del_init(&tmr->tmr_list); spin_unlock_irqrestore(&dev->se_tmr_lock, flags); } @@ -194,9 +194,11 @@ static void core_tmr_drain_tmr_list( struct list_head *preempt_and_abort_list) { LIST_HEAD(drain_tmr_list); + struct se_session *sess; struct se_tmr_req *tmr_p, *tmr_pp; struct se_cmd *cmd; unsigned long flags; + bool rc; /* * Release all pending and outgoing TMRs aside from the received * LUN_RESET tmr.. @@ -222,17 +224,31 @@ static void core_tmr_drain_tmr_list( if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd)) continue; + sess = cmd->se_sess; + if (WARN_ON_ONCE(!sess)) + continue; + + spin_lock(&sess->sess_cmd_lock); spin_lock(&cmd->t_state_lock); if (!(cmd->transport_state & CMD_T_ACTIVE)) { spin_unlock(&cmd->t_state_lock); + 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; } + cmd->transport_state |= CMD_T_ABORTED; spin_unlock(&cmd->t_state_lock); + rc = kref_get_unless_zero(&cmd->cmd_kref); + spin_unlock(&sess->sess_cmd_lock); + if (!rc) { + printk("LUN_RESET TMR: non-zero kref_get_unless_zero\n"); + continue; + } list_move_tail(&tmr_p->tmr_list, &drain_tmr_list); } spin_unlock_irqrestore(&dev->se_tmr_lock, flags); @@ -246,7 +262,11 @@ static void core_tmr_drain_tmr_list( (preempt_and_abort_list) ? "Preempt" : "", tmr_p, tmr_p->function, tmr_p->response, cmd->t_state); + cancel_work_sync(&cmd->work); + transport_wait_for_tasks(cmd); + transport_cmd_finish_abort(cmd, 1); + target_put_sess_cmd(cmd); } } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index af52f8bd8954..94e372af9e28 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2900,8 +2900,17 @@ static void target_tmr_work(struct work_struct *work) struct se_cmd *cmd = container_of(work, struct se_cmd, work); struct se_device *dev = cmd->se_dev; struct se_tmr_req *tmr = cmd->se_tmr_req; + unsigned long flags; int ret; + spin_lock_irqsave(&cmd->t_state_lock, flags); + if (cmd->transport_state & CMD_T_ABORTED) { + tmr->response = TMR_FUNCTION_REJECTED; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + goto check_stop; + } + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + switch (tmr->function) { case TMR_ABORT_TASK: core_tmr_abort_task(dev, tmr, cmd->se_sess); @@ -2934,9 +2943,17 @@ static void target_tmr_work(struct work_struct *work) break; } + spin_lock_irqsave(&cmd->t_state_lock, flags); + if (cmd->transport_state & CMD_T_ABORTED) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + goto check_stop; + } cmd->t_state = TRANSPORT_ISTATE_PROCESSING; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + cmd->se_tfo->queue_tm_rsp(cmd); +check_stop: transport_cmd_check_stop_to_fabric(cmd); } -- cgit v1.2.3 From ebde1ca5a908b10312db4ecd7553e3ba039319ab Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 16 Jan 2016 12:49:49 -0800 Subject: target: Fix TAS handling for multi-session se_node_acls This patch fixes a bug in TMR task aborted status (TAS) handling when multiple sessions are connected to the same target WWPN endpoint and se_node_acl descriptor, resulting in TASK_ABORTED status to not be generated for aborted se_cmds on the remote port. This is due to core_tmr_handle_tas_abort() incorrectly comparing se_node_acl instead of se_session, for which the multi-session case is expected to be sharing the same se_node_acl. Instead, go ahead and update core_tmr_handle_tas_abort() to compare tmr_sess + cmd->se_sess in order to determine if the LUN_RESET was received on a different I_T nexus, and TASK_ABORTED status response needs to be generated. Reviewed-by: Christoph Hellwig Cc: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 072af07bd2ff..3e0d77a4c7b6 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -76,7 +76,7 @@ void core_tmr_release_req(struct se_tmr_req *tmr) } static void core_tmr_handle_tas_abort( - struct se_node_acl *tmr_nacl, + struct se_session *tmr_sess, struct se_cmd *cmd, int tas) { @@ -84,7 +84,7 @@ static void core_tmr_handle_tas_abort( /* * TASK ABORTED status (TAS) bit support */ - if ((tmr_nacl && (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { + if (tmr_sess && tmr_sess != cmd->se_sess && tas) { remove = false; transport_send_task_abort(cmd); } @@ -273,7 +273,7 @@ static void core_tmr_drain_tmr_list( static void core_tmr_drain_state_list( struct se_device *dev, struct se_cmd *prout_cmd, - struct se_node_acl *tmr_nacl, + struct se_session *tmr_sess, int tas, struct list_head *preempt_and_abort_list) { @@ -364,7 +364,7 @@ static void core_tmr_drain_state_list( cancel_work_sync(&cmd->work); transport_wait_for_tasks(cmd); - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas); + core_tmr_handle_tas_abort(tmr_sess, cmd, tas); target_put_sess_cmd(cmd); } } @@ -377,6 +377,7 @@ int core_tmr_lun_reset( { struct se_node_acl *tmr_nacl = NULL; struct se_portal_group *tmr_tpg = NULL; + struct se_session *tmr_sess = NULL; int tas; /* * TASK_ABORTED status bit, this is configurable via ConfigFS @@ -395,8 +396,9 @@ int core_tmr_lun_reset( * or struct se_device passthrough.. */ if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) { - tmr_nacl = tmr->task_cmd->se_sess->se_node_acl; - tmr_tpg = tmr->task_cmd->se_sess->se_tpg; + tmr_sess = tmr->task_cmd->se_sess; + tmr_nacl = tmr_sess->se_node_acl; + tmr_tpg = tmr_sess->se_tpg; if (tmr_nacl && tmr_tpg) { pr_debug("LUN_RESET: TMR caller fabric: %s" " initiator port %s\n", @@ -409,7 +411,7 @@ int core_tmr_lun_reset( dev->transport->name, tas); core_tmr_drain_tmr_list(dev, tmr, preempt_and_abort_list); - core_tmr_drain_state_list(dev, prout_cmd, tmr_nacl, tas, + core_tmr_drain_state_list(dev, prout_cmd, tmr_sess, tas, preempt_and_abort_list); /* -- cgit v1.2.3 From 51fd2df1e882a3c2a3f4b6c9ff243a93c9046dba Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 3 Feb 2016 08:43:56 +0100 Subject: perf stat: Fix interval output values We broke interval data displays with commit: 3f416f22d1e2 ("perf stat: Do not clean event's private stats") This commit removed stats cleaning, which is important for '-r' option to carry counters data over the whole run. But it's necessary to clean it for interval mode, otherwise the displayed value is avg of all previous values. Before: $ perf stat -e cycles -a -I 1000 record # time counts unit events 1.000240796 75,216,287 cycles 2.000512791 107,823,524 cycles $ perf stat report # time counts unit events 1.000240796 75,216,287 cycles 2.000512791 91,519,906 cycles Now: $ perf stat report # time counts unit events 1.000240796 75,216,287 cycles 2.000512791 107,823,524 cycles Notice the second value being bigger (91,.. < 107,..). This could be easily verified by using perf script which displays raw stat data: $ perf script CPU THREAD VAL ENA RUN TIME EVENT 0 -1 23855779 1000209530 1000209530 1000240796 cycles 1 -1 33340397 1000224964 1000224964 1000240796 cycles 2 -1 15835415 1000226695 1000226695 1000240796 cycles 3 -1 2184696 1000228245 1000228245 1000240796 cycles 0 -1 97014312 2000514533 2000514533 2000512791 cycles 1 -1 46121497 2000543795 2000543795 2000512791 cycles 2 -1 32269530 2000543566 2000543566 2000512791 cycles 3 -1 7634472 2000544108 2000544108 2000512791 cycles The sum of the first 4 values is the first interval aggregated value: 23855779 + 33340397 + 15835415 + 2184696 = 75,216,287 The sum of the second 4 values minus first value is the second interval aggregated value: 97014312 + 46121497 + 32269530 + 7634472 - 75216287 = 107,823,524 Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1454485436-20639-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 2b58edccd56f..afb0c45eba34 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -311,6 +311,16 @@ int perf_stat_process_counter(struct perf_stat_config *config, aggr->val = aggr->ena = aggr->run = 0; + /* + * We calculate counter's data every interval, + * and the display code shows ps->res_stats + * avg value. We need to zero the stats for + * interval mode, otherwise overall avg running + * averages will be shown for each interval. + */ + if (config->interval) + init_stats(ps->res_stats); + if (counter->per_pkg) zero_per_pkg(counter); -- cgit v1.2.3 From d7de413475f443957a0c1d256e405d19b3a2cb22 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 4 Feb 2016 01:24:40 +0100 Subject: MIPS: Fix 64k page support for 32 bit kernels. TASK_SIZE was defined as 0x7fff8000UL which for 64k pages is not a multiple of the page size. Somewhere further down the math fails such that executing an ELF binary fails. Signed-off-by: Ralf Baechle Tested-by: Joshua Henderson --- arch/mips/include/asm/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h index 3f832c3dd8f5..041153f5cf93 100644 --- a/arch/mips/include/asm/processor.h +++ b/arch/mips/include/asm/processor.h @@ -45,7 +45,7 @@ extern unsigned int vced_count, vcei_count; * User space process size: 2GB. This is hardcoded into a few places, * so don't change it unless you know what you are doing. */ -#define TASK_SIZE 0x7fff8000UL +#define TASK_SIZE 0x80000000UL #endif #define STACK_TOP_MAX TASK_SIZE -- cgit v1.2.3 From 41d80025a83b9c7a94f97ef25c4cd3345bdc3c5e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 3 Feb 2016 21:59:50 +0100 Subject: ASoC: dapm: Don't prefix autodisable widgets twice When a DAPM context has a prefix the autodisable widgets get prefixed twice, once for the control and once for the widget. To avoid this use the un-prefixed control name to construct the autodisable widget name. This change is purely cosmetic. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5a2812fa8946..0d3707987900 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -310,7 +310,7 @@ struct dapm_kcontrol_data { }; static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol) + struct snd_kcontrol *kcontrol, const char *ctrl_name) { struct dapm_kcontrol_data *data; struct soc_mixer_control *mc; @@ -333,7 +333,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, if (mc->autodisable) { struct snd_soc_dapm_widget template; - name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, "Autodisable"); if (!name) { ret = -ENOMEM; @@ -371,7 +371,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, if (e->autodisable) { struct snd_soc_dapm_widget template; - name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, "Autodisable"); if (!name) { ret = -ENOMEM; @@ -871,7 +871,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, kcontrol->private_free = dapm_kcontrol_free; - ret = dapm_kcontrol_data_alloc(w, kcontrol); + ret = dapm_kcontrol_data_alloc(w, kcontrol, name); if (ret) { snd_ctl_free_one(kcontrol); goto exit_free; -- cgit v1.2.3 From 41556f68d1dd0b6bbf311a220523b034d2a040e7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 3 Feb 2016 17:59:44 +0530 Subject: ASoC: Intel: Skylake: Fix the memory overwrite of tlv buffer TLV buffer can be smaller than the module data, so update the size of data to be copied before doing the copy. Also TLV header consists of two unsigned ints, this is also taken into account here and size modified to reflect this Suggested-by: Takashi Iwai Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c7816d52ad08..c67e3acb8102 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -916,6 +916,13 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, skl_get_module_params(skl->skl_sst, (u32 *)bc->params, bc->max, bc->param_id, mconfig); + /* decrement size for TLV header */ + size -= 2 * sizeof(u32); + + /* check size as we don't want to send kernel data */ + if (size > bc->max) + size = bc->max; + if (bc->params) { if (copy_to_user(data, &bc->param_id, sizeof(u32))) return -EFAULT; -- cgit v1.2.3 From ee564d489cc47b1b6043bbe7e95464306d112cf5 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 3 Feb 2016 17:59:45 +0530 Subject: ASoC: Intel: Skylake: Fix delay wrap condition When delay reported by HW is equal to buffersize, it means the value is wrapped so we should report as 0. So add the condition to check this while reporting the delay from LPIB. Signed-off-by: Guneshwor Singh Signed-off-by: Dharageswari.R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b89ae6f7c096..f9297dc4b25f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -829,6 +829,7 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, else delay += hstream->bufsize; } + delay = (hstream->bufsize == delay) ? 0 : delay; if (delay >= hstream->period_bytes) { dev_info(bus->dev, -- cgit v1.2.3 From 7ca42f5ac5e0d8011086bcfa00e85aede42f0b78 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 3 Feb 2016 17:59:46 +0530 Subject: ASoC: Intel: Skylake: Fix mcps freeup after module unbind failure While cleaning resources on module pmd event, we check for return of skl_unbind_modules(). On failure this causes leak as all modules attached do not have resources freed. So ignore return value of module unbind and continue freeing resources. This makes dapm state and resources correct. Signed-off-by: Guneshwor Singh Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c67e3acb8102..86d5323e9184 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -98,7 +98,7 @@ static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, "%s: module_id %d instance %d\n", __func__, mconfig->id.module_id, mconfig->id.instance_id); dev_err(ctx->dev, - "exceeds ppl memory available %d > mem %d\n", + "exceeds ppl mcps available %d > mem %d\n", skl->resource.max_mcps, skl->resource.mcps); return false; } @@ -773,10 +773,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, continue; } - ret = skl_unbind_modules(ctx, src_module, dst_module); - if (ret < 0) - return ret; - + skl_unbind_modules(ctx, src_module, dst_module); src_module = dst_module; } -- cgit v1.2.3 From 9ba8ffef9635c11102bc42d0f2d0a4213de273d5 Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Wed, 3 Feb 2016 17:59:47 +0530 Subject: ASoC: Intel: Skylake: Fix pipe memory allocation leak We check and allocate pipeline resources in one shot. That causes leaks if module creation fails later as that is not freed. So split the resource allocation into two, first check if resources are available and then add the resources upon successful creation. So two new functions are added for checking and current functions are re-purposed to only add the resources for memory and MCPS. Signed-off-by: Dharageswari.R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 86d5323e9184..efe001162204 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -54,12 +54,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) /* * Each pipelines needs memory to be allocated. Check if we have free memory - * from available pool. Then only add this to pool - * This is freed when pipe is deleted - * Note: DSP does actual memory management we only keep track for complete - * pool + * from available pool. */ -static bool skl_tplg_alloc_pipe_mem(struct skl *skl, +static bool skl_is_pipe_mem_avail(struct skl *skl, struct skl_module_cfg *mconfig) { struct skl_sst *ctx = skl->skl_sst; @@ -74,10 +71,20 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl, "exceeds ppl memory available %d mem %d\n", skl->resource.max_mem, skl->resource.mem); return false; + } else { + return true; } +} +/* + * Add the mem to the mem pool. This is freed when pipe is deleted. + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static void skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ skl->resource.mem += mconfig->pipe->memory_pages; - return true; } /* @@ -85,10 +92,10 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl, * quantified in MCPS (Million Clocks Per Second) required for module/pipe * * Each pipelines needs mcps to be allocated. Check if we have mcps for this - * pipe. This adds the mcps to driver counter - * This is removed on pipeline delete + * pipe. */ -static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + +static bool skl_is_pipe_mcps_avail(struct skl *skl, struct skl_module_cfg *mconfig) { struct skl_sst *ctx = skl->skl_sst; @@ -101,10 +108,15 @@ static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, "exceeds ppl mcps available %d > mem %d\n", skl->resource.max_mcps, skl->resource.mcps); return false; + } else { + return true; } +} +static void skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ skl->resource.mcps += mconfig->mcps; - return true; } /* @@ -411,7 +423,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) mconfig = w->priv; /* check resource available */ - if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { @@ -435,6 +447,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) return ret; + skl_tplg_alloc_pipe_mcps(skl, mconfig); } return 0; @@ -477,10 +490,10 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl_sst *ctx = skl->skl_sst; /* check resource available */ - if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -EBUSY; - if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + if (!skl_is_pipe_mem_avail(skl, mconfig)) return -ENOMEM; /* @@ -526,6 +539,9 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, src_module = dst_module; } + skl_tplg_alloc_pipe_mem(skl, mconfig); + skl_tplg_alloc_pipe_mcps(skl, mconfig); + return 0; } -- cgit v1.2.3 From 9cf3049e21e4e6873aae45df19c11f7243e2f03f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:48 +0530 Subject: ASoC: Intel: Skylake: Fix return of skl_get_queue_index In unbind modules, the skl_get_queue_index() can return error if the pin is dynamic and module is not bound yet. So instead of returning error this check should return success as modules is not yet bound. This will let the module be bound when connected pipes are enabled and will bind this as well. So change the return value to 0 Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index de6dac496a0d..bb5f1d7d0cad 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -688,14 +688,14 @@ int skl_unbind_modules(struct skl_sst *ctx, /* get src queue index */ src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max); if (src_index < 0) - return -EINVAL; + return 0; msg.src_queue = src_index; /* get dst queue index */ dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max); if (dst_index < 0) - return -EINVAL; + return 0; msg.dst_queue = dst_index; -- cgit v1.2.3 From 0c684c48257bc6033bdd3b942babef22d0a1852a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:49 +0530 Subject: ASoC: Intel: Skylake: Fix the module state check condition For binding modules we should check if source or destination module is in UNINT state. We canot bind even if one of them is in this state. So update the check from logical AND to logical OR and do not bind modules for this case Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bb5f1d7d0cad..4629372d7c8e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -747,7 +747,7 @@ int skl_bind_modules(struct skl_sst *ctx, skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); - if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && + if (src_mcfg->m_state < SKL_MODULE_INIT_DONE || dst_mcfg->m_state < SKL_MODULE_INIT_DONE) return 0; -- cgit v1.2.3 From 9946f70906eebf2a305d0b189de52eec8ba39649 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:50 +0530 Subject: ASoC: Intel: Skylake: Fix not to stop sink pipe in pga pmd event We should not stop the sink pipe in it's pmd handler for a mixin module as this module may still be connected to other pipes. This will be stopped and freed by current implementation on last connected pipe unbind. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index efe001162204..a356f3b1dd5b 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -827,9 +827,6 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, * This is a connecter and if path is found that means * unbind between source and sink has not happened yet */ - ret = skl_stop_pipe(ctx, sink_mconfig->pipe); - if (ret < 0) - return ret; ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); } -- cgit v1.2.3 From 6bd4cf855698312133b7776c77ee78af865608eb Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:51 +0530 Subject: ASoC: Intel: Skylake: Fix bind of source with multiple sinks skl_tplg_bind_sinks() takes only the first sink widget. This breaks in case we have multiple sinks for a module. So pass source widget to skl_tplg_bind_sinks() and bind for all sinks by calling this recursively Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a356f3b1dd5b..77a688d00fc6 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -547,6 +547,7 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, struct skl *skl, + struct snd_soc_dapm_widget *src_w, struct skl_module_cfg *src_mconfig) { struct snd_soc_dapm_path *p; @@ -563,6 +564,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); next_sink = p->sink; + + if (!is_skl_dsp_widget_type(p->sink)) + return skl_tplg_bind_sinks(p->sink, skl, src_w, src_mconfig); + /* * here we will check widgets in sink pipelines, so that * can be any widgets type and we are only interested if @@ -592,7 +597,7 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, } if (!sink) - return skl_tplg_bind_sinks(next_sink, skl, src_mconfig); + return skl_tplg_bind_sinks(next_sink, skl, src_w, src_mconfig); return 0; } @@ -621,7 +626,7 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, * if sink is not started, start sink pipe first, then start * this pipe */ - ret = skl_tplg_bind_sinks(w, skl, src_mconfig); + ret = skl_tplg_bind_sinks(w, skl, w, src_mconfig); if (ret) return ret; -- cgit v1.2.3 From de1fedf25b075664320010789ede2a0f9f4de07d Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:52 +0530 Subject: ASoC: Intel: Skylake: Add missing PRE/POST_PMU handlers for vmixer Some modules may be directly connected to a pipeline without a mixer module. For these modules, we require PRE_PMU and POST_PMU handler which will do bind between the pipelines, so add these missing handlers. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 77a688d00fc6..489848637df5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -857,6 +857,12 @@ static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_PRE_PMU: return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + case SND_SOC_DAPM_POST_PMD: return skl_tplg_mixer_dapm_post_pmd_event(w, skl); } -- cgit v1.2.3 From 6e3ffa00424e198d2f0c628e7575c5adefeda3d7 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 3 Feb 2016 17:59:53 +0530 Subject: ASoC: Intel: Skylake: Fix stereo DMIC record DMIC BE can have 2 or 4 channels supported. The DMIC fixup needs to take this into account. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 7396ddb427d8..2cbcbe412661 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -212,7 +212,10 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 4; + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; return 0; } -- cgit v1.2.3 From 38c079e230f25969e7ce3501fa967b003a2abc39 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 3 Feb 2016 17:59:54 +0530 Subject: ASoC: Intel: Skylake: Remove autosuspend delay The driver used autosuspend delay to delay going to D3. But per HW recommendation we should go to D3 soon, so remove the delay from driver Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index c38bf99ced10..1d36b28d6489 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -558,8 +558,6 @@ static int skl_probe(struct pci_dev *pci, goto out_unregister; /*configure PM */ - pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY); - pm_runtime_use_autosuspend(bus->dev); pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); -- cgit v1.2.3 From bb6a777369449d15a4a890306d2f925cae720e1c Mon Sep 17 00:00:00 2001 From: Carol L Soto Date: Wed, 3 Feb 2016 15:55:37 -0500 Subject: IB/IPoIB: Do not set skb truesize since using one linearskb We are seeing this warning: at net/core/skbuff.c:4174 and before commit a44878d10063 ("IB/ipoib: Use one linear skb in RX flow") skb truesize was not being set when ipoib was using just one skb. Removing this line avoids the warning when running tcp tests like iperf. Fixes: a44878d10063 ("IB/ipoib: Use one linear skb in RX flow") Signed-off-by: Carol L Soto Signed-off-by: Doug Ledford --- drivers/infiniband/ulp/ipoib/ipoib_ib.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 5ea0c14070d1..fa9c42ff1fb0 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -245,8 +245,6 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) skb_reset_mac_header(skb); skb_pull(skb, IPOIB_ENCAP_LEN); - skb->truesize = SKB_TRUESIZE(skb->len); - ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; -- cgit v1.2.3 From 9f780dab7fb5618ee8bacbf6f91832374231c30c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 25 Jan 2016 18:38:06 +0000 Subject: IB/sysfs: remove unused va_list args _show_port_gid_attr performs a va_end on some unused va_list args. Clean this up by removing the args completely. Fixes: 470be516a226e8 ("IB/core: Add gid attributes to sysfs") Signed-off-by: Colin Ian King Reviewed-by: Ira Weiny Reviewed-by: Jason Gunthorpe Signed-off-by: Doug Ledford --- drivers/infiniband/core/sysfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 3de93517efe4..ec46386e3c7f 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -336,7 +336,6 @@ static ssize_t _show_port_gid_attr(struct ib_port *p, union ib_gid gid; struct ib_gid_attr gid_attr = {}; ssize_t ret; - va_list args; ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid, &gid_attr); @@ -348,7 +347,6 @@ static ssize_t _show_port_gid_attr(struct ib_port *p, err: if (gid_attr.ndev) dev_put(gid_attr.ndev); - va_end(args); return ret; } -- cgit v1.2.3 From 1a485f4d2e28efd77075b2952926683d6c245633 Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Mon, 1 Feb 2016 20:19:44 -0600 Subject: irqchip/gicv3-its: Fix memory leak in its_free_tables() The current ITS driver has a memory leak in its_free_tables(). It happens on tear down path of the driver when its_probe() call fails. its_free_tables() should free the exact number of pages that have been allocated, not just a single page as current code does. This patch records the memory size for each ITS_BASERn at the time of page allocation and uses the same size information when freeing pages to fix the issue. Signed-off-by: Shanker Donthineni Acked-by: Marc Zyngier Cc: Jason Cooper Cc: Vikram Sethi Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1454379584-21772-1-git-send-email-shankerd@codeaurora.org Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 3447549fcc93..0a73632b28d5 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -66,7 +66,10 @@ struct its_node { unsigned long phys_base; struct its_cmd_block *cmd_base; struct its_cmd_block *cmd_write; - void *tables[GITS_BASER_NR_REGS]; + struct { + void *base; + u32 order; + } tables[GITS_BASER_NR_REGS]; struct its_collection *collections; struct list_head its_device_list; u64 flags; @@ -807,9 +810,10 @@ static void its_free_tables(struct its_node *its) int i; for (i = 0; i < GITS_BASER_NR_REGS; i++) { - if (its->tables[i]) { - free_page((unsigned long)its->tables[i]); - its->tables[i] = NULL; + if (its->tables[i].base) { + free_pages((unsigned long)its->tables[i].base, + its->tables[i].order); + its->tables[i].base = NULL; } } } @@ -890,7 +894,8 @@ retry_alloc_baser: goto out_free; } - its->tables[i] = base; + its->tables[i].base = base; + its->tables[i].order = order; retry_baser: val = (virt_to_phys(base) | @@ -940,7 +945,7 @@ retry_baser: * something is horribly wrong... */ free_pages((unsigned long)base, order); - its->tables[i] = NULL; + its->tables[i].base = NULL; switch (psz) { case SZ_16K: -- cgit v1.2.3 From 0c0fe3b0fa45082cd752553fdb3a4b42503a118e Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 3 Feb 2016 19:17:27 +0000 Subject: Btrfs: fix hang on extent buffer lock caused by the inode_paths ioctl While doing some tests I ran into an hang on an extent buffer's rwlock that produced the following trace: [39389.800012] NMI watchdog: BUG: soft lockup - CPU#15 stuck for 22s! [fdm-stress:32166] [39389.800016] NMI watchdog: BUG: soft lockup - CPU#14 stuck for 22s! [fdm-stress:32165] [39389.800016] Modules linked in: btrfs dm_mod ppdev xor sha256_generic hmac raid6_pq drbg ansi_cprng aesni_intel i2c_piix4 acpi_cpufreq aes_x86_64 ablk_helper tpm_tis parport_pc i2c_core sg cryptd evdev psmouse lrw tpm parport gf128mul serio_raw pcspkr glue_helper processor button loop autofs4 ext4 crc16 mbcache jbd2 sd_mod sr_mod cdrom ata_generic virtio_scsi ata_piix libata virtio_pci virtio_ring crc32c_intel scsi_mod e1000 virtio floppy [last unloaded: btrfs] [39389.800016] irq event stamp: 0 [39389.800016] hardirqs last enabled at (0): [< (null)>] (null) [39389.800016] hardirqs last disabled at (0): [] copy_process+0x638/0x1a35 [39389.800016] softirqs last enabled at (0): [] copy_process+0x638/0x1a35 [39389.800016] softirqs last disabled at (0): [< (null)>] (null) [39389.800016] CPU: 14 PID: 32165 Comm: fdm-stress Not tainted 4.4.0-rc6-btrfs-next-18+ #1 [39389.800016] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS by qemu-project.org 04/01/2014 [39389.800016] task: ffff880175b1ca40 ti: ffff8800a185c000 task.ti: ffff8800a185c000 [39389.800016] RIP: 0010:[] [] queued_spin_lock_slowpath+0x57/0x158 [39389.800016] RSP: 0018:ffff8800a185fb80 EFLAGS: 00000202 [39389.800016] RAX: 0000000000000101 RBX: ffff8801710c4e9c RCX: 0000000000000101 [39389.800016] RDX: 0000000000000100 RSI: 0000000000000001 RDI: 0000000000000001 [39389.800016] RBP: ffff8800a185fb98 R08: 0000000000000001 R09: 0000000000000000 [39389.800016] R10: ffff8800a185fb68 R11: 6db6db6db6db6db7 R12: ffff8801710c4e98 [39389.800016] R13: ffff880175b1ca40 R14: ffff8800a185fc10 R15: ffff880175b1ca40 [39389.800016] FS: 00007f6d37fff700(0000) GS:ffff8802be9c0000(0000) knlGS:0000000000000000 [39389.800016] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [39389.800016] CR2: 00007f6d300019b8 CR3: 0000000037c93000 CR4: 00000000001406e0 [39389.800016] Stack: [39389.800016] ffff8801710c4e98 ffff8801710c4e98 ffff880175b1ca40 ffff8800a185fbb0 [39389.800016] ffffffff81091e11 ffff8801710c4e98 ffff8800a185fbc8 ffffffff81091895 [39389.800016] ffff8801710c4e98 ffff8800a185fbe8 ffffffff81486c5c ffffffffa067288c [39389.800016] Call Trace: [39389.800016] [] queued_read_lock_slowpath+0x46/0x60 [39389.800016] [] do_raw_read_lock+0x3e/0x41 [39389.800016] [] _raw_read_lock+0x3d/0x44 [39389.800016] [] ? btrfs_tree_read_lock+0x54/0x125 [btrfs] [39389.800016] [] btrfs_tree_read_lock+0x54/0x125 [btrfs] [39389.800016] [] ? btrfs_find_item+0xa7/0xd2 [btrfs] [39389.800016] [] btrfs_ref_to_path+0xd6/0x174 [btrfs] [39389.800016] [] inode_to_path+0x53/0xa2 [btrfs] [39389.800016] [] paths_from_inode+0x117/0x2ec [btrfs] [39389.800016] [] btrfs_ioctl+0xd5b/0x2793 [btrfs] [39389.800016] [] ? arch_local_irq_save+0x9/0xc [39389.800016] [] ? __this_cpu_preempt_check+0x13/0x15 [39389.800016] [] ? arch_local_irq_save+0x9/0xc [39389.800016] [] ? rcu_read_unlock+0x3e/0x5d [39389.800016] [] do_vfs_ioctl+0x42b/0x4ea [39389.800016] [] ? __fget_light+0x62/0x71 [39389.800016] [] SyS_ioctl+0x57/0x79 [39389.800016] [] entry_SYSCALL_64_fastpath+0x12/0x6f [39389.800016] Code: b9 01 01 00 00 f7 c6 00 ff ff ff 75 32 83 fe 01 89 ca 89 f0 0f 45 d7 f0 0f b1 13 39 f0 74 04 89 c6 eb e2 ff ca 0f 84 fa 00 00 00 <8b> 03 84 c0 74 04 f3 90 eb f6 66 c7 03 01 00 e9 e6 00 00 00 e8 [39389.800012] Modules linked in: btrfs dm_mod ppdev xor sha256_generic hmac raid6_pq drbg ansi_cprng aesni_intel i2c_piix4 acpi_cpufreq aes_x86_64 ablk_helper tpm_tis parport_pc i2c_core sg cryptd evdev psmouse lrw tpm parport gf128mul serio_raw pcspkr glue_helper processor button loop autofs4 ext4 crc16 mbcache jbd2 sd_mod sr_mod cdrom ata_generic virtio_scsi ata_piix libata virtio_pci virtio_ring crc32c_intel scsi_mod e1000 virtio floppy [last unloaded: btrfs] [39389.800012] irq event stamp: 0 [39389.800012] hardirqs last enabled at (0): [< (null)>] (null) [39389.800012] hardirqs last disabled at (0): [] copy_process+0x638/0x1a35 [39389.800012] softirqs last enabled at (0): [] copy_process+0x638/0x1a35 [39389.800012] softirqs last disabled at (0): [< (null)>] (null) [39389.800012] CPU: 15 PID: 32166 Comm: fdm-stress Tainted: G L 4.4.0-rc6-btrfs-next-18+ #1 [39389.800012] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS by qemu-project.org 04/01/2014 [39389.800012] task: ffff880179294380 ti: ffff880034a60000 task.ti: ffff880034a60000 [39389.800012] RIP: 0010:[] [] queued_write_lock_slowpath+0x62/0x72 [39389.800012] RSP: 0018:ffff880034a639f0 EFLAGS: 00000206 [39389.800012] RAX: 0000000000000101 RBX: ffff8801710c4e98 RCX: 0000000000000000 [39389.800012] RDX: 00000000000000ff RSI: 0000000000000000 RDI: ffff8801710c4e9c [39389.800012] RBP: ffff880034a639f8 R08: 0000000000000001 R09: 0000000000000000 [39389.800012] R10: ffff880034a639b0 R11: 0000000000001000 R12: ffff8801710c4e98 [39389.800012] R13: 0000000000000001 R14: ffff880172cbc000 R15: ffff8801710c4e00 [39389.800012] FS: 00007f6d377fe700(0000) GS:ffff8802be9e0000(0000) knlGS:0000000000000000 [39389.800012] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [39389.800012] CR2: 00007f6d3d3c1000 CR3: 0000000037c93000 CR4: 00000000001406e0 [39389.800012] Stack: [39389.800012] ffff8801710c4e98 ffff880034a63a10 ffffffff81091963 ffff8801710c4e98 [39389.800012] ffff880034a63a30 ffffffff81486f1b ffffffffa0672cb3 ffff8801710c4e00 [39389.800012] ffff880034a63a78 ffffffffa0672cb3 ffff8801710c4e00 ffff880034a63a58 [39389.800012] Call Trace: [39389.800012] [] do_raw_write_lock+0x72/0x8c [39389.800012] [] _raw_write_lock+0x3a/0x41 [39389.800012] [] ? btrfs_tree_lock+0x119/0x251 [btrfs] [39389.800012] [] btrfs_tree_lock+0x119/0x251 [btrfs] [39389.800012] [] ? rcu_read_unlock+0x5b/0x5d [btrfs] [39389.800012] [] ? btrfs_root_node+0xda/0xe6 [btrfs] [39389.800012] [] btrfs_lock_root_node+0x22/0x42 [btrfs] [39389.800012] [] btrfs_search_slot+0x1b8/0x758 [btrfs] [39389.800012] [] ? time_hardirqs_on+0x15/0x28 [39389.800012] [] btrfs_lookup_inode+0x31/0x95 [btrfs] [39389.800012] [] ? trace_hardirqs_on+0xd/0xf [39389.800012] [] ? mutex_lock_nested+0x397/0x3bc [39389.800012] [] __btrfs_update_delayed_inode+0x59/0x1c0 [btrfs] [39389.800012] [] __btrfs_commit_inode_delayed_items+0x194/0x5aa [btrfs] [39389.800012] [] ? _raw_spin_unlock+0x31/0x44 [39389.800012] [] __btrfs_run_delayed_items+0xa4/0x15c [btrfs] [39389.800012] [] btrfs_run_delayed_items+0x11/0x13 [btrfs] [39389.800012] [] btrfs_commit_transaction+0x234/0x96e [btrfs] [39389.800012] [] btrfs_sync_fs+0x145/0x1ad [btrfs] [39389.800012] [] btrfs_ioctl+0x11d2/0x2793 [btrfs] [39389.800012] [] ? arch_local_irq_save+0x9/0xc [39389.800012] [] ? __might_fault+0x4c/0xa7 [39389.800012] [] ? __might_fault+0x4c/0xa7 [39389.800012] [] ? arch_local_irq_save+0x9/0xc [39389.800012] [] ? rcu_read_unlock+0x3e/0x5d [39389.800012] [] do_vfs_ioctl+0x42b/0x4ea [39389.800012] [] ? __fget_light+0x62/0x71 [39389.800012] [] SyS_ioctl+0x57/0x79 [39389.800012] [] entry_SYSCALL_64_fastpath+0x12/0x6f [39389.800012] Code: f0 0f b1 13 85 c0 75 ef eb 2a f3 90 8a 03 84 c0 75 f8 f0 0f b0 13 84 c0 75 f0 ba ff 00 00 00 eb 0a f0 0f b1 13 ff c8 74 0b f3 90 <8b> 03 83 f8 01 75 f7 eb ed c6 43 04 00 5b 5d c3 0f 1f 44 00 00 This happens because in the code path executed by the inode_paths ioctl we end up nesting two calls to read lock a leaf's rwlock when after the first call to read_lock() and before the second call to read_lock(), another task (running the delayed items as part of a transaction commit) has already called write_lock() against the leaf's rwlock. This situation is illustrated by the following diagram: Task A Task B btrfs_ref_to_path() btrfs_commit_transaction() read_lock(&eb->lock); btrfs_run_delayed_items() __btrfs_commit_inode_delayed_items() __btrfs_update_delayed_inode() btrfs_lookup_inode() write_lock(&eb->lock); --> task waits for lock read_lock(&eb->lock); --> makes this task hang forever (and task B too of course) So fix this by avoiding doing the nested read lock, which is easily avoidable. This issue does not happen if task B calls write_lock() after task A does the second call to read_lock(), however there does not seem to exist anything in the documentation that mentions what is the expected behaviour for recursive locking of rwlocks (leaving the idea that doing so is not a good usage of rwlocks). Also, as a side effect necessary for this fix, make sure we do not needlessly read lock extent buffers when the input path has skip_locking set (used when called from send). Cc: stable@vger.kernel.org Signed-off-by: Filipe Manana --- fs/btrfs/backref.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index b90cd3776f8e..f6dac40f87ff 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1406,7 +1406,8 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, read_extent_buffer(eb, dest + bytes_left, name_off, name_len); if (eb != eb_in) { - btrfs_tree_read_unlock_blocking(eb); + if (!path->skip_locking) + btrfs_tree_read_unlock_blocking(eb); free_extent_buffer(eb); } ret = btrfs_find_item(fs_root, path, parent, 0, @@ -1426,9 +1427,10 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, eb = path->nodes[0]; /* make sure we can use eb after releasing the path */ if (eb != eb_in) { - atomic_inc(&eb->refs); - btrfs_tree_read_lock(eb); - btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); + if (!path->skip_locking) + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); + path->nodes[0] = NULL; + path->locks[0] = 0; } btrfs_release_path(path); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); -- cgit v1.2.3 From d2d06d4fe0f2cc2df9b17fefec96e6e1a1271d91 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jan 2016 15:42:41 +0100 Subject: scsi_dh_rdac: always retry MODE SELECT on command lock violation If MODE SELECT returns with sense '05/91/36' (command lock violation) it should always be retried without counting the number of retries. During an HBA upgrade or similar circumstances one might see a flood of MODE SELECT command from various HBAs, which will easily trigger the sense code and exceed the retry count. Cc: Signed-off-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/device_handler/scsi_dh_rdac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 361358134315..93880ed6291c 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -562,7 +562,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev, /* * Command Lock contention */ - err = SCSI_DH_RETRY; + err = SCSI_DH_IMM_RETRY; break; default: break; @@ -612,6 +612,8 @@ retry: err = mode_select_handle_sense(sdev, h->sense); if (err == SCSI_DH_RETRY && retry_cnt--) goto retry; + if (err == SCSI_DH_IMM_RETRY) + goto retry; } if (err == SCSI_DH_OK) { h->state = RDAC_STATE_ACTIVE; -- cgit v1.2.3 From 82c43310508eb19eb41fe7862e89afeb74030b84 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 27 Jan 2016 16:19:13 +0200 Subject: SCSI: Add Marvell Console to VPD blacklist I have a Marvell 88SE9230 SATA Controller that has some sort of integrated console SCSI device attached to one of the ports. ata14: SATA link up 1.5 Gbps (SStatus 113 SControl 300) ata14.00: ATAPI: MARVELL VIRTUALL, 1.09, max UDMA/66 ata14.00: configured for UDMA/66 scsi 13:0:0:0: Processor Marvell Console 1.01 PQ: 0 ANSI: 5 Sending it VPD INQUIRY command seem to always fail with following error: ata14.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 ata14.00: irq_stat 0x40000001 ata14.00: cmd a0/01:00:00:00:01/00:00:00:00:00/a0 tag 2 dma 16640 in Inquiry 12 01 00 00 ff 00res 00/00:00:00:00:00/00:00:00:00:00/00 Emask 0x3 (HSM violation) ata14: hard resetting link This has been minor annoyance (only error printed on dmesg) until commit 09e2b0b14690 ("scsi: rescan VPD attributes") added call to scsi_attach_vpd() in scsi_rescan_device(). The commit causes the system to splat out following errors continuously without ever reaching the UI: ata14.00: configured for UDMA/66 ata14: EH complete ata14.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 ata14.00: irq_stat 0x40000001 ata14.00: cmd a0/01:00:00:00:01/00:00:00:00:00/a0 tag 6 dma 16640 in Inquiry 12 01 00 00 ff 00res 00/00:00:00:00:00/00:00:00:00:00/00 Emask 0x3 (HSM violation) ata14: hard resetting link ata14: SATA link up 1.5 Gbps (SStatus 113 SControl 300) ata14.00: configured for UDMA/66 ata14: EH complete ata14.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 ata14.00: irq_stat 0x40000001 ata14.00: cmd a0/01:00:00:00:01/00:00:00:00:00/a0 tag 7 dma 16640 in Inquiry 12 01 00 00 ff 00res 00/00:00:00:00:00/00:00:00:00:00/00 Emask 0x3 (HSM violation) Without in-depth understanding of SCSI layer and the Marvell controller, I suspect this happens because when the link goes down (because of an error) we schedule scsi_rescan_device() which again fails to read VPD data... ad infinitum. Since VPD data cannot be read from the device anyway we prevent the SCSI layer from even trying by blacklisting the device. This gets away the error and the system starts up normally. [mkp: Widened the match to all revisions of this device] Cc: Signed-off-by: Mika Westerberg Reported-by: Kirill A. Shutemov Reported-by: Alexander Duyck Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 2c1160c7ec92..820416665324 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -205,6 +205,7 @@ static struct { {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC}, {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"Marvell", "Console", NULL, BLIST_SKIP_VPD_PAGES}, {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA", "DMC-LC5", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"MATSHITA", "DMC-LC40", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, -- cgit v1.2.3 From 0fb5b1fb30fba3671dd5b1489d78e93e08d62e4e Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Thu, 4 Feb 2016 00:52:12 -0500 Subject: block/sd: Return -EREMOTEIO when WRITE SAME and DISCARD are disabled When a storage device rejects a WRITE SAME command we will disable write same functionality for the device and return -EREMOTEIO to the block layer. -EREMOTEIO will in turn prevent DM from retrying the I/O and/or failing the path. Yiwen Jiang discovered a small race where WRITE SAME requests issued simultaneously would cause -EIO to be returned. This happened because any requests being prepared after WRITE SAME had been disabled for the device caused us to return BLKPREP_KILL. The latter caused the block layer to return -EIO upon completion. To overcome this we introduce BLKPREP_INVALID which indicates that this is an invalid request for the device. blk_peek_request() is modified to return -EREMOTEIO in that case. Reported-by: Yiwen Jiang Suggested-by: Mike Snitzer Reviewed-by: Hannes Reinicke Reviewed-by: Ewan Milne Reviewed-by: Yiwen Jiang Signed-off-by: Martin K. Petersen --- block/blk-core.c | 6 ++++-- drivers/scsi/sd.c | 4 ++-- include/linux/blkdev.h | 9 ++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 476244d59309..35607dd0223b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2447,14 +2447,16 @@ struct request *blk_peek_request(struct request_queue *q) rq = NULL; break; - } else if (ret == BLKPREP_KILL) { + } else if (ret == BLKPREP_KILL || ret == BLKPREP_INVALID) { + int err = (ret == BLKPREP_INVALID) ? -EREMOTEIO : -EIO; + rq->cmd_flags |= REQ_QUIET; /* * Mark this request as started so we don't trigger * any debug logic in the end I/O path. */ blk_start_request(rq); - __blk_end_request_all(rq, -EIO); + __blk_end_request_all(rq, err); } else { printk(KERN_ERR "%s: bad return=%d\n", __func__, ret); break; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ec163d08f6c3..6e841c6da632 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -761,7 +761,7 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) break; default: - ret = BLKPREP_KILL; + ret = BLKPREP_INVALID; goto out; } @@ -839,7 +839,7 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) int ret; if (sdkp->device->no_write_same) - return BLKPREP_KILL; + return BLKPREP_INVALID; BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d372ea87ead5..a9b643a0647f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -681,9 +681,12 @@ static inline bool blk_write_same_mergeable(struct bio *a, struct bio *b) /* * q->prep_rq_fn return values */ -#define BLKPREP_OK 0 /* serve it */ -#define BLKPREP_KILL 1 /* fatal error, kill */ -#define BLKPREP_DEFER 2 /* leave on queue */ +enum { + BLKPREP_OK, /* serve it */ + BLKPREP_KILL, /* fatal error, kill, return -EIO */ + BLKPREP_DEFER, /* leave on queue */ + BLKPREP_INVALID, /* invalid command, kill, return -EREMOTEIO */ +}; extern unsigned long blk_max_low_pfn, blk_max_pfn; -- cgit v1.2.3 From 75edb54a1dea5ea1c8d3d82e27dc9ee3070f5935 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 4 Feb 2016 16:27:50 +0100 Subject: x86: Fix KASAN false positives in thread_saved_pc() thread_saved_pc() reads stack of a potentially running task. This can cause false KASAN stack-out-of-bounds reports, because the running task concurrently poisons and unpoisons own stack. The same happens in get_wchan(), and get get_wchan() was fixed by using READ_ONCE_NOCHECK(). Do the same here. Example KASAN report triggered by sysrq-t: BUG: KASAN: out-of-bounds in sched_show_task+0x306/0x3b0 at addr ffff880043c97c18 Read of size 8 by task syz-executor/23839 [...] page dumped because: kasan: bad access detected [...] Call Trace: [] __asan_report_load8_noabort+0x3e/0x40 [] sched_show_task+0x306/0x3b0 [] show_state_filter+0x124/0x1a0 [] fn_show_state+0x10/0x20 [] k_spec+0xa8/0xe0 [] kbd_event+0xb9f/0x4000 [] input_to_handler+0x3a7/0x4b0 [] input_pass_values.part.5+0x554/0x6b0 [] input_handle_event+0x2ac/0x1070 [] input_inject_event+0x237/0x280 [] evdev_write+0x478/0x680 [] __vfs_write+0x113/0x480 [] vfs_write+0x167/0x4a0 [] SyS_write+0x111/0x220 Signed-off-by: Dmitry Vyukov Acked-by: Andrey Ryabinin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: glider@google.com Cc: kasan-dev@googlegroups.com Cc: kcc@google.com Cc: linux-kernel@vger.kernel.org Cc: ryabinin.a.a@gmail.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 2d5a50cb61a2..20c11d1aa4cc 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -766,7 +766,7 @@ extern unsigned long thread_saved_pc(struct task_struct *tsk); * Return saved PC of a blocked thread. * What is this good for? it will be always the scheduler or ret_from_fork. */ -#define thread_saved_pc(t) (*(unsigned long *)((t)->thread.sp - 8)) +#define thread_saved_pc(t) READ_ONCE_NOCHECK(*(unsigned long *)((t)->thread.sp - 8)) #define task_pt_regs(tsk) ((struct pt_regs *)(tsk)->thread.sp0 - 1) extern unsigned long KSTK_ESP(struct task_struct *task); -- cgit v1.2.3 From 320549a22484952d88d4e0320218765b16cd2174 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Feb 2016 11:22:04 +0000 Subject: regmap: mmio: Revert to v4.4 endianness handling Commit 29bb45f25ff3 (regmap-mmio: Use native endianness for read/write) attempted to fix some long standing bugs in the MMIO implementation for big endian systems caused by duplicate byte swapping in both regmap and readl()/writel() which affected MIPS systems as when they are in big endian mode they flip the endianness of all registers in the system, not just the CPU. MIPS systems had worked around this by declaring regmap using IPs as little endian which is inaccurate, unfortunately the issue had not been reported. Sadly the fix makes things worse rather than better. By changing the behaviour to match the documentation it caused behaviour changes for other IPs which broke them and by using the __raw I/O accessors to avoid the endianness swapping in readl()/writel() it removed some memory ordering guarantees and could potentially generate unvirtualisable instructions on some architectures. Unfortunately sorting out all this mess in any half way sensible fashion was far too invasive to go in during an -rc cycle so instead let's go back to the old broken behaviour for v4.5, the better fixes are already queued for v4.6. This does mean that we keep the broken MIPS DTs for another release but that seems the least bad way of handling the situation. Reported-by: Johannes Berg Signed-off-by: Mark Brown --- arch/mips/boot/dts/brcm/bcm6328.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7125.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7346.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7358.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7360.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7362.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7420.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7425.dtsi | 1 + arch/mips/boot/dts/brcm/bcm7435.dtsi | 1 + drivers/base/regmap/regmap-mmio.c | 16 ++++++++-------- 10 files changed, 17 insertions(+), 8 deletions(-) diff --git a/arch/mips/boot/dts/brcm/bcm6328.dtsi b/arch/mips/boot/dts/brcm/bcm6328.dtsi index 459b9b252c3b..d61b1616b604 100644 --- a/arch/mips/boot/dts/brcm/bcm6328.dtsi +++ b/arch/mips/boot/dts/brcm/bcm6328.dtsi @@ -74,6 +74,7 @@ timer: timer@10000040 { compatible = "syscon"; reg = <0x10000040 0x2c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7125.dtsi b/arch/mips/boot/dts/brcm/bcm7125.dtsi index 4fc7ecee273c..1a7efa883c5e 100644 --- a/arch/mips/boot/dts/brcm/bcm7125.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7125.dtsi @@ -98,6 +98,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7125-sun-top-ctrl", "syscon"; reg = <0x404000 0x60c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7346.dtsi b/arch/mips/boot/dts/brcm/bcm7346.dtsi index a3039bb53477..d4bf52cfcf17 100644 --- a/arch/mips/boot/dts/brcm/bcm7346.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7346.dtsi @@ -118,6 +118,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7346-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7358.dtsi b/arch/mips/boot/dts/brcm/bcm7358.dtsi index 4274ff41ec21..8e2501694d03 100644 --- a/arch/mips/boot/dts/brcm/bcm7358.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7358.dtsi @@ -112,6 +112,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7358-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7360.dtsi b/arch/mips/boot/dts/brcm/bcm7360.dtsi index 0dcc9163c27b..7e5f76040fb8 100644 --- a/arch/mips/boot/dts/brcm/bcm7360.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7360.dtsi @@ -112,6 +112,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7360-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7362.dtsi b/arch/mips/boot/dts/brcm/bcm7362.dtsi index 2f3f9fc2c478..c739ea77acb0 100644 --- a/arch/mips/boot/dts/brcm/bcm7362.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7362.dtsi @@ -118,6 +118,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7362-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7420.dtsi b/arch/mips/boot/dts/brcm/bcm7420.dtsi index bee221b3b568..5f55d0a50a28 100644 --- a/arch/mips/boot/dts/brcm/bcm7420.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7420.dtsi @@ -99,6 +99,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7420-sun-top-ctrl", "syscon"; reg = <0x404000 0x60c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7425.dtsi b/arch/mips/boot/dts/brcm/bcm7425.dtsi index 571f30f52e3f..e24d41ab4e30 100644 --- a/arch/mips/boot/dts/brcm/bcm7425.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7425.dtsi @@ -100,6 +100,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7425-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7435.dtsi b/arch/mips/boot/dts/brcm/bcm7435.dtsi index 614ee211f71a..8b9432cc062b 100644 --- a/arch/mips/boot/dts/brcm/bcm7435.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7435.dtsi @@ -114,6 +114,7 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7425-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; + little-endian; }; reboot { diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 8812bfb9e3b8..eea51569f0eb 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -133,17 +133,17 @@ static int regmap_mmio_gather_write(void *context, while (val_size) { switch (ctx->val_bytes) { case 1: - __raw_writeb(*(u8 *)val, ctx->regs + offset); + writeb(*(u8 *)val, ctx->regs + offset); break; case 2: - __raw_writew(*(u16 *)val, ctx->regs + offset); + writew(*(u16 *)val, ctx->regs + offset); break; case 4: - __raw_writel(*(u32 *)val, ctx->regs + offset); + writel(*(u32 *)val, ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - __raw_writeq(*(u64 *)val, ctx->regs + offset); + writeq(*(u64 *)val, ctx->regs + offset); break; #endif default: @@ -193,17 +193,17 @@ static int regmap_mmio_read(void *context, while (val_size) { switch (ctx->val_bytes) { case 1: - *(u8 *)val = __raw_readb(ctx->regs + offset); + *(u8 *)val = readb(ctx->regs + offset); break; case 2: - *(u16 *)val = __raw_readw(ctx->regs + offset); + *(u16 *)val = readw(ctx->regs + offset); break; case 4: - *(u32 *)val = __raw_readl(ctx->regs + offset); + *(u32 *)val = readl(ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - *(u64 *)val = __raw_readq(ctx->regs + offset); + *(u64 *)val = readq(ctx->regs + offset); break; #endif default: -- cgit v1.2.3 From 360a8245680053619205a3ae10e6bfe624a5da1d Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 5 Feb 2016 09:05:41 +0100 Subject: ALSA: hda - Fix static checker warning in patch_hdmi.c The static checker warning is: sound/pci/hda/patch_hdmi.c:460 hdmi_eld_ctl_get() error: __memcpy() 'eld->eld_buffer' too small (256 vs 512) I have a hard time figuring out if this can ever cause an information leak (I don't think so), but nonetheless it does not hurt to increase the robustness of the code. Fixes: 68e03de98507 ('ALSA: hda - hdmi: Do not expose eld data when eld is invalid') Reported-by: Dan Carpenter Signed-off-by: David Henningsson Cc: # v3.9+ Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1f52b55d77c9..2191e2359315 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -448,7 +448,8 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, eld = &per_pin->sink_eld; mutex_lock(&per_pin->lock); - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { mutex_unlock(&per_pin->lock); snd_BUG(); return -EINVAL; -- cgit v1.2.3 From 5d2560a427fc7c4050a320be62c4994705ca81b1 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 5 Feb 2016 09:56:05 +0900 Subject: ALSA: firewire-tascam: fix NULL pointer dereference when model identification fails When unsupported models are connected, snd-firewire-tascam module causes NULL pointer dereference in fw_core_remove_address_handler() (due to list_del_rcu()). This commit prevents this bug. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 904ce0329fa1..040a96d1ba8e 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -230,6 +230,7 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm) return err; error: fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; return err; } @@ -276,6 +277,9 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) __be32 reg; unsigned int i; + if (tscm->async_handler.callback_data == NULL) + return; + /* Turn off FireWire LED. */ reg = cpu_to_be32(0x0000008e); snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -297,6 +301,8 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) ®, sizeof(reg), 0); fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; + for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) snd_fw_async_midi_port_destroy(&tscm->out_ports[i]); } -- cgit v1.2.3 From 3e78e1518e129407fae75c867e48828262b3ea6d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 5 Feb 2016 09:56:06 +0900 Subject: ALSA: firewire-tascam: add support for FW-1804 This model supports: * maximum 12 PCM channels for PCM playback * maximum 18 PCM channels for PCM capture * 4 ports for MIDI playback * 4 ports for MIDI capture * control and status messages in tx isochronous packets * up to 96.0 kHz This commit adds support for the model. As the other supported models, all of available PCM channels are always enabled. As I described in commit c0949b278515da94, Ilya Zimnovich had investigated TASCAM FireWire series in 2011 with his FW-1804. In his report, this model has internal multiplexer and any software implementation can control it. Following to the design of ALSA firewire stack, this commit won't implement it. It should be in userspace via Linux fw character device. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index ee0bc1839508..dcb11c26c225 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -33,7 +33,16 @@ static struct snd_tscm_spec model_specs[] = { .midi_playback_ports = 2, .is_controller = true, }, - /* FW-1804 may be supported. */ + { + .name = "FW-1804", + .has_adat = true, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 2, + .midi_capture_ports = 2, + .midi_playback_ports = 4, + .is_controller = false, + }, }; static int identify_model(struct snd_tscm *tscm) -- cgit v1.2.3 From 61ebe499643703af517a8253662982f6f4764c92 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 5 Feb 2016 09:56:07 +0900 Subject: ALSA: firewire-tascam: remove a flag for controller Currently, 'struct snd_tscm_spec' has a member named as 'is_controller' to identify MIDI controller. This member was originally added to skip parse control and status messages in isochronous packets for non-controller model. As long as I investigate, FW-1804 (non-controller) also transfers the control and status message, thus it becomes meaningless. This commit removes it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.c | 3 --- sound/firewire/tascam/tascam.h | 1 - 2 files changed, 4 deletions(-) diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index dcb11c26c225..e281c338e562 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -21,7 +21,6 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 8, .midi_capture_ports = 4, .midi_playback_ports = 4, - .is_controller = true, }, { .name = "FW-1082", @@ -31,7 +30,6 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 2, .midi_capture_ports = 2, .midi_playback_ports = 2, - .is_controller = true, }, { .name = "FW-1804", @@ -41,7 +39,6 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 2, .midi_capture_ports = 2, .midi_playback_ports = 4, - .is_controller = false, }, }; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 2d028d2bd3bd..66268600c357 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -39,7 +39,6 @@ struct snd_tscm_spec { unsigned int pcm_playback_analog_channels; unsigned int midi_capture_ports; unsigned int midi_playback_ports; - bool is_controller; }; #define TSCM_MIDI_IN_PORT_MAX 4 -- cgit v1.2.3 From 56661a2ed5348f3d7a3ac8788656654dd50904cd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 5 Feb 2016 09:56:08 +0900 Subject: ALSA: firewire-tascam: remove needless member for control and status message Commit 3beab0f844fa added a member for control and status message, while it's planned and not implemented yet. This commit removes it. Fixes: 3beab0f844fa('ALSA: firewire-tascam: add support for outgoing MIDI messages by asynchronous transaction') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 66268600c357..30ab77e924f7 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -71,9 +71,6 @@ struct snd_tscm { struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; u8 running_status[TSCM_MIDI_OUT_PORT_MAX]; bool on_sysex[TSCM_MIDI_OUT_PORT_MAX]; - - /* For control messages. */ - struct snd_firewire_tascam_status *status; }; #define TSCM_ADDR_BASE 0xffff00000000ull -- cgit v1.2.3 From e6c058f9b2700a720d3fad0f6caad1d030c533ee Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 5 Feb 2016 17:15:42 +0100 Subject: MIPS: Wire up copy_file_range syscall. Signed-off-by: Ralf Baechle --- arch/mips/include/uapi/asm/unistd.h | 15 +++++++++------ arch/mips/kernel/scall32-o32.S | 1 + arch/mips/kernel/scall64-64.S | 1 + arch/mips/kernel/scall64-n32.S | 1 + arch/mips/kernel/scall64-o32.S | 1 + 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h index 90f03a7da665..3129795de940 100644 --- a/arch/mips/include/uapi/asm/unistd.h +++ b/arch/mips/include/uapi/asm/unistd.h @@ -380,16 +380,17 @@ #define __NR_userfaultfd (__NR_Linux + 357) #define __NR_membarrier (__NR_Linux + 358) #define __NR_mlock2 (__NR_Linux + 359) +#define __NR_copy_file_range (__NR_Linux + 360) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 359 +#define __NR_Linux_syscalls 360 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 359 +#define __NR_O32_Linux_syscalls 360 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -717,16 +718,17 @@ #define __NR_userfaultfd (__NR_Linux + 317) #define __NR_membarrier (__NR_Linux + 318) #define __NR_mlock2 (__NR_Linux + 319) +#define __NR_copy_file_range (__NR_Linux + 320) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 319 +#define __NR_Linux_syscalls 320 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 319 +#define __NR_64_Linux_syscalls 320 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -1058,15 +1060,16 @@ #define __NR_userfaultfd (__NR_Linux + 321) #define __NR_membarrier (__NR_Linux + 322) #define __NR_mlock2 (__NR_Linux + 323) +#define __NR_copy_file_range (__NR_Linux + 324) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 323 +#define __NR_Linux_syscalls 324 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 323 +#define __NR_N32_Linux_syscalls 324 #endif /* _UAPI_ASM_UNISTD_H */ diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 2d23c834ba96..a56317444bda 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -595,3 +595,4 @@ EXPORT(sys_call_table) PTR sys_userfaultfd PTR sys_membarrier PTR sys_mlock2 + PTR sys_copy_file_range /* 4360 */ diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index deac63315d0e..2b2dc14610d0 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -433,4 +433,5 @@ EXPORT(sys_call_table) PTR sys_userfaultfd PTR sys_membarrier PTR sys_mlock2 + PTR sys_copy_file_range /* 5320 */ .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 5a69eb48d0a8..2bf5c8593d91 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -423,4 +423,5 @@ EXPORT(sysn32_call_table) PTR sys_userfaultfd PTR sys_membarrier PTR sys_mlock2 + PTR sys_copy_file_range .size sysn32_call_table,.-sysn32_call_table diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index e4b6d7c97822..c5b759e584c7 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -578,4 +578,5 @@ EXPORT(sys32_call_table) PTR sys_userfaultfd PTR sys_membarrier PTR sys_mlock2 + PTR sys_copy_file_range /* 4360 */ .size sys32_call_table,.-sys32_call_table -- cgit v1.2.3 From 6c361d10e0eb859233c71954abcd20d2d8700587 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Feb 2016 20:12:24 +0100 Subject: Revert "ALSA: hda - Fix noise on Gigabyte Z170X mobo" This reverts commit 0c25ad80408e95e0a4fbaf0056950206e95f726f. The original commit disabled the aamixer path due to the noise problem, but it turned out that some mobo with the same PCI SSID doesn't suffer from the issue, and the disabled function (analog loopback) is still demanded by users. Since the recent commit [e7fdd52779a6: ALSA: hda - Implement loopback control switch for Realtek and other codecs], we have the dynamic mixer switch to enable/disable the aamix path, and we don't have to disable the path statically any longer. So, let's revert the disablement, so that only the user suffering from the noise problem can turn off the aamix on the fly. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=108301 Reported-by: Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 21992fb7035d..a733e5dc701d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1787,7 +1787,6 @@ enum { ALC882_FIXUP_NO_PRIMARY_HP, ALC887_FIXUP_ASUS_BASS, ALC887_FIXUP_BASS_CHMAP, - ALC882_FIXUP_DISABLE_AAMIX, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -1949,8 +1948,6 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, static void alc_fixup_bass_chmap(struct hda_codec *codec, const struct hda_fixup *fix, int action); -static void alc_fixup_disable_aamix(struct hda_codec *codec, - const struct hda_fixup *fix, int action); static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { @@ -2188,10 +2185,6 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_bass_chmap, }, - [ALC882_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -2259,7 +2252,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), - SND_PCI_QUIRK(0x1458, 0xa182, "Gigabyte Z170X-UD3", ALC882_FIXUP_DISABLE_AAMIX), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), -- cgit v1.2.3 From a176cd304a8a07d6d9191126fd9bece4f67358c3 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 5 Feb 2016 14:10:04 -0600 Subject: PCI: rcar: Add gen2 device tree support for r8a7793 Add "renesas,pci-r8a7793" as a compatibility string for "renesas,pci-rcar-gen2". This doesn't change the driver, so it does nothing by itself. But it does mean that checkpatch won't complain about a future patch that adds "renesas,pci-r8a7793" to a DT, which helps ensure that shipped DTs use documented compatibility strings. [bhelgaas: changelog] Signed-off-by: Simon Horman Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring --- Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt b/Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt index 4e8b90e43dd8..07a75094c5a8 100644 --- a/Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt +++ b/Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt @@ -8,6 +8,7 @@ OHCI and EHCI controllers. Required properties: - compatible: "renesas,pci-r8a7790" for the R8A7790 SoC; "renesas,pci-r8a7791" for the R8A7791 SoC; + "renesas,pci-r8a7793" for the R8A7793 SoC; "renesas,pci-r8a7794" for the R8A7794 SoC; "renesas,pci-rcar-gen2" for a generic R-Car Gen2 compatible device -- cgit v1.2.3 From 0cf1337e0b83c16de4e7e98dad3a6afce6043fea Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 5 Feb 2016 14:10:13 -0600 Subject: PCI: rcar: Add device tree support for r8a7793 Add "renesas,pcie-r8a7793" as a compatibility string for "renesas,pcie-rcar-gen2". This doesn't change the driver, so it does nothing by itself. But it does mean that checkpatch won't complain about a future patch that adds "renesas,pci-r8a7793" to a DT, which helps ensure that shipped DTs use documented compatibility strings. [bhelgaas: changelog] Signed-off-by: Simon Horman Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring --- Documentation/devicetree/bindings/pci/rcar-pci.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt index 558fe528ae19..6cf99690eef9 100644 --- a/Documentation/devicetree/bindings/pci/rcar-pci.txt +++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt @@ -4,6 +4,7 @@ Required properties: compatible: "renesas,pcie-r8a7779" for the R8A7779 SoC; "renesas,pcie-r8a7790" for the R8A7790 SoC; "renesas,pcie-r8a7791" for the R8A7791 SoC; + "renesas,pcie-r8a7793" for the R8A7793 SoC; "renesas,pcie-r8a7795" for the R8A7795 SoC; "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device. -- cgit v1.2.3 From fd98d89698855f6cf5650c014e5d3fca38783c8b Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Fri, 5 Feb 2016 20:06:37 +0530 Subject: RDMA/ocrdma: Initialize stats resources in the driver before ib device registration. In the latest kernel, process_mad hook of the driver can be invoked as soon as device is registered. In this hook, ocrdma driver is issuing a command to get the stats counters from the HW. This is triggering system crash since the statistics command resources are not allocated by the driver. Changing the sequence of initialization to avoid this crash. Signed-off-by: Selvin Xavier Signed-off-by: Doug Ledford --- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 6 ++++++ drivers/infiniband/hw/ocrdma/ocrdma_stats.c | 14 ++++---------- drivers/infiniband/hw/ocrdma/ocrdma_stats.h | 2 ++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index 573849354cb9..f38743018cb4 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -228,6 +228,11 @@ static int ocrdma_alloc_resources(struct ocrdma_dev *dev) ocrdma_alloc_pd_pool(dev); + if (!ocrdma_alloc_stats_resources(dev)) { + pr_err("%s: stats resource allocation failed\n", __func__); + goto alloc_err; + } + spin_lock_init(&dev->av_tbl.lock); spin_lock_init(&dev->flush_q_lock); return 0; @@ -238,6 +243,7 @@ alloc_err: static void ocrdma_free_resources(struct ocrdma_dev *dev) { + ocrdma_release_stats_resources(dev); kfree(dev->stag_arr); kfree(dev->qp_tbl); kfree(dev->cq_tbl); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c index 86c303a620c1..fc02e86849ce 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c @@ -64,10 +64,11 @@ static int ocrdma_add_stat(char *start, char *pcur, return cpy_len; } -static bool ocrdma_alloc_stats_mem(struct ocrdma_dev *dev) +bool ocrdma_alloc_stats_resources(struct ocrdma_dev *dev) { struct stats_mem *mem = &dev->stats_mem; + mutex_init(&dev->stats_lock); /* Alloc mbox command mem*/ mem->size = max_t(u32, sizeof(struct ocrdma_rdma_stats_req), sizeof(struct ocrdma_rdma_stats_resp)); @@ -91,13 +92,14 @@ static bool ocrdma_alloc_stats_mem(struct ocrdma_dev *dev) return true; } -static void ocrdma_release_stats_mem(struct ocrdma_dev *dev) +void ocrdma_release_stats_resources(struct ocrdma_dev *dev) { struct stats_mem *mem = &dev->stats_mem; if (mem->va) dma_free_coherent(&dev->nic_info.pdev->dev, mem->size, mem->va, mem->pa); + mem->va = NULL; kfree(mem->debugfs_mem); } @@ -838,15 +840,9 @@ void ocrdma_add_port_stats(struct ocrdma_dev *dev) &dev->reset_stats, &ocrdma_dbg_ops)) goto err; - /* Now create dma_mem for stats mbx command */ - if (!ocrdma_alloc_stats_mem(dev)) - goto err; - - mutex_init(&dev->stats_lock); return; err: - ocrdma_release_stats_mem(dev); debugfs_remove_recursive(dev->dir); dev->dir = NULL; } @@ -856,8 +852,6 @@ void ocrdma_rem_port_stats(struct ocrdma_dev *dev) if (!dev->dir) return; debugfs_remove(dev->dir); - mutex_destroy(&dev->stats_lock); - ocrdma_release_stats_mem(dev); } void ocrdma_init_debugfs(void) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.h b/drivers/infiniband/hw/ocrdma/ocrdma_stats.h index c9e58d04c7b8..bba1fec4f11f 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.h +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.h @@ -65,6 +65,8 @@ enum OCRDMA_STATS_TYPE { void ocrdma_rem_debugfs(void); void ocrdma_init_debugfs(void); +bool ocrdma_alloc_stats_resources(struct ocrdma_dev *dev); +void ocrdma_release_stats_resources(struct ocrdma_dev *dev); void ocrdma_rem_port_stats(struct ocrdma_dev *dev); void ocrdma_add_port_stats(struct ocrdma_dev *dev); int ocrdma_pma_counters(struct ocrdma_dev *dev, -- cgit v1.2.3 From 7d82df1663e8a66961954258f3a99701f0126e73 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Fri, 5 Feb 2016 20:06:38 +0530 Subject: RDMA/ocrdma: populate max_sge_rd in device attributes max_sge_rd is used by some of the ULPs to calculate the maximum number of SGEs that can be used for RDMA READ. Populating this value in the response of query_device verb. Also, avoid checking the max_srq_sge while populating max_sge. Signed-off-by: Selvin Xavier Signed-off-by: Doug Ledford --- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index d4c687b548d8..7cf1324ad081 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -125,8 +125,8 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, IB_DEVICE_SYS_IMAGE_GUID | IB_DEVICE_LOCAL_DMA_LKEY | IB_DEVICE_MEM_MGT_EXTENSIONS; - attr->max_sge = min(dev->attr.max_send_sge, dev->attr.max_srq_sge); - attr->max_sge_rd = 0; + attr->max_sge = dev->attr.max_send_sge; + attr->max_sge_rd = attr->max_sge; attr->max_cq = dev->attr.max_cq; attr->max_cqe = dev->attr.max_cqe; attr->max_mr = dev->attr.max_mr; -- cgit v1.2.3 From aff3ead9fa25b2a472d1a894365f66b28e1e2787 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Fri, 5 Feb 2016 20:06:39 +0530 Subject: RDMA/ocrdma: Fix pkey_index returned by driver in rq work completion Currently returning the pkey value instead of pkey index. pkey index is always zero since ocrdma supports only default pkey. Signed-off-by: Selvin Xavier Signed-off-by: Doug Ledford --- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 7cf1324ad081..37620b4baafb 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -2726,8 +2726,7 @@ static int ocrdma_update_ud_rcqe(struct ib_wc *ibwc, struct ocrdma_cqe *cqe) OCRDMA_CQE_UD_STATUS_MASK) >> OCRDMA_CQE_UD_STATUS_SHIFT; ibwc->src_qp = le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_SRCQP_MASK; - ibwc->pkey_index = le32_to_cpu(cqe->ud.rxlen_pkey) & - OCRDMA_CQE_PKEY_MASK; + ibwc->pkey_index = 0; ibwc->wc_flags = IB_WC_GRH; ibwc->byte_len = (le32_to_cpu(cqe->ud.rxlen_pkey) >> OCRDMA_CQE_UD_XFER_LEN_SHIFT); -- cgit v1.2.3 From 7425f410ca6cffe81400906286f80e8e15d9b301 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Fri, 5 Feb 2016 20:06:40 +0530 Subject: RDMA/ocrdma: Fixing ocrdma debugfs directory remove During the ocrdma device remove sequence, the debugfs directory tree of each ocrdma device needs to be removed. Use debugfs_remove_recursive instead of debugfs_remove. Signed-off-by: Selvin Xavier Signed-off-by: Doug Ledford --- drivers/infiniband/hw/ocrdma/ocrdma_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c index fc02e86849ce..255f774080a4 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c @@ -851,7 +851,7 @@ void ocrdma_rem_port_stats(struct ocrdma_dev *dev) { if (!dev->dir) return; - debugfs_remove(dev->dir); + debugfs_remove_recursive(dev->dir); } void ocrdma_init_debugfs(void) -- cgit v1.2.3 From 0f4a943168f31d29a1701908931acaba518b131a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 19 Jan 2016 15:23:02 -0800 Subject: target: Fix remote-port TMR ABORT + se_cmd fabric stop To address the bug where fabric driver level shutdown of se_cmd occurs at the same time when TMR CMD_T_ABORTED is happening resulting in a -1 ->cmd_kref, this patch adds a CMD_T_FABRIC_STOP bit that is used to determine when TMR + driver I_T nexus shutdown is happening concurrently. It changes target_sess_cmd_list_set_waiting() to obtain se_cmd->cmd_kref + set CMD_T_FABRIC_STOP, and drop local reference in target_wait_for_sess_cmds() and invoke extra target_put_sess_cmd() during Task Aborted Status (TAS) when necessary. Also, it adds a new target_wait_free_cmd() wrapper around transport_wait_for_tasks() for the special case within transport_generic_free_cmd() to set CMD_T_FABRIC_STOP, and is now aware of CMD_T_ABORTED + CMD_T_TAS status bits to know when an extra transport_put_cmd() during TAS is required. Note transport_generic_free_cmd() is expected to block on cmd->cmd_wait_comp in order to follow what iscsi-target expects during iscsi_conn context se_cmd shutdown. Cc: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 54 ++++++++---- drivers/target/target_core_transport.c | 145 +++++++++++++++++++++++++-------- include/target/target_core_base.h | 2 + 3 files changed, 150 insertions(+), 51 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 3e0d77a4c7b6..82a663ba9800 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -75,16 +75,18 @@ void core_tmr_release_req(struct se_tmr_req *tmr) kfree(tmr); } -static void core_tmr_handle_tas_abort( - struct se_session *tmr_sess, - struct se_cmd *cmd, - int tas) +static void core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas) { - bool remove = true; + unsigned long flags; + bool remove = true, send_tas; /* * TASK ABORTED status (TAS) bit support */ - if (tmr_sess && tmr_sess != cmd->se_sess && tas) { + spin_lock_irqsave(&cmd->t_state_lock, flags); + send_tas = (cmd->transport_state & CMD_T_TAS); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + if (send_tas) { remove = false; transport_send_task_abort(cmd); } @@ -107,7 +109,8 @@ static int target_check_cdb_and_preempt(struct list_head *list, return 1; } -static bool __target_check_io_state(struct se_cmd *se_cmd) +static bool __target_check_io_state(struct se_cmd *se_cmd, + struct se_session *tmr_sess, int tas) { struct se_session *sess = se_cmd->se_sess; @@ -115,21 +118,32 @@ static bool __target_check_io_state(struct se_cmd *se_cmd) WARN_ON_ONCE(!irqs_disabled()); /* * If command already reached CMD_T_COMPLETE state within - * target_complete_cmd(), this se_cmd has been passed to - * fabric driver and will not be aborted. + * target_complete_cmd() or CMD_T_FABRIC_STOP due to shutdown, + * this se_cmd has been passed to fabric driver and will + * not be aborted. * * Otherwise, obtain a local se_cmd->cmd_kref now for TMR * ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as * long as se_cmd->cmd_kref is still active unless zero. */ spin_lock(&se_cmd->t_state_lock); - if (se_cmd->transport_state & CMD_T_COMPLETE) { - pr_debug("Attempted to abort io tag: %llu already complete," + if (se_cmd->transport_state & (CMD_T_COMPLETE | CMD_T_FABRIC_STOP)) { + pr_debug("Attempted to abort io tag: %llu already complete or" + " fabric stop, skipping\n", se_cmd->tag); + spin_unlock(&se_cmd->t_state_lock); + return false; + } + if (sess->sess_tearing_down || se_cmd->cmd_wait_set) { + pr_debug("Attempted to abort io tag: %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) + se_cmd->transport_state |= CMD_T_TAS; + spin_unlock(&se_cmd->t_state_lock); return kref_get_unless_zero(&se_cmd->cmd_kref); @@ -161,7 +175,7 @@ void core_tmr_abort_task( printk("ABORT_TASK: Found referenced %s task_tag: %llu\n", se_cmd->se_tfo->get_fabric_name(), ref_tag); - if (!__target_check_io_state(se_cmd)) { + if (!__target_check_io_state(se_cmd, se_sess, 0)) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); target_put_sess_cmd(se_cmd); goto out; @@ -230,7 +244,8 @@ static void core_tmr_drain_tmr_list( spin_lock(&sess->sess_cmd_lock); spin_lock(&cmd->t_state_lock); - if (!(cmd->transport_state & CMD_T_ACTIVE)) { + 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; @@ -240,15 +255,22 @@ static void core_tmr_drain_tmr_list( spin_unlock(&sess->sess_cmd_lock); continue; } + if (sess->sess_tearing_down || cmd->cmd_wait_set) { + 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 = kref_get_unless_zero(&cmd->cmd_kref); - spin_unlock(&sess->sess_cmd_lock); 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); } spin_unlock_irqrestore(&dev->se_tmr_lock, flags); @@ -325,7 +347,7 @@ static void core_tmr_drain_state_list( continue; spin_lock(&sess->sess_cmd_lock); - rc = __target_check_io_state(cmd); + rc = __target_check_io_state(cmd, tmr_sess, tas); spin_unlock(&sess->sess_cmd_lock); if (!rc) continue; @@ -364,7 +386,7 @@ static void core_tmr_drain_state_list( cancel_work_sync(&cmd->work); transport_wait_for_tasks(cmd); - core_tmr_handle_tas_abort(tmr_sess, cmd, tas); + core_tmr_handle_tas_abort(cmd, tas); target_put_sess_cmd(cmd); } } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 94e372af9e28..3441b159e306 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2431,18 +2431,33 @@ static void transport_write_pending_qf(struct se_cmd *cmd) } } +static bool +__transport_wait_for_tasks(struct se_cmd *, bool, bool *, bool *, + unsigned long *flags); + +static void target_wait_free_cmd(struct se_cmd *cmd, bool *aborted, bool *tas) +{ + unsigned long flags; + + spin_lock_irqsave(&cmd->t_state_lock, flags); + __transport_wait_for_tasks(cmd, true, aborted, tas, &flags); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); +} + int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) { int ret = 0; + bool aborted = false, tas = false; if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) { if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) - transport_wait_for_tasks(cmd); + target_wait_free_cmd(cmd, &aborted, &tas); - ret = transport_put_cmd(cmd); + if (!aborted || tas) + ret = transport_put_cmd(cmd); } else { if (wait_for_tasks) - transport_wait_for_tasks(cmd); + target_wait_free_cmd(cmd, &aborted, &tas); /* * Handle WRITE failure case where transport_generic_new_cmd() * has already added se_cmd to state_list, but fabric has @@ -2454,7 +2469,20 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) if (cmd->se_lun) transport_lun_remove_cmd(cmd); - ret = transport_put_cmd(cmd); + if (!aborted || tas) + ret = transport_put_cmd(cmd); + } + /* + * If the task has been internally aborted due to TMR ABORT_TASK + * or LUN_RESET, target_core_tmr.c is responsible for performing + * the remaining calls to target_put_sess_cmd(), and not the + * callers of this function. + */ + if (aborted) { + pr_debug("Detected CMD_T_ABORTED for ITT: %llu\n", cmd->tag); + wait_for_completion(&cmd->cmd_wait_comp); + cmd->se_tfo->release_cmd(cmd); + ret = 1; } return ret; } @@ -2509,6 +2537,7 @@ static void target_release_cmd_kref(struct kref *kref) struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref); struct se_session *se_sess = se_cmd->se_sess; unsigned long flags; + bool fabric_stop; spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (list_empty(&se_cmd->se_cmd_list)) { @@ -2517,13 +2546,19 @@ static void target_release_cmd_kref(struct kref *kref) se_cmd->se_tfo->release_cmd(se_cmd); return; } - if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) { + + spin_lock(&se_cmd->t_state_lock); + fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP); + spin_unlock(&se_cmd->t_state_lock); + + if (se_cmd->cmd_wait_set || fabric_stop) { + list_del_init(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); target_free_cmd_mem(se_cmd); complete(&se_cmd->cmd_wait_comp); return; } - list_del(&se_cmd->se_cmd_list); + list_del_init(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); target_free_cmd_mem(se_cmd); @@ -2555,6 +2590,7 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess) { struct se_cmd *se_cmd; unsigned long flags; + int rc; spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (se_sess->sess_tearing_down) { @@ -2564,8 +2600,15 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess) se_sess->sess_tearing_down = 1; list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list); - list_for_each_entry(se_cmd, &se_sess->sess_wait_list, se_cmd_list) - se_cmd->cmd_wait_set = 1; + list_for_each_entry(se_cmd, &se_sess->sess_wait_list, se_cmd_list) { + rc = kref_get_unless_zero(&se_cmd->cmd_kref); + if (rc) { + se_cmd->cmd_wait_set = 1; + spin_lock(&se_cmd->t_state_lock); + se_cmd->transport_state |= CMD_T_FABRIC_STOP; + spin_unlock(&se_cmd->t_state_lock); + } + } spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); } @@ -2578,15 +2621,25 @@ void target_wait_for_sess_cmds(struct se_session *se_sess) { struct se_cmd *se_cmd, *tmp_cmd; unsigned long flags; + bool tas; list_for_each_entry_safe(se_cmd, tmp_cmd, &se_sess->sess_wait_list, se_cmd_list) { - list_del(&se_cmd->se_cmd_list); + list_del_init(&se_cmd->se_cmd_list); pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:" " %d\n", se_cmd, se_cmd->t_state, se_cmd->se_tfo->get_cmd_state(se_cmd)); + spin_lock_irqsave(&se_cmd->t_state_lock, flags); + tas = (se_cmd->transport_state & CMD_T_TAS); + spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); + + if (!target_put_sess_cmd(se_cmd)) { + if (tas) + target_put_sess_cmd(se_cmd); + } + wait_for_completion(&se_cmd->cmd_wait_comp); pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d" " fabric state: %d\n", se_cmd, se_cmd->t_state, @@ -2608,53 +2661,75 @@ void transport_clear_lun_ref(struct se_lun *lun) wait_for_completion(&lun->lun_ref_comp); } -/** - * transport_wait_for_tasks - wait for completion to occur - * @cmd: command to wait - * - * Called from frontend fabric context to wait for storage engine - * to pause and/or release frontend generated struct se_cmd. - */ -bool transport_wait_for_tasks(struct se_cmd *cmd) +static bool +__transport_wait_for_tasks(struct se_cmd *cmd, bool fabric_stop, + bool *aborted, bool *tas, unsigned long *flags) + __releases(&cmd->t_state_lock) + __acquires(&cmd->t_state_lock) { - unsigned long flags; - spin_lock_irqsave(&cmd->t_state_lock, flags); + assert_spin_locked(&cmd->t_state_lock); + WARN_ON_ONCE(!irqs_disabled()); + + if (fabric_stop) + cmd->transport_state |= CMD_T_FABRIC_STOP; + + if (cmd->transport_state & CMD_T_ABORTED) + *aborted = true; + + if (cmd->transport_state & CMD_T_TAS) + *tas = true; + if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) && - !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) return false; - } if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) && - !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) return false; - } - if (!(cmd->transport_state & CMD_T_ACTIVE)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + if (!(cmd->transport_state & CMD_T_ACTIVE)) + return false; + + if (fabric_stop && *aborted) return false; - } cmd->transport_state |= CMD_T_STOP; - pr_debug("wait_for_tasks: Stopping %p ITT: 0x%08llx i_state: %d, t_state: %d, CMD_T_STOP\n", - cmd, cmd->tag, cmd->se_tfo->get_cmd_state(cmd), cmd->t_state); + pr_debug("wait_for_tasks: Stopping %p ITT: 0x%08llx i_state: %d," + " t_state: %d, CMD_T_STOP\n", cmd, cmd->tag, + cmd->se_tfo->get_cmd_state(cmd), cmd->t_state); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + spin_unlock_irqrestore(&cmd->t_state_lock, *flags); wait_for_completion(&cmd->t_transport_stop_comp); - spin_lock_irqsave(&cmd->t_state_lock, flags); + spin_lock_irqsave(&cmd->t_state_lock, *flags); cmd->transport_state &= ~(CMD_T_ACTIVE | CMD_T_STOP); - pr_debug("wait_for_tasks: Stopped wait_for_completion(&cmd->t_transport_stop_comp) for ITT: 0x%08llx\n", - cmd->tag); + pr_debug("wait_for_tasks: Stopped wait_for_completion(&cmd->" + "t_transport_stop_comp) for ITT: 0x%08llx\n", cmd->tag); + return true; +} + +/** + * transport_wait_for_tasks - wait for completion to occur + * @cmd: command to wait + * + * Called from frontend fabric context to wait for storage engine + * to pause and/or release frontend generated struct se_cmd. + */ +bool transport_wait_for_tasks(struct se_cmd *cmd) +{ + unsigned long flags; + bool ret, aborted = false, tas = false; + + spin_lock_irqsave(&cmd->t_state_lock, flags); + ret = __transport_wait_for_tasks(cmd, false, &aborted, &tas, &flags); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return true; + return ret; } EXPORT_SYMBOL(transport_wait_for_tasks); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 1a76726c45a2..1579539e5669 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -493,6 +493,8 @@ struct se_cmd { #define CMD_T_DEV_ACTIVE (1 << 7) #define CMD_T_REQUEST_STOP (1 << 8) #define CMD_T_BUSY (1 << 9) +#define CMD_T_TAS (1 << 10) +#define CMD_T_FABRIC_STOP (1 << 11) spinlock_t t_state_lock; struct kref cmd_kref; struct completion t_transport_stop_comp; -- cgit v1.2.3 From 63e41ebc6630f39422d87f8a4bade1e793f37a01 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Mon, 1 Feb 2016 14:27:30 +0100 Subject: crypto: user - lock crypto_alg_list on alg dump We miss to take the crypto_alg_sem semaphore when traversing the crypto_alg_list for CRYPTO_MSG_GETALG dumps. This allows a race with crypto_unregister_alg() removing algorithms from the list while we're still traversing it, thereby leading to a use-after-free as show below: [ 3482.071639] general protection fault: 0000 [#1] SMP [ 3482.075639] Modules linked in: aes_x86_64 glue_helper lrw ablk_helper cryptd gf128mul ipv6 pcspkr serio_raw virtio_net microcode virtio_pci virtio_ring virtio sr_mod cdrom [last unloaded: aesni_intel] [ 3482.075639] CPU: 1 PID: 11065 Comm: crconf Not tainted 4.3.4-grsec+ #126 [ 3482.075639] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 3482.075639] task: ffff88001cd41a40 ti: ffff88001cd422c8 task.ti: ffff88001cd422c8 [ 3482.075639] RIP: 0010:[] [] strncpy+0x13/0x30 [ 3482.075639] RSP: 0018:ffff88001f713b60 EFLAGS: 00010202 [ 3482.075639] RAX: ffff88001f6c4430 RBX: ffff88001f6c43a0 RCX: ffff88001f6c4430 [ 3482.075639] RDX: 0000000000000040 RSI: fefefefefefeff16 RDI: ffff88001f6c4430 [ 3482.075639] RBP: ffff88001f713b60 R08: ffff88001f6c4470 R09: ffff88001f6c4480 [ 3482.075639] R10: 0000000000000002 R11: 0000000000000246 R12: ffff88001ce2aa28 [ 3482.075639] R13: ffff880000093700 R14: ffff88001f5e4bf8 R15: 0000000000003b20 [ 3482.075639] FS: 0000033826fa2700(0000) GS:ffff88001e900000(0000) knlGS:0000000000000000 [ 3482.075639] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 3482.075639] CR2: ffffffffff600400 CR3: 00000000139ec000 CR4: 00000000001606f0 [ 3482.075639] Stack: [ 3482.075639] ffff88001f713bd8 ffffffff936ccd00 ffff88001e5c4200 ffff880000093700 [ 3482.075639] ffff88001f713bd0 ffffffff938ef4bf 0000000000000000 0000000000003b20 [ 3482.075639] ffff88001f5e4bf8 ffff88001f5e4848 0000000000000000 0000000000003b20 [ 3482.075639] Call Trace: [ 3482.075639] [] crypto_report_alg+0xc0/0x3e0 [ 3482.075639] [] ? __alloc_skb+0x16f/0x300 [ 3482.075639] [] crypto_dump_report+0x6a/0x90 [ 3482.075639] [] netlink_dump+0x147/0x2e0 [ 3482.075639] [] __netlink_dump_start+0x159/0x190 [ 3482.075639] [] crypto_user_rcv_msg+0xc3/0x130 [ 3482.075639] [] ? crypto_report_alg+0x3e0/0x3e0 [ 3482.075639] [] ? alg_test_crc32c+0x120/0x120 [ 3482.075639] [] ? __netlink_lookup+0xd5/0x120 [ 3482.075639] [] ? crypto_add_alg+0x1d0/0x1d0 [ 3482.075639] [] netlink_rcv_skb+0xe1/0x130 [ 3482.075639] [] crypto_netlink_rcv+0x28/0x40 [ 3482.075639] [] netlink_unicast+0x108/0x180 [ 3482.075639] [] netlink_sendmsg+0x541/0x770 [ 3482.075639] [] sock_sendmsg+0x21/0x40 [ 3482.075639] [] SyS_sendto+0xf3/0x130 [ 3482.075639] [] ? bad_area_nosemaphore+0x13/0x20 [ 3482.075639] [] ? __do_page_fault+0x80/0x3a0 [ 3482.075639] [] entry_SYSCALL_64_fastpath+0x12/0x6e [ 3482.075639] Code: 88 4a ff 75 ed 5d 48 0f ba 2c 24 3f c3 66 66 2e 0f 1f 84 00 00 00 00 00 55 48 85 d2 48 89 f8 48 89 f9 4c 8d 04 17 48 89 e5 74 15 <0f> b6 16 80 fa 01 88 11 48 83 de ff 48 83 c1 01 4c 39 c1 75 eb [ 3482.075639] RIP [] strncpy+0x13/0x30 To trigger the race run the following loops simultaneously for a while: $ while : ; do modprobe aesni-intel; rmmod aesni-intel; done $ while : ; do crconf show all > /dev/null; done Fix the race by taking the crypto_alg_sem read lock, thereby preventing crypto_unregister_alg() from modifying the algorithm list during the dump. This bug has been detected by the PaX memory sanitize feature. Cc: stable@vger.kernel.org Signed-off-by: Mathias Krause Cc: Steffen Klassert Cc: PaX Team Signed-off-by: Herbert Xu --- crypto/crypto_user.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 237f3795cfaa..43fe85f20d57 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -499,6 +499,7 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (link->dump == NULL) return -EINVAL; + down_read(&crypto_alg_sem); list_for_each_entry(alg, &crypto_alg_list, cra_list) dump_alloc += CRYPTO_REPORT_MAXSIZE; @@ -508,8 +509,11 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) .done = link->done, .min_dump_alloc = dump_alloc, }; - return netlink_dump_start(crypto_nlsk, skb, nlh, &c); + err = netlink_dump_start(crypto_nlsk, skb, nlh, &c); } + up_read(&crypto_alg_sem); + + return err; } err = nlmsg_parse(nlh, crypto_msg_min[type], attrs, CRYPTOCFGA_MAX, -- cgit v1.2.3 From ec69bbfb9902c32a5c1492f2b1b8ad032a66d724 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 3 Feb 2016 21:39:24 +0800 Subject: crypto: algif_skcipher - Do not assume that req is unchanged The async path in algif_skcipher assumes that the crypto completion function will be called with the original request. This is not necessarily the case. In fact there is no need for this anyway since we already embed information into the request with struct skcipher_async_req. This patch adds a pointer to that struct and then passes it as the data to the callback function. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu Tested-by: Tadeusz Struk --- crypto/algif_skcipher.c | 60 +++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 38c1aa89d3a0..ec07a864b9c5 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -65,18 +65,10 @@ struct skcipher_async_req { struct skcipher_async_rsgl first_sgl; struct list_head list; struct scatterlist *tsg; - char iv[]; + atomic_t *inflight; + struct skcipher_request req; }; -#define GET_SREQ(areq, ctx) (struct skcipher_async_req *)((char *)areq + \ - crypto_skcipher_reqsize(crypto_skcipher_reqtfm(&ctx->req))) - -#define GET_REQ_SIZE(ctx) \ - crypto_skcipher_reqsize(crypto_skcipher_reqtfm(&ctx->req)) - -#define GET_IV_SIZE(ctx) \ - crypto_skcipher_ivsize(crypto_skcipher_reqtfm(&ctx->req)) - #define MAX_SGL_ENTS ((4096 - sizeof(struct skcipher_sg_list)) / \ sizeof(struct scatterlist) - 1) @@ -102,15 +94,12 @@ static void skcipher_free_async_sgls(struct skcipher_async_req *sreq) static void skcipher_async_cb(struct crypto_async_request *req, int err) { - struct sock *sk = req->data; - struct alg_sock *ask = alg_sk(sk); - struct skcipher_ctx *ctx = ask->private; - struct skcipher_async_req *sreq = GET_SREQ(req, ctx); + struct skcipher_async_req *sreq = req->data; struct kiocb *iocb = sreq->iocb; - atomic_dec(&ctx->inflight); + atomic_dec(sreq->inflight); skcipher_free_async_sgls(sreq); - kfree(req); + kzfree(sreq); iocb->ki_complete(iocb, err, err); } @@ -509,37 +498,42 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); struct skcipher_ctx *ctx = ask->private; + struct skcipher_tfm *skc = pask->private; + struct crypto_skcipher *tfm = skc->skcipher; struct skcipher_sg_list *sgl; struct scatterlist *sg; struct skcipher_async_req *sreq; struct skcipher_request *req; struct skcipher_async_rsgl *last_rsgl = NULL; unsigned int txbufs = 0, len = 0, tx_nents = skcipher_all_sg_nents(ctx); - unsigned int reqlen = sizeof(struct skcipher_async_req) + - GET_REQ_SIZE(ctx) + GET_IV_SIZE(ctx); + unsigned int reqsize = crypto_skcipher_reqsize(tfm); + unsigned int ivsize = crypto_skcipher_ivsize(tfm); int err = -ENOMEM; bool mark = false; + char *iv; - lock_sock(sk); - req = kmalloc(reqlen, GFP_KERNEL); - if (unlikely(!req)) - goto unlock; + sreq = kzalloc(sizeof(*sreq) + reqsize + ivsize, GFP_KERNEL); + if (unlikely(!sreq)) + goto out; - sreq = GET_SREQ(req, ctx); + req = &sreq->req; + iv = (char *)(req + 1) + reqsize; sreq->iocb = msg->msg_iocb; - memset(&sreq->first_sgl, '\0', sizeof(struct skcipher_async_rsgl)); INIT_LIST_HEAD(&sreq->list); + sreq->inflight = &ctx->inflight; + + lock_sock(sk); sreq->tsg = kcalloc(tx_nents, sizeof(*sg), GFP_KERNEL); - if (unlikely(!sreq->tsg)) { - kfree(req); + if (unlikely(!sreq->tsg)) goto unlock; - } sg_init_table(sreq->tsg, tx_nents); - memcpy(sreq->iv, ctx->iv, GET_IV_SIZE(ctx)); - skcipher_request_set_tfm(req, crypto_skcipher_reqtfm(&ctx->req)); + memcpy(iv, ctx->iv, ivsize); + skcipher_request_set_tfm(req, tfm); skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - skcipher_async_cb, sk); + skcipher_async_cb, sreq); while (iov_iter_count(&msg->msg_iter)) { struct skcipher_async_rsgl *rsgl; @@ -615,20 +609,22 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg, sg_mark_end(sreq->tsg + txbufs - 1); skcipher_request_set_crypt(req, sreq->tsg, sreq->first_sgl.sgl.sg, - len, sreq->iv); + len, iv); err = ctx->enc ? crypto_skcipher_encrypt(req) : crypto_skcipher_decrypt(req); if (err == -EINPROGRESS) { atomic_inc(&ctx->inflight); err = -EIOCBQUEUED; + sreq = NULL; goto unlock; } free: skcipher_free_async_sgls(sreq); - kfree(req); unlock: skcipher_wmem_wakeup(sk); release_sock(sk); + kzfree(sreq); +out: return err; } -- cgit v1.2.3 From 6454c2b83f719057069777132b13949e4c6b6350 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 3 Feb 2016 21:39:26 +0800 Subject: crypto: algif_skcipher - Do not dereference ctx without socket lock Any access to non-constant bits of the private context must be done under the socket lock, in particular, this includes ctx->req. This patch moves such accesses under the lock, and fetches the tfm from the parent socket which is guaranteed to be constant, rather than from ctx->req. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index ec07a864b9c5..ef84353e0f24 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -295,8 +295,11 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); struct skcipher_ctx *ctx = ask->private; - struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(&ctx->req); + struct skcipher_tfm *skc = pask->private; + struct crypto_skcipher *tfm = skc->skcipher; unsigned ivsize = crypto_skcipher_ivsize(tfm); struct skcipher_sg_list *sgl; struct af_alg_control con = {}; @@ -508,7 +511,7 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg, struct skcipher_async_req *sreq; struct skcipher_request *req; struct skcipher_async_rsgl *last_rsgl = NULL; - unsigned int txbufs = 0, len = 0, tx_nents = skcipher_all_sg_nents(ctx); + unsigned int txbufs = 0, len = 0, tx_nents; unsigned int reqsize = crypto_skcipher_reqsize(tfm); unsigned int ivsize = crypto_skcipher_ivsize(tfm); int err = -ENOMEM; @@ -526,6 +529,7 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg, sreq->inflight = &ctx->inflight; lock_sock(sk); + tx_nents = skcipher_all_sg_nents(ctx); sreq->tsg = kcalloc(tx_nents, sizeof(*sg), GFP_KERNEL); if (unlikely(!sreq->tsg)) goto unlock; @@ -633,9 +637,12 @@ static int skcipher_recvmsg_sync(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); struct skcipher_ctx *ctx = ask->private; - unsigned bs = crypto_skcipher_blocksize(crypto_skcipher_reqtfm( - &ctx->req)); + struct skcipher_tfm *skc = pask->private; + struct crypto_skcipher *tfm = skc->skcipher; + unsigned bs = crypto_skcipher_blocksize(tfm); struct skcipher_sg_list *sgl; struct scatterlist *sg; int err = -EAGAIN; -- cgit v1.2.3 From dad41997063723eaf5f77bc2015606a5a9bce320 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 3 Feb 2016 21:39:27 +0800 Subject: crypto: algif_skcipher - Do not set MAY_BACKLOG on the async path The async path cannot use MAY_BACKLOG because it is not meant to block, which is what MAY_BACKLOG does. On the other hand, both the sync and async paths can make use of MAY_SLEEP. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index ef84353e0f24..28556fce4267 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -536,7 +536,7 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg, sg_init_table(sreq->tsg, tx_nents); memcpy(iv, ctx->iv, ivsize); skcipher_request_set_tfm(req, tfm); - skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, skcipher_async_cb, sreq); while (iov_iter_count(&msg->msg_iter)) { @@ -950,7 +950,8 @@ static int skcipher_accept_parent_nokey(void *private, struct sock *sk) ask->private = ctx; skcipher_request_set_tfm(&ctx->req, skcipher); - skcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, + skcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, af_alg_complete, &ctx->completion); sk->sk_destruct = skcipher_sock_destruct; -- cgit v1.2.3 From d961436c11482e974b702c8324426208f00cd7c4 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Fri, 5 Feb 2016 13:45:12 +0100 Subject: crypto: atmel-sha - fix atmel_sha_remove() Since atmel_sha_probe() uses devm_xxx functions to allocate resources, atmel_sha_remove() should no longer explicitly release them. Cc: stable@vger.kernel.org Signed-off-by: Cyrille Pitchen Fixes: b0e8b3417a62 ("crypto: atmel - use devm_xxx() managed function") Signed-off-by: Herbert Xu --- drivers/crypto/atmel-sha.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 20de861aa0ea..b5ab7400008d 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -1483,13 +1483,6 @@ static int atmel_sha_remove(struct platform_device *pdev) if (sha_dd->caps.has_dma) atmel_sha_dma_cleanup(sha_dd); - iounmap(sha_dd->io_base); - - clk_put(sha_dd->iclk); - - if (sha_dd->irq >= 0) - free_irq(sha_dd->irq, sha_dd); - return 0; } -- cgit v1.2.3 From c033042aa8f69894df37dabcaa0231594834a4e4 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Fri, 5 Feb 2016 13:45:13 +0100 Subject: crypto: atmel-sha - remove calls of clk_prepare() from atomic contexts clk_prepare()/clk_unprepare() must not be called within atomic context. This patch calls clk_prepare() once for all from atmel_sha_probe() and clk_unprepare() from atmel_sha_remove(). Then calls of clk_prepare_enable()/clk_disable_unprepare() were replaced by calls of clk_enable()/clk_disable(). Cc: stable@vger.kernel.org Signed-off-by: Cyrille Pitchen Reported-by: Matthias Mayr Signed-off-by: Herbert Xu --- drivers/crypto/atmel-sha.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index b5ab7400008d..8bf9914d4d15 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -782,7 +782,7 @@ static void atmel_sha_finish_req(struct ahash_request *req, int err) dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU | SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY); - clk_disable_unprepare(dd->iclk); + clk_disable(dd->iclk); if (req->base.complete) req->base.complete(&req->base, err); @@ -795,7 +795,7 @@ static int atmel_sha_hw_init(struct atmel_sha_dev *dd) { int err; - err = clk_prepare_enable(dd->iclk); + err = clk_enable(dd->iclk); if (err) return err; @@ -822,7 +822,7 @@ static void atmel_sha_hw_version_init(struct atmel_sha_dev *dd) dev_info(dd->dev, "version: 0x%x\n", dd->hw_version); - clk_disable_unprepare(dd->iclk); + clk_disable(dd->iclk); } static int atmel_sha_handle_queue(struct atmel_sha_dev *dd, @@ -1410,6 +1410,10 @@ static int atmel_sha_probe(struct platform_device *pdev) goto res_err; } + err = clk_prepare(sha_dd->iclk); + if (err) + goto res_err; + atmel_sha_hw_version_init(sha_dd); atmel_sha_get_cap(sha_dd); @@ -1421,12 +1425,12 @@ static int atmel_sha_probe(struct platform_device *pdev) if (IS_ERR(pdata)) { dev_err(&pdev->dev, "platform data not available\n"); err = PTR_ERR(pdata); - goto res_err; + goto iclk_unprepare; } } if (!pdata->dma_slave) { err = -ENXIO; - goto res_err; + goto iclk_unprepare; } err = atmel_sha_dma_init(sha_dd, pdata); if (err) @@ -1457,6 +1461,8 @@ err_algs: if (sha_dd->caps.has_dma) atmel_sha_dma_cleanup(sha_dd); err_sha_dma: +iclk_unprepare: + clk_unprepare(sha_dd->iclk); res_err: tasklet_kill(&sha_dd->done_task); sha_dd_err: @@ -1483,6 +1489,8 @@ static int atmel_sha_remove(struct platform_device *pdev) if (sha_dd->caps.has_dma) atmel_sha_dma_cleanup(sha_dd); + clk_unprepare(sha_dd->iclk); + return 0; } -- cgit v1.2.3 From 8a3978ad55fb4c0564d285fb2f6cdee2313fce01 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 5 Feb 2016 17:45:48 +0100 Subject: crypto: marvell/cesa - fix test in mv_cesa_dev_dma_init() We are checking twice if dma->cache_pool is not NULL but are never testing dma->padding_pool value. Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon Signed-off-by: Herbert Xu --- drivers/crypto/marvell/cesa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c index 0643e3366e33..c0656e7f37b5 100644 --- a/drivers/crypto/marvell/cesa.c +++ b/drivers/crypto/marvell/cesa.c @@ -306,7 +306,7 @@ static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa) return -ENOMEM; dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0); - if (!dma->cache_pool) + if (!dma->padding_pool) return -ENOMEM; cesa->dma = dma; -- cgit v1.2.3 From 16186a82de1fdd868255448274e64ae2616e2640 Mon Sep 17 00:00:00 2001 From: "subashab@codeaurora.org" Date: Tue, 2 Feb 2016 02:11:10 +0000 Subject: ipv6: addrconf: Fix recursive spin lock call A rcu stall with the following backtrace was seen on a system with forwarding, optimistic_dad and use_optimistic set. To reproduce, set these flags and allow ipv6 autoconf. This occurs because the device write_lock is acquired while already holding the read_lock. Back trace below - INFO: rcu_preempt self-detected stall on CPU { 1} (t=2100 jiffies g=3992 c=3991 q=4471) <6> Task dump for CPU 1: <2> kworker/1:0 R running task 12168 15 2 0x00000002 <2> Workqueue: ipv6_addrconf addrconf_dad_work <6> Call trace: <2> [] el1_irq+0x68/0xdc <2> [] _raw_write_lock_bh+0x20/0x30 <2> [] __ipv6_dev_ac_inc+0x64/0x1b4 <2> [] addrconf_join_anycast+0x9c/0xc4 <2> [] __ipv6_ifa_notify+0x160/0x29c <2> [] ipv6_ifa_notify+0x50/0x70 <2> [] addrconf_dad_work+0x314/0x334 <2> [] process_one_work+0x244/0x3fc <2> [] worker_thread+0x2f8/0x418 <2> [] kthread+0xe0/0xec v2: do addrconf_dad_kick inside read lock and then acquire write lock for ipv6_ifa_notify as suggested by Eric Fixes: 7fd2561e4ebdd ("net: ipv6: Add a sysctl to make optimistic addresses useful candidates") Cc: Eric Dumazet Cc: Erik Kline Cc: Hannes Frederic Sowa Signed-off-by: Subash Abhinov Kasiviswanathan Acked-by: Hannes Frederic Sowa Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 38eeddedfc21..9efd9ffdc34c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3538,6 +3538,7 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) { struct inet6_dev *idev = ifp->idev; struct net_device *dev = idev->dev; + bool notify = false; addrconf_join_solict(dev, &ifp->addr); @@ -3583,7 +3584,7 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) /* Because optimistic nodes can use this address, * notify listeners. If DAD fails, RTM_DELADDR is sent. */ - ipv6_ifa_notify(RTM_NEWADDR, ifp); + notify = true; } } @@ -3591,6 +3592,8 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) out: spin_unlock(&ifp->lock); read_unlock_bh(&idev->lock); + if (notify) + ipv6_ifa_notify(RTM_NEWADDR, ifp); } static void addrconf_dad_start(struct inet6_ifaddr *ifp) -- cgit v1.2.3 From 3647bc35bd427610a70eb83c58c5e834262badfa Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 2 Feb 2016 09:39:02 +0100 Subject: dwc_eth_qos: Reset hardware before PHY start The hardware reset is currently done after phy_start() is called, leading to a race where we can lose the link status if the phy state machine calls dwceqos_adjust_link() before we reset the MAC registers. Acked-by: Lars Persson Signed-off-by: Rabin Vincent Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc_eth_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index 70814b7386b3..fc8bbff2d7e3 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -1880,9 +1880,9 @@ static int dwceqos_open(struct net_device *ndev) } netdev_reset_queue(ndev); + dwceqos_init_hw(lp); napi_enable(&lp->napi); phy_start(lp->phy_dev); - dwceqos_init_hw(lp); netif_start_queue(ndev); tasklet_enable(&lp->tx_bdreclaim_tasklet); -- cgit v1.2.3 From 310d3d314be7f0a84011ebdc4bdccbcae9755a87 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 5 Feb 2016 14:51:36 -0800 Subject: target: Fix race with SCF_SEND_DELAYED_TAS handling This patch fixes a race between setting of SCF_SEND_DELAYED_TAS in transport_send_task_abort(), and check of the same bit in transport_check_aborted_status(). It adds a __transport_check_aborted_status() version that is used by target_execute_cmd() when se_cmd->t_state_lock is held, and a transport_check_aborted_status() wrapper for all other existing callers. Also, it handles the case where the check happens before transport_send_task_abort() gets called. For this, go ahead and set SCF_SEND_DELAYED_TAS early when necessary, and have transport_send_task_abort() send the abort. Cc: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 53 ++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 3441b159e306..2e0b23a3d5c4 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1858,19 +1858,21 @@ static bool target_handle_task_attr(struct se_cmd *cmd) return true; } +static int __transport_check_aborted_status(struct se_cmd *, int); + void target_execute_cmd(struct se_cmd *cmd) { - /* - * If the received CDB has aleady been aborted stop processing it here. - */ - if (transport_check_aborted_status(cmd, 1)) - return; - /* * Determine if frontend context caller is requesting the stopping of * this command for frontend exceptions. + * + * If the received CDB has aleady been aborted stop processing it here. */ spin_lock_irq(&cmd->t_state_lock); + if (__transport_check_aborted_status(cmd, 1)) { + spin_unlock_irq(&cmd->t_state_lock); + return; + } if (cmd->transport_state & CMD_T_STOP) { pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08llx\n", __func__, __LINE__, cmd->tag); @@ -2911,28 +2913,49 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, } EXPORT_SYMBOL(transport_send_check_condition_and_sense); -int transport_check_aborted_status(struct se_cmd *cmd, int send_status) +static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status) + __releases(&cmd->t_state_lock) + __acquires(&cmd->t_state_lock) { + assert_spin_locked(&cmd->t_state_lock); + WARN_ON_ONCE(!irqs_disabled()); + if (!(cmd->transport_state & CMD_T_ABORTED)) return 0; - /* * If cmd has been aborted but either no status is to be sent or it has * already been sent, just return */ - if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS)) + if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS)) { + if (send_status) + cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; return 1; + } - pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB: 0x%02x ITT: 0x%08llx\n", - cmd->t_task_cdb[0], cmd->tag); + pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB:" + " 0x%02x ITT: 0x%08llx\n", cmd->t_task_cdb[0], cmd->tag); cmd->se_cmd_flags &= ~SCF_SEND_DELAYED_TAS; cmd->scsi_status = SAM_STAT_TASK_ABORTED; trace_target_cmd_complete(cmd); + + spin_unlock_irq(&cmd->t_state_lock); cmd->se_tfo->queue_status(cmd); + spin_lock_irq(&cmd->t_state_lock); return 1; } + +int transport_check_aborted_status(struct se_cmd *cmd, int send_status) +{ + int ret; + + spin_lock_irq(&cmd->t_state_lock); + ret = __transport_check_aborted_status(cmd, send_status); + spin_unlock_irq(&cmd->t_state_lock); + + return ret; +} EXPORT_SYMBOL(transport_check_aborted_status); void transport_send_task_abort(struct se_cmd *cmd) @@ -2954,11 +2977,17 @@ void transport_send_task_abort(struct se_cmd *cmd) */ if (cmd->data_direction == DMA_TO_DEVICE) { if (cmd->se_tfo->write_pending_status(cmd) != 0) { - cmd->transport_state |= CMD_T_ABORTED; + spin_lock_irqsave(&cmd->t_state_lock, flags); + if (cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + goto send_abort; + } cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); return; } } +send_abort: cmd->scsi_status = SAM_STAT_TASK_ABORTED; transport_lun_remove_cmd(cmd); -- cgit v1.2.3 From 57dae19065bde296dfdf08b8e46c102a671ff741 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 29 Jan 2016 00:07:25 -0800 Subject: target: Drop legacy se_cmd->task_stop_comp + REQUEST_STOP usage With CMD_T_FABRIC_STOP + se_cmd->cmd_wait_set usage in place, go ahead and drop left-over CMD_T_REQUEST_STOP checks in target_complete_cmd() and unused target_stop_cmd(). Reviewed-by: Christoph Hellwig Cc: Quinn Tran Cc: Himanshu Madhani Cc: Sagi Grimberg Cc: Hannes Reinecke Cc: Andy Grover Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_internal.h | 1 - drivers/target/target_core_transport.c | 37 ---------------------------------- include/target/target_core_base.h | 4 ---- 3 files changed, 42 deletions(-) diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index dae0750c2032..db4412fe6b8a 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -141,7 +141,6 @@ void transport_dump_vpd_proto_id(struct t10_vpd *, unsigned char *, int); int transport_dump_vpd_assoc(struct t10_vpd *, unsigned char *, int); int transport_dump_vpd_ident_type(struct t10_vpd *, unsigned char *, int); int transport_dump_vpd_ident(struct t10_vpd *, unsigned char *, int); -bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags); void transport_clear_lun_ref(struct se_lun *); void transport_send_task_abort(struct se_cmd *); sense_reason_t target_cmd_size_check(struct se_cmd *cmd, unsigned int size); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2e0b23a3d5c4..d92cb644d8f9 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -692,15 +692,6 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) success = 1; } - /* - * See if we are waiting to complete for an exception condition. - */ - if (cmd->transport_state & CMD_T_REQUEST_STOP) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - complete(&cmd->task_stop_comp); - return; - } - /* * Check for case where an explicit ABORT_TASK has been received * and transport_wait_for_tasks() will be waiting for completion.. @@ -1202,7 +1193,6 @@ void transport_init_se_cmd( INIT_LIST_HEAD(&cmd->state_list); init_completion(&cmd->t_transport_stop_comp); init_completion(&cmd->cmd_wait_comp); - init_completion(&cmd->task_stop_comp); spin_lock_init(&cmd->t_state_lock); kref_init(&cmd->cmd_kref); cmd->transport_state = CMD_T_DEV_ACTIVE; @@ -1633,33 +1623,6 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, } EXPORT_SYMBOL(target_submit_tmr); -/* - * If the cmd is active, request it to be stopped and sleep until it - * has completed. - */ -bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags) - __releases(&cmd->t_state_lock) - __acquires(&cmd->t_state_lock) -{ - bool was_active = false; - - if (cmd->transport_state & CMD_T_BUSY) { - cmd->transport_state |= CMD_T_REQUEST_STOP; - spin_unlock_irqrestore(&cmd->t_state_lock, *flags); - - pr_debug("cmd %p waiting to complete\n", cmd); - wait_for_completion(&cmd->task_stop_comp); - pr_debug("cmd %p stopped successfully\n", cmd); - - spin_lock_irqsave(&cmd->t_state_lock, *flags); - cmd->transport_state &= ~CMD_T_REQUEST_STOP; - cmd->transport_state &= ~CMD_T_BUSY; - was_active = true; - } - - return was_active; -} - /* * Handle SAM-esque emulation for generic transport request failures. */ diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 1579539e5669..d71a3ead9e63 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -491,7 +491,6 @@ struct se_cmd { #define CMD_T_SENT (1 << 4) #define CMD_T_STOP (1 << 5) #define CMD_T_DEV_ACTIVE (1 << 7) -#define CMD_T_REQUEST_STOP (1 << 8) #define CMD_T_BUSY (1 << 9) #define CMD_T_TAS (1 << 10) #define CMD_T_FABRIC_STOP (1 << 11) @@ -514,9 +513,6 @@ struct se_cmd { struct list_head state_list; - /* old task stop completion, consider merging with some of the above */ - struct completion task_stop_comp; - /* backend private data */ void *priv; -- cgit v1.2.3 From 0633e123465b61a12a262b742bebf2a9945f7964 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 1 Feb 2016 17:29:45 +0100 Subject: target/user: Fix cast from pointer to phys_addr_t The uio_mem structure has a member that is a phys_addr_t, but can be a number of other types too. The target core driver attempts to assign a pointer from vmalloc() to it, by casting it to phys_addr_t, but that causes a warning when phys_addr_t is longer than a pointer: drivers/target/target_core_user.c: In function 'tcmu_configure_device': drivers/target/target_core_user.c:906:22: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] This adds another cast to uintptr_t to shut up the warning. A nicer fix might be to have additional fields in uio_mem for the different purposes, so we can assign a pointer directly. Signed-off-by: Arnd Bergmann Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index dd600e5ead71..94f5154ac788 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -903,7 +903,7 @@ static int tcmu_configure_device(struct se_device *dev) info->version = __stringify(TCMU_MAILBOX_VERSION); info->mem[0].name = "tcm-user command & data buffer"; - info->mem[0].addr = (phys_addr_t) udev->mb_addr; + info->mem[0].addr = (phys_addr_t)(uintptr_t)udev->mb_addr; info->mem[0].size = TCMU_RING_SIZE; info->mem[0].memtype = UIO_MEM_VIRTUAL; -- cgit v1.2.3 From e9036d0662360cd4c79578565ce422ed5872f301 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 5 Feb 2016 10:49:36 -0800 Subject: tty: Drop krefs for interrupted tty lock When the tty lock is interrupted on attempted re-open, 2 tty krefs are still held. Drop extra kref before returning failure from tty_lock_interruptible(), and drop lookup kref before returning failure from tty_open(). Fixes: 0bfd464d3fdd ("tty: Wait interruptibly for tty lock on reopen") Reported-by: Dmitry Vyukov Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 3 +-- drivers/tty/tty_mutex.c | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 5cec01c75691..a7eacef1bd22 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2066,13 +2066,12 @@ retry_open: if (tty) { mutex_unlock(&tty_mutex); retval = tty_lock_interruptible(tty); + tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */ if (retval) { if (retval == -EINTR) retval = -ERESTARTSYS; goto err_unref; } - /* safe to drop the kref from tty_driver_lookup_tty() */ - tty_kref_put(tty); retval = tty_reopen(tty); if (retval < 0) { tty_unlock(tty); diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index d2f3c4cd697f..dfa9ec03fa8e 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -21,10 +21,15 @@ EXPORT_SYMBOL(tty_lock); int tty_lock_interruptible(struct tty_struct *tty) { + int ret; + if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty)) return -EIO; tty_kref_get(tty); - return mutex_lock_interruptible(&tty->legacy_mutex); + ret = mutex_lock_interruptible(&tty->legacy_mutex); + if (ret) + tty_kref_put(tty); + return ret; } void __lockfunc tty_unlock(struct tty_struct *tty) -- cgit v1.2.3 From cb43285ff7039fe3c4b0bc476e6d6569c31104f3 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 4 Feb 2016 11:45:16 -0500 Subject: qla2xxx: Fix stale pointer access. [ Upstream Commit 84e32a06f4f8756ce9ec3c8dc7e97896575f0771 ] Commit 84e32a0 ("qla2xxx: Use pci_enable_msix_range() instead of pci_enable_msix()") introduced a regression when target mode is enabled. In qla24xx_enable_msix(), ha->max_rsp_queues was incorrectly set to a value higher than the number of response queues allocated causing an invalid dereference. Specifically here in qla2x00_init_rings(): *rsp->in_ptr = 0; Add additional check to make sure the pointer is valid. following call stack will be seen ---- 8< ---- RIP: 0010:[] [] qla2x00_init_rings+0xdc/0x320 [qla2xxx] RSP: 0018:ffff880429447dd8 EFLAGS: 00010082 .... Call Trace: [] qla2x00_abort_isp+0x170/0x6b0 [qla2xxx] [] qla2x00_do_dpc+0x357/0x7f0 [qla2xxx] [] ? qla2x00_relogin+0x260/0x260 [qla2xxx] [] kthread+0xc9/0xe0 [] ? flush_kthread_worker+0x90/0x90 [] ret_from_fork+0x3f/0x70 [] ? flush_kthread_worker+0x90/0x90 ---- 8< ---- Cc: Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_init.c | 10 +++++----- drivers/scsi/qla2xxx/qla_isr.c | 4 ++-- drivers/scsi/qla2xxx/qla_mid.c | 4 ++-- drivers/scsi/qla2xxx/qla_os.c | 6 ++++++ drivers/scsi/qla2xxx/qla_tmpl.c | 16 ++++++++++++++++ 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 52a87657c7dd..692a7570b5e1 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2204,7 +2204,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) /* Clear outstanding commands array. */ for (que = 0; que < ha->max_req_queues; que++) { req = ha->req_q_map[que]; - if (!req) + if (!req || !test_bit(que, ha->req_qid_map)) continue; req->out_ptr = (void *)(req->ring + req->length); *req->out_ptr = 0; @@ -2221,7 +2221,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) for (que = 0; que < ha->max_rsp_queues; que++) { rsp = ha->rsp_q_map[que]; - if (!rsp) + if (!rsp || !test_bit(que, ha->rsp_qid_map)) continue; rsp->in_ptr = (void *)(rsp->ring + rsp->length); *rsp->in_ptr = 0; @@ -4981,7 +4981,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) for (i = 1; i < ha->max_rsp_queues; i++) { rsp = ha->rsp_q_map[i]; - if (rsp) { + if (rsp && test_bit(i, ha->rsp_qid_map)) { rsp->options &= ~BIT_0; ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) @@ -4996,8 +4996,8 @@ qla25xx_init_queues(struct qla_hw_data *ha) } for (i = 1; i < ha->max_req_queues; i++) { req = ha->req_q_map[i]; - if (req) { - /* Clear outstanding commands array. */ + if (req && test_bit(i, ha->req_qid_map)) { + /* Clear outstanding commands array. */ req->options &= ~BIT_0; ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index d4d65eb0e9b4..4af95479a9db 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -3063,9 +3063,9 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) "MSI-X: Failed to enable support " "-- %d/%d\n Retry with %d vectors.\n", ha->msix_count, ret, ret); + ha->msix_count = ret; + ha->max_rsp_queues = ha->msix_count - 1; } - ha->msix_count = ret; - ha->max_rsp_queues = ha->msix_count - 1; ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) * ha->msix_count, GFP_KERNEL); if (!ha->msix_entries) { diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index c5dd594f6c31..cf7ba52bae66 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -600,7 +600,7 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) /* Delete request queues */ for (cnt = 1; cnt < ha->max_req_queues; cnt++) { req = ha->req_q_map[cnt]; - if (req) { + if (req && test_bit(cnt, ha->req_qid_map)) { ret = qla25xx_delete_req_que(vha, req); if (ret != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x00ea, @@ -614,7 +614,7 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) /* Delete response queues */ for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) { rsp = ha->rsp_q_map[cnt]; - if (rsp) { + if (rsp && test_bit(cnt, ha->rsp_qid_map)) { ret = qla25xx_delete_rsp_que(vha, rsp); if (ret != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x00eb, diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index f1788db43195..f6c7ce35b542 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -409,6 +409,9 @@ static void qla2x00_free_queues(struct qla_hw_data *ha) int cnt; for (cnt = 0; cnt < ha->max_req_queues; cnt++) { + if (!test_bit(cnt, ha->req_qid_map)) + continue; + req = ha->req_q_map[cnt]; qla2x00_free_req_que(ha, req); } @@ -416,6 +419,9 @@ static void qla2x00_free_queues(struct qla_hw_data *ha) ha->req_q_map = NULL; for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) { + if (!test_bit(cnt, ha->rsp_qid_map)) + continue; + rsp = ha->rsp_q_map[cnt]; qla2x00_free_rsp_que(ha, rsp); } diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c index ddbe2e7ac14d..c3e622524604 100644 --- a/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -395,6 +395,10 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, if (ent->t263.queue_type == T263_QUEUE_TYPE_REQ) { for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; + + if (!test_bit(i, vha->hw->req_qid_map)) + continue; + if (req || !buf) { length = req ? req->length : REQUEST_ENTRY_CNT_24XX; @@ -408,6 +412,10 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, } else if (ent->t263.queue_type == T263_QUEUE_TYPE_RSP) { for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + + if (!test_bit(i, vha->hw->rsp_qid_map)) + continue; + if (rsp || !buf) { length = rsp ? rsp->length : RESPONSE_ENTRY_CNT_MQ; @@ -634,6 +642,10 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, if (ent->t274.queue_type == T274_QUEUE_TYPE_REQ_SHAD) { for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; + + if (!test_bit(i, vha->hw->req_qid_map)) + continue; + if (req || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); @@ -645,6 +657,10 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, } else if (ent->t274.queue_type == T274_QUEUE_TYPE_RSP_SHAD) { for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + + if (!test_bit(i, vha->hw->rsp_qid_map)) + continue; + if (rsp || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); -- cgit v1.2.3 From d7236ac368212bd6fc8b45f050136ee53e6a6f2d Mon Sep 17 00:00:00 2001 From: Swapnil Nagle Date: Thu, 4 Feb 2016 11:45:17 -0500 Subject: qla2xxx: Use ATIO type to send correct tmr response The function value inside se_cmd can change if the TMR is cancelled. Use original ATIO Type to correctly determine CTIO response. Signed-off-by: Swapnil Nagle Signed-off-by: Himanshu Madhani Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 74eb776d2faa..46c667979411 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1756,7 +1756,7 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy, 0, 0, 0, 0, 0, 0); else { - if (mcmd->se_cmd.se_tmr_req->function == TMR_ABORT_TASK) + if (mcmd->orig_iocb.atio.u.raw.entry_type == ABTS_RECV_24XX) qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts, mcmd->fc_tm_rsp, false); else -- cgit v1.2.3 From 2bcfdc23c6a418b441a1d17f237e9ea347e2fb1f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Jan 2016 14:46:34 +0200 Subject: mei: bus: remove redundant uuid string in debug messages Remove uuid from the debug messages in bus-fixup.c as this is already part of the device name. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 020de5919c21..b2d2a6ea576c 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -48,8 +48,7 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; */ static void number_of_connections(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s on %pUl\n", - __func__, mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); if (cldev->me_cl->props.max_number_of_connections > 1) cldev->do_match = 0; @@ -62,8 +61,8 @@ static void number_of_connections(struct mei_cl_device *cldev) */ static void blacklist(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s on %pUl\n", - __func__, mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); + cldev->do_match = 0; } @@ -208,8 +207,7 @@ static void mei_nfc(struct mei_cl_device *cldev) bus = cldev->bus; - dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n", - __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); mutex_lock(&bus->device_lock); /* we need to connect to INFO GUID */ -- cgit v1.2.3 From 13cf988562b0c7f06e0016bea2b5304358397702 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Jan 2016 14:46:36 +0200 Subject: mei: prevent queuing new flow control credit. The MEI FW can receive only one flow control for read. Currently the driver only checks if a flow control credit was already sent and read is pending in the rd_pending queue, but it also has to check if flow control credit already queued in the write control queue to prevent sending more than one flow control credits. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a6c87c713193..72e32615acd9 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1421,6 +1421,25 @@ out: return 0; } +/** + * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control + * for given host client + * + * @cl: host client + * + * Return: true, if found at least one cb. + */ +static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; + + list_for_each_entry(cb, &dev->ctrl_wr_list.list, list) + if (cb->fop_type == MEI_FOP_READ && cb->cl == cl) + return true; + return false; +} + /** * mei_cl_read_start - the start read client message function. * @@ -1445,7 +1464,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) return -ENODEV; /* HW currently supports only one pending read */ - if (!list_empty(&cl->rd_pending)) + if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl)) return -EBUSY; if (!mei_me_cl_is_active(cl->me_cl)) { -- cgit v1.2.3 From 8b2458f413c429e4c5c4799e02ec2820396aaac4 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Jan 2016 14:46:37 +0200 Subject: mei: always copy the read buffer if data is ready Copy completed callback content to the user space if we have such callback ready in the beginning of the read. Simplify offset processing logic as byproduct. This is a refinement for: commit 139aacf757fc ("mei: fix read after read scenario") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 677d0362f334..36ca15234344 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -159,27 +159,22 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } + if (ubuf == NULL) { + rets = -EMSGSIZE; + goto out; + } + if (cl == &dev->iamthif_cl) { rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; } cb = mei_cl_read_cb(cl, file); - if (cb) { - /* read what left */ - if (cb->buf_idx > *offset) - goto copy_buffer; - /* offset is beyond buf_idx we have no more data return 0 */ - if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { - rets = 0; - goto free; - } - /* Offset needs to be cleaned for contiguous reads*/ - if (cb->buf_idx == 0 && *offset > 0) - *offset = 0; - } else if (*offset > 0) { + if (cb) + goto copy_buffer; + + if (*offset > 0) *offset = 0; - } err = mei_cl_read_start(cl, length, file); if (err && err != -EBUSY) { @@ -231,10 +226,10 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld\n", - cb->buf.size, cb->buf_idx); - if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { - rets = -EMSGSIZE; + cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld offset = %lld\n", + cb->buf.size, cb->buf_idx, *offset); + if (*offset >= cb->buf_idx) { + rets = 0; goto free; } @@ -255,6 +250,7 @@ copy_buffer: free: mei_io_cb_free(cb); + *offset = 0; out: cl_dbg(dev, cl, "end mei read rets = %d\n", rets); -- cgit v1.2.3 From b86d1bd8d1a0f67f12b62e5ee724514f19dc6bf8 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Jan 2016 14:46:39 +0200 Subject: mei: drop nfc leftovers from the mei driver We left few function prototypes in the header file after moving nfc logic to bus. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei_dev.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4250555d5e72..b54d9d9cacea 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -649,17 +649,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_cl_cb *complete_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); -/* - * NFC functions - */ -int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -void mei_nfc_host_exit(struct mei_device *dev); - -/* - * NFC Client UUID - */ -extern const uuid_le mei_nfc_guid; - int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev); int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -- cgit v1.2.3 From fdd9b8655933c3eb3154fe1ed351c17b654258bd Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 8 Jan 2016 00:49:21 +0200 Subject: mei: wd: drop the watchdog code from the core mei driver Instead of integrating the iAMT watchdog in the mei core driver we will create a watchdog device on the mei client bus and create a driver for it. This patch removes the watchdog code from the mei core driver. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 6 +- drivers/misc/mei/Makefile | 1 - drivers/misc/mei/client.c | 12 +- drivers/misc/mei/client.h | 4 - drivers/misc/mei/init.c | 10 +- drivers/misc/mei/interrupt.c | 15 -- drivers/misc/mei/mei_dev.h | 61 +------ drivers/misc/mei/wd.c | 391 ------------------------------------------- 8 files changed, 9 insertions(+), 491 deletions(-) delete mode 100644 drivers/misc/mei/wd.c diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d23384dde73b..c49e1d2269af 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -1,6 +1,6 @@ config INTEL_MEI tristate "Intel Management Engine Interface" - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. @@ -12,7 +12,7 @@ config INTEL_MEI config INTEL_MEI_ME tristate "ME Enabled Intel Chipsets" select INTEL_MEI - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help MEI support for ME Enabled Intel chipsets. @@ -37,7 +37,7 @@ config INTEL_MEI_ME config INTEL_MEI_TXE tristate "Intel Trusted Execution Environment with ME Interface" select INTEL_MEI - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help MEI Support for Trusted Execution Environment device on Intel SoCs diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 01447ca21c26..59e6b0aede34 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -9,7 +9,6 @@ mei-objs += interrupt.o mei-objs += client.o mei-objs += main.o mei-objs += amthif.o -mei-objs += wd.o mei-objs += bus.o mei-objs += bus-fixup.o mei-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 72e32615acd9..e069fcaed7aa 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -648,7 +648,7 @@ int mei_cl_unlink(struct mei_cl *cl) if (!cl) return 0; - /* wd and amthif might not be initialized */ + /* amthif might not be initialized */ if (!cl->dev) return 0; @@ -679,17 +679,11 @@ void mei_host_client_init(struct work_struct *work) mutex_lock(&dev->device_lock); - me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (me_cl) mei_amthif_host_init(dev, me_cl); mei_me_cl_put(me_cl); - me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (me_cl) - mei_wd_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; mutex_unlock(&dev->device_lock); @@ -1153,7 +1147,7 @@ err: * * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. */ -int mei_cl_flow_ctrl_creds(struct mei_cl *cl) +static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { int rets; @@ -1186,7 +1180,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) * 0 on success * -EINVAL when ctrl credits are <= 0 */ -int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) +static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) { if (WARN_ON(!cl || !cl->me_cl)) return -EINVAL; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 04e1aa39243f..2e90a25f896e 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -18,7 +18,6 @@ #define _MEI_CLIENT_H_ #include -#include #include #include @@ -120,9 +119,6 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); -int mei_cl_flow_ctrl_creds(struct mei_cl *cl); - -int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); /* * MEI input output function prototype */ diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 3edafc8d3ad4..46a4302e5524 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -156,8 +156,7 @@ int mei_reset(struct mei_device *dev) mei_cl_all_wakeup(dev); /* remove entry if already in list */ - dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n"); - mei_cl_unlink(&dev->wd_cl); + dev_dbg(dev->dev, "remove iamthif from the file list.\n"); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); } @@ -165,7 +164,6 @@ int mei_reset(struct mei_device *dev) mei_hbm_reset(dev); dev->rd_msg_hdr = 0; - dev->wd_pending = false; if (ret) { dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); @@ -335,16 +333,12 @@ void mei_stop(struct mei_device *dev) mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev); /* move device to disabled state unconditionally */ dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); - - mei_watchdog_unregister(dev); } EXPORT_SYMBOL_GPL(mei_stop); @@ -394,7 +388,6 @@ void mei_device_init(struct mei_device *dev, init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); init_waitqueue_head(&dev->wait_hbm_start); - init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; @@ -407,7 +400,6 @@ void mei_device_init(struct mei_device *dev, INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); - INIT_LIST_HEAD(&dev->wd_cl.link); INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); mei_io_list_init(&dev->amthif_rd_complete_list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 64b568a0268d..6340dee33052 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -360,21 +360,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) list_move_tail(&cb->list, &cmpl_list->list); } - if (dev->wd_state == MEI_WD_STOPPING) { - dev->wd_state = MEI_WD_IDLE; - wake_up(&dev->wait_stop_wd); - } - - if (mei_cl_is_connected(&dev->wd_cl)) { - if (dev->wd_pending && - mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { - ret = mei_wd_send(dev); - if (ret) - return ret; - dev->wd_pending = false; - } - } - /* complete control write list CB */ dev_dbg(dev->dev, "complete control write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index b54d9d9cacea..da613268480c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -18,7 +18,7 @@ #define _MEI_DEV_H_ #include -#include +#include #include #include #include @@ -26,33 +26,13 @@ #include "hw.h" #include "hbm.h" -/* - * watch dog definition - */ -#define MEI_WD_HDR_SIZE 4 -#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE -#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16) - -#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */ -#define MEI_WD_MIN_TIMEOUT 120 /* seconds */ -#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ - -#define MEI_WD_STOP_TIMEOUT 10 /* msecs */ - -#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) - -#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) - /* * AMTHI Client UUID */ extern const uuid_le mei_amthif_guid; -/* - * Watchdog Client UUID - */ -extern const uuid_le mei_wd_guid; +#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) /* * Number of Maximum MEI Clients @@ -78,7 +58,6 @@ extern const uuid_le mei_wd_guid; */ #define MEI_HOST_CLIENT_ID_ANY (-1) #define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */ -#define MEI_WD_HOST_CLIENT_ID 1 #define MEI_IAMTHIF_HOST_CLIENT_ID 2 @@ -123,12 +102,6 @@ enum mei_file_transaction_states { MEI_READ_COMPLETE }; -enum mei_wd_states { - MEI_WD_IDLE, - MEI_WD_RUNNING, - MEI_WD_STOPPING, -}; - /** * enum mei_cb_file_ops - file operation associated with the callback * @MEI_FOP_READ: read @@ -404,7 +377,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @wait_hw_ready : wait queue for receive HW ready message form FW * @wait_pg : wait queue for receive PG message from FW * @wait_hbm_start : wait queue for receive HBM start message from FW - * @wait_stop_wd : wait queue for receive WD stop message from FW * * @reset_count : number of consecutive resets * @dev_state : device state @@ -435,12 +407,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @allow_fixed_address: allow user space to connect a fixed client * - * @wd_cl : watchdog client - * @wd_state : watchdog client state - * @wd_pending : watchdog command is pending - * @wd_timeout : watchdog expiration timeout - * @wd_data : watchdog message buffer - * * @amthif_cmd_list : amthif list for cmd waiting * @amthif_rd_complete_list : amthif list for reading completed cmd data * @iamthif_file_object : file for current amthif operation @@ -486,7 +452,6 @@ struct mei_device { wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_pg; wait_queue_head_t wait_hbm_start; - wait_queue_head_t wait_stop_wd; /* * mei device states @@ -531,13 +496,6 @@ struct mei_device { bool allow_fixed_address; - struct mei_cl wd_cl; - enum mei_wd_states wd_state; - bool wd_pending; - u16 wd_timeout; - unsigned char wd_data[MEI_WD_START_MSG_SIZE]; - - /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ @@ -649,21 +607,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_cl_cb *complete_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); -int mei_wd_send(struct mei_device *dev); -int mei_wd_stop(struct mei_device *dev); -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -/* - * mei_watchdog_register - Registering watchdog interface - * once we got connection to the WD Client - * @dev: mei device - */ -int mei_watchdog_register(struct mei_device *dev); -/* - * mei_watchdog_unregister - Unregistering watchdog interface - * @dev: mei device - */ -void mei_watchdog_unregister(struct mei_device *dev); - /* * Register Access Function */ diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c deleted file mode 100644 index b346638833b0..000000000000 --- a/drivers/misc/mei/wd.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2012, 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 "mei_dev.h" -#include "hbm.h" -#include "client.h" - -static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; -static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; - -/* - * AMT Watchdog Device - */ -#define INTEL_AMT_WATCHDOG_ID "INTCAMT" - -/* UUIDs for AMT F/W clients */ -const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, - 0x9D, 0xA9, 0x15, 0x14, 0xCB, - 0x32, 0xAB); - -static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) -{ - dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); - memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); - memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); -} - -/** - * mei_wd_host_init - connect to the watchdog client - * - * @dev: the device structure - * @me_cl: me client - * - * Return: -ENOTTY if wd client cannot be found - * -EIO if write has failed - * 0 on success - */ -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - mei_cl_init(cl, dev); - - dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; - dev->wd_state = MEI_WD_IDLE; - - ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); - if (ret < 0) { - dev_info(dev->dev, "wd: failed link client\n"); - return ret; - } - - ret = mei_cl_connect(cl, me_cl, NULL); - if (ret) { - dev_err(dev->dev, "wd: failed to connect = %d\n", ret); - mei_cl_unlink(cl); - return ret; - } - - ret = mei_watchdog_register(dev); - if (ret) { - mei_cl_disconnect(cl); - mei_cl_unlink(cl); - } - return ret; -} - -/** - * mei_wd_send - sends watch dog message to fw. - * - * @dev: the device structure - * - * Return: 0 if success, - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ENODEV on flow control failure - */ -int mei_wd_send(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - struct mei_msg_hdr hdr; - int ret; - - hdr.host_addr = cl->host_client_id; - hdr.me_addr = mei_cl_me_id(cl); - hdr.msg_complete = 1; - hdr.reserved = 0; - hdr.internal = 0; - - if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_START_MSG_SIZE; - else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_STOP_MSG_SIZE; - else { - dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); - return -EINVAL; - } - - ret = mei_write_message(dev, &hdr, dev->wd_data); - if (ret) { - dev_err(dev->dev, "wd: write message failed\n"); - return ret; - } - - ret = mei_cl_flow_ctrl_reduce(cl); - if (ret) { - dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); - return ret; - } - - return 0; -} - -/** - * mei_wd_stop - sends watchdog stop message to fw. - * - * @dev: the device structure - * - * Return: 0 if success - * on error: - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ETIME on message timeout - */ -int mei_wd_stop(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - if (!mei_cl_is_connected(cl) || - dev->wd_state != MEI_WD_RUNNING) - return 0; - - memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); - - dev->wd_state = MEI_WD_STOPPING; - - ret = mei_cl_flow_ctrl_creds(cl); - if (ret < 0) - goto err; - - if (ret && mei_hbuf_acquire(dev)) { - ret = mei_wd_send(dev); - if (ret) - goto err; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - - mutex_unlock(&dev->device_lock); - - ret = wait_event_timeout(dev->wait_stop_wd, - dev->wd_state == MEI_WD_IDLE, - msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); - mutex_lock(&dev->device_lock); - if (dev->wd_state != MEI_WD_IDLE) { - /* timeout */ - ret = -ETIME; - dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); - goto err; - } - dev_dbg(dev->dev, "wd: stop completed after %u msec\n", - MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); - return 0; -err: - return ret; -} - -/** - * mei_wd_ops_start - wd start command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_start(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int err = -ENODEV; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", - mei_dev_state_str(dev->dev_state)); - goto end_unlock; - } - - if (!mei_cl_is_connected(cl)) { - cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); - goto end_unlock; - } - - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - err = 0; -end_unlock: - mutex_unlock(&dev->device_lock); - return err; -} - -/** - * mei_wd_ops_stop - wd stop command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_stop(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - mutex_unlock(&dev->device_lock); - - return 0; -} - -/** - * mei_wd_ops_ping - wd ping command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_ping(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int ret; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (!mei_cl_is_connected(cl)) { - cl_err(dev, cl, "wd: not connected.\n"); - ret = -ENODEV; - goto end; - } - - dev->wd_state = MEI_WD_RUNNING; - - ret = mei_cl_flow_ctrl_creds(cl); - if (ret < 0) - goto end; - - /* Check if we can send the ping to HW*/ - if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(dev->dev, "wd: sending ping\n"); - - ret = mei_wd_send(dev); - if (ret) - goto end; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - -end: - mutex_unlock(&dev->device_lock); - return ret; -} - -/** - * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. - * - * @wd_dev: watchdog device struct - * @timeout: timeout value to set - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, - unsigned int timeout) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - /* Check Timeout value */ - if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) - return -EINVAL; - - mutex_lock(&dev->device_lock); - - dev->wd_timeout = timeout; - wd_dev->timeout = timeout; - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - mutex_unlock(&dev->device_lock); - - return 0; -} - -/* - * Watchdog Device structs - */ -static const struct watchdog_ops wd_ops = { - .owner = THIS_MODULE, - .start = mei_wd_ops_start, - .stop = mei_wd_ops_stop, - .ping = mei_wd_ops_ping, - .set_timeout = mei_wd_ops_set_timeout, -}; -static const struct watchdog_info wd_info = { - .identity = INTEL_AMT_WATCHDOG_ID, - .options = WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_ALARMONLY, -}; - -static struct watchdog_device amt_wd_dev = { - .info = &wd_info, - .ops = &wd_ops, - .timeout = MEI_WD_DEFAULT_TIMEOUT, - .min_timeout = MEI_WD_MIN_TIMEOUT, - .max_timeout = MEI_WD_MAX_TIMEOUT, -}; - - -int mei_watchdog_register(struct mei_device *dev) -{ - - int ret; - - amt_wd_dev.parent = dev->dev; - /* unlock to perserve correct locking order */ - mutex_unlock(&dev->device_lock); - ret = watchdog_register_device(&amt_wd_dev); - mutex_lock(&dev->device_lock); - if (ret) { - dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", - ret); - return ret; - } - - dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); - watchdog_set_drvdata(&amt_wd_dev, dev); - return 0; -} - -void mei_watchdog_unregister(struct mei_device *dev) -{ - if (watchdog_get_drvdata(&amt_wd_dev) == NULL) - return; - - watchdog_set_drvdata(&amt_wd_dev, NULL); - watchdog_unregister_device(&amt_wd_dev); -} - -- cgit v1.2.3 From 222818c3d84c1f3190767f5f09f2b9b9a0e0ca7f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 8 Jan 2016 00:49:22 +0200 Subject: watchdog: mei_wdt: implement MEI iAMT watchdog driver Create a driver with the generic watchdog interface for the MEI iAMT watchdog device. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- Documentation/misc-devices/mei/mei.txt | 12 +- MAINTAINERS | 1 + drivers/watchdog/Kconfig | 15 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/mei_wdt.c | 404 +++++++++++++++++++++++++++++++++ 5 files changed, 427 insertions(+), 6 deletions(-) create mode 100644 drivers/watchdog/mei_wdt.c diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt index 91c1fa34f48b..2b80a0cd621f 100644 --- a/Documentation/misc-devices/mei/mei.txt +++ b/Documentation/misc-devices/mei/mei.txt @@ -231,15 +231,15 @@ IT knows when a platform crashes even when there is a hard failure on the host. The Intel AMT Watchdog is composed of two parts: 1) Firmware feature - receives the heartbeats and sends an event when the heartbeats stop. - 2) Intel MEI driver - connects to the watchdog feature, configures the - watchdog and sends the heartbeats. + 2) Intel MEI iAMT watchdog driver - connects to the watchdog feature, + configures the watchdog and sends the heartbeats. -The Intel MEI driver uses the kernel watchdog API to configure the Intel AMT -Watchdog and to send heartbeats to it. The default timeout of the +The Intel iAMT watchdog MEI driver uses the kernel watchdog API to configure +the Intel AMT Watchdog and to send heartbeats to it. The default timeout of the watchdog is 120 seconds. -If the Intel AMT Watchdog feature does not exist (i.e. the connection failed), -the Intel MEI driver will disable the sending of heartbeats. +If the Intel AMT is not enabled in the firmware then the watchdog client won't enumerate +on the me client bus and watchdog devices won't be exposed. Supported Chipsets diff --git a/MAINTAINERS b/MAINTAINERS index 30aca4aa5467..d63b3c773c7d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5751,6 +5751,7 @@ S: Supported F: include/uapi/linux/mei.h F: include/linux/mei_cl_bus.h F: drivers/misc/mei/* +F: drivers/watchdog/mei_wdt.c F: Documentation/misc-devices/mei/* INTEL MIC DRIVERS (mic) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4f0e7be0da34..57f872122b16 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1212,6 +1212,21 @@ config SBC_EPX_C3_WATCHDOG To compile this driver as a module, choose M here: the module will be called sbc_epx_c3. +config INTEL_MEI_WDT + tristate "Intel MEI iAMT Watchdog" + depends on INTEL_MEI && X86 + select WATCHDOG_CORE + ---help--- + A device driver for the Intel MEI iAMT watchdog. + + The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog. + Whenever the OS hangs or crashes, iAMT will send an event + to any subscriber to this event. The watchdog doesn't reset the + the platform. + + To compile this driver as a module, choose M here: + the module will be called mei_wdt. + # M32R Architecture # M68K Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f566753256ab..efc4f788e0f2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o +obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o # M32R Architecture diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c new file mode 100644 index 000000000000..32e3e1d55ef3 --- /dev/null +++ b/drivers/watchdog/mei_wdt.c @@ -0,0 +1,404 @@ +/* + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2015, 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 + +/* + * iAMT Watchdog Device + */ +#define INTEL_AMT_WATCHDOG_ID "iamt_wdt" + +#define MEI_WDT_DEFAULT_TIMEOUT 120 /* seconds */ +#define MEI_WDT_MIN_TIMEOUT 120 /* seconds */ +#define MEI_WDT_MAX_TIMEOUT 65535 /* seconds */ + +/* Commands */ +#define MEI_MANAGEMENT_CONTROL 0x02 + +/* MEI Management Control version number */ +#define MEI_MC_VERSION_NUMBER 0x10 + +/* Sub Commands */ +#define MEI_MC_START_WD_TIMER_REQ 0x13 +#define MEI_MC_STOP_WD_TIMER_REQ 0x14 + +/** + * enum mei_wdt_state - internal watchdog state + * + * @MEI_WDT_IDLE: wd is idle and not opened + * @MEI_WDT_START: wd was opened, start was called + * @MEI_WDT_RUNNING: wd is expecting keep alive pings + * @MEI_WDT_STOPPING: wd is stopping and will move to IDLE + */ +enum mei_wdt_state { + MEI_WDT_IDLE, + MEI_WDT_START, + MEI_WDT_RUNNING, + MEI_WDT_STOPPING, +}; + +/** + * struct mei_wdt - mei watchdog driver + * @wdd: watchdog device + * + * @cldev: mei watchdog client device + * @state: watchdog internal state + * @timeout: watchdog current timeout + */ +struct mei_wdt { + struct watchdog_device wdd; + + struct mei_cl_device *cldev; + enum mei_wdt_state state; + u16 timeout; +}; + +/* + * struct mei_mc_hdr - Management Control Command Header + * + * @command: Management Control (0x2) + * @bytecount: Number of bytes in the message beyond this byte + * @subcommand: Management Control Subcommand + * @versionnumber: Management Control Version (0x10) + */ +struct mei_mc_hdr { + u8 command; + u8 bytecount; + u8 subcommand; + u8 versionnumber; +}; + +/** + * struct mei_wdt_start_request watchdog start/ping + * + * @hdr: Management Control Command Header + * @timeout: timeout value + * @reserved: reserved (legacy) + */ +struct mei_wdt_start_request { + struct mei_mc_hdr hdr; + u16 timeout; + u8 reserved[17]; +} __packed; + +/** + * struct mei_wdt_stop_request - watchdog stop + * + * @hdr: Management Control Command Header + */ +struct mei_wdt_stop_request { + struct mei_mc_hdr hdr; +} __packed; + +/** + * mei_wdt_ping - send wd start/ping command + * + * @wdt: mei watchdog device + * + * Return: 0 on success, + * negative errno code on failure + */ +static int mei_wdt_ping(struct mei_wdt *wdt) +{ + struct mei_wdt_start_request req; + const size_t req_len = sizeof(req); + int ret; + + memset(&req, 0, req_len); + req.hdr.command = MEI_MANAGEMENT_CONTROL; + req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand); + req.hdr.subcommand = MEI_MC_START_WD_TIMER_REQ; + req.hdr.versionnumber = MEI_MC_VERSION_NUMBER; + req.timeout = wdt->timeout; + + ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len); + if (ret < 0) + return ret; + + return 0; +} + +/** + * mei_wdt_stop - send wd stop command + * + * @wdt: mei watchdog device + * + * Return: 0 on success, + * negative errno code on failure + */ +static int mei_wdt_stop(struct mei_wdt *wdt) +{ + struct mei_wdt_stop_request req; + const size_t req_len = sizeof(req); + int ret; + + memset(&req, 0, req_len); + req.hdr.command = MEI_MANAGEMENT_CONTROL; + req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand); + req.hdr.subcommand = MEI_MC_STOP_WD_TIMER_REQ; + req.hdr.versionnumber = MEI_MC_VERSION_NUMBER; + + ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len); + if (ret < 0) + return ret; + + return 0; +} + +/** + * mei_wdt_ops_start - wd start command from the watchdog core. + * + * @wdd: watchdog device + * + * Return: 0 on success or -ENODEV; + */ +static int mei_wdt_ops_start(struct watchdog_device *wdd) +{ + struct mei_wdt *wdt = watchdog_get_drvdata(wdd); + + wdt->state = MEI_WDT_START; + wdd->timeout = wdt->timeout; + return 0; +} + +/** + * mei_wdt_ops_stop - wd stop command from the watchdog core. + * + * @wdd: watchdog device + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wdt_ops_stop(struct watchdog_device *wdd) +{ + struct mei_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; + + if (wdt->state != MEI_WDT_RUNNING) + return 0; + + wdt->state = MEI_WDT_STOPPING; + + ret = mei_wdt_stop(wdt); + if (ret) + return ret; + + wdt->state = MEI_WDT_IDLE; + + return 0; +} + +/** + * mei_wdt_ops_ping - wd ping command from the watchdog core. + * + * @wdd: watchdog device + * + * Return: 0 if success, negative errno code on failure + */ +static int mei_wdt_ops_ping(struct watchdog_device *wdd) +{ + struct mei_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; + + if (wdt->state != MEI_WDT_START && wdt->state != MEI_WDT_RUNNING) + return 0; + + ret = mei_wdt_ping(wdt); + if (ret) + return ret; + + wdt->state = MEI_WDT_RUNNING; + + return 0; +} + +/** + * mei_wdt_ops_set_timeout - wd set timeout command from the watchdog core. + * + * @wdd: watchdog device + * @timeout: timeout value to set + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wdt_ops_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + + struct mei_wdt *wdt = watchdog_get_drvdata(wdd); + + /* valid value is already checked by the caller */ + wdt->timeout = timeout; + wdd->timeout = timeout; + + return 0; +} + +static const struct watchdog_ops wd_ops = { + .owner = THIS_MODULE, + .start = mei_wdt_ops_start, + .stop = mei_wdt_ops_stop, + .ping = mei_wdt_ops_ping, + .set_timeout = mei_wdt_ops_set_timeout, +}; + +/* not const as the firmware_version field need to be retrieved */ +static struct watchdog_info wd_info = { + .identity = INTEL_AMT_WATCHDOG_ID, + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_ALARMONLY, +}; + +/** + * mei_wdt_unregister - unregister from the watchdog subsystem + * + * @wdt: mei watchdog device + */ +static void mei_wdt_unregister(struct mei_wdt *wdt) +{ + watchdog_unregister_device(&wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, NULL); +} + +/** + * mei_wdt_register - register with the watchdog subsystem + * + * @wdt: mei watchdog device + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wdt_register(struct mei_wdt *wdt) +{ + struct device *dev; + int ret; + + if (!wdt || !wdt->cldev) + return -EINVAL; + + dev = &wdt->cldev->dev; + + wdt->wdd.info = &wd_info; + wdt->wdd.ops = &wd_ops; + wdt->wdd.parent = dev; + wdt->wdd.timeout = MEI_WDT_DEFAULT_TIMEOUT; + wdt->wdd.min_timeout = MEI_WDT_MIN_TIMEOUT; + wdt->wdd.max_timeout = MEI_WDT_MAX_TIMEOUT; + + watchdog_set_drvdata(&wdt->wdd, wdt); + ret = watchdog_register_device(&wdt->wdd); + if (ret) { + dev_err(dev, "unable to register watchdog device = %d.\n", ret); + watchdog_set_drvdata(&wdt->wdd, NULL); + } + + return ret; +} + +static int mei_wdt_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct mei_wdt *wdt; + int ret; + + wdt = kzalloc(sizeof(struct mei_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->timeout = MEI_WDT_DEFAULT_TIMEOUT; + wdt->state = MEI_WDT_IDLE; + wdt->cldev = cldev; + mei_cldev_set_drvdata(cldev, wdt); + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + dev_err(&cldev->dev, "Could not enable cl device\n"); + goto err_out; + } + + wd_info.firmware_version = mei_cldev_ver(cldev); + + ret = mei_wdt_register(wdt); + if (ret) + goto err_disable; + + return 0; + +err_disable: + mei_cldev_disable(cldev); + +err_out: + kfree(wdt); + + return ret; +} + +static int mei_wdt_remove(struct mei_cl_device *cldev) +{ + struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev); + + mei_wdt_unregister(wdt); + + mei_cldev_disable(cldev); + + kfree(wdt); + + return 0; +} + +#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ + 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) + +static struct mei_cl_device_id mei_wdt_tbl[] = { + { .uuid = MEI_UUID_WD, .version = 0x1}, + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, mei_wdt_tbl); + +static struct mei_cl_driver mei_wdt_driver = { + .id_table = mei_wdt_tbl, + .name = KBUILD_MODNAME, + + .probe = mei_wdt_probe, + .remove = mei_wdt_remove, +}; + +static int __init mei_wdt_init(void) +{ + int ret; + + ret = mei_cldev_driver_register(&mei_wdt_driver); + if (ret) { + pr_err(KBUILD_MODNAME ": module registration failed\n"); + return ret; + } + return 0; +} + +static void __exit mei_wdt_exit(void) +{ + mei_cldev_driver_unregister(&mei_wdt_driver); +} + +module_init(mei_wdt_init); +module_exit(mei_wdt_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device driver for Intel MEI iAMT watchdog"); -- cgit v1.2.3 From c9cf20ee45602a7a5512b7fbbef5672382790555 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 8 Jan 2016 00:49:23 +0200 Subject: watchdog: mei_wdt: add status debugfs entry Add entry for displaying current watchdog internal state cat /mei_wdt/state IDLE|START|RUNNING|STOPPING Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/mei_wdt.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index 32e3e1d55ef3..e7e3f144f2b0 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,24 @@ enum mei_wdt_state { MEI_WDT_STOPPING, }; +#if IS_ENABLED(CONFIG_DEBUG_FS) +static const char *mei_wdt_state_str(enum mei_wdt_state state) +{ + switch (state) { + case MEI_WDT_IDLE: + return "IDLE"; + case MEI_WDT_START: + return "START"; + case MEI_WDT_RUNNING: + return "RUNNING"; + case MEI_WDT_STOPPING: + return "STOPPING"; + default: + return "unknown"; + } +} +#endif /* CONFIG_DEBUG_FS */ + /** * struct mei_wdt - mei watchdog driver * @wdd: watchdog device @@ -61,6 +80,8 @@ enum mei_wdt_state { * @cldev: mei watchdog client device * @state: watchdog internal state * @timeout: watchdog current timeout + * + * @dbgfs_dir: debugfs dir entry */ struct mei_wdt { struct watchdog_device wdd; @@ -68,6 +89,10 @@ struct mei_wdt { struct mei_cl_device *cldev; enum mei_wdt_state state; u16 timeout; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ }; /* @@ -310,6 +335,63 @@ static int mei_wdt_register(struct mei_wdt *wdt) return ret; } +#if IS_ENABLED(CONFIG_DEBUG_FS) + +static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_wdt *wdt = file->private_data; + const size_t bufsz = 32; + char buf[bufsz]; + ssize_t pos; + + pos = scnprintf(buf, bufsz, "state: %s\n", + mei_wdt_state_str(wdt->state)); + + return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); +} + +static const struct file_operations dbgfs_fops_state = { + .open = simple_open, + .read = mei_dbgfs_read_state, + .llseek = generic_file_llseek, +}; + +static void dbgfs_unregister(struct mei_wdt *wdt) +{ + debugfs_remove_recursive(wdt->dbgfs_dir); + wdt->dbgfs_dir = NULL; +} + +static int dbgfs_register(struct mei_wdt *wdt) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!dir) + return -ENOMEM; + + wdt->dbgfs_dir = dir; + f = debugfs_create_file("state", S_IRUSR, dir, wdt, &dbgfs_fops_state); + if (!f) + goto err; + + return 0; +err: + dbgfs_unregister(wdt); + return -ENODEV; +} + +#else + +static inline void dbgfs_unregister(struct mei_wdt *wdt) {} + +static inline int dbgfs_register(struct mei_wdt *wdt) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + static int mei_wdt_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { @@ -337,6 +419,9 @@ static int mei_wdt_probe(struct mei_cl_device *cldev, if (ret) goto err_disable; + if (dbgfs_register(wdt)) + dev_warn(&cldev->dev, "cannot register debugfs\n"); + return 0; err_disable: @@ -356,6 +441,8 @@ static int mei_wdt_remove(struct mei_cl_device *cldev) mei_cldev_disable(cldev); + dbgfs_unregister(wdt); + kfree(wdt); return 0; -- cgit v1.2.3 From e97cdb303c04bfe60283094bdced68a0d363bd5d Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 8 Jan 2016 00:49:24 +0200 Subject: mei: bus: whitelist the watchdog client The iAMT WD client has to be whitelisted sice it has two connections and is filtered out by number_of_connections fixup. Also the API has changed for BDW and SKL but firmware haven't updated the protocol version. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index b2d2a6ea576c..b87323f4bb14 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -35,6 +35,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) +#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ + 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) + #define MEI_UUID_ANY NULL_UUID_LE /** @@ -66,6 +69,31 @@ static void blacklist(struct mei_cl_device *cldev) cldev->do_match = 0; } +/** + * mei_wd - wd client on the bus, change protocol version + * as the API has changed. + * + * @cldev: me clients device + */ +#if IS_ENABLED(CONFIG_INTEL_MEI_ME) +#include +#include "hw-me-regs.h" +static void mei_wd(struct mei_cl_device *cldev) +{ + struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); + + dev_dbg(&cldev->dev, "running hook %s\n", __func__); + if (pdev->device == MEI_DEV_ID_WPT_LP || + pdev->device == MEI_DEV_ID_SPT || + pdev->device == MEI_DEV_ID_SPT_H) + cldev->me_cl->props.protocol_version = 0x2; + + cldev->do_match = 1; +} +#else +static inline void mei_wd(struct mei_cl_device *cldev) {} +#endif /* CONFIG_INTEL_MEI_ME */ + struct mei_nfc_cmd { u8 command; u8 status; @@ -280,6 +308,7 @@ static struct mei_fixup { MEI_FIXUP(MEI_UUID_ANY, number_of_connections), MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), + MEI_FIXUP(MEI_UUID_WD, mei_wd), }; /** -- cgit v1.2.3 From 7326fffb712f09a315bc73cc1ee63843f59b8bd4 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 17 Jan 2016 12:25:01 +0200 Subject: mei: validate request value in client notify request ioctl This patch address a possible security issue: The request field in client notify request ioctl comes from user space as u32 and is downcasted to u8 with out validation. Check request field to have approved values MEI_HBM_NOTIFICATION_STAR/STOP Cc: #4.3+ Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 677d0362f334..80f9afcb1382 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -458,7 +458,11 @@ static int mei_ioctl_client_notify_request(struct file *file, u32 request) { struct mei_cl *cl = file->private_data; - return mei_cl_notify_request(cl, file, request); + if (request != MEI_HBM_NOTIFICATION_START && + request != MEI_HBM_NOTIFICATION_STOP) + return -EINVAL; + + return mei_cl_notify_request(cl, file, (u8)request); } /** -- cgit v1.2.3 From d56edd7ed0ed46a8043ee3040ededbd190818ccf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Jan 2016 23:33:59 +0100 Subject: 8250: uniphier: allow modular build with 8250 console The recently added uniphier 8250 port driver supports early console probing, and it supports being built as a module, but the combination of the two fails to link: ERROR: "early_serial8250_setup" [drivers/tty/serial/8250/8250_uniphier.ko] undefined! Given that earlycon support in a loadable module makes no sense, making that code conditional on 'MODULE' is a correct solution. Signed-off-by: Arnd Bergmann Fixes: b8d20e06eaad ("serial: 8250_uniphier: add earlycon support") Acked-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index bab6b3ae2540..1b7bd26555b7 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -35,7 +35,7 @@ struct uniphier8250_priv { spinlock_t atomic_write_lock; }; -#ifdef CONFIG_SERIAL_8250_CONSOLE +#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE) static int __init uniphier_early_console_setup(struct earlycon_device *device, const char *options) { -- cgit v1.2.3 From 308bbc9ab838d0ace0298268c7970ba9513e2c65 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 12 Jan 2016 15:14:46 -0800 Subject: serial: omap: Prevent DoS using unprivileged ioctl(TIOCSRS485) The omap-serial driver emulates RS485 delays using software timers, but neglects to clamp the input values from the unprivileged ioctl(TIOCSRS485). Because the software implementation busy-waits, malicious userspace could stall the cpu for ~49 days. Clamp the input values to < 100ms. Fixes: 4a0ac0f55b18 ("OMAP: add RS485 support") Cc: # 3.12+ Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index b645f9228ed7..27d3b978f16e 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1343,7 +1343,7 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) /* Enable or disable the rs485 support */ static int -serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { struct uart_omap_port *up = to_uart_omap_port(port); unsigned int mode; @@ -1356,8 +1356,12 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) up->ier = 0; serial_out(up, UART_IER, 0); + /* Clamp the delays to [0, 100ms] */ + rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); + rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); + /* store new config */ - port->rs485 = *rs485conf; + port->rs485 = *rs485; /* * Just as a precaution, only allow rs485 -- cgit v1.2.3 From b4a512b8bf7185787e291cddad3b6457b2367120 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Jan 2016 21:59:23 +0100 Subject: serial/omap: mark wait_for_xmitr as __maybe_unused The wait_for_xmitr() function is only used if CONFIG_CONSOLE_POLL or CONFIG_SERIAL_OMAP_CONSOLE are set, but when both are disabled, the compiler warns about it being unused: drivers/tty/serial/omap-serial.c:1168:13: warning: 'wait_for_xmitr' defined but not used [-Wunused-func We could add more #ifdefs to work around it, but adding __maybe_unused seems nicer. Signed-off-by: Arnd Bergmann Fixes: 2172076d2399 ("serial/omap-serial: Deinline wait_for_xmitr, save 165 bytes") Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 27d3b978f16e..fa49eb1e2fa2 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1165,7 +1165,7 @@ serial_omap_type(struct uart_port *port) #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -static void wait_for_xmitr(struct uart_omap_port *up) +static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up) { unsigned int status, tmout = 10000; -- cgit v1.2.3 From 7dde55787b43a8f2b4021916db38d90c03a2ec64 Mon Sep 17 00:00:00 2001 From: Jeremy McNicoll Date: Tue, 2 Feb 2016 13:00:45 -0800 Subject: tty: Add support for PCIe WCH382 2S multi-IO card WCH382 2S board is a PCIe card with 2 DB9 COM ports detected as Serial controller: Device 1c00:3253 (rev 10) (prog-if 05 [16850]) Signed-off-by: Jeremy McNicoll Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index e71ec78fc11e..7cd6f9a90542 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1941,6 +1941,7 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCIE_VENDOR_ID_WCH 0x1c00 #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 #define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 +#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253 #define PCI_VENDOR_ID_PERICOM 0x12D8 #define PCI_DEVICE_ID_PERICOM_PI7C9X7951 0x7951 @@ -2637,6 +2638,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, + /* WCH CH382 2S card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH382_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, /* WCH CH382 2S1P card (16850 clone) */ { .vendor = PCIE_VENDOR_ID_WCH, @@ -2955,6 +2964,7 @@ enum pci_board_num_t { pbn_fintek_4, pbn_fintek_8, pbn_fintek_12, + pbn_wch382_2, pbn_wch384_4, pbn_pericom_PI7C9X7951, pbn_pericom_PI7C9X7952, @@ -3775,6 +3785,13 @@ static struct pciserial_board pci_boards[] = { .base_baud = 115200, .first_offset = 0x40, }, + [pbn_wch382_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, [pbn_wch384_4] = { .flags = FL_BASE0, .num_ports = 4, @@ -5574,6 +5591,10 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH382_2S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_wch382_2 }, + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_4 }, -- cgit v1.2.3 From 2831c89f42dcde440cfdccb9fee9f42d54bbc1ef Mon Sep 17 00:00:00 2001 From: "Herton R. Krzesinski" Date: Mon, 11 Jan 2016 12:07:43 -0200 Subject: pty: fix possible use after free of tty->driver_data This change fixes a bug for a corner case where we have the the last release from a pty master/slave coming from a previously opened /dev/tty file. When this happens, the tty->driver_data can be stale, due to all ptmx or pts/N files having already been closed before (and thus the inode related to these files, which tty->driver_data points to, being already freed/destroyed). The fix here is to keep a reference on the opened master ptmx inode. We maintain the inode referenced until the final pty_unix98_shutdown, and only pass this inode to devpts_kill_index. Signed-off-by: Herton R. Krzesinski Cc: # 2.6.29+ Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index b3110040164a..3b5cde833ee5 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -681,7 +681,14 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) /* this is called once with whichever end is closed last */ static void pty_unix98_shutdown(struct tty_struct *tty) { - devpts_kill_index(tty->driver_data, tty->index); + struct inode *ptmx_inode; + + if (tty->driver->subtype == PTY_TYPE_MASTER) + ptmx_inode = tty->driver_data; + else + ptmx_inode = tty->link->driver_data; + devpts_kill_index(ptmx_inode, tty->index); + iput(ptmx_inode); /* drop reference we acquired at ptmx_open */ } static const struct tty_operations ptm_unix98_ops = { @@ -773,6 +780,15 @@ static int ptmx_open(struct inode *inode, struct file *filp) set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty->driver_data = inode; + /* + * In the case where all references to ptmx inode are dropped and we + * still have /dev/tty opened pointing to the master/slave pair (ptmx + * is closed/released before /dev/tty), we must make sure that the inode + * is still valid when we call the final pty_unix98_shutdown, thus we + * hold an additional reference to the ptmx inode + */ + ihold(inode); + tty_add_file(tty, filp); slave_inode = devpts_pty_new(inode, -- cgit v1.2.3 From 1f55c718c290616889c04946864a13ef30f64929 Mon Sep 17 00:00:00 2001 From: "Herton R. Krzesinski" Date: Thu, 14 Jan 2016 17:56:58 -0200 Subject: pty: make sure super_block is still valid in final /dev/tty close Considering current pty code and multiple devpts instances, it's possible to umount a devpts file system while a program still has /dev/tty opened pointing to a previosuly closed pty pair in that instance. In the case all ptmx and pts/N files are closed, umount can be done. If the program closes /dev/tty after umount is done, devpts_kill_index will use now an invalid super_block, which was already destroyed in the umount operation after running ->kill_sb. This is another "use after free" type of issue, but now related to the allocated super_block instance. To avoid the problem (warning at ida_remove and potential crashes) for this specific case, I added two functions in devpts which grabs additional references to the super_block, which pty code now uses so it makes sure the super block structure is still valid until pty shutdown is done. I also moved the additional inode references to the same functions, which also covered similar case with inode being freed before /dev/tty final close/shutdown. Signed-off-by: Herton R. Krzesinski Cc: stable@vger.kernel.org # 2.6.29+ Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 9 ++++++--- fs/devpts/inode.c | 20 ++++++++++++++++++++ include/linux/devpts_fs.h | 4 ++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 3b5cde833ee5..2348fa613707 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -688,7 +688,7 @@ static void pty_unix98_shutdown(struct tty_struct *tty) else ptmx_inode = tty->link->driver_data; devpts_kill_index(ptmx_inode, tty->index); - iput(ptmx_inode); /* drop reference we acquired at ptmx_open */ + devpts_del_ref(ptmx_inode); } static const struct tty_operations ptm_unix98_ops = { @@ -785,9 +785,12 @@ static int ptmx_open(struct inode *inode, struct file *filp) * still have /dev/tty opened pointing to the master/slave pair (ptmx * is closed/released before /dev/tty), we must make sure that the inode * is still valid when we call the final pty_unix98_shutdown, thus we - * hold an additional reference to the ptmx inode + * hold an additional reference to the ptmx inode. For the same /dev/tty + * last close case, we also need to make sure the super_block isn't + * destroyed (devpts instance unmounted), before /dev/tty is closed and + * on its release devpts_kill_index is called. */ - ihold(inode); + devpts_add_ref(inode); tty_add_file(tty, filp); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 1f107fd51328..655f21f99160 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -575,6 +575,26 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } +/* + * pty code needs to hold extra references in case of last /dev/tty close + */ + +void devpts_add_ref(struct inode *ptmx_inode) +{ + struct super_block *sb = pts_sb_from_inode(ptmx_inode); + + atomic_inc(&sb->s_active); + ihold(ptmx_inode); +} + +void devpts_del_ref(struct inode *ptmx_inode) +{ + struct super_block *sb = pts_sb_from_inode(ptmx_inode); + + iput(ptmx_inode); + deactivate_super(sb); +} + /** * devpts_pty_new -- create a new inode in /dev/pts/ * @ptmx_inode: inode of the master diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 251a2090a554..e0ee0b3000b2 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -19,6 +19,8 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); +void devpts_add_ref(struct inode *ptmx_inode); +void devpts_del_ref(struct inode *ptmx_inode); /* mknod in devpts */ struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, void *priv); @@ -32,6 +34,8 @@ void devpts_pty_kill(struct inode *inode); /* Dummy stubs in the no-pty case */ static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } +static inline void devpts_add_ref(struct inode *ptmx_inode) { } +static inline void devpts_del_ref(struct inode *ptmx_inode) { } static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, void *priv) { -- cgit v1.2.3 From c44d9b1181cf34e0860c72cc8a00e0c47417aac0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 7 Feb 2016 09:38:26 +0100 Subject: ALSA: hda - Fix speaker output from VAIO AiO machines Some Sony VAIO AiO models (VGC-JS4EF and VGC-JS25G, both with PCI SSID 104d:9044) need the same quirk to make the speaker working properly. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=112031 Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a733e5dc701d..b43c0f7994eb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2223,6 +2223,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), /* All Apple entries are in codec SSIDs */ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), -- cgit v1.2.3 From aa7b45378059a3eba1529d76f6d0b367ba614646 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 2 Feb 2016 08:10:10 -0500 Subject: update be2net maintainers' email addresses be2net maintainers' email addresses changed from avagotech.com to broadcom.com starting today. While updating the list, I'm also adding Somnath's name to the list. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- MAINTAINERS | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f678c37107f5..413777aaddc5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9793,10 +9793,11 @@ S: Supported F: drivers/scsi/be2iscsi/ Emulex 10Gbps NIC BE2, BE3-R, Lancer, Skyhawk-R DRIVER -M: Sathya Perla -M: Ajit Khaparde -M: Padmanabh Ratnakar -M: Sriharsha Basavapatna +M: Sathya Perla +M: Ajit Khaparde +M: Padmanabh Ratnakar +M: Sriharsha Basavapatna +M: Somnath Kotur L: netdev@vger.kernel.org W: http://www.emulex.com S: Supported -- cgit v1.2.3 From 7a23f80eaae9c6b5175cd7a96634a91ed9928aff Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 22:46:48 +0200 Subject: watchdog: mei_wdt: register wd device only if required For Intel Broadwell and newer platforms, the ME device can inform the host whether the watchdog functionality is activated or not. If the watchdog functionality is not activated then the watchdog interface can be not registered and eliminate unnecessary pings and hence lower the power consumption by avoiding waking up the device. The feature can be deactivated also without reboot in that case the watchdog device should be unregistered at runtime. The information regarding the deactivation is reported in the ping response command. In runtime case the unregistration has to be run from a worker so that the ping initiated by the watchdog core completes. Otherwise the flow will deadlock on watchdog core mutex which both ping and unregistration acquire. Reviewed-by: Guenter Roeck Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/mei_wdt.c | 200 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 191 insertions(+), 9 deletions(-) diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index e7e3f144f2b0..9addf8902f74 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -38,27 +39,35 @@ /* Sub Commands */ #define MEI_MC_START_WD_TIMER_REQ 0x13 +#define MEI_MC_START_WD_TIMER_RES 0x83 +#define MEI_WDT_STATUS_SUCCESS 0 +#define MEI_WDT_WDSTATE_NOT_REQUIRED 0x1 #define MEI_MC_STOP_WD_TIMER_REQ 0x14 /** * enum mei_wdt_state - internal watchdog state * + * @MEI_WDT_PROBE: wd in probing stage * @MEI_WDT_IDLE: wd is idle and not opened * @MEI_WDT_START: wd was opened, start was called * @MEI_WDT_RUNNING: wd is expecting keep alive pings * @MEI_WDT_STOPPING: wd is stopping and will move to IDLE + * @MEI_WDT_NOT_REQUIRED: wd device is not required */ enum mei_wdt_state { + MEI_WDT_PROBE, MEI_WDT_IDLE, MEI_WDT_START, MEI_WDT_RUNNING, MEI_WDT_STOPPING, + MEI_WDT_NOT_REQUIRED, }; -#if IS_ENABLED(CONFIG_DEBUG_FS) static const char *mei_wdt_state_str(enum mei_wdt_state state) { switch (state) { + case MEI_WDT_PROBE: + return "PROBE"; case MEI_WDT_IDLE: return "IDLE"; case MEI_WDT_START: @@ -67,11 +76,12 @@ static const char *mei_wdt_state_str(enum mei_wdt_state state) return "RUNNING"; case MEI_WDT_STOPPING: return "STOPPING"; + case MEI_WDT_NOT_REQUIRED: + return "NOT_REQUIRED"; default: return "unknown"; } } -#endif /* CONFIG_DEBUG_FS */ /** * struct mei_wdt - mei watchdog driver @@ -79,6 +89,10 @@ static const char *mei_wdt_state_str(enum mei_wdt_state state) * * @cldev: mei watchdog client device * @state: watchdog internal state + * @resp_required: ping required response + * @response: ping response completion + * @unregister: unregister worker + * @reg_lock: watchdog device registration lock * @timeout: watchdog current timeout * * @dbgfs_dir: debugfs dir entry @@ -88,6 +102,10 @@ struct mei_wdt { struct mei_cl_device *cldev; enum mei_wdt_state state; + bool resp_required; + struct completion response; + struct work_struct unregister; + struct mutex reg_lock; u16 timeout; #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -123,6 +141,19 @@ struct mei_wdt_start_request { u8 reserved[17]; } __packed; +/** + * struct mei_wdt_start_response watchdog start/ping response + * + * @hdr: Management Control Command Header + * @status: operation status + * @wdstate: watchdog status bit mask + */ +struct mei_wdt_start_response { + struct mei_mc_hdr hdr; + u8 status; + u8 wdstate; +} __packed; + /** * struct mei_wdt_stop_request - watchdog stop * @@ -244,13 +275,18 @@ static int mei_wdt_ops_ping(struct watchdog_device *wdd) if (wdt->state != MEI_WDT_START && wdt->state != MEI_WDT_RUNNING) return 0; + if (wdt->resp_required) + init_completion(&wdt->response); + + wdt->state = MEI_WDT_RUNNING; ret = mei_wdt_ping(wdt); if (ret) return ret; - wdt->state = MEI_WDT_RUNNING; + if (wdt->resp_required) + ret = wait_for_completion_killable(&wdt->response); - return 0; + return ret; } /** @@ -290,6 +326,19 @@ static struct watchdog_info wd_info = { WDIOF_ALARMONLY, }; +/** + * __mei_wdt_is_registered - check if wdt is registered + * + * @wdt: mei watchdog device + * + * Return: true if the wdt is registered with the watchdog subsystem + * Locking: should be called under wdt->reg_lock + */ +static inline bool __mei_wdt_is_registered(struct mei_wdt *wdt) +{ + return !!watchdog_get_drvdata(&wdt->wdd); +} + /** * mei_wdt_unregister - unregister from the watchdog subsystem * @@ -297,8 +346,15 @@ static struct watchdog_info wd_info = { */ static void mei_wdt_unregister(struct mei_wdt *wdt) { - watchdog_unregister_device(&wdt->wdd); - watchdog_set_drvdata(&wdt->wdd, NULL); + mutex_lock(&wdt->reg_lock); + + if (__mei_wdt_is_registered(wdt)) { + watchdog_unregister_device(&wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, NULL); + memset(&wdt->wdd, 0, sizeof(wdt->wdd)); + } + + mutex_unlock(&wdt->reg_lock); } /** @@ -318,6 +374,13 @@ static int mei_wdt_register(struct mei_wdt *wdt) dev = &wdt->cldev->dev; + mutex_lock(&wdt->reg_lock); + + if (__mei_wdt_is_registered(wdt)) { + ret = 0; + goto out; + } + wdt->wdd.info = &wd_info; wdt->wdd.ops = &wd_ops; wdt->wdd.parent = dev; @@ -332,9 +395,106 @@ static int mei_wdt_register(struct mei_wdt *wdt) watchdog_set_drvdata(&wdt->wdd, NULL); } + wdt->state = MEI_WDT_IDLE; + +out: + mutex_unlock(&wdt->reg_lock); return ret; } +static void mei_wdt_unregister_work(struct work_struct *work) +{ + struct mei_wdt *wdt = container_of(work, struct mei_wdt, unregister); + + mei_wdt_unregister(wdt); +} + +/** + * mei_wdt_event_rx - callback for data receive + * + * @cldev: bus device + */ +static void mei_wdt_event_rx(struct mei_cl_device *cldev) +{ + struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev); + struct mei_wdt_start_response res; + const size_t res_len = sizeof(res); + int ret; + + ret = mei_cldev_recv(wdt->cldev, (u8 *)&res, res_len); + if (ret < 0) { + dev_err(&cldev->dev, "failure in recv %d\n", ret); + return; + } + + /* Empty response can be sent on stop */ + if (ret == 0) + return; + + if (ret < sizeof(struct mei_mc_hdr)) { + dev_err(&cldev->dev, "recv small data %d\n", ret); + return; + } + + if (res.hdr.command != MEI_MANAGEMENT_CONTROL || + res.hdr.versionnumber != MEI_MC_VERSION_NUMBER) { + dev_err(&cldev->dev, "wrong command received\n"); + return; + } + + if (res.hdr.subcommand != MEI_MC_START_WD_TIMER_RES) { + dev_warn(&cldev->dev, "unsupported command %d :%s[%d]\n", + res.hdr.subcommand, + mei_wdt_state_str(wdt->state), + wdt->state); + return; + } + + /* Run the unregistration in a worker as this can be + * run only after ping completion, otherwise the flow will + * deadlock on watchdog core mutex. + */ + if (wdt->state == MEI_WDT_RUNNING) { + if (res.wdstate & MEI_WDT_WDSTATE_NOT_REQUIRED) { + wdt->state = MEI_WDT_NOT_REQUIRED; + schedule_work(&wdt->unregister); + } + goto out; + } + + if (wdt->state == MEI_WDT_PROBE) { + if (res.wdstate & MEI_WDT_WDSTATE_NOT_REQUIRED) { + wdt->state = MEI_WDT_NOT_REQUIRED; + } else { + /* stop the watchdog and register watchdog device */ + mei_wdt_stop(wdt); + mei_wdt_register(wdt); + } + return; + } + + dev_warn(&cldev->dev, "not in correct state %s[%d]\n", + mei_wdt_state_str(wdt->state), wdt->state); + +out: + if (!completion_done(&wdt->response)) + complete(&wdt->response); +} + +/** + * mei_wdt_event - callback for event receive + * + * @cldev: bus device + * @events: event mask + * @context: callback context + */ +static void mei_wdt_event(struct mei_cl_device *cldev, + u32 events, void *context) +{ + if (events & BIT(MEI_CL_EVENT_RX)) + mei_wdt_event_rx(cldev); +} + #if IS_ENABLED(CONFIG_DEBUG_FS) static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf, @@ -403,8 +563,13 @@ static int mei_wdt_probe(struct mei_cl_device *cldev, return -ENOMEM; wdt->timeout = MEI_WDT_DEFAULT_TIMEOUT; - wdt->state = MEI_WDT_IDLE; + wdt->state = MEI_WDT_PROBE; wdt->cldev = cldev; + wdt->resp_required = mei_cldev_ver(cldev) > 0x1; + mutex_init(&wdt->reg_lock); + init_completion(&wdt->response); + INIT_WORK(&wdt->unregister, mei_wdt_unregister_work); + mei_cldev_set_drvdata(cldev, wdt); ret = mei_cldev_enable(cldev); @@ -413,9 +578,20 @@ static int mei_wdt_probe(struct mei_cl_device *cldev, goto err_out; } + ret = mei_cldev_register_event_cb(wdt->cldev, BIT(MEI_CL_EVENT_RX), + mei_wdt_event, NULL); + if (ret) { + dev_err(&cldev->dev, "Could not register event ret=%d\n", ret); + goto err_disable; + } + wd_info.firmware_version = mei_cldev_ver(cldev); - ret = mei_wdt_register(wdt); + if (wdt->resp_required) + ret = mei_wdt_ping(wdt); + else + ret = mei_wdt_register(wdt); + if (ret) goto err_disable; @@ -437,6 +613,12 @@ static int mei_wdt_remove(struct mei_cl_device *cldev) { struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev); + /* Free the caller in case of fw initiated or unexpected reset */ + if (!completion_done(&wdt->response)) + complete(&wdt->response); + + cancel_work_sync(&wdt->unregister); + mei_wdt_unregister(wdt); mei_cldev_disable(cldev); @@ -452,7 +634,7 @@ static int mei_wdt_remove(struct mei_cl_device *cldev) 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) static struct mei_cl_device_id mei_wdt_tbl[] = { - { .uuid = MEI_UUID_WD, .version = 0x1}, + { .uuid = MEI_UUID_WD, .version = MEI_CL_VERSION_ANY }, /* required last entry */ { } }; -- cgit v1.2.3 From ad1cd720b18330599a9cabaf970095b74c9c3355 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 22:46:49 +0200 Subject: watchdog: mei_wdt: add activation debugfs entry Add entry for displaying whether the device has activated or deactivated watchdog fw application. cat /mei_wdt/activation activated | deactivated Reviewed-by: Guenter Roeck Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/mei_wdt.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index 9addf8902f74..b93474ec34a1 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -497,6 +497,28 @@ static void mei_wdt_event(struct mei_cl_device *cldev, #if IS_ENABLED(CONFIG_DEBUG_FS) +static ssize_t mei_dbgfs_read_activation(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_wdt *wdt = file->private_data; + const size_t bufsz = 32; + char buf[32]; + ssize_t pos; + + mutex_lock(&wdt->reg_lock); + pos = scnprintf(buf, bufsz, "%s\n", + __mei_wdt_is_registered(wdt) ? "activated" : "deactivated"); + mutex_unlock(&wdt->reg_lock); + + return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); +} + +static const struct file_operations dbgfs_fops_activation = { + .open = simple_open, + .read = mei_dbgfs_read_activation, + .llseek = generic_file_llseek, +}; + static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { @@ -536,6 +558,11 @@ static int dbgfs_register(struct mei_wdt *wdt) if (!f) goto err; + f = debugfs_create_file("activation", S_IRUSR, + dir, wdt, &dbgfs_fops_activation); + if (!f) + goto err; + return 0; err: dbgfs_unregister(wdt); -- cgit v1.2.3 From 3a20a5c339cce042e53557be067e121e4e984adf Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 22:46:50 +0200 Subject: watchdog: mei_wdt: re-register device on event For Intel SKL platform the ME device can inform the host via asynchronous notification that the watchdog feature was activated on the device. The activation doesn't require reboot. In that case the driver registers the watchdog device with the kernel. Reviewed-by: Guenter Roeck Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/mei_wdt.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index b93474ec34a1..630bd189f167 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -481,6 +481,21 @@ out: complete(&wdt->response); } +/* + * mei_wdt_notify_event - callback for event notification + * + * @cldev: bus device + */ +static void mei_wdt_notify_event(struct mei_cl_device *cldev) +{ + struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev); + + if (wdt->state != MEI_WDT_NOT_REQUIRED) + return; + + mei_wdt_register(wdt); +} + /** * mei_wdt_event - callback for event receive * @@ -493,6 +508,9 @@ static void mei_wdt_event(struct mei_cl_device *cldev, { if (events & BIT(MEI_CL_EVENT_RX)) mei_wdt_event_rx(cldev); + + if (events & BIT(MEI_CL_EVENT_NOTIF)) + mei_wdt_notify_event(cldev); } #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -605,9 +623,15 @@ static int mei_wdt_probe(struct mei_cl_device *cldev, goto err_out; } - ret = mei_cldev_register_event_cb(wdt->cldev, BIT(MEI_CL_EVENT_RX), + ret = mei_cldev_register_event_cb(wdt->cldev, + BIT(MEI_CL_EVENT_RX) | + BIT(MEI_CL_EVENT_NOTIF), mei_wdt_event, NULL); - if (ret) { + + /* on legacy devices notification is not supported + * this doesn't fail the registration for RX event + */ + if (ret && ret != -EOPNOTSUPP) { dev_err(&cldev->dev, "Could not register event ret=%d\n", ret); goto err_disable; } -- cgit v1.2.3 From a96c548291719ae40da1b3c52493f40a63d3dd84 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 22:46:51 +0200 Subject: mei: trace pci configuration space io Use tracing events also for reading and writing pci configuration space /tracing/events/mei/mei_pci_reg_{read,write} Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 10 ++++++++-- drivers/misc/mei/hw-txe.c | 10 ++++++++-- drivers/misc/mei/mei-trace.c | 2 ++ drivers/misc/mei/mei-trace.h | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 25b1997a62cb..e2fb44cc5c37 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -189,8 +189,11 @@ static int mei_me_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = pci_read_config_dword(pdev, - fw_src->status[i], &fw_status->status[i]); + ret = pci_read_config_dword(pdev, fw_src->status[i], + &fw_status->status[i]); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", + fw_src->status[i], + fw_status->status[i]); if (ret) return ret; } @@ -215,6 +218,7 @@ static void mei_me_hw_config(struct mei_device *dev) reg = 0; pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); hw->d0i3_supported = ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); @@ -1248,6 +1252,7 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev) u32 reg; pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; } @@ -1260,6 +1265,7 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) u32 reg; /* Read ME FW Status check for SPS Firmware */ pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); /* if bits [19:16] = 15, running SPS Firmware */ return (reg & 0xf0000) == 0xf0000; } diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index bae680c648ff..4a6c1b85f11e 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -28,6 +28,9 @@ #include "client.h" #include "hbm.h" +#include "mei-trace.h" + + /** * mei_txe_reg_read - Reads 32bit data from the txe device * @@ -640,8 +643,11 @@ static int mei_txe_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = pci_read_config_dword(pdev, - fw_src->status[i], &fw_status->status[i]); + ret = pci_read_config_dword(pdev, fw_src->status[i], + &fw_status->status[i]); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", + fw_src->status[i], + fw_status->status[i]); if (ret) return ret; } diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c index 388efb519138..e19e6acb191b 100644 --- a/drivers/misc/mei/mei-trace.c +++ b/drivers/misc/mei/mei-trace.c @@ -22,4 +22,6 @@ EXPORT_TRACEPOINT_SYMBOL(mei_reg_read); EXPORT_TRACEPOINT_SYMBOL(mei_reg_write); +EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_read); +EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_write); #endif /* __CHECKER__ */ diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 47e1bc6551d4..86e5068837c1 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -64,6 +64,44 @@ TRACE_EVENT(mei_reg_write, __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); +TRACE_EVENT(mei_pci_cfg_read, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] pci cfg read %s:[%#x] = %#x", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + +TRACE_EVENT(mei_pci_cfg_write, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] pci cfg write %s[%#x] = %#x)", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + #endif /* _MEI_TRACE_H_ */ /* This part must be outside protection */ -- cgit v1.2.3 From 26900254827878c4f98c0d169f8ba290099b1ef3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:17 +0200 Subject: mei: debugfs: adjust active clients print buffer In case of many active host clients clients (41 and more) 1K buffer is not enough for full information print. Calculate buffer size according to real clients number. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index a138d8a27ab5..9f5410b98688 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -50,6 +50,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, } pos += scnprintf(buf + pos, bufsz - pos, HDR); +#undef HDR /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) @@ -90,23 +91,37 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, { struct mei_device *dev = fp->private_data; struct mei_cl *cl; - const size_t bufsz = 1024; + size_t bufsz = 1; char *buf; int i = 0; int pos = 0; int ret; +#define HDR " |me|host|state|rd|wr|\n" + if (!dev) return -ENODEV; + mutex_lock(&dev->device_lock); + + /* + * if the driver is not enabled the list won't be consistent, + * we output empty table + */ + if (dev->dev_state == MEI_DEV_ENABLED) + list_for_each_entry(cl, &dev->file_list, link) + bufsz++; + + bufsz *= sizeof(HDR) + 1; + buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) + if (!buf) { + mutex_unlock(&dev->device_lock); return -ENOMEM; + } - pos += scnprintf(buf + pos, bufsz - pos, - " |me|host|state|rd|wr|\n"); - - mutex_lock(&dev->device_lock); + pos += scnprintf(buf + pos, bufsz - pos, HDR); +#undef HDR /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) @@ -115,7 +130,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, list_for_each_entry(cl, &dev->file_list, link) { pos += scnprintf(buf + pos, bufsz - pos, - "%2d|%2d|%4d|%5d|%2d|%2d|\n", + "%3d|%2d|%4d|%5d|%2d|%2d|\n", i, mei_cl_me_id(cl), cl->host_client_id, cl->state, !list_empty(&cl->rd_completed), cl->writing_state); i++; -- cgit v1.2.3 From 439a74b3373dab4214b4191d50dfb1d78be5b18e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:18 +0200 Subject: mei: debugfs: allow hbm features list dump in earlier stages HBM features list is ready while sending enumerate request and enumerating clients, output it to debugfs in these states too. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 9f5410b98688..6bdd75424fe8 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -165,7 +165,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", mei_hbm_state_str(dev->hbm_state)); - if (dev->hbm_state == MEI_HBM_STARTED) { + if (dev->hbm_state >= MEI_HBM_ENUM_CLIENTS && + dev->hbm_state <= MEI_HBM_STARTED) { pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n", dev->hbm_f_pg_supported); -- cgit v1.2.3 From f862b6b24f0ffd954633a55f39251a6873b664ca Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:19 +0200 Subject: mei: fix possible integer overflow issue There is a possible integer overflow following by a buffer overflow when accumulating messages coming from the FW to compose a full payload. Occurrence of wrap around has to be prevented for next message size calculation. For unsigned integer the addition overflow has occurred when the result is smaller than one of the arguments. To simplify the fix, the types of buf.size and buf_idx are set to the same width, namely size_t also to be aligned with the type of length parameter in file read/write ops. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 5 ++--- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/interrupt.c | 21 ++++++++++++++++----- drivers/misc/mei/main.c | 5 +++-- drivers/misc/mei/mei_dev.h | 4 ++-- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index cd0403f09267..b753df98b476 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -195,9 +195,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(dev->dev, "amthif cb->buf size - %d\n", - cb->buf.size); - dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); + dev_dbg(dev->dev, "amthif cb->buf.size - %zd cb->buf_idx - %zd\n", + cb->buf.size, cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, * the buf_idx may point beyond */ diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e069fcaed7aa..738f3d703323 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1569,7 +1569,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", + cl_dbg(dev, cl, "buf: size = %zd idx = %zd\n", cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 6340dee33052..b8aa047ec258 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -104,6 +104,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; unsigned char *buffer = NULL; + size_t buf_sz; cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); if (!cb) { @@ -124,11 +125,21 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, goto out; } - if (cb->buf.size < mei_hdr->length + cb->buf_idx) { - cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", + buf_sz = mei_hdr->length + cb->buf_idx; + /* catch for integer overflow */ + if (buf_sz < cb->buf_idx) { + cl_err(dev, cl, "message is too big len %d idx %ld\n", + mei_hdr->length, cb->buf_idx); + + list_move_tail(&cb->list, &complete_list->list); + cb->status = -EMSGSIZE; + goto out; + } + + if (cb->buf.size < buf_sz) { + cl_dbg(dev, cl, "message overflow. size %zd len %d idx %zd\n", cb->buf.size, mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx, - GFP_KERNEL); + buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL); if (!buffer) { cb->status = -ENOMEM; @@ -136,7 +147,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, goto out; } cb->buf.data = buffer; - cb->buf.size = mei_hdr->length + cb->buf_idx; + cb->buf.size = buf_sz; } buffer = cb->buf.data + cb->buf_idx; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 36ca15234344..47dc6d9ae655 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -226,7 +226,7 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld offset = %lld\n", + cl_dbg(dev, cl, "buf.size = %zd buf.idx = %zd offset = %lld\n", cb->buf.size, cb->buf_idx, *offset); if (*offset >= cb->buf_idx) { rets = 0; @@ -245,7 +245,8 @@ copy_buffer: rets = length; *offset += length; - if ((unsigned long)*offset < cb->buf_idx) + /* not all data was read, keep the cb */ + if (*offset < cb->buf_idx) goto out; free: diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index da613268480c..9b793f87b7d4 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -126,7 +126,7 @@ enum mei_cb_file_ops { * Intel MEI message data struct */ struct mei_msg_data { - u32 size; + size_t size; unsigned char *data; }; @@ -190,7 +190,7 @@ struct mei_cl_cb { struct mei_cl *cl; enum mei_cb_file_ops fop_type; struct mei_msg_data buf; - unsigned long buf_idx; + size_t buf_idx; unsigned long read_time; struct file *file_object; int status; -- cgit v1.2.3 From 1f7e489a285c8b1aca9a7b66a55bbc95eb031721 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:20 +0200 Subject: mei: call stop on failed char device register If registering of character device failed stop the device properly. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/pci-me.c | 4 +++- drivers/misc/mei/pci-txe.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 75fc9c688df8..996344f6c32d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -210,7 +210,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = mei_register(dev, &pdev->dev); if (err) - goto release_irq; + goto stop; pci_set_drvdata(pdev, dev); @@ -231,6 +231,8 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +stop: + mei_stop(dev); release_irq: mei_cancel_work(dev); mei_disable_interrupts(dev); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 71f8a7475717..30cc30683c07 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -154,7 +154,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = mei_register(dev, &pdev->dev); if (err) - goto release_irq; + goto stop; pci_set_drvdata(pdev, dev); @@ -170,6 +170,8 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +stop: + mei_stop(dev); release_irq: mei_cancel_work(dev); -- cgit v1.2.3 From 5ba0bf476c792b91543da77e0bdec311b0eaa8f7 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:21 +0200 Subject: mei: amthif: don't copy from an empty buffer If empty message come from FW (buf_idx == 0) then the current code will still try to copy data from not filled buffer to the user-space, instead the code should behave the same as when end of a message has been reached, clean resources and return 0 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b753df98b476..c3f514027c80 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -185,7 +185,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) list_del_init(&cb->list); - else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { + else if (cb->buf_idx <= *offset) { /* end of the message has been reached */ list_del_init(&cb->list); rets = 0; -- cgit v1.2.3 From d0df8dfb8582f50053bde6a771262571daedf5d0 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:22 +0200 Subject: mei: amthif: don't drop read packets on timeout Since the driver now uses a list for storing read packets instead of single variable a pending read is no longer blocking other connections. A pending read will be discarded up the file closure. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 27 --------------------------- drivers/misc/mei/hw.h | 1 - drivers/misc/mei/interrupt.c | 32 -------------------------------- drivers/misc/mei/main.c | 16 ---------------- drivers/misc/mei/mei_dev.h | 4 ---- 5 files changed, 80 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c3f514027c80..5da1654bc209 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -50,7 +50,6 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_current_cb = NULL; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; dev->iamthif_stall_timer = 0; dev->iamthif_open_count = 0; } @@ -124,18 +123,10 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { - struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb; - unsigned long timeout; int rets; int wait_ret; - /* Only possible if we are in timeout */ - if (!cl) { - dev_err(dev->dev, "bad file ext.\n"); - return -ETIME; - } - dev_dbg(dev->dev, "checking amthif data\n"); cb = mei_amthif_find_read_list_entry(dev, file); @@ -168,20 +159,6 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, } dev_dbg(dev->dev, "Got amthif data\n"); - dev->iamthif_timer = 0; - - timeout = cb->read_time + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(dev->dev, "amthif timeout = %lud\n", - timeout); - - if (time_after(jiffies, timeout)) { - dev_dbg(dev->dev, "amthif Time out\n"); - /* 15 sec for the message has expired */ - list_del_init(&cb->list); - rets = -ETIME; - goto free; - } /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) list_del_init(&cb->list); @@ -303,7 +280,6 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); @@ -462,9 +438,6 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_stall_timer = 0; list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); dev_dbg(dev->dev, "amthif read completed\n"); - dev->iamthif_timer = jiffies; - dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", - dev->iamthif_timer); } else { mei_amthif_run_next_cmd(dev); } diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 4cebde85924f..4c5d6cfd79b4 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -29,7 +29,6 @@ #define MEI_CLIENTS_INIT_TIMEOUT 15 /* HPS: Clients Enumeration Timeout */ #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ -#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ #define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ #define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */ diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index b8aa047ec258..78978193d19b 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -156,7 +156,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cb->buf_idx += mei_hdr->length; if (mei_hdr->msg_complete) { - cb->read_time = jiffies; cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } else { @@ -458,7 +457,6 @@ static void mei_connect_timeout(struct mei_cl *cl) */ void mei_timer(struct work_struct *work) { - unsigned long timeout; struct mei_cl *cl; struct mei_device *dev = container_of(work, @@ -504,7 +502,6 @@ void mei_timer(struct work_struct *work) mei_reset(dev); dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; @@ -514,35 +511,6 @@ void mei_timer(struct work_struct *work) } } - if (dev->iamthif_timer) { - - timeout = dev->iamthif_timer + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - - dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", - dev->iamthif_timer); - dev_dbg(dev->dev, "timeout = %ld\n", timeout); - dev_dbg(dev->dev, "jiffies = %ld\n", jiffies); - if (time_after(jiffies, timeout)) { - /* - * User didn't read the AMTHI data on time (15sec) - * freeing AMTHI for other requests - */ - - dev_dbg(dev->dev, "freeing AMTHI for other requests\n"); - - mei_io_list_flush(&dev->amthif_rd_complete_list, - &dev->iamthif_cl); - mei_io_cb_free(dev->iamthif_current_cb); - dev->iamthif_current_cb = NULL; - - dev->iamthif_file_object->private_data = NULL; - dev->iamthif_file_object = NULL; - dev->iamthif_timer = 0; - mei_amthif_run_next_cmd(dev); - - } - } out: if (dev->dev_state != MEI_DEV_DISABLED) schedule_delayed_work(&dev->timer_work, 2 * HZ); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 47dc6d9ae655..5f2b63460d88 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -274,7 +274,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, struct mei_cl *cl = file->private_data; struct mei_cl_cb *write_cb = NULL; struct mei_device *dev; - unsigned long timeout = 0; int rets; if (WARN_ON(!cl || !cl->dev)) @@ -310,21 +309,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (cl == &dev->iamthif_cl) { - write_cb = mei_amthif_find_read_list_entry(dev, file); - - if (write_cb) { - timeout = write_cb->read_time + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - - if (time_after(jiffies, timeout)) { - *offset = 0; - mei_io_cb_free(write_cb); - write_cb = NULL; - } - } - } - *offset = 0; write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); if (!write_cb) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 9b793f87b7d4..4d0e066703ef 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -179,7 +179,6 @@ struct mei_cl; * @fop_type: file operation type * @buf: buffer for data associated with the callback * @buf_idx: last read index - * @read_time: last read operation time stamp (iamthif) * @file_object: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag @@ -191,7 +190,6 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - unsigned long read_time; struct file *file_object; int status; u32 internal:1; @@ -413,7 +411,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback * @iamthif_open_count : number of opened amthif connections - * @iamthif_timer : time stamp of current amthif command completion * @iamthif_stall_timer : timer to detect amthif hang * @iamthif_state : amthif processor state * @iamthif_canceled : current amthif command is canceled @@ -504,7 +501,6 @@ struct mei_device { struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; - unsigned long iamthif_timer; u32 iamthif_stall_timer; enum iamthif_states iamthif_state; bool iamthif_canceled; -- cgit v1.2.3 From f23e2cc4bb1d8bbbe02dcd3539ed531a3f40277c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:23 +0200 Subject: mei: constify struct file pointer The struct file file pointer is used as an opaque handle to for a connected client, for this part the pointer should be immutable and should be set to count. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 6 +++--- drivers/misc/mei/client.c | 12 +++++++----- drivers/misc/mei/client.h | 12 +++++++----- drivers/misc/mei/main.c | 4 ++-- drivers/misc/mei/mei_dev.h | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 5da1654bc209..16cceedeed97 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -93,7 +93,7 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) * Return: returned a list entry on success, NULL on failure. */ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - struct file *file) + const struct file *file) { struct mei_cl_cb *cb; @@ -205,7 +205,7 @@ out: * * Return: 0 on success, <0 on failure. */ -static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) +static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -495,7 +495,7 @@ static bool mei_clear_list(struct mei_device *dev, * * Return: true if callback removed from the list, false otherwise */ -static bool mei_clear_lists(struct mei_device *dev, struct file *file) +static bool mei_clear_lists(struct mei_device *dev, const struct file *file) { bool removed = false; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 738f3d703323..4c23caee04ab 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -359,7 +359,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) * Return: mei_cl_cb pointer or NULL; */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - struct file *fp) + const struct file *fp) { struct mei_cl_cb *cb; @@ -455,7 +455,8 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) * Return: cb on success and NULL on failure */ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, - enum mei_cb_file_ops type, struct file *fp) + enum mei_cb_file_ops type, + const struct file *fp) { struct mei_cl_cb *cb; @@ -1028,7 +1029,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, - struct file *file) + const struct file *file) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1277,7 +1278,8 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, * * Return: 0 on such and error otherwise. */ -int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request) +int mei_cl_notify_request(struct mei_cl *cl, + const struct file *file, u8 request) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1443,7 +1445,7 @@ static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) * * Return: 0 on success, <0 on failure. */ -int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) +int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) { struct mei_device *dev; struct mei_cl_cb *cb; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 2e90a25f896e..fdc43279368f 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,7 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) * MEI IO Functions */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - struct file *fp); + const struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length); @@ -116,7 +116,8 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, - enum mei_cb_file_ops type, struct file *fp); + enum mei_cb_file_ops type, + const struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); /* @@ -213,10 +214,10 @@ void mei_cl_set_disconnected(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, - struct file *file); + const struct file *file); int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); -int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp); +int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, struct mei_cl_cb *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); @@ -229,7 +230,8 @@ void mei_host_client_init(struct work_struct *work); u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop); enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); -int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request); +int mei_cl_notify_request(struct mei_cl *cl, + const struct file *file, u8 request); int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5f2b63460d88..03741c456ee4 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -435,7 +435,7 @@ end: * * Return: 0 on success , <0 on error */ -static int mei_ioctl_client_notify_request(struct file *file, u32 request) +static int mei_ioctl_client_notify_request(const struct file *file, u32 request) { struct mei_cl *cl = file->private_data; @@ -450,7 +450,7 @@ static int mei_ioctl_client_notify_request(struct file *file, u32 request) * * Return: 0 on success , <0 on error */ -static int mei_ioctl_client_notify_get(struct file *file, u32 *notify_get) +static int mei_ioctl_client_notify_get(const struct file *file, u32 *notify_get) { struct mei_cl *cl = file->private_data; bool notify_ev; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4d0e066703ef..947e0a70577a 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -190,7 +190,7 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - struct file *file_object; + const struct file *file_object; int status; u32 internal:1; u32 completed:1; @@ -497,7 +497,7 @@ struct mei_device { struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ struct mei_cl_cb amthif_rd_complete_list; - struct file *iamthif_file_object; + const struct file *iamthif_file_object; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; @@ -590,7 +590,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, int mei_amthif_release(struct mei_device *dev, struct file *file); struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - struct file *file); + const struct file *file); int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); -- cgit v1.2.3 From 62e8e6ad60975df491d886efaa7fe0aa832947fb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:24 +0200 Subject: mei: rename variable names 'file_object' to fp The driver uses three names file, fp, and file_object for struct file type. To improve code clarity and adjust to my taste rename file_object to more common and shorter fp. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 20 ++++++++++---------- drivers/misc/mei/client.c | 8 ++++---- drivers/misc/mei/interrupt.c | 2 +- drivers/misc/mei/mei_dev.h | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 16cceedeed97..647edc68884f 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -98,7 +98,7 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct mei_cl_cb *cb; list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) - if (cb->file_object == file) + if (cb->fp == file) return cb; return NULL; } @@ -224,7 +224,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) list_add_tail(&cb->list, &dev->ctrl_wr_list.list); dev->iamthif_state = MEI_IAMTHIF_READING; - dev->iamthif_file_object = cb->file_object; + dev->iamthif_fp = cb->fp; dev->iamthif_current_cb = cb; return 0; @@ -253,7 +253,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; - dev->iamthif_file_object = cb->file_object; + dev->iamthif_fp = cb->fp; dev->iamthif_canceled = false; ret = mei_cl_write(cl, cb, false); @@ -261,7 +261,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->file_object); + cb->status = mei_amthif_read_start(cl, cb->fp); return 0; } @@ -280,7 +280,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_file_object = NULL; + dev->iamthif_fp = NULL; dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); @@ -338,7 +338,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, poll_wait(file, &dev->iamthif_cl.wait, wait); if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_file_object == file) { + dev->iamthif_fp == file) { mask |= POLLIN | POLLRDNORM; mei_amthif_run_next_cmd(dev); @@ -368,7 +368,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return ret; if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->file_object); + cb->status = mei_amthif_read_start(cl, cb->fp); return 0; } @@ -469,7 +469,7 @@ static bool mei_clear_list(struct mei_device *dev, /* list all list member */ list_for_each_entry_safe(cb, next, mei_cb_list, list) { /* check if list member associated with a file */ - if (file == cb->file_object) { + if (file == cb->fp) { /* check if cb equal to current iamthif cb */ if (dev->iamthif_current_cb == cb) { dev->iamthif_current_cb = NULL; @@ -518,7 +518,7 @@ static bool mei_clear_lists(struct mei_device *dev, const struct file *file) /* check if iamthif_current_cb not NULL */ if (dev->iamthif_current_cb && !removed) { /* check file and iamthif current cb association */ - if (dev->iamthif_current_cb->file_object == file) { + if (dev->iamthif_current_cb->fp == file) { /* remove cb */ mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; @@ -541,7 +541,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (dev->iamthif_file_object == file && + if (dev->iamthif_fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 4c23caee04ab..c57ac25ce8db 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -368,7 +368,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, return NULL; INIT_LIST_HEAD(&cb->list); - cb->file_object = fp; + cb->fp = fp; cb->cl = cl; cb->buf_idx = 0; cb->fop_type = type; @@ -486,7 +486,7 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) struct mei_cl_cb *cb; list_for_each_entry(cb, &cl->rd_completed, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) return cb; return NULL; @@ -504,12 +504,12 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) struct mei_cl_cb *cb, *next; list_for_each_entry_safe(cb, next, &cl->rd_completed, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) mei_io_cb_free(cb); list_for_each_entry_safe(cb, next, &cl->rd_pending, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) mei_io_cb_free(cb); } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 78978193d19b..37c8f0e14387 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -506,7 +506,7 @@ void mei_timer(struct work_struct *work) mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; - dev->iamthif_file_object = NULL; + dev->iamthif_fp = NULL; mei_amthif_run_next_cmd(dev); } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 947e0a70577a..3acbbb49d08c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -179,7 +179,7 @@ struct mei_cl; * @fop_type: file operation type * @buf: buffer for data associated with the callback * @buf_idx: last read index - * @file_object: pointer to file structure + * @fp: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag * @completed: the transfer or reception has completed @@ -190,7 +190,7 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - const struct file *file_object; + const struct file *fp; int status; u32 internal:1; u32 completed:1; @@ -407,7 +407,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @amthif_cmd_list : amthif list for cmd waiting * @amthif_rd_complete_list : amthif list for reading completed cmd data - * @iamthif_file_object : file for current amthif operation + * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback * @iamthif_open_count : number of opened amthif connections @@ -497,7 +497,7 @@ struct mei_device { struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ struct mei_cl_cb amthif_rd_complete_list; - const struct file *iamthif_file_object; + const struct file *iamthif_fp; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; -- cgit v1.2.3 From 5cf8b2a9be0abec35f675bc719ca6eba9e559b1d Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:25 +0200 Subject: mei: amthif: allow only one request at a time A next amthif write can be executed only after the previous one has completed. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 647edc68884f..4383a9ad9208 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -315,6 +315,14 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) dev = cl->dev; list_add_tail(&cb->list, &dev->amthif_cmd_list.list); + + /* + * The previous request is still in processing, queue this one. + */ + if (dev->iamthif_state > MEI_IAMTHIF_IDLE && + dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE) + return 0; + return mei_amthif_run_next_cmd(dev); } -- cgit v1.2.3 From 9abd8b31292497530085156b41746e2a1cd9c934 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:26 +0200 Subject: mei: amthif: replace amthif_rd_complete_list with rd_completed Now when we have per client rd_completed list we can remove the amthif specific amthif_rd_complete_list. In addition in the function mei_amthif_read do not loop over the rd_completed list like the original code as the code path is unlocked. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 39 +++++++++++---------------------------- drivers/misc/mei/client.c | 1 - drivers/misc/mei/init.c | 1 - drivers/misc/mei/interrupt.c | 2 +- drivers/misc/mei/mei_dev.h | 7 +------ 5 files changed, 13 insertions(+), 37 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 4383a9ad9208..058dd6917805 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -84,26 +84,6 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) return ret; } -/** - * mei_amthif_find_read_list_entry - finds a amthilist entry for current file - * - * @dev: the device structure - * @file: pointer to file object - * - * Return: returned a list entry on success, NULL on failure. - */ -struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - const struct file *file) -{ - struct mei_cl_cb *cb; - - list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) - if (cb->fp == file) - return cb; - return NULL; -} - - /** * mei_amthif_read - read data from AMTHIF client * @@ -123,12 +103,13 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { + struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb; int rets; int wait_ret; dev_dbg(dev->dev, "checking amthif data\n"); - cb = mei_amthif_find_read_list_entry(dev, file); + cb = mei_cl_read_cb(cl, file); /* Check for if we can block or not*/ if (cb == NULL && file->f_flags & O_NONBLOCK) @@ -141,7 +122,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, mutex_unlock(&dev->device_lock); wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, - (cb = mei_amthif_find_read_list_entry(dev, file))); + !list_empty(&cl->rd_completed)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -149,7 +130,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; - dev_dbg(dev->dev, "woke up from sleep\n"); + cb = mei_cl_read_cb(cl, file); } if (cb->status) { @@ -420,11 +401,12 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, /** * mei_amthif_complete - complete amthif callback. * - * @dev: the device structure. + * @cl: host client * @cb: callback block. */ -void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) +void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) { + struct mei_device *dev = cl->dev; if (cb->fop_type == MEI_FOP_WRITE) { if (!cb->status) { @@ -436,7 +418,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) * in case of error enqueue the write cb to complete read list * so it can be propagated to the reader */ - list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); + list_add_tail(&cb->list, &cl->rd_completed); wake_up_interruptible(&dev->iamthif_cl.wait); return; } @@ -444,7 +426,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) if (!dev->iamthif_canceled) { dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; dev->iamthif_stall_timer = 0; - list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); + list_add_tail(&cb->list, &cl->rd_completed); dev_dbg(dev->dev, "amthif read completed\n"); } else { mei_amthif_run_next_cmd(dev); @@ -506,10 +488,11 @@ static bool mei_clear_list(struct mei_device *dev, static bool mei_clear_lists(struct mei_device *dev, const struct file *file) { bool removed = false; + struct mei_cl *cl = &dev->iamthif_cl; /* remove callbacks associated with a file */ mei_clear_list(dev, file, &dev->amthif_cmd_list.list); - if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list)) + if (mei_clear_list(dev, file, &cl->rd_completed)) removed = true; mei_clear_list(dev, file, &dev->ctrl_rd_list.list); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c57ac25ce8db..32991684563b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -536,7 +536,6 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); - mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); mei_cl_read_cb_flush(cl, fp); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 46a4302e5524..a1d978a82806 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -402,7 +402,6 @@ void mei_device_init(struct mei_device *dev, INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); - mei_io_list_init(&dev->amthif_rd_complete_list); bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); dev->open_handle_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 37c8f0e14387..72806ef6efdd 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -48,7 +48,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) dev_dbg(dev->dev, "completing call back.\n"); if (cl == &dev->iamthif_cl) - mei_amthif_complete(dev, cb); + mei_amthif_complete(cl, cb); else mei_cl_complete(cl, cb); } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 3acbbb49d08c..81901967911d 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -406,7 +406,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @allow_fixed_address: allow user space to connect a fixed client * * @amthif_cmd_list : amthif list for cmd waiting - * @amthif_rd_complete_list : amthif list for reading completed cmd data * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback @@ -496,7 +495,6 @@ struct mei_device { /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ - struct mei_cl_cb amthif_rd_complete_list; const struct file *iamthif_fp; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; @@ -589,15 +587,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev, int mei_amthif_release(struct mei_device *dev, struct file *file); -struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - const struct file *file); - int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); -void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); +void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list); -- cgit v1.2.3 From 77b007b1b50ef728be8252cbdf3f7e7e45b29bc5 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:27 +0200 Subject: mei: amthif: drop parameter validation from mei_amthif_write Remove duplicated parameter validation from mei_amthif_write functions, The parameter check is already performed by the caller function mei_write Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 058dd6917805..911b663a8fdb 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -285,15 +285,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) { - struct mei_device *dev; - - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - - if (WARN_ON(!cb)) - return -EINVAL; - - dev = cl->dev; + struct mei_device *dev = cl->dev; list_add_tail(&cb->list, &dev->amthif_cmd_list.list); -- cgit v1.2.3 From 4bddf56fc93c6ad7a451f15a69e3d89d49db0747 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:28 +0200 Subject: mei: amthif: use rx_wait queue also for amthif client Switch using cl->rx_wait wait queue also for amthif, there is nothing special about amthif in that matter in Rx flow. The cl->wait is reserved for hbm flows and asynchronous events Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 911b663a8fdb..b98eac389782 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -121,8 +121,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, /* unlock the Mutex */ mutex_unlock(&dev->device_lock); - wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, - !list_empty(&cl->rd_completed)); + wait_ret = wait_event_interruptible(cl->rx_wait, + !list_empty(&cl->rd_completed)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -316,7 +316,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, { unsigned int mask = 0; - poll_wait(file, &dev->iamthif_cl.wait, wait); + poll_wait(file, &dev->iamthif_cl.rx_wait, wait); if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && dev->iamthif_fp == file) { @@ -411,7 +411,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) * so it can be propagated to the reader */ list_add_tail(&cb->list, &cl->rd_completed); - wake_up_interruptible(&dev->iamthif_cl.wait); + wake_up_interruptible(&cl->rx_wait); return; } @@ -425,7 +425,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } dev_dbg(dev->dev, "completing amthif call back.\n"); - wake_up_interruptible(&dev->iamthif_cl.wait); + wake_up_interruptible(&cl->rx_wait); } /** -- cgit v1.2.3 From b74d883138911b110cf8f5b7b94790e16bb56795 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:29 +0200 Subject: mei: amthif: interrupt reader on link reset In case of link reset all waiting readers should be interrupted. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b98eac389782..ca2efc3602df 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -122,7 +122,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, mutex_unlock(&dev->device_lock); wait_ret = wait_event_interruptible(cl->rx_wait, - !list_empty(&cl->rd_completed)); + !list_empty(&cl->rd_completed) || + !mei_cl_is_connected(cl)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -130,6 +131,11 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; + if (!mei_cl_is_connected(cl)) { + rets = -EBUSY; + goto out; + } + cb = mei_cl_read_cb(cl, file); } -- cgit v1.2.3 From a1f9ae2bd264e3aed95aacd0102bd22a0422b8d1 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:30 +0200 Subject: mei: bus: fix RX event scheduling In this particular case this more correct and safer to check if the RX event is set in the event mask rather than query waitqueue_active Since the check is already performed in the mei_cl_bus_rx_event function, it is just required to check for its return value. Second, since we don't have exclusive waiter wake_up_interruptible_all is not used correctly here. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++---- drivers/misc/mei/client.c | 6 ++---- drivers/misc/mei/mei_dev.h | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0b05aa938799..a40d8e24ecda 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -252,23 +252,28 @@ void mei_cl_bus_notify_event(struct mei_cl *cl) } /** - * mei_cl_bus_rx_event - schedule rx evenet + * mei_cl_bus_rx_event - schedule rx event * * @cl: host client + * + * Return: true if event was scheduled + * false if the client is not waiting for event */ -void mei_cl_bus_rx_event(struct mei_cl *cl) +bool mei_cl_bus_rx_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; if (!cldev || !cldev->event_cb) - return; + return false; if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) - return; + return false; set_bit(MEI_CL_EVENT_RX, &cldev->events); schedule_work(&cldev->event_work); + + return true; } /** diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 32991684563b..574f7394fb2b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1735,10 +1735,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) case MEI_FOP_READ: list_add_tail(&cb->list, &cl->rd_completed); - if (waitqueue_active(&cl->rx_wait)) - wake_up_interruptible_all(&cl->rx_wait); - else - mei_cl_bus_rx_event(cl); + if (!mei_cl_bus_rx_event(cl)) + wake_up_interruptible(&cl->rx_wait); break; case MEI_FOP_CONNECT: diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 81901967911d..be97b8f2a883 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -316,7 +316,7 @@ void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); -void mei_cl_bus_rx_event(struct mei_cl *cl); +bool mei_cl_bus_rx_event(struct mei_cl *cl); void mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); -- cgit v1.2.3 From 850f8940a6a38563ff401073fd845414ab49c5ed Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:31 +0200 Subject: mei: bus: fix notification event delivery Call wake_up cl->ev_wait only in case there is no bus client registered to the event notification. Second, since we don't have exclusive waiter wake_up_interruptible_all is not used correctly here. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++---- drivers/misc/mei/client.c | 4 ++-- drivers/misc/mei/mei_dev.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index a40d8e24ecda..990f4f65c35e 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -230,25 +230,30 @@ static void mei_cl_bus_event_work(struct work_struct *work) * mei_cl_bus_notify_event - schedule notify cb on bus client * * @cl: host client + * + * Return: true if event was scheduled + * false if the client is not waiting for event */ -void mei_cl_bus_notify_event(struct mei_cl *cl) +bool mei_cl_bus_notify_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; if (!cldev || !cldev->event_cb) - return; + return false; if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) - return; + return false; if (!cl->notify_ev) - return; + return false; set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); schedule_work(&cldev->event_work); cl->notify_ev = false; + + return true; } /** diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 574f7394fb2b..3fd070a698ce 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1363,12 +1363,12 @@ void mei_cl_notify(struct mei_cl *cl) cl_dbg(dev, cl, "notify event"); cl->notify_ev = true; - wake_up_interruptible_all(&cl->ev_wait); + if (!mei_cl_bus_notify_event(cl)) + wake_up_interruptible(&cl->ev_wait); if (cl->ev_async) kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); - mei_cl_bus_notify_event(cl); } /** diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index be97b8f2a883..70c4da015401 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -317,7 +317,7 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); bool mei_cl_bus_rx_event(struct mei_cl *cl); -void mei_cl_bus_notify_event(struct mei_cl *cl); +bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -- cgit v1.2.3 From 15c13dfcad883a1e76b714480fb27be96247fd82 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:32 +0200 Subject: mei: bus: check if the device is enabled before data transfer The bus data transfer interface was missing the check if the device is in enabled state, this may lead to stack corruption during link reset. Cc: #4.0 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 990f4f65c35e..83b084558ee5 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -53,6 +53,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } + if (!mei_cl_is_connected(cl)) { rets = -ENODEV; goto out; @@ -109,6 +114,10 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } cb = mei_cl_read_cb(cl, NULL); if (cb) -- cgit v1.2.3 From e8466b336adc20bacb597670e0a89e695626e000 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:33 +0200 Subject: mei: drop superfluous closing bracket from write traces Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei-trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 86e5068837c1..7d2d5d4a1624 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -60,7 +60,7 @@ TRACE_EVENT(mei_reg_write, __entry->offs = offs; __entry->val = val; ), - TP_printk("[%s] write %s[%#x] = %#x)", + TP_printk("[%s] write %s[%#x] = %#x", __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); @@ -98,7 +98,7 @@ TRACE_EVENT(mei_pci_cfg_write, __entry->offs = offs; __entry->val = val; ), - TP_printk("[%s] pci cfg write %s[%#x] = %#x)", + TP_printk("[%s] pci cfg write %s[%#x] = %#x", __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); -- cgit v1.2.3 From 0faf6a3bbae74ca08c2ecc09e5f6d350ad54c1ab Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:34 +0200 Subject: mei: wake blocked write on link reset In case of link reset all blocked writes should be interrupted. Note, that currently blocking write is used only through bus layer. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3fd070a698ce..5ddc690752c2 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1691,7 +1691,8 @@ out: mutex_unlock(&dev->device_lock); rets = wait_event_interruptible(cl->tx_wait, - cl->writing_state == MEI_WRITE_COMPLETE); + cl->writing_state == MEI_WRITE_COMPLETE || + (!mei_cl_is_connected(cl))); mutex_lock(&dev->device_lock); /* wait_event_interruptible returns -ERESTARTSYS */ if (rets) { @@ -1699,6 +1700,10 @@ out: rets = -EINTR; goto err; } + if (cl->writing_state != MEI_WRITE_COMPLETE) { + rets = -EFAULT; + goto err; + } } rets = size; -- cgit v1.2.3 From a4307fe45aa9be03d5d7194b317a40b0d0558bee Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:35 +0200 Subject: mei: clean write queues and wake waiters on disconnect Clean write and write_waiting queues in disconnect. Requests in those queues are stale and processing will lead to fat warnings. In multi thread operations on disconnect and in FW disconnect case - write/read/event waiters should end wait and return error. Wake all waiters for disconnecting client to achieve that. Drop wake all and write queue clean on reset, as now we waking all waiters and cleaning write queues on disconnect. No need to do it twice. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 71 ++++++++++++++++++++--------------------------- drivers/misc/mei/client.h | 2 -- drivers/misc/mei/init.c | 5 ---- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 5ddc690752c2..3a9c656f1425 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -719,6 +719,33 @@ bool mei_hbuf_acquire(struct mei_device *dev) return true; } +/** + * mei_cl_wake_all - wake up readers, writers and event waiters so + * they can be interrupted + * + * @cl: host client + */ +static void mei_cl_wake_all(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + + /* synchronized under device mutex */ + if (waitqueue_active(&cl->rx_wait)) { + cl_dbg(dev, cl, "Waking up reading client!\n"); + wake_up_interruptible(&cl->rx_wait); + } + /* synchronized under device mutex */ + if (waitqueue_active(&cl->tx_wait)) { + cl_dbg(dev, cl, "Waking up writing client!\n"); + wake_up_interruptible(&cl->tx_wait); + } + /* synchronized under device mutex */ + if (waitqueue_active(&cl->ev_wait)) { + cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); + wake_up_interruptible(&cl->ev_wait); + } +} + /** * mei_cl_set_disconnected - set disconnected state and clear * associated states and resources @@ -734,8 +761,11 @@ void mei_cl_set_disconnected(struct mei_cl *cl) return; cl->state = MEI_FILE_DISCONNECTED; + mei_io_list_free(&dev->write_list, cl); + mei_io_list_free(&dev->write_waiting_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_cl_wake_all(cl); cl->mei_flow_ctrl_creds = 0; cl->timer_count = 0; @@ -1770,44 +1800,3 @@ void mei_cl_all_disconnect(struct mei_device *dev) list_for_each_entry(cl, &dev->file_list, link) mei_cl_set_disconnected(cl); } - - -/** - * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted - * - * @dev: mei device - */ -void mei_cl_all_wakeup(struct mei_device *dev) -{ - struct mei_cl *cl; - - list_for_each_entry(cl, &dev->file_list, link) { - if (waitqueue_active(&cl->rx_wait)) { - cl_dbg(dev, cl, "Waking up reading client!\n"); - wake_up_interruptible(&cl->rx_wait); - } - if (waitqueue_active(&cl->tx_wait)) { - cl_dbg(dev, cl, "Waking up writing client!\n"); - wake_up_interruptible(&cl->tx_wait); - } - - /* synchronized under device mutex */ - if (waitqueue_active(&cl->ev_wait)) { - cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); - wake_up_interruptible(&cl->ev_wait); - } - } -} - -/** - * mei_cl_all_write_clear - clear all pending writes - * - * @dev: mei device - */ -void mei_cl_all_write_clear(struct mei_device *dev) -{ - mei_io_list_free(&dev->write_list, NULL); - mei_io_list_free(&dev->write_waiting_list, NULL); -} - - diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index fdc43279368f..9925cf1a91ef 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -238,8 +238,6 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); void mei_cl_notify(struct mei_cl *cl); void mei_cl_all_disconnect(struct mei_device *dev); -void mei_cl_all_wakeup(struct mei_device *dev); -void mei_cl_all_write_clear(struct mei_device *dev); #define MEI_CL_FMT "cl:host=%02d me=%02d " #define MEI_CL_PRM(cl) (cl)->host_client_id, mei_cl_me_id(cl) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index a1d978a82806..cfcb46da13f1 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -148,13 +148,8 @@ int mei_reset(struct mei_device *dev) state != MEI_DEV_POWER_UP) { /* remove all waiting requests */ - mei_cl_all_write_clear(dev); - mei_cl_all_disconnect(dev); - /* wake up all readers and writers so they can be interrupted */ - mei_cl_all_wakeup(dev); - /* remove entry if already in list */ dev_dbg(dev->dev, "remove iamthif from the file list.\n"); mei_cl_unlink(&dev->iamthif_cl); -- cgit v1.2.3 From 603c53e42adf5f2d29ffdd1ff1edda0c27e400df Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:36 +0200 Subject: mei: discard replies from unconnected fixed address clients A fixed address client in the FW doesn't have a notion of connection and can send message after the file associated with it was already closed. Silently discard such messages. Add inline helpers to detect whether a message is hbm or intended for a fixed address client Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/interrupt.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 72806ef6efdd..06b744a503a3 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -239,6 +239,16 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } +static inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr) +{ + return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0; +} + +static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) +{ + return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0; +} + /** * mei_irq_read_handler - bottom half read routine after ISR to * handle the read processing. @@ -280,7 +290,7 @@ int mei_irq_read_handler(struct mei_device *dev, } /* HBM message */ - if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { + if (hdr_is_hbm(mei_hdr)) { ret = mei_hbm_dispatch(dev, mei_hdr); if (ret) { dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", @@ -300,6 +310,14 @@ int mei_irq_read_handler(struct mei_device *dev, /* if no recipient cl was found we assume corrupted header */ if (&cl->link == &dev->file_list) { + /* A message for not connected fixed address clients + * should be silently discarded + */ + if (hdr_is_fixed(mei_hdr)) { + mei_irq_discard_msg(dev, mei_hdr); + ret = 0; + goto reset_slots; + } dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; -- cgit v1.2.3 From 06ee536bcb15ca12868289467762130fa0a426f3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:37 +0200 Subject: mei: fill file pointer in read cb for fixed address client The read callback created from a flow control request for a fixed address client have NULL in the file pointer. Fill the file pointer using a data from a write callback. This allows us to drop workaround introduced in: commit eeabfcf5a92a ("mei: connection to fixed address clients from user-space") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 11 +- drivers/misc/mei/main.c | 5 - drivers/misc/mei/wd.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+), 10 deletions(-) create mode 100644 drivers/misc/mei/wd.c diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3a9c656f1425..a27ae2deecb4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1173,11 +1173,12 @@ err: /** * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. * - * @cl: private data of the file object + * @cl: host client + * @fp: the file pointer associated with the pointer * * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. */ -static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) +static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp) { int rets; @@ -1188,7 +1189,7 @@ static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) return 1; if (mei_cl_is_fixed_address(cl)) { - rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL); + rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp); if (rets && rets != -EBUSY) return rets; return 1; @@ -1568,7 +1569,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, first_chunk = cb->buf_idx == 0; - rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1; + rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1; if (rets < 0) return rets; @@ -1674,7 +1675,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.msg_complete = 0; mei_hdr.internal = cb->internal; - rets = mei_cl_flow_ctrl_creds(cl); + rets = mei_cl_flow_ctrl_creds(cl, cb->fp); if (rets < 0) goto err; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 03741c456ee4..f1fdd65e9678 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -209,11 +209,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, cb = mei_cl_read_cb(cl, file); if (!cb) { - if (mei_cl_is_fixed_address(cl) && dev->allow_fixed_address) { - cb = mei_cl_read_cb(cl, NULL); - if (cb) - goto copy_buffer; - } rets = 0; goto out; } diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c new file mode 100644 index 000000000000..7d9b4ee42f65 --- /dev/null +++ b/drivers/misc/mei/wd.c @@ -0,0 +1,391 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, 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 "mei_dev.h" +#include "hbm.h" +#include "client.h" + +static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; +static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; + +/* + * AMT Watchdog Device + */ +#define INTEL_AMT_WATCHDOG_ID "INTCAMT" + +/* UUIDs for AMT F/W clients */ +const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, + 0x9D, 0xA9, 0x15, 0x14, 0xCB, + 0x32, 0xAB); + +static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) +{ + dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); + memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); + memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); +} + +/** + * mei_wd_host_init - connect to the watchdog client + * + * @dev: the device structure + * @me_cl: me client + * + * Return: -ENOTTY if wd client cannot be found + * -EIO if write has failed + * 0 on success + */ +int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) +{ + struct mei_cl *cl = &dev->wd_cl; + int ret; + + mei_cl_init(cl, dev); + + dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; + dev->wd_state = MEI_WD_IDLE; + + ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); + if (ret < 0) { + dev_info(dev->dev, "wd: failed link client\n"); + return ret; + } + + ret = mei_cl_connect(cl, me_cl, NULL); + if (ret) { + dev_err(dev->dev, "wd: failed to connect = %d\n", ret); + mei_cl_unlink(cl); + return ret; + } + + ret = mei_watchdog_register(dev); + if (ret) { + mei_cl_disconnect(cl); + mei_cl_unlink(cl); + } + return ret; +} + +/** + * mei_wd_send - sends watch dog message to fw. + * + * @dev: the device structure + * + * Return: 0 if success, + * -EIO when message send fails + * -EINVAL when invalid message is to be sent + * -ENODEV on flow control failure + */ +int mei_wd_send(struct mei_device *dev) +{ + struct mei_cl *cl = &dev->wd_cl; + struct mei_msg_hdr hdr; + int ret; + + hdr.host_addr = cl->host_client_id; + hdr.me_addr = mei_cl_me_id(cl); + hdr.msg_complete = 1; + hdr.reserved = 0; + hdr.internal = 0; + + if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) + hdr.length = MEI_WD_START_MSG_SIZE; + else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) + hdr.length = MEI_WD_STOP_MSG_SIZE; + else { + dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); + return -EINVAL; + } + + ret = mei_write_message(dev, &hdr, dev->wd_data); + if (ret) { + dev_err(dev->dev, "wd: write message failed\n"); + return ret; + } + + ret = mei_cl_flow_ctrl_reduce(cl); + if (ret) { + dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); + return ret; + } + + return 0; +} + +/** + * mei_wd_stop - sends watchdog stop message to fw. + * + * @dev: the device structure + * + * Return: 0 if success + * on error: + * -EIO when message send fails + * -EINVAL when invalid message is to be sent + * -ETIME on message timeout + */ +int mei_wd_stop(struct mei_device *dev) +{ + struct mei_cl *cl = &dev->wd_cl; + int ret; + + if (!mei_cl_is_connected(cl) || + dev->wd_state != MEI_WD_RUNNING) + return 0; + + memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); + + dev->wd_state = MEI_WD_STOPPING; + + ret = mei_cl_flow_ctrl_creds(cl, NULL); + if (ret < 0) + goto err; + + if (ret && mei_hbuf_acquire(dev)) { + ret = mei_wd_send(dev); + if (ret) + goto err; + dev->wd_pending = false; + } else { + dev->wd_pending = true; + } + + mutex_unlock(&dev->device_lock); + + ret = wait_event_timeout(dev->wait_stop_wd, + dev->wd_state == MEI_WD_IDLE, + msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); + mutex_lock(&dev->device_lock); + if (dev->wd_state != MEI_WD_IDLE) { + /* timeout */ + ret = -ETIME; + dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); + goto err; + } + dev_dbg(dev->dev, "wd: stop completed after %u msec\n", + MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); + return 0; +err: + return ret; +} + +/** + * mei_wd_ops_start - wd start command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_start(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + struct mei_cl *cl; + int err = -ENODEV; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + cl = &dev->wd_cl; + + mutex_lock(&dev->device_lock); + + if (dev->dev_state != MEI_DEV_ENABLED) { + dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", + mei_dev_state_str(dev->dev_state)); + goto end_unlock; + } + + if (!mei_cl_is_connected(cl)) { + cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); + goto end_unlock; + } + + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + err = 0; +end_unlock: + mutex_unlock(&dev->device_lock); + return err; +} + +/** + * mei_wd_ops_stop - wd stop command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_stop(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + mutex_lock(&dev->device_lock); + mei_wd_stop(dev); + mutex_unlock(&dev->device_lock); + + return 0; +} + +/** + * mei_wd_ops_ping - wd ping command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_ping(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + struct mei_cl *cl; + int ret; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + cl = &dev->wd_cl; + + mutex_lock(&dev->device_lock); + + if (!mei_cl_is_connected(cl)) { + cl_err(dev, cl, "wd: not connected.\n"); + ret = -ENODEV; + goto end; + } + + dev->wd_state = MEI_WD_RUNNING; + + ret = mei_cl_flow_ctrl_creds(cl, NULL); + if (ret < 0) + goto end; + + /* Check if we can send the ping to HW*/ + if (ret && mei_hbuf_acquire(dev)) { + dev_dbg(dev->dev, "wd: sending ping\n"); + + ret = mei_wd_send(dev); + if (ret) + goto end; + dev->wd_pending = false; + } else { + dev->wd_pending = true; + } + +end: + mutex_unlock(&dev->device_lock); + return ret; +} + +/** + * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. + * + * @wd_dev: watchdog device struct + * @timeout: timeout value to set + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, + unsigned int timeout) +{ + struct mei_device *dev; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + /* Check Timeout value */ + if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) + return -EINVAL; + + mutex_lock(&dev->device_lock); + + dev->wd_timeout = timeout; + wd_dev->timeout = timeout; + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + mutex_unlock(&dev->device_lock); + + return 0; +} + +/* + * Watchdog Device structs + */ +static const struct watchdog_ops wd_ops = { + .owner = THIS_MODULE, + .start = mei_wd_ops_start, + .stop = mei_wd_ops_stop, + .ping = mei_wd_ops_ping, + .set_timeout = mei_wd_ops_set_timeout, +}; +static const struct watchdog_info wd_info = { + .identity = INTEL_AMT_WATCHDOG_ID, + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_ALARMONLY, +}; + +static struct watchdog_device amt_wd_dev = { + .info = &wd_info, + .ops = &wd_ops, + .timeout = MEI_WD_DEFAULT_TIMEOUT, + .min_timeout = MEI_WD_MIN_TIMEOUT, + .max_timeout = MEI_WD_MAX_TIMEOUT, +}; + + +int mei_watchdog_register(struct mei_device *dev) +{ + + int ret; + + amt_wd_dev.parent = dev->dev; + /* unlock to perserve correct locking order */ + mutex_unlock(&dev->device_lock); + ret = watchdog_register_device(&amt_wd_dev); + mutex_lock(&dev->device_lock); + if (ret) { + dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", + ret); + return ret; + } + + dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); + watchdog_set_drvdata(&amt_wd_dev, dev); + return 0; +} + +void mei_watchdog_unregister(struct mei_device *dev) +{ + if (watchdog_get_drvdata(&amt_wd_dev) == NULL) + return; + + watchdog_set_drvdata(&amt_wd_dev, NULL); + watchdog_unregister_device(&amt_wd_dev); +} + -- cgit v1.2.3 From f4e06246183f187d1327fdba18a797eb091a7d03 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:38 +0200 Subject: mei: fixed address clients for the new platforms Enable by default connection to fixed address clients from user-space for skylake and newer platform. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 31 +++++++++++++++++++++++++++++-- drivers/misc/mei/hbm.c | 4 ++++ drivers/misc/mei/hw.h | 6 ++++++ drivers/misc/mei/main.c | 18 ++++++++++++++---- drivers/misc/mei/mei_dev.h | 4 ++++ 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 6bdd75424fe8..8ad406315741 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -176,6 +176,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_dot_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", dev->hbm_f_ev_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n", + dev->hbm_f_fa_supported); } pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", @@ -191,6 +193,30 @@ static const struct file_operations mei_dbgfs_fops_devstate = { .llseek = generic_file_llseek, }; +static ssize_t mei_dbgfs_write_allow_fa(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mei_device *dev; + int ret; + + dev = container_of(file->private_data, + struct mei_device, allow_fixed_address); + + ret = debugfs_write_file_bool(file, user_buf, count, ppos); + if (ret < 0) + return ret; + dev->override_fixed_address = true; + return ret; +} + +static const struct file_operations mei_dbgfs_fops_allow_fa = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = mei_dbgfs_write_allow_fa, + .llseek = generic_file_llseek, +}; + /** * mei_dbgfs_deregister - Remove the debugfs files and directories * @@ -240,8 +266,9 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) dev_err(dev->dev, "devstate: registration failed\n"); goto err; } - f = debugfs_create_bool("allow_fixed_address", S_IRUSR | S_IWUSR, dir, - &dev->allow_fixed_address); + f = debugfs_create_file("allow_fixed_address", S_IRUSR | S_IWUSR, dir, + &dev->allow_fixed_address, + &mei_dbgfs_fops_allow_fa); if (!f) { dev_err(dev->dev, "allow_fixed_address: registration failed\n"); goto err; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index e7b7aad0999b..3915b8e8d0f1 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -979,6 +979,10 @@ static void mei_hbm_config_features(struct mei_device *dev) /* Notification Event Support */ if (dev->version.major_version >= HBM_MAJOR_VERSION_EV) dev->hbm_f_ev_supported = 1; + + /* Fixed Address Client Support */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_FA) + dev->hbm_f_fa_supported = 1; } /** diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 4c5d6cfd79b4..459ad596df66 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -64,6 +64,12 @@ #define HBM_MINOR_VERSION_EV 0 #define HBM_MAJOR_VERSION_EV 2 +/* + * MEI version with fixed address client support + */ +#define HBM_MINOR_VERSION_FA 0 +#define HBM_MAJOR_VERSION_FA 2 + /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f /* Host bus message command RESPONSE */ diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index f1fdd65e9678..58fa2c83ee39 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -369,12 +369,22 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (!me_cl || - (me_cl->props.fixed_address && !dev->allow_fixed_address)) { + if (!me_cl) { dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); - mei_me_cl_put(me_cl); - return -ENOTTY; + rets = -ENOTTY; + goto end; + } + + if (me_cl->props.fixed_address) { + bool forbidden = dev->override_fixed_address ? + !dev->allow_fixed_address : !dev->hbm_f_fa_supported; + if (forbidden) { + dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", + &data->in_client_uuid); + rets = -ENOTTY; + goto end; + } } dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 70c4da015401..6d97f3335e22 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -396,6 +396,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_dc_supported : hbm feature dynamic clients * @hbm_f_dot_supported : hbm feature disconnect on timeout * @hbm_f_ev_supported : hbm feature event notification + * @hbm_f_fa_supported : hbm feature fixed address client * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -404,6 +405,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @me_client_index : last FW client index in enumeration * * @allow_fixed_address: allow user space to connect a fixed client + * @override_fixed_address: force allow fixed address behavior * * @amthif_cmd_list : amthif list for cmd waiting * @iamthif_fp : file for current amthif operation @@ -483,6 +485,7 @@ struct mei_device { unsigned int hbm_f_dc_supported:1; unsigned int hbm_f_dot_supported:1; unsigned int hbm_f_ev_supported:1; + unsigned int hbm_f_fa_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; @@ -491,6 +494,7 @@ struct mei_device { unsigned long me_client_index; bool allow_fixed_address; + bool override_fixed_address; /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; -- cgit v1.2.3 From 6938c1923f8ac6eb6d1c99b924d3e377201143ed Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:39 +0200 Subject: mei: hbm: warn about fw-initiated disconnect The FW can initiate client disconnection only because an error condition, hence it make sense to bump the debug message to the warning level to have an entery in the log. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.h | 3 +++ drivers/misc/mei/hbm.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 9925cf1a91ef..be6929620d61 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -245,6 +245,9 @@ void mei_cl_all_disconnect(struct mei_device *dev); #define cl_dbg(dev, cl, format, arg...) \ dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) +#define cl_warn(dev, cl, format, arg...) \ + dev_warn((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + #define cl_err(dev, cl, format, arg...) \ dev_err((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 3915b8e8d0f1..a2f32b0eb649 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -866,7 +866,7 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req); if (cl) { - cl_dbg(dev, cl, "fw disconnect request received\n"); + cl_warn(dev, cl, "fw disconnect request received\n"); cl->state = MEI_FILE_DISCONNECTING; cl->timer_count = 0; -- cgit v1.2.3 From 7851e008703e2f7073802e560293213e93dcdde6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:40 +0200 Subject: mei: drop reserved host client ids The reserved host clients can be obsoleted now, a portion of the platforms is shipped without iAMT enabled, where the reservation is not relevant and for platforms with iAMT dynamic allocation is sufficient. Dropping reserved ids makes enumeration more flexible and generic Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- drivers/misc/mei/bus-fixup.c | 2 +- drivers/misc/mei/bus.c | 2 +- drivers/misc/mei/client.c | 15 +++++---------- drivers/misc/mei/client.h | 4 ++-- drivers/misc/mei/main.c | 2 +- drivers/misc/mei/mei_dev.h | 8 -------- 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index ca2efc3602df..de194ef573ee 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -71,7 +71,7 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) mei_cl_init(cl, dev); - ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); + ret = mei_cl_link(cl); if (ret < 0) { dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); return ret; diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index b87323f4bb14..e9e6ea3ab73c 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -239,7 +239,7 @@ static void mei_nfc(struct mei_cl_device *cldev) mutex_lock(&bus->device_lock); /* we need to connect to INFO GUID */ - cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(bus); if (IS_ERR(cl)) { ret = PTR_ERR(cl); cl = NULL; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 83b084558ee5..bc18e5519da6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -417,7 +417,7 @@ int mei_cldev_enable(struct mei_cl_device *cldev) if (!cl) { mutex_lock(&bus->device_lock); - cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(bus); mutex_unlock(&bus->device_lock); if (IS_ERR(cl)) return PTR_ERR(cl); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a27ae2deecb4..2890669b81f9 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -587,27 +587,23 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) * mei_cl_link - allocate host id in the host map * * @cl: host client - * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one * * Return: 0 on success * -EINVAL on incorrect values * -EMFILE if open count exceeded. */ -int mei_cl_link(struct mei_cl *cl, int id) +int mei_cl_link(struct mei_cl *cl) { struct mei_device *dev; long open_handle_count; + int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - /* If Id is not assigned get one*/ - if (id == MEI_HOST_CLIENT_ID_ANY) - id = find_first_zero_bit(dev->host_clients_map, - MEI_CLIENTS_MAX); - + id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); if (id >= MEI_CLIENTS_MAX) { dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); return -EMFILE; @@ -1143,11 +1139,10 @@ nortpm: * mei_cl_alloc_linked - allocate and link host client * * @dev: the device structure - * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one * * Return: cl on success ERR_PTR on failure */ -struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) { struct mei_cl *cl; int ret; @@ -1158,7 +1153,7 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) goto err; } - ret = mei_cl_link(cl, id); + ret = mei_cl_link(cl); if (ret) goto err; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index be6929620d61..a912ea686d97 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -107,10 +107,10 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev); void mei_cl_init(struct mei_cl *cl, struct mei_device *dev); -int mei_cl_link(struct mei_cl *cl, int id); +int mei_cl_link(struct mei_cl *cl); int mei_cl_unlink(struct mei_cl *cl); -struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id); +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev); struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 58fa2c83ee39..17970163eacc 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -65,7 +65,7 @@ static int mei_open(struct inode *inode, struct file *file) goto err_unlock; } - cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(dev); if (IS_ERR(cl)) { err = PTR_ERR(cl); goto err_unlock; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 6d97f3335e22..320ddae5ee1e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -53,14 +53,6 @@ extern const uuid_le mei_amthif_guid; */ #define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 1) -/* - * Internal Clients Number - */ -#define MEI_HOST_CLIENT_ID_ANY (-1) -#define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */ -#define MEI_IAMTHIF_HOST_CLIENT_ID 2 - - /* File state */ enum file_state { MEI_FILE_INITIALIZING = 0, -- cgit v1.2.3 From a816a00ece63d16ade7e9c0ca8b5a7e4c5ea2453 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:41 +0200 Subject: mei: bus: run rescan on me_clients list change Since clients can be now added and removed during runtime we need to run bus rescan whenever me_clients list is modified. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 8 ++++++++ drivers/misc/mei/hbm.c | 8 +++++++- drivers/misc/mei/init.c | 2 ++ drivers/misc/mei/mei_dev.h | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index bc18e5519da6..951d32265040 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -977,6 +977,14 @@ void mei_cl_bus_rescan(struct mei_device *bus) dev_dbg(bus->dev, "rescan end"); } +void mei_cl_bus_rescan_work(struct work_struct *work) +{ + struct mei_device *bus = + container_of(work, struct mei_device, bus_rescan_work); + + mei_cl_bus_rescan(bus); +} + int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, struct module *owner) { diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index a2f32b0eb649..0c9310ad6136 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -401,6 +401,9 @@ static int mei_hbm_fw_add_cl_req(struct mei_device *dev, if (ret) status = !MEI_HBMS_SUCCESS; + if (dev->dev_state == MEI_DEV_ENABLED) + schedule_work(&dev->bus_rescan_work); + return mei_hbm_add_cl_resp(dev, req->me_addr, status); } @@ -789,8 +792,11 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl, cl->state = MEI_FILE_CONNECTED; else { cl->state = MEI_FILE_DISCONNECT_REPLY; - if (rs->status == MEI_CL_CONN_NOT_FOUND) + if (rs->status == MEI_CL_CONN_NOT_FOUND) { mei_me_cl_del(dev, cl->me_cl); + if (dev->dev_state == MEI_DEV_ENABLED) + schedule_work(&dev->bus_rescan_work); + } } cl->status = mei_cl_conn_status_to_errno(rs->status); } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index cfcb46da13f1..52fde2b498ef 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -93,6 +93,7 @@ void mei_cancel_work(struct mei_device *dev) { cancel_work_sync(&dev->init_work); cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->bus_rescan_work); cancel_delayed_work(&dev->timer_work); } @@ -394,6 +395,7 @@ void mei_device_init(struct mei_device *dev, INIT_DELAYED_WORK(&dev->timer_work, mei_timer); INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); + INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 320ddae5ee1e..c31adb8a0cff 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -304,6 +304,7 @@ struct mei_hw_ops { /* MEI bus API*/ void mei_cl_bus_rescan(struct mei_device *bus); +void mei_cl_bus_rescan_work(struct work_struct *work); void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); @@ -410,6 +411,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @init_work : work item for the device init * @reset_work : work item for the device reset + * @bus_rescan_work : work item for the bus rescan * * @device_list : mei client bus list * @cl_bus_lock : client bus list lock @@ -501,6 +503,7 @@ struct mei_device { struct work_struct init_work; struct work_struct reset_work; + struct work_struct bus_rescan_work; /* List of bus devices */ struct list_head device_list; -- cgit v1.2.3 From 27f476ea98ed495839662db4e3a76357bbeb1bb3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:42 +0200 Subject: mei: hbm: send immediate reply flag in enum request Signal the FW that it can send an HBM enumeration answer immediately, without waiting for FW initialization completion, meaning before all the FW clients are ready and registered. Organize enumeration response options to enum as a byproduct. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 2 ++ drivers/misc/mei/hbm.c | 8 +++++++- drivers/misc/mei/hw.h | 25 +++++++++++++++++++++---- drivers/misc/mei/mei_dev.h | 2 ++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 8ad406315741..c6c051b52f55 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -172,6 +172,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_pg_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", dev->hbm_f_dc_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tIE: %01d\n", + dev->hbm_f_ie_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", dev->hbm_f_dot_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 0c9310ad6136..d2798d5b0a9d 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -301,7 +301,10 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data; memset(enum_req, 0, len); enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; - enum_req->allow_add = dev->hbm_f_dc_supported; + enum_req->flags |= dev->hbm_f_dc_supported ? + MEI_HBM_ENUM_F_ALLOW_ADD : 0; + enum_req->flags |= dev->hbm_f_ie_supported ? + MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { @@ -978,6 +981,9 @@ static void mei_hbm_config_features(struct mei_device *dev) if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) dev->hbm_f_dc_supported = 1; + if (dev->version.major_version >= HBM_MAJOR_VERSION_IE) + dev->hbm_f_ie_supported = 1; + /* disconnect on connect timeout instead of link reset */ if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) dev->hbm_f_dot_supported = 1; diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 459ad596df66..9daf3f9aed25 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -52,6 +52,12 @@ #define HBM_MINOR_VERSION_DC 0 #define HBM_MAJOR_VERSION_DC 2 +/* + * MEI version with immediate reply to enum request support + */ +#define HBM_MINOR_VERSION_IE 0 +#define HBM_MAJOR_VERSION_IE 2 + /* * MEI version with disconnect on connection timeout support */ @@ -246,15 +252,26 @@ struct hbm_me_stop_request { } __packed; /** - * struct hbm_host_enum_request - enumeration request from host to fw + * enum hbm_host_enum_flags - enumeration request flags (HBM version >= 2.0) * - * @hbm_cmd: bus message command header - * @allow_add: allow dynamic clients add HBM version >= 2.0 + * @MEI_HBM_ENUM_F_ALLOW_ADD: allow dynamic clients add + * @MEI_HBM_ENUM_F_IMMEDIATE_ENUM: allow FW to send answer immediately + */ +enum hbm_host_enum_flags { + MEI_HBM_ENUM_F_ALLOW_ADD = BIT(0), + MEI_HBM_ENUM_F_IMMEDIATE_ENUM = BIT(1), +}; + +/** + * struct hbm_host_enum_request - enumeration request from host to fw + * + * @hbm_cmd : bus message command header + * @flags : request flags * @reserved: reserved */ struct hbm_host_enum_request { u8 hbm_cmd; - u8 allow_add; + u8 flags; u8 reserved[2]; } __packed; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index c31adb8a0cff..2b9160e506d7 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -390,6 +390,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_dot_supported : hbm feature disconnect on timeout * @hbm_f_ev_supported : hbm feature event notification * @hbm_f_fa_supported : hbm feature fixed address client + * @hbm_f_ie_supported : hbm feature immediate reply to enum request * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -480,6 +481,7 @@ struct mei_device { unsigned int hbm_f_dot_supported:1; unsigned int hbm_f_ev_supported:1; unsigned int hbm_f_fa_supported:1; + unsigned int hbm_f_ie_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; -- cgit v1.2.3 From 025fb792bac33632c19fe12265ba1f6108921300 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:43 +0200 Subject: mei: split amthif client init from end of clients enumeration The amthif FW client can appear after the end of client enumeration. Amthif host client initialization is done now at FW client discovery time. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 5 +++-- drivers/misc/mei/bus.c | 8 ++++++++ drivers/misc/mei/client.c | 17 ++--------------- drivers/misc/mei/client.h | 2 +- drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/init.c | 2 -- drivers/misc/mei/mei_dev.h | 2 -- 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index de194ef573ee..04525ada9eda 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -67,6 +67,9 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) struct mei_cl *cl = &dev->iamthif_cl; int ret; + if (mei_cl_is_connected(cl)) + return 0; + dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_cl_init(cl, dev); @@ -79,8 +82,6 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) ret = mei_cl_connect(cl, me_cl, NULL); - dev->iamthif_state = MEI_IAMTHIF_IDLE; - return ret; } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 951d32265040..f4cf43b47c7a 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -981,6 +981,14 @@ void mei_cl_bus_rescan_work(struct work_struct *work) { struct mei_device *bus = container_of(work, struct mei_device, bus_rescan_work); + struct mei_me_client *me_cl; + + mutex_lock(&bus->device_lock); + me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); + if (me_cl) + mei_amthif_host_init(bus, me_cl); + mei_me_cl_put(me_cl); + mutex_unlock(&bus->device_lock); mei_cl_bus_rescan(bus); } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2890669b81f9..af6816bc268f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -666,25 +666,12 @@ int mei_cl_unlink(struct mei_cl *cl) return 0; } - -void mei_host_client_init(struct work_struct *work) +void mei_host_client_init(struct mei_device *dev) { - struct mei_device *dev = - container_of(work, struct mei_device, init_work); - struct mei_me_client *me_cl; - - mutex_lock(&dev->device_lock); - - me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); - if (me_cl) - mei_amthif_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; - mutex_unlock(&dev->device_lock); - mei_cl_bus_rescan(dev); + schedule_work(&dev->bus_rescan_work); pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index a912ea686d97..0d7a3a1fef78 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -226,7 +226,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); -void mei_host_client_init(struct work_struct *work); +void mei_host_client_init(struct mei_device *dev); u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop); enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index d2798d5b0a9d..5e305d2605f3 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -549,7 +549,7 @@ static int mei_hbm_prop_req(struct mei_device *dev) /* We got all client properties */ if (next_client_index == MEI_CLIENTS_MAX) { dev->hbm_state = MEI_HBM_STARTED; - schedule_work(&dev->init_work); + mei_host_client_init(dev); return 0; } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 52fde2b498ef..f7c8dfdb6a12 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -91,7 +91,6 @@ EXPORT_SYMBOL_GPL(mei_fw_status2str); */ void mei_cancel_work(struct mei_device *dev) { - cancel_work_sync(&dev->init_work); cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->bus_rescan_work); @@ -393,7 +392,6 @@ void mei_device_init(struct mei_device *dev, mei_io_list_init(&dev->ctrl_rd_list); INIT_DELAYED_WORK(&dev->timer_work, mei_timer); - INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 2b9160e506d7..db78e6d99456 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -410,7 +410,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_state : amthif processor state * @iamthif_canceled : current amthif command is canceled * - * @init_work : work item for the device init * @reset_work : work item for the device reset * @bus_rescan_work : work item for the bus rescan * @@ -503,7 +502,6 @@ struct mei_device { enum iamthif_states iamthif_state; bool iamthif_canceled; - struct work_struct init_work; struct work_struct reset_work; struct work_struct bus_rescan_work; -- cgit v1.2.3 From 8e0bd4925bf693520295de403483efad4dc5cc16 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 8 Feb 2016 11:03:58 +1100 Subject: xfs: fix endianness error when checking log block crc on big endian platforms Since the checksum function and the field are both __le32, don't perform endian conversion when comparing the two. This fixes mount failures on ppc64. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Dave Chinner --- fs/xfs/xfs_log_recover.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index da37beb76f6e..594f7e63b432 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -4491,7 +4491,7 @@ xlog_recover_process( * know precisely what failed. */ if (pass == XLOG_RECOVER_CRCPASS) { - if (rhead->h_crc && crc != le32_to_cpu(rhead->h_crc)) + if (rhead->h_crc && crc != rhead->h_crc) return -EFSBADCRC; return 0; } @@ -4502,7 +4502,7 @@ xlog_recover_process( * zero CRC check prevents warnings from being emitted when upgrading * the kernel from one that does not add CRCs by default. */ - if (crc != le32_to_cpu(rhead->h_crc)) { + if (crc != rhead->h_crc) { if (rhead->h_crc || xfs_sb_version_hascrc(&log->l_mp->m_sb)) { xfs_alert(log->l_mp, "log record CRC mismatch: found 0x%x, expected 0x%x.", -- cgit v1.2.3 From c8053b58762745d93930826b60a4073854a15ce5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Feb 2016 18:22:54 -0800 Subject: Revert "8250: uniphier: allow modular build with 8250 console" This reverts commit d56edd7ed0ed46a8043ee3040ededbd190818ccf, it shouldn't have been applied, it was fixed properly with commit 71f50c6d9a2276f3ec85384bffe2aee1962f4669 ("of: drop symbols declared by _OF_DECLARE() from modules") Reported-by: Masahiro Yamada Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 1b7bd26555b7..bab6b3ae2540 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -35,7 +35,7 @@ struct uniphier8250_priv { spinlock_t atomic_write_lock; }; -#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE) +#ifdef CONFIG_SERIAL_8250_CONSOLE static int __init uniphier_early_console_setup(struct earlycon_device *device, const char *options) { -- cgit v1.2.3 From b0e302b40873c0418aacaad2aa455bf867c9d1a2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 1 Feb 2016 11:33:00 +0100 Subject: staging: goldfish: use div64_s64 instead of do_div The goldfish nand driver divides a signed 64-bit number (loff_t) in multiple places using the do_div() function. This has always been unreliable but now produces a compiler warning (since 4.5-rc1): goldfish/goldfish_nand.c: In function 'goldfish_nand_erase': goldfish/goldfish_nand.c:107:91: error: comparison of distinct pointer types lacks a cast [-Werror] goldfish/goldfish_nand.c: In function 'goldfish_nand_read_oob': goldfish/goldfish_nand.c:145:91: error: comparison of distinct pointer types lacks a cast [-Werror] This changes the code to the equivalent div_s64{,_rem} that works correctly for negative numbers (which we should never get here). The warning has shown up on ARM allmodconfig builds after the goldfish bus driver has become visible on ARM. Signed-off-by: Arnd Bergmann Fixes: bd2f348db503 ("goldfish: refactor goldfish platform configs") Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/staging/goldfish/goldfish_nand.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c index f223b3a5a428..76d60eed1490 100644 --- a/drivers/staging/goldfish/goldfish_nand.c +++ b/drivers/staging/goldfish/goldfish_nand.c @@ -100,11 +100,11 @@ static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr) { loff_t ofs = instr->addr; u32 len = instr->len; - u32 rem; + s32 rem; if (ofs + len > mtd->size) goto invalid_arg; - rem = do_div(ofs, mtd->writesize); + ofs = div_s64_rem(ofs, mtd->writesize, &rem); if (rem) goto invalid_arg; ofs *= (mtd->writesize + mtd->oobsize); @@ -133,7 +133,7 @@ invalid_arg: static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs, struct mtd_oob_ops *ops) { - u32 rem; + s32 rem; if (ofs + ops->len > mtd->size) goto invalid_arg; @@ -142,7 +142,7 @@ static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs, if (ops->ooblen + ops->ooboffs > mtd->oobsize) goto invalid_arg; - rem = do_div(ofs, mtd->writesize); + ofs = div_s64_rem(ofs, mtd->writesize, &rem); if (rem) goto invalid_arg; ofs *= (mtd->writesize + mtd->oobsize); @@ -165,7 +165,7 @@ invalid_arg: static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs, struct mtd_oob_ops *ops) { - u32 rem; + s32 rem; if (ofs + ops->len > mtd->size) goto invalid_arg; @@ -174,7 +174,7 @@ static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs, if (ops->ooblen + ops->ooboffs > mtd->oobsize) goto invalid_arg; - rem = do_div(ofs, mtd->writesize); + ofs = div_s64_rem(ofs, mtd->writesize, &rem); if (rem) goto invalid_arg; ofs *= (mtd->writesize + mtd->oobsize); @@ -197,12 +197,12 @@ invalid_arg: static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - u32 rem; + s32 rem; if (from + len > mtd->size) goto invalid_arg; - rem = do_div(from, mtd->writesize); + from = div_s64_rem(from, mtd->writesize, &rem); if (rem) goto invalid_arg; from *= (mtd->writesize + mtd->oobsize); @@ -219,12 +219,12 @@ invalid_arg: static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - u32 rem; + s32 rem; if (to + len > mtd->size) goto invalid_arg; - rem = do_div(to, mtd->writesize); + to = div_s64_rem(to, mtd->writesize, &rem); if (rem) goto invalid_arg; to *= (mtd->writesize + mtd->oobsize); @@ -240,12 +240,12 @@ invalid_arg: static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) { - u32 rem; + s32 rem; if (ofs >= mtd->size) goto invalid_arg; - rem = do_div(ofs, mtd->erasesize); + ofs = div_s64_rem(ofs, mtd->writesize, &rem); if (rem) goto invalid_arg; ofs *= mtd->erasesize / mtd->writesize; @@ -261,12 +261,12 @@ invalid_arg: static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - u32 rem; + s32 rem; if (ofs >= mtd->size) goto invalid_arg; - rem = do_div(ofs, mtd->erasesize); + ofs = div_s64_rem(ofs, mtd->writesize, &rem); if (rem) goto invalid_arg; ofs *= mtd->erasesize / mtd->writesize; @@ -321,7 +321,7 @@ static int goldfish_nand_init_device(struct platform_device *pdev, mtd->oobavail = mtd->oobsize; mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) / (mtd->writesize + mtd->oobsize) * mtd->writesize; - do_div(mtd->size, mtd->writesize + mtd->oobsize); + mtd->size = div_s64(mtd->size, mtd->writesize + mtd->oobsize); mtd->size *= mtd->writesize; dev_dbg(&pdev->dev, "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n", -- cgit v1.2.3 From 2ce601b033376905d585e0825d5499616a22b565 Mon Sep 17 00:00:00 2001 From: Christian Colic Date: Thu, 19 Nov 2015 09:38:21 +0100 Subject: staging: goldfish: (coding style) Add spaces around the "+" to conform to checkpatch Added a space around the "+" at: "reg_base+addr2" to clear up a checkpatch check. Signed-off-by: Christian Colic Signed-off-by: Greg Kroah-Hartman --- drivers/staging/goldfish/goldfish_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c index b0927e49d0a8..f1e18386e34d 100644 --- a/drivers/staging/goldfish/goldfish_audio.c +++ b/drivers/staging/goldfish/goldfish_audio.c @@ -63,7 +63,7 @@ struct goldfish_audio { #define AUDIO_READ(data, addr) (readl(data->reg_base + addr)) #define AUDIO_WRITE(data, addr, x) (writel(x, data->reg_base + addr)) #define AUDIO_WRITE64(data, addr, addr2, x) \ - (gf_write_dma_addr((x), data->reg_base + addr, data->reg_base+addr2)) + (gf_write_dma_addr((x), data->reg_base + addr, data->reg_base + addr2)) /* * temporary variable used between goldfish_audio_probe() and -- cgit v1.2.3 From 269ad6e018146dafb70c9a40e546f73752d44db2 Mon Sep 17 00:00:00 2001 From: Christian Colic Date: Thu, 19 Nov 2015 09:47:24 +0100 Subject: staging: goldfish: (coding style) Rewrite comparisons to NULL as "!data->reg_base" Rewrite comparisons to NULL "data->reg_base == NULL" as "!data->reg_base" to conform to checkpatch. Signed-off-by: Christian Colic Signed-off-by: Greg Kroah-Hartman --- drivers/staging/goldfish/goldfish_audio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c index f1e18386e34d..364fdcdd3a06 100644 --- a/drivers/staging/goldfish/goldfish_audio.c +++ b/drivers/staging/goldfish/goldfish_audio.c @@ -280,12 +280,12 @@ static int goldfish_audio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { + if (!r) { dev_err(&pdev->dev, "platform_get_resource failed\n"); return -ENODEV; } data->reg_base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); - if (data->reg_base == NULL) + if (!data->reg_base) return -ENOMEM; data->irq = platform_get_irq(pdev, 0); @@ -295,7 +295,7 @@ static int goldfish_audio_probe(struct platform_device *pdev) } data->buffer_virt = dmam_alloc_coherent(&pdev->dev, COMBINED_BUFFER_SIZE, &buf_addr, GFP_KERNEL); - if (data->buffer_virt == NULL) { + if (!data->buffer_virt) { dev_err(&pdev->dev, "allocate buffer failed\n"); return -ENOMEM; } -- cgit v1.2.3 From 7b64dbf849abdd7e769820e25120758f956a7f13 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 19 Oct 2015 14:19:01 +0300 Subject: misc: mic/scif: fix wrap around tests Signed integer overflow is undefined. Also I added a check for "(offset < 0)" in scif_unregister() because that makes it match the other conditions and because I didn't want to subtract a negative. Fixes: ba612aa8b487 ('misc: mic: SCIF memory registration and unregistration') Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_rma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 8310b4dbff06..6a451bd65bf3 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -1511,7 +1511,7 @@ off_t scif_register_pinned_pages(scif_epd_t epd, if ((map_flags & SCIF_MAP_FIXED) && ((ALIGN(offset, PAGE_SIZE) != offset) || (offset < 0) || - (offset + (off_t)len < offset))) + (len > LONG_MAX - offset))) return -EINVAL; might_sleep(); @@ -1614,7 +1614,7 @@ off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset, if ((map_flags & SCIF_MAP_FIXED) && ((ALIGN(offset, PAGE_SIZE) != offset) || (offset < 0) || - (offset + (off_t)len < offset))) + (len > LONG_MAX - offset))) return -EINVAL; /* Unsupported protection requested */ @@ -1732,7 +1732,8 @@ scif_unregister(scif_epd_t epd, off_t offset, size_t len) /* Offset is not page aligned or offset+len wraps around */ if ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset + (off_t)len < offset)) + (offset < 0) || + (len > LONG_MAX - offset)) return -EINVAL; err = scif_verify_epd(ep); -- cgit v1.2.3 From 0d0ce9c00b866090a69a2d63118b3e09cc71c96d Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:31 +0800 Subject: misc: mic/scif: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 95a13c629a8e..8804bccf04b6 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -74,11 +74,6 @@ struct scif_copy_work { bool ordered; }; -#ifndef list_entry_next -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) -#endif - /** * scif_reserve_dma_chan: * @ep: Endpoint Descriptor. @@ -851,7 +846,7 @@ static void scif_rma_local_cpu_copy(s64 offset, struct scif_window *window, (window->nr_pages << PAGE_SHIFT); while (rem_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -957,7 +952,7 @@ scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, remaining_len -= tail_len; while (remaining_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -1064,7 +1059,7 @@ scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, } if (tail_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -1147,13 +1142,13 @@ static int _scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, (dst_window->nr_pages << PAGE_SHIFT); while (remaining_len) { if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); end_src_offset = src_window->offset + (src_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(dst_window, &dst_win_iter); @@ -1314,13 +1309,13 @@ static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, remaining_len -= tail_len; while (remaining_len) { if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); end_src_offset = src_window->offset + (src_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(dst_window, &dst_win_iter); @@ -1405,9 +1400,9 @@ static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, if (remaining_len) { loop_len = remaining_len; if (src_offset == end_src_offset) - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); if (dst_offset == end_dst_offset) - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); @@ -1550,12 +1545,12 @@ static int scif_rma_list_cpu_copy(struct scif_copy_work *work) end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); scif_init_window_iter(dst_window, &dst_win_iter); } -- cgit v1.2.3 From d40a09464401265e2894061cca388fb86e05b4be Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 11 Dec 2015 20:09:16 -0600 Subject: misc: mic: fix incorrect use of error codes in SCIF DMA driver The error code passed to ERR_PTR() always should be negated. Also, the return value of scif_add_mmu_notifier() was never checked. Signed-off-by: Eric Biggers Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 8804bccf04b6..cd01a0efda6b 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -271,13 +271,10 @@ static struct scif_mmu_notif * scif_find_mmu_notifier(struct mm_struct *mm, struct scif_endpt_rma_info *rma) { struct scif_mmu_notif *mmn; - struct list_head *item; - list_for_each(item, &rma->mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); + list_for_each_entry(mmn, &rma->mmn_list, list) if (mmn->mm == mm) return mmn; - } return NULL; } @@ -288,13 +285,12 @@ scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) = kzalloc(sizeof(*mmn), GFP_KERNEL); if (!mmn) - return ERR_PTR(ENOMEM); + return ERR_PTR(-ENOMEM); scif_init_mmu_notifier(mmn, current->mm, ep); - if (mmu_notifier_register(&mmn->ep_mmu_notifier, - current->mm)) { + if (mmu_notifier_register(&mmn->ep_mmu_notifier, current->mm)) { kfree(mmn); - return ERR_PTR(EBUSY); + return ERR_PTR(-EBUSY); } list_add(&mmn->list, &ep->rma_info.mmn_list); return mmn; @@ -1725,7 +1721,7 @@ static int scif_rma_copy(scif_epd_t epd, off_t loffset, unsigned long addr, mutex_lock(&ep->rma_info.mmn_lock); mmn = scif_find_mmu_notifier(current->mm, &ep->rma_info); if (!mmn) - scif_add_mmu_notifier(current->mm, ep); + mmn = scif_add_mmu_notifier(current->mm, ep); mutex_unlock(&ep->rma_info.mmn_lock); if (IS_ERR(mmn)) { scif_put_peer_dev(spdev); -- cgit v1.2.3 From 5fae054c2b5a7c10475d2e425dfea0c567c1c5dd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 2 Feb 2016 12:48:09 +0300 Subject: goldfish: locking bugs in goldfish_pipe_read_write() We recently messed up the error handling here. We can return with the pipe->lock held or sometimes we unlock twice by mistake. Fixes: 2f3be88237a3 ('goldfish_pipe: Pin pages to memory while copying and other cleanups') Signed-off-by: Dan Carpenter Reviewed-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index e3fab9a1d9f7..839df4aace76 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -313,7 +313,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, !is_write, 0, &page, NULL); up_read(¤t->mm->mmap_sem); if (ret < 0) - return ret; + break; if (dev->version) { /* Device version 1 or newer (qemu-android) expects the @@ -400,22 +400,16 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, while (test_bit(wakeBit, &pipe->flags)) { if (wait_event_interruptible( pipe->wake_queue, - !test_bit(wakeBit, &pipe->flags))) { - ret = -ERESTARTSYS; - break; - } - - if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) { - ret = -EIO; - break; - } + !test_bit(wakeBit, &pipe->flags))) + return -ERESTARTSYS; + + if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) + return -EIO; } /* Try to re-acquire the lock */ - if (mutex_lock_interruptible(&pipe->lock)) { - ret = -ERESTARTSYS; - break; - } + if (mutex_lock_interruptible(&pipe->lock)) + return -ERESTARTSYS; } mutex_unlock(&pipe->lock); -- cgit v1.2.3 From 1b807e1011af46a595ba46c75ad5e20ad7177af7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 21 Dec 2015 15:12:20 -0800 Subject: Drivers: hv: vmbus: Cleanup vmbus_set_event() Cleanup vmbus_set_event() by inlining the hypercall to post the event and since the return value of vmbus_set_event() is not checked, make it void. As part of this cleanup, get rid of the function hv_signal_event() as it is only callled from vmbus_set_event(). Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 4 ++-- drivers/hv/hv.c | 16 ---------------- drivers/hv/hyperv_vmbus.h | 4 +--- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 3dc5a9c7fad6..4a320e60641a 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -474,7 +474,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) /* * vmbus_set_event - Send an event notification to the parent */ -int vmbus_set_event(struct vmbus_channel *channel) +void vmbus_set_event(struct vmbus_channel *channel) { u32 child_relid = channel->offermsg.child_relid; @@ -485,5 +485,5 @@ int vmbus_set_event(struct vmbus_channel *channel) (child_relid >> 5)); } - return hv_signal_event(channel->sig_event); + hv_do_hypercall(HVCALL_SIGNAL_EVENT, channel->sig_event, NULL); } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 11bca51ef5ff..1c677d0f16d6 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -337,22 +337,6 @@ int hv_post_message(union hv_connection_id connection_id, return status & 0xFFFF; } - -/* - * hv_signal_event - - * Signal an event on the specified connection using the hypervisor event IPC. - * - * This involves a hypercall. - */ -int hv_signal_event(void *con_id) -{ - u64 status; - - status = hv_do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL); - - return status & 0xFFFF; -} - static int hv_ce_set_next_event(unsigned long delta, struct clock_event_device *evt) { diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 4ebc796b4f33..ac7aa303c37d 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -501,8 +501,6 @@ extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, void *payload, size_t payload_size); -extern int hv_signal_event(void *con_id); - extern int hv_synic_alloc(void); extern void hv_synic_free(void); @@ -650,7 +648,7 @@ void vmbus_disconnect(void); int vmbus_post_msg(void *buffer, size_t buflen); -int vmbus_set_event(struct vmbus_channel *channel); +void vmbus_set_event(struct vmbus_channel *channel); void vmbus_on_event(unsigned long data); -- cgit v1.2.3 From 7047f17d70fc0599563d30d0791692cb5fe42ae6 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 25 Dec 2015 20:00:30 -0800 Subject: Drivers: hv: vmbus: Add vendor and device atttributes Add vendor and device attributes to VMBUS devices. These will be used by Hyper-V tools as well user-level RDMA libraries that will use the vendor/device tuple to discover the RDMA device. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-bus-vmbus | 14 +++ drivers/hv/channel_mgmt.c | 166 +++++++++++++++++++++++-------- drivers/hv/vmbus_drv.c | 21 ++++ include/linux/hyperv.h | 28 ++++++ 4 files changed, 186 insertions(+), 43 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 636e938d5e33..5d0125f7bcaf 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -27,3 +27,17 @@ Description: The mapping of which primary/sub channels are bound to which Virtual Processors. Format: Users: tools/hv/lsvmbus + +What: /sys/bus/vmbus/devices/vmbus_*/device +Date: Dec. 2015 +KernelVersion: 4.5 +Contact: K. Y. Srinivasan +Description: The 16 bit device ID of the device +Users: tools/hv/lsvmbus and user level RDMA libraries + +What: /sys/bus/vmbus/devices/vmbus_*/vendor +Date: Dec. 2015 +KernelVersion: 4.5 +Contact: K. Y. Srinivasan +Description: The 16 bit vendor ID of the device +Users: tools/hv/lsvmbus and user level RDMA libraries diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 1c1ad47042c5..107d72f9834d 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -32,8 +32,122 @@ #include "hyperv_vmbus.h" -static void init_vp_index(struct vmbus_channel *channel, - const uuid_le *type_guid); +static void init_vp_index(struct vmbus_channel *channel, u16 dev_type); + +static const struct vmbus_device vmbus_devs[] = { + /* IDE */ + { .dev_type = HV_IDE, + HV_IDE_GUID, + .perf_device = true, + }, + + /* SCSI */ + { .dev_type = HV_SCSI, + HV_SCSI_GUID, + .perf_device = true, + }, + + /* Fibre Channel */ + { .dev_type = HV_FC, + HV_SYNTHFC_GUID, + .perf_device = true, + }, + + /* Synthetic NIC */ + { .dev_type = HV_NIC, + HV_NIC_GUID, + .perf_device = true, + }, + + /* Network Direct */ + { .dev_type = HV_ND, + HV_ND_GUID, + .perf_device = true, + }, + + /* PCIE */ + { .dev_type = HV_PCIE, + HV_PCIE_GUID, + .perf_device = true, + }, + + /* Synthetic Frame Buffer */ + { .dev_type = HV_FB, + HV_SYNTHVID_GUID, + .perf_device = false, + }, + + /* Synthetic Keyboard */ + { .dev_type = HV_KBD, + HV_KBD_GUID, + .perf_device = false, + }, + + /* Synthetic MOUSE */ + { .dev_type = HV_MOUSE, + HV_MOUSE_GUID, + .perf_device = false, + }, + + /* KVP */ + { .dev_type = HV_KVP, + HV_KVP_GUID, + .perf_device = false, + }, + + /* Time Synch */ + { .dev_type = HV_TS, + HV_TS_GUID, + .perf_device = false, + }, + + /* Heartbeat */ + { .dev_type = HV_HB, + HV_HEART_BEAT_GUID, + .perf_device = false, + }, + + /* Shutdown */ + { .dev_type = HV_SHUTDOWN, + HV_SHUTDOWN_GUID, + .perf_device = false, + }, + + /* File copy */ + { .dev_type = HV_FCOPY, + HV_FCOPY_GUID, + .perf_device = false, + }, + + /* Backup */ + { .dev_type = HV_BACKUP, + HV_VSS_GUID, + .perf_device = false, + }, + + /* Dynamic Memory */ + { .dev_type = HV_DM, + HV_DM_GUID, + .perf_device = false, + }, + + /* Unknown GUID */ + { .dev_type = HV_UNKOWN, + .perf_device = false, + }, +}; + +static u16 hv_get_dev_type(const uuid_le *guid) +{ + u16 i; + + for (i = HV_IDE; i < HV_UNKOWN; i++) { + if (!uuid_le_cmp(*guid, vmbus_devs[i].guid)) + return i; + } + pr_info("Unknown GUID: %pUl\n", guid); + return i; +} /** * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message @@ -251,6 +365,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) struct vmbus_channel *channel; bool fnew = true; unsigned long flags; + u16 dev_type; /* Make sure this is a new offer */ mutex_lock(&vmbus_connection.channel_mutex); @@ -288,7 +403,9 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) goto err_free_chan; } - init_vp_index(newchannel, &newchannel->offermsg.offer.if_type); + dev_type = hv_get_dev_type(&newchannel->offermsg.offer.if_type); + + init_vp_index(newchannel, dev_type); if (newchannel->target_cpu != get_cpu()) { put_cpu(); @@ -325,6 +442,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) if (!newchannel->device_obj) goto err_deq_chan; + newchannel->device_obj->device_id = dev_type; /* * Add the new device to the bus. This will kick off device-driver * binding which eventually invokes the device driver's AddDevice() @@ -358,37 +476,6 @@ err_free_chan: free_channel(newchannel); } -enum { - IDE = 0, - SCSI, - FC, - NIC, - ND_NIC, - PCIE, - MAX_PERF_CHN, -}; - -/* - * This is an array of device_ids (device types) that are performance critical. - * We attempt to distribute the interrupt load for these devices across - * all available CPUs. - */ -static const struct hv_vmbus_device_id hp_devs[] = { - /* IDE */ - { HV_IDE_GUID, }, - /* Storage - SCSI */ - { HV_SCSI_GUID, }, - /* Storage - FC */ - { HV_SYNTHFC_GUID, }, - /* Network */ - { HV_NIC_GUID, }, - /* NetworkDirect Guest RDMA */ - { HV_ND_GUID, }, - /* PCI Express Pass Through */ - { HV_PCIE_GUID, }, -}; - - /* * We use this state to statically distribute the channel interrupt load. */ @@ -405,22 +492,15 @@ static int next_numa_node_id; * For pre-win8 hosts or non-performance critical channels we assign the * first CPU in the first NUMA node. */ -static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_guid) +static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) { u32 cur_cpu; - int i; - bool perf_chn = false; + bool perf_chn = vmbus_devs[dev_type].perf_device; struct vmbus_channel *primary = channel->primary_channel; int next_node; struct cpumask available_mask; struct cpumask *alloced_mask; - for (i = IDE; i < MAX_PERF_CHN; i++) { - if (!uuid_le_cmp(*type_guid, hp_devs[i].guid)) { - perf_chn = true; - break; - } - } if ((vmbus_proto_version == VERSION_WS2008) || (vmbus_proto_version == VERSION_WIN7) || (!perf_chn)) { /* diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 328e4c3808e0..3668a95778ec 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -477,6 +477,24 @@ static ssize_t channel_vp_mapping_show(struct device *dev, } static DEVICE_ATTR_RO(channel_vp_mapping); +static ssize_t vendor_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + return sprintf(buf, "0x%x\n", hv_dev->vendor_id); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t device_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + return sprintf(buf, "0x%x\n", hv_dev->device_id); +} +static DEVICE_ATTR_RO(device); + /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, @@ -502,6 +520,8 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_in_read_bytes_avail.attr, &dev_attr_in_write_bytes_avail.attr, &dev_attr_channel_vp_mapping.attr, + &dev_attr_vendor.attr, + &dev_attr_device.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); @@ -957,6 +977,7 @@ struct hv_device *vmbus_device_create(const uuid_le *type, memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le)); memcpy(&child_device_obj->dev_instance, instance, sizeof(uuid_le)); + child_device_obj->vendor_id = 0x1414; /* MSFT vendor ID */ return child_device_obj; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 753dbad0bf94..3172521df805 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -633,6 +633,32 @@ enum hv_signal_policy { HV_SIGNAL_POLICY_EXPLICIT, }; +enum vmbus_device_type { + HV_IDE = 0, + HV_SCSI, + HV_FC, + HV_NIC, + HV_ND, + HV_PCIE, + HV_FB, + HV_KBD, + HV_MOUSE, + HV_KVP, + HV_TS, + HV_HB, + HV_SHUTDOWN, + HV_FCOPY, + HV_BACKUP, + HV_DM, + HV_UNKOWN, +}; + +struct vmbus_device { + u16 dev_type; + uuid_le guid; + bool perf_device; +}; + struct vmbus_channel { /* Unique channel id */ int id; @@ -959,6 +985,8 @@ struct hv_device { /* the device instance id of this device */ uuid_le dev_instance; + u16 vendor_id; + u16 device_id; struct device device; -- cgit v1.2.3 From 50fe6dd10069e7c062e27f29606f6e91ea979399 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Wed, 27 Jan 2016 22:29:33 -0800 Subject: tools/hv: Use include/uapi with __EXPORTED_HEADERS__ Use the local uapi headers to keep in sync with "recently" added #define's (e.g. VSS_OP_REGISTER1). Fixes: 3eb2094c59e8 ("Adding makefile for tools/hv") Cc: Signed-off-by: Kamal Mostafa Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/hv/Makefile b/tools/hv/Makefile index a8ab79556926..a8c4644022a6 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -5,6 +5,8 @@ PTHREAD_LIBS = -lpthread WARNINGS = -Wall -Wextra CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS) +CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include + all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon %: %.c $(CC) $(CFLAGS) -o $@ $^ -- cgit v1.2.3 From 79fd8e706637a5c7c41f9498fe0fbfb437abfdc8 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 27 Jan 2016 22:29:34 -0800 Subject: Drivers: hv: vmbus: avoid infinite loop in init_vp_index() When we pick a CPU to use for a new subchannel we try find a non-used one on the appropriate NUMA node, we keep track of them with the primary->alloced_cpus_in_node mask. Under normal circumstances we don't run out of available CPUs but it is possible when we we don't initialize some cpus in Linux, e.g. when we boot with 'nr_cpus=' limitation. Avoid the infinite loop in init_vp_index() by checking that we still have non-used CPUs in the alloced_cpus_in_node mask and resetting it in case we don't. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 107d72f9834d..af1d82eb8ecf 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -549,6 +549,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) cpumask_of_node(primary->numa_node)); cur_cpu = -1; + + /* + * Normally Hyper-V host doesn't create more subchannels than there + * are VCPUs on the node but it is possible when not all present VCPUs + * on the node are initialized by guest. Clear the alloced_cpus_in_node + * to start over. + */ + if (cpumask_equal(&primary->alloced_cpus_in_node, + cpumask_of_node(primary->numa_node))) + cpumask_clear(&primary->alloced_cpus_in_node); + while (true) { cur_cpu = cpumask_next(cur_cpu, &available_mask); if (cur_cpu >= nr_cpu_ids) { -- cgit v1.2.3 From 415719160de3fae3bb9cbc617664649919cd00d0 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 27 Jan 2016 22:29:35 -0800 Subject: Drivers: hv: vmbus: avoid scheduling in interrupt context in vmbus_initiate_unload() We have to call vmbus_initiate_unload() on crash to make kdump work but the crash can also be happening in interrupt (e.g. Sysrq + c results in such) where we can't schedule or the following will happen: [ 314.905786] bad: scheduling from the idle thread! Just skipping the wait (and even adding some random wait here) won't help: to make host-side magic working we're supposed to receive CHANNELMSG_UNLOAD (and actually confirm the fact that we received it) but we can't use interrupt-base path (vmbus_isr()-> vmbus_on_msg_dpc()). Implement a simple busy wait ignoring all the other messages and use it if we're in an interrupt context. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index af1d82eb8ecf..d6c611457601 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -589,6 +590,40 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) channel->target_vp = hv_context.vp_index[cur_cpu]; } +static void vmbus_wait_for_unload(void) +{ + int cpu = smp_processor_id(); + void *page_addr = hv_context.synic_message_page[cpu]; + struct hv_message *msg = (struct hv_message *)page_addr + + VMBUS_MESSAGE_SINT; + struct vmbus_channel_message_header *hdr; + bool unloaded = false; + + while (1) { + if (msg->header.message_type == HVMSG_NONE) { + mdelay(10); + continue; + } + + hdr = (struct vmbus_channel_message_header *)msg->u.payload; + if (hdr->msgtype == CHANNELMSG_UNLOAD_RESPONSE) + unloaded = true; + + msg->header.message_type = HVMSG_NONE; + /* + * header.message_type needs to be written before we do + * wrmsrl() below. + */ + mb(); + + if (msg->header.message_flags.msg_pending) + wrmsrl(HV_X64_MSR_EOM, 0); + + if (unloaded) + break; + } +} + /* * vmbus_unload_response - Handler for the unload response. */ @@ -614,7 +649,14 @@ void vmbus_initiate_unload(void) hdr.msgtype = CHANNELMSG_UNLOAD; vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header)); - wait_for_completion(&vmbus_connection.unload_event); + /* + * vmbus_initiate_unload() is also called on crash and the crash can be + * happening in an interrupt context, where scheduling is impossible. + */ + if (!in_interrupt()) + wait_for_completion(&vmbus_connection.unload_event); + else + vmbus_wait_for_unload(); } /* -- cgit v1.2.3 From 3ccb4fd8f492f99aece21acc1bd6142275f26236 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 27 Jan 2016 22:29:36 -0800 Subject: Drivers: hv: vmbus: don't manipulate with clocksources on crash clocksource_change_rating() involves mutex usage and can't be called in interrupt context. It also makes sense to avoid doing redundant work on crash. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 1c677d0f16d6..ccb335f57c88 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -295,8 +295,14 @@ void hv_cleanup(void) * Cleanup the TSC page based CS. */ if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { - clocksource_change_rating(&hyperv_cs_tsc, 10); - clocksource_unregister(&hyperv_cs_tsc); + /* + * Crash can happen in an interrupt context and unregistering + * a clocksource is impossible and redundant in this case. + */ + if (!oops_in_progress) { + clocksource_change_rating(&hyperv_cs_tsc, 10); + clocksource_unregister(&hyperv_cs_tsc); + } hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); -- cgit v1.2.3 From 3c75354d043ad546148d6992e40033ecaefc5ea5 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:37 -0800 Subject: Drivers: hv: vmbus: add a helper function to set a channel's pending send size This will be used by the coming net/hvsock driver. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 3172521df805..4af51a3f98d9 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -816,6 +816,12 @@ static inline void *get_per_channel_state(struct vmbus_channel *c) return c->per_channel_state; } +static inline void set_channel_pending_send_size(struct vmbus_channel *c, + u32 size) +{ + c->outbound.ring_buffer->pending_send_sz = size; +} + void vmbus_onmessage(void *context); int vmbus_request_offers(void); -- cgit v1.2.3 From e8d6ca023efce3bd80050dcd9e708ee3cf8babd4 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:38 -0800 Subject: Drivers: hv: vmbus: define the new offer type for Hyper-V socket (hvsock) A helper function is also added. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 4af51a3f98d9..79c4aa70eff3 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -235,6 +235,7 @@ struct vmbus_channel_offer { #define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 #define VMBUS_CHANNEL_PARENT_OFFER 0x200 #define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 +#define VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER 0x2000 struct vmpacket_descriptor { u16 type; @@ -795,6 +796,12 @@ struct vmbus_channel { enum hv_signal_policy signal_policy; }; +static inline bool is_hvsock_channel(const struct vmbus_channel *c) +{ + return !!(c->offermsg.offer.chn_flags & + VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER); +} + static inline void set_channel_signal_state(struct vmbus_channel *c, enum hv_signal_policy policy) { -- cgit v1.2.3 From 5f363bc38f810d238d1e8b19998625ddec3b8138 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:39 -0800 Subject: Drivers: hv: vmbus: vmbus_sendpacket_ctl: hvsock: avoid unnecessary signaling When the hvsock channel's outbound ringbuffer is full (i.e., hv_ringbuffer_write() returns -EAGAIN), we should avoid the unnecessary signaling the host. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 1161d68a1863..3f0453302146 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -659,6 +659,9 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, * If we cannot write to the ring-buffer; signal the host * even if we may not have written anything. This is a rare * enough condition that it should not matter. + * NOTE: in this case, the hvsock channel is an exception, because + * it looks the host side's hvsock implementation has a throttling + * mechanism which can hurt the performance otherwise. */ if (channel->signal_policy) @@ -666,7 +669,8 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, else kick_q = true; - if (((ret == 0) && kick_q && signal) || (ret)) + if (((ret == 0) && kick_q && signal) || + (ret && !is_hvsock_channel(channel))) vmbus_setevent(channel); return ret; -- cgit v1.2.3 From 5c23a1a5c60b0f472cfa61cd7d8279f8aaeb5b64 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:40 -0800 Subject: Drivers: hv: vmbus: define a new VMBus message type for hvsock A function to send the type of message is also added. The coming net/hvsock driver will use this function to proactively request the host to offer a VMBus channel for a new hvsock connection. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 15 +++++++++++++++ drivers/hv/channel_mgmt.c | 4 ++++ include/linux/hyperv.h | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 3f0453302146..fcab234796ef 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -219,6 +219,21 @@ error0: } EXPORT_SYMBOL_GPL(vmbus_open); +/* Used for Hyper-V Socket: a guest client's connect() to the host */ +int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id, + const uuid_le *shv_host_servie_id) +{ + struct vmbus_channel_tl_connect_request conn_msg; + + memset(&conn_msg, 0, sizeof(conn_msg)); + conn_msg.header.msgtype = CHANNELMSG_TL_CONNECT_REQUEST; + conn_msg.guest_endpoint_id = *shv_guest_servie_id; + conn_msg.host_service_id = *shv_host_servie_id; + + return vmbus_post_msg(&conn_msg, sizeof(conn_msg)); +} +EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); + /* * create_gpadl_header - Creates a gpadl for the specified buffer */ diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index d6c611457601..60ca25b93b4c 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -958,6 +958,10 @@ struct vmbus_channel_message_table_entry {CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response}, {CHANNELMSG_UNLOAD, 0, NULL}, {CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response}, + {CHANNELMSG_18, 0, NULL}, + {CHANNELMSG_19, 0, NULL}, + {CHANNELMSG_20, 0, NULL}, + {CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL}, }; /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 79c4aa70eff3..898eac9e8c13 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -392,6 +392,10 @@ enum vmbus_channel_message_type { CHANNELMSG_VERSION_RESPONSE = 15, CHANNELMSG_UNLOAD = 16, CHANNELMSG_UNLOAD_RESPONSE = 17, + CHANNELMSG_18 = 18, + CHANNELMSG_19 = 19, + CHANNELMSG_20 = 20, + CHANNELMSG_TL_CONNECT_REQUEST = 21, CHANNELMSG_COUNT }; @@ -562,6 +566,13 @@ struct vmbus_channel_initiate_contact { u64 monitor_page2; } __packed; +/* Hyper-V socket: guest's connect()-ing to host */ +struct vmbus_channel_tl_connect_request { + struct vmbus_channel_message_header header; + uuid_le guest_endpoint_id; + uuid_le host_service_id; +} __packed; + struct vmbus_channel_version_response { struct vmbus_channel_message_header header; u8 version_supported; @@ -1283,4 +1294,6 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); extern __u32 vmbus_proto_version; +int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id, + const uuid_le *shv_host_servie_id); #endif /* _HYPERV_H */ -- cgit v1.2.3 From 8981da320a11217589aa3c50f9e891bcdef07ece Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:41 -0800 Subject: Drivers: hv: vmbus: add a hvsock flag in struct hv_driver Only the coming hv_sock driver has a "true" value for this flag. We treat the hvsock offers/channels as special VMBus devices. Since the hv_sock driver handles all the hvsock offers/channels, we need to tweak vmbus_match() for hv_sock driver, so we introduce this flag. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 4 ++++ include/linux/hyperv.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 3668a95778ec..063e5f53ca78 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -582,6 +582,10 @@ static int vmbus_match(struct device *device, struct device_driver *driver) struct hv_driver *drv = drv_to_hv_drv(driver); struct hv_device *hv_dev = device_to_hv_device(device); + /* The hv_sock driver handles all hv_sock offers. */ + if (is_hvsock_channel(hv_dev->channel)) + return drv->hvsock; + if (hv_vmbus_get_id(drv->id_table, &hv_dev->dev_type)) return 1; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 898eac9e8c13..f636f91f104b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -990,6 +990,20 @@ extern void vmbus_ontimer(unsigned long data); struct hv_driver { const char *name; + /* + * A hvsock offer, which has a VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER + * channel flag, actually doesn't mean a synthetic device because the + * offer's if_type/if_instance can change for every new hvsock + * connection. + * + * However, to facilitate the notification of new-offer/rescind-offer + * from vmbus driver to hvsock driver, we can handle hvsock offer as + * a special vmbus device, and hence we need the below flag to + * indicate if the driver is the hvsock driver or not: we need to + * specially treat the hvosck offer & driver in vmbus_match(). + */ + bool hvsock; + /* the device type supported by this driver */ uuid_le dev_type; const struct hv_vmbus_device_id *id_table; -- cgit v1.2.3 From 499e8401a515d04daa986b995da710d2b9737764 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:42 -0800 Subject: Drivers: hv: vmbus: add a per-channel rescind callback This will be used by the coming hv_sock driver. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 11 +++++++++++ include/linux/hyperv.h | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 60ca25b93b4c..76864c98a110 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -741,6 +741,10 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) spin_unlock_irqrestore(&channel->lock, flags); if (channel->device_obj) { + if (channel->chn_rescind_callback) { + channel->chn_rescind_callback(channel); + return; + } /* * We will have to unregister this device from the * driver core. @@ -1110,3 +1114,10 @@ bool vmbus_are_subchannels_present(struct vmbus_channel *primary) return ret; } EXPORT_SYMBOL_GPL(vmbus_are_subchannels_present); + +void vmbus_set_chn_rescind_callback(struct vmbus_channel *channel, + void (*chn_rescind_cb)(struct vmbus_channel *)) +{ + channel->chn_rescind_callback = chn_rescind_cb; +} +EXPORT_SYMBOL_GPL(vmbus_set_chn_rescind_callback); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index f636f91f104b..2e54e34e5feb 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -765,6 +765,12 @@ struct vmbus_channel { */ void (*sc_creation_callback)(struct vmbus_channel *new_sc); + /* + * Channel rescind callback. Some channels (the hvsock ones), need to + * register a callback which is invoked in vmbus_onoffer_rescind(). + */ + void (*chn_rescind_callback)(struct vmbus_channel *channel); + /* * The spinlock to protect the structure. It is being used to protect * test-and-set access to various attributes of the structure as well @@ -851,6 +857,9 @@ int vmbus_request_offers(void); void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel, void (*sc_cr_cb)(struct vmbus_channel *new_sc)); +void vmbus_set_chn_rescind_callback(struct vmbus_channel *channel, + void (*chn_rescind_cb)(struct vmbus_channel *)); + /* * Retrieve the (sub) channel on which to send an outgoing request. * When a primary channel has multiple sub-channels, we choose a -- cgit v1.2.3 From 85d9aa705184a4504d0330017e3956fcdae8a9d6 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 27 Jan 2016 22:29:43 -0800 Subject: Drivers: hv: vmbus: add an API vmbus_hvsock_device_unregister() The hvsock driver needs this API to release all the resources related to the channel. Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 33 ++++++++++++++++++++++++++++----- drivers/hv/connection.c | 4 ++-- include/linux/hyperv.h | 2 ++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 76864c98a110..cf311be88cb4 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -310,6 +310,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) vmbus_release_relid(relid); BUG_ON(!channel->rescind); + BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); if (channel->target_cpu != get_cpu()) { put_cpu(); @@ -321,9 +322,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) } if (channel->primary_channel == NULL) { - mutex_lock(&vmbus_connection.channel_mutex); list_del(&channel->listentry); - mutex_unlock(&vmbus_connection.channel_mutex); primary_channel = channel; } else { @@ -367,6 +366,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) bool fnew = true; unsigned long flags; u16 dev_type; + int ret; /* Make sure this is a new offer */ mutex_lock(&vmbus_connection.channel_mutex); @@ -449,7 +449,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) * binding which eventually invokes the device driver's AddDevice() * method. */ - if (vmbus_device_register(newchannel->device_obj) != 0) { + mutex_lock(&vmbus_connection.channel_mutex); + ret = vmbus_device_register(newchannel->device_obj); + mutex_unlock(&vmbus_connection.channel_mutex); + + if (ret != 0) { pr_err("unable to add child device object (relid %d)\n", newchannel->offermsg.child_relid); kfree(newchannel->device_obj); @@ -725,6 +729,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) struct device *dev; rescind = (struct vmbus_channel_rescind_offer *)hdr; + + mutex_lock(&vmbus_connection.channel_mutex); channel = relid2channel(rescind->child_relid); if (channel == NULL) { @@ -733,7 +739,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) * vmbus_process_offer(), we have already invoked * vmbus_release_relid() on error. */ - return; + goto out; } spin_lock_irqsave(&channel->lock, flags); @@ -743,7 +749,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) if (channel->device_obj) { if (channel->chn_rescind_callback) { channel->chn_rescind_callback(channel); - return; + goto out; } /* * We will have to unregister this device from the @@ -758,8 +764,25 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) hv_process_channel_removal(channel, channel->offermsg.child_relid); } + +out: + mutex_unlock(&vmbus_connection.channel_mutex); } +void vmbus_hvsock_device_unregister(struct vmbus_channel *channel) +{ + mutex_lock(&vmbus_connection.channel_mutex); + + BUG_ON(!is_hvsock_channel(channel)); + + channel->rescind = true; + vmbus_device_unregister(channel->device_obj); + + mutex_unlock(&vmbus_connection.channel_mutex); +} +EXPORT_SYMBOL_GPL(vmbus_hvsock_device_unregister); + + /* * vmbus_onoffers_delivered - * This is invoked when all offers have been delivered. diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 4a320e60641a..fa86b2cb28b8 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -288,7 +288,8 @@ struct vmbus_channel *relid2channel(u32 relid) struct list_head *cur, *tmp; struct vmbus_channel *cur_sc; - mutex_lock(&vmbus_connection.channel_mutex); + BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (channel->offermsg.child_relid == relid) { found_channel = channel; @@ -307,7 +308,6 @@ struct vmbus_channel *relid2channel(u32 relid) } } } - mutex_unlock(&vmbus_connection.channel_mutex); return found_channel; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 2e54e34e5feb..c056f058dcf8 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1069,6 +1069,8 @@ int __must_check __vmbus_driver_register(struct hv_driver *hv_driver, const char *mod_name); void vmbus_driver_unregister(struct hv_driver *hv_driver); +void vmbus_hvsock_device_unregister(struct vmbus_channel *channel); + int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, resource_size_t min, resource_size_t max, resource_size_t size, resource_size_t align, -- cgit v1.2.3 From 3eba9a77d5fc2cee486a16fff435686f024f61cf Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 27 Jan 2016 22:29:44 -0800 Subject: Drivers: hv: vmbus: Eliminate the spin lock on the read path The function hv_ringbuffer_read() is called always on a pre-assigned CPU. Each chnnel is bound to a specific CPU and this function is always called on the CPU the channel is bound. There is no need to acquire the spin lock; get rid of this overhead. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index b53702ce692f..1145f3b8e4e0 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -388,7 +388,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, u32 bytes_avail_toread; u32 next_read_location = 0; u64 prev_indices = 0; - unsigned long flags; struct vmpacket_descriptor desc; u32 offset; u32 packetlen; @@ -397,7 +396,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, if (buflen <= 0) return -EINVAL; - spin_lock_irqsave(&inring_info->ring_lock, flags); *buffer_actual_len = 0; *requestid = 0; @@ -412,7 +410,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, * No error is set when there is even no header, drivers are * supposed to analyze buffer_actual_len. */ - goto out_unlock; + return ret; } next_read_location = hv_get_next_read_location(inring_info); @@ -425,15 +423,11 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, *buffer_actual_len = packetlen; *requestid = desc.trans_id; - if (bytes_avail_toread < packetlen + offset) { - ret = -EAGAIN; - goto out_unlock; - } + if (bytes_avail_toread < packetlen + offset) + return -EAGAIN; - if (packetlen > buflen) { - ret = -ENOBUFS; - goto out_unlock; - } + if (packetlen > buflen) + return -ENOBUFS; next_read_location = hv_get_next_readlocation_withoffset(inring_info, offset); @@ -460,7 +454,5 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, *signal = hv_need_to_signal_on_read(bytes_avail_towrite, inring_info); -out_unlock: - spin_unlock_irqrestore(&inring_info->ring_lock, flags); return ret; } -- cgit v1.2.3 From fe760e4d64fe5c17c39e86c410d41f6587ee88bc Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 27 Jan 2016 22:29:45 -0800 Subject: Drivers: hv: vmbus: Give control over how the ring access is serialized On the channel send side, many of the VMBUS device drivers explicity serialize access to the outgoing ring buffer. Give more control to the VMBUS device drivers in terms how to serialize accesss to the outgoing ring buffer. The default behavior will be to aquire the ring lock to preserve the current behavior. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 15 +++++++++++---- drivers/hv/channel_mgmt.c | 1 + drivers/hv/hyperv_vmbus.h | 2 +- drivers/hv/ring_buffer.c | 13 ++++++++----- include/linux/hyperv.h | 16 ++++++++++++++++ 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index fcab234796ef..56dd261f7142 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -639,6 +639,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, u64 aligned_data = 0; int ret; bool signal = false; + bool lock = channel->acquire_ring_lock; int num_vecs = ((bufferlen != 0) ? 3 : 1); @@ -658,7 +659,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs, - &signal); + &signal, lock); /* * Signalling the host is conditional on many factors: @@ -738,6 +739,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, struct kvec bufferlist[3]; u64 aligned_data = 0; bool signal = false; + bool lock = channel->acquire_ring_lock; if (pagecount > MAX_PAGE_BUFFER_COUNT) return -EINVAL; @@ -774,7 +776,8 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, + &signal, lock); /* * Signalling the host is conditional on many factors: @@ -837,6 +840,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, struct kvec bufferlist[3]; u64 aligned_data = 0; bool signal = false; + bool lock = channel->acquire_ring_lock; packetlen = desc_size + bufferlen; packetlen_aligned = ALIGN(packetlen, sizeof(u64)); @@ -856,7 +860,8 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, + &signal, lock); if (ret == 0 && signal) vmbus_setevent(channel); @@ -881,6 +886,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, struct kvec bufferlist[3]; u64 aligned_data = 0; bool signal = false; + bool lock = channel->acquire_ring_lock; u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, multi_pagebuffer->len); @@ -919,7 +925,8 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, + &signal, lock); if (ret == 0 && signal) vmbus_setevent(channel); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index cf311be88cb4..b40f429aaa13 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -259,6 +259,7 @@ static struct vmbus_channel *alloc_channel(void) return NULL; channel->id = atomic_inc_return(&chan_num); + channel->acquire_ring_lock = true; spin_lock_init(&channel->inbound_lock); spin_lock_init(&channel->lock); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index ac7aa303c37d..b9ea7f59036b 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -529,7 +529,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, struct kvec *kv_list, - u32 kv_count, bool *signal); + u32 kv_count, bool *signal, bool lock); int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, u32 buflen, u32 *buffer_actual_len, diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 1145f3b8e4e0..5613e2b5cff7 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -314,7 +314,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) /* Write to the ring buffer. */ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, - struct kvec *kv_list, u32 kv_count, bool *signal) + struct kvec *kv_list, u32 kv_count, bool *signal, bool lock) { int i = 0; u32 bytes_avail_towrite; @@ -324,14 +324,15 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, u32 next_write_location; u32 old_write; u64 prev_indices = 0; - unsigned long flags; + unsigned long flags = 0; for (i = 0; i < kv_count; i++) totalbytes_towrite += kv_list[i].iov_len; totalbytes_towrite += sizeof(u64); - spin_lock_irqsave(&outring_info->ring_lock, flags); + if (lock) + spin_lock_irqsave(&outring_info->ring_lock, flags); hv_get_ringbuffer_availbytes(outring_info, &bytes_avail_toread, @@ -343,7 +344,8 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, * is empty since the read index == write index. */ if (bytes_avail_towrite <= totalbytes_towrite) { - spin_unlock_irqrestore(&outring_info->ring_lock, flags); + if (lock) + spin_unlock_irqrestore(&outring_info->ring_lock, flags); return -EAGAIN; } @@ -374,7 +376,8 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, hv_set_next_write_location(outring_info, next_write_location); - spin_unlock_irqrestore(&outring_info->ring_lock, flags); + if (lock) + spin_unlock_irqrestore(&outring_info->ring_lock, flags); *signal = hv_need_to_signal(old_write, outring_info); return 0; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index c056f058dcf8..d23dab0d770b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -811,8 +811,24 @@ struct vmbus_channel { * signaling control. */ enum hv_signal_policy signal_policy; + /* + * On the channel send side, many of the VMBUS + * device drivers explicity serialize access to the + * outgoing ring buffer. Give more control to the + * VMBUS device drivers in terms how to serialize + * accesss to the outgoing ring buffer. + * The default behavior will be to aquire the + * ring lock to preserve the current behavior. + */ + bool acquire_ring_lock; + }; +static inline void set_channel_lock_state(struct vmbus_channel *c, bool state) +{ + c->acquire_ring_lock = state; +} + static inline bool is_hvsock_channel(const struct vmbus_channel *c) { return !!(c->offermsg.offer.chn_flags & -- cgit v1.2.3 From f42a0fd13bd281811e7457b28d939c8e8b808868 Mon Sep 17 00:00:00 2001 From: Jorgen Hansen Date: Thu, 12 Nov 2015 01:29:32 -0800 Subject: VMCI: Use 32bit atomics for queue headers on X86_32 This change restricts the reading and setting of the head and tail pointers on 32bit X86 to 32bit for both correctness and performance reasons. On uniprocessor X86_32, the atomic64_read may be implemented as a non-locked cmpxchg8b. This may result in updates to the pointers done by the VMCI device being overwritten. On MP systems, there is no such correctness issue, but using 32bit atomics avoids the overhead of the locked 64bit operation. All this is safe because the queue size on 32bit systems will never exceed a 32bit value. Reviewed-by: Thomas Hellstrom Signed-off-by: Jorgen Hansen Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_driver.c | 2 +- include/linux/vmw_vmci_defs.h | 43 +++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index b823f9a6e464..896be150e28f 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.c +++ b/drivers/misc/vmw_vmci/vmci_driver.c @@ -113,5 +113,5 @@ module_exit(vmci_drv_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); -MODULE_VERSION("1.1.3.0-k"); +MODULE_VERSION("1.1.4.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index 65ac54c61c18..1bd31a38c51e 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -733,6 +733,41 @@ static inline void *vmci_event_data_payload(struct vmci_event_data *ev_data) return (void *)vmci_event_data_const_payload(ev_data); } +/* + * Helper to read a value from a head or tail pointer. For X86_32, the + * pointer is treated as a 32bit value, since the pointer value + * never exceeds a 32bit value in this case. Also, doing an + * atomic64_read on X86_32 uniprocessor systems may be implemented + * as a non locked cmpxchg8b, that may end up overwriting updates done + * by the VMCI device to the memory location. On 32bit SMP, the lock + * prefix will be used, so correctness isn't an issue, but using a + * 64bit operation still adds unnecessary overhead. + */ +static inline u64 vmci_q_read_pointer(atomic64_t *var) +{ +#if defined(CONFIG_X86_32) + return atomic_read((atomic_t *)var); +#else + return atomic64_read(var); +#endif +} + +/* + * Helper to set the value of a head or tail pointer. For X86_32, the + * pointer is treated as a 32bit value, since the pointer value + * never exceeds a 32bit value in this case. On 32bit SMP, using a + * locked cmpxchg8b adds unnecessary overhead. + */ +static inline void vmci_q_set_pointer(atomic64_t *var, + u64 new_val) +{ +#if defined(CONFIG_X86_32) + return atomic_set((atomic_t *)var, (u32)new_val); +#else + return atomic64_set(var, new_val); +#endif +} + /* * Helper to add a given offset to a head or tail pointer. Wraps the * value of the pointer around the max size of the queue. @@ -741,14 +776,14 @@ static inline void vmci_qp_add_pointer(atomic64_t *var, size_t add, u64 size) { - u64 new_val = atomic64_read(var); + u64 new_val = vmci_q_read_pointer(var); if (new_val >= size - add) new_val -= size; new_val += add; - atomic64_set(var, new_val); + vmci_q_set_pointer(var, new_val); } /* @@ -758,7 +793,7 @@ static inline u64 vmci_q_header_producer_tail(const struct vmci_queue_header *q_header) { struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; - return atomic64_read(&qh->producer_tail); + return vmci_q_read_pointer(&qh->producer_tail); } /* @@ -768,7 +803,7 @@ static inline u64 vmci_q_header_consumer_head(const struct vmci_queue_header *q_header) { struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; - return atomic64_read(&qh->consumer_head); + return vmci_q_read_pointer(&qh->consumer_head); } /* -- cgit v1.2.3 From ff8742279ff163e36ee50bb4e75090af1d7d6e3b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:55 -0700 Subject: coresight: fixing indentation problem Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 93738dfbf631..4236a02cdab2 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -536,7 +536,7 @@ static void coresight_fixup_orphan_conns(struct coresight_device *csdev) * are hooked-up with each newly added component. */ bus_for_each_dev(&coresight_bustype, NULL, - csdev, coresight_orphan_match); + csdev, coresight_orphan_match); } -- cgit v1.2.3 From a9ddc71f5840c2711e530f2e055b278f79948b29 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:56 -0700 Subject: coresight: fixing lockdep error On some platform the following lockdep error occurs when doing simple manipulations: [ 23.197021] [ 23.198608] ====================================================== [ 23.205078] [ INFO: possible circular locking dependency detected ] [ 23.211639] 4.4.0-rc8-00025-gbbf360b #172 Not tainted [ 23.216918] ------------------------------------------------------- [ 23.223480] sh/858 is trying to acquire lock: [ 23.228057] (coresight_mutex){+.+.+.}, at: [] coresight_enable+0x1c/0x1b4 [ 23.236206] [ 23.236206] but task is already holding lock: [ 23.242309] (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.250122] [ 23.250122] which lock already depends on the new lock. [ 23.250122] [ 23.258697] [ 23.258697] the existing dependency chain (in reverse order) is: [ 23.266510] -> #1 (s_active#52){++++.+}: [ 23.270843] [] __kernfs_remove+0x294/0x35c [ 23.276672] [] kernfs_remove_by_name_ns+0x44/0x8c [ 23.283172] [] remove_files+0x3c/0x84 [ 23.288543] [] sysfs_remove_group+0x48/0x9c [ 23.294494] [] sysfs_remove_groups+0x2c/0x3c [ 23.300506] [] device_remove_attrs+0x5c/0x74 [ 23.306549] [] device_del+0x110/0x218 [ 23.311950] [] device_unregister+0x2c/0x6c [ 23.317779] [] coresight_unregister+0x30/0x40 [ 23.323883] [] etm_probe+0x228/0x2e8 [ 23.329193] [] amba_probe+0xe4/0x160 [ 23.334503] [] driver_probe_device+0x23c/0x480 [ 23.340728] [] __driver_attach+0x9c/0xa0 [ 23.346374] [] bus_for_each_dev+0x70/0xa4 [ 23.352142] [] driver_attach+0x24/0x28 [ 23.357604] [] bus_add_driver+0x1e0/0x278 [ 23.363372] [] driver_register+0x80/0x100 [ 23.369110] [] amba_driver_register+0x58/0x5c [ 23.375244] [] etm_driver_init+0x18/0x1c [ 23.380889] [] do_one_initcall+0xc4/0x20c [ 23.386657] [] kernel_init_freeable+0x160/0x208 [ 23.392974] [] kernel_init+0x18/0xf0 [ 23.398254] [] ret_from_fork+0x14/0x24 [ 23.403747] -> #0 (coresight_mutex){+.+.+.}: [ 23.408447] [] lock_acquire+0xe4/0x210 [ 23.413909] [] mutex_lock_nested+0x74/0x450 [ 23.419860] [] coresight_enable+0x1c/0x1b4 [ 23.425689] [] enable_source_store+0x58/0x68 [ 23.431732] [] dev_attr_store+0x20/0x2c [ 23.437286] [] sysfs_kf_write+0x50/0x54 [ 23.442871] [] kernfs_fop_write+0xc4/0x1c0 [ 23.448699] [] __vfs_write+0x34/0xe4 [ 23.454040] [] vfs_write+0x98/0x174 [ 23.459228] [] SyS_write+0x4c/0xa8 [ 23.464355] [] ret_fast_syscall+0x0/0x1c [ 23.470031] [ 23.470031] other info that might help us debug this: [ 23.470031] [ 23.478393] Possible unsafe locking scenario: [ 23.478393] [ 23.484619] CPU0 CPU1 [ 23.489349] ---- ---- [ 23.494079] lock(s_active#52); [ 23.497497] lock(coresight_mutex); [ 23.503906] lock(s_active#52); [ 23.509918] lock(coresight_mutex); [ 23.513702] [ 23.513702] *** DEADLOCK *** [ 23.513702] [ 23.519897] 3 locks held by sh/858: [ 23.523529] #0: (sb_writers#7){.+.+.+}, at: [] __sb_start_write+0xa8/0xd4 [ 23.531799] #1: (&of->mutex){+.+...}, at: [] kernfs_fop_write+0x54/0x1c0 [ 23.539916] #2: (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.548156] [ 23.548156] stack backtrace: [ 23.552734] CPU: 0 PID: 858 Comm: sh Not tainted 4.4.0-rc8-00025-gbbf360b #172 [ 23.560302] Hardware name: Generic OMAP4 (Flattened Device Tree) [ 23.566589] Backtrace: [ 23.569152] [] (dump_backtrace) from [] (show_stack+0x18/0x1c) [ 23.577087] r7:ed4b8570 r6:c0936400 r5:c07ae71c r4:00000000 [ 23.583038] [] (show_stack) from [] (dump_stack+0x98/0xc0) [ 23.590606] [] (dump_stack) from [] (print_circular_bug+0x21c/0x33c) [ 23.599090] r5:c0939d60 r4:c0936400 [ 23.602874] [] (print_circular_bug) from [] (__lock_acquire+0x1c98/0x1d88) [ 23.611877] r10:00000003 r9:c0fd7a5c r8:ed4b8550 r7:ed4b8570 r6:ed4b8000 r5:c0ff69e4 [ 23.620117] r4:c0936400 r3:ed4b8550 [ 23.623901] [] (__lock_acquire) from [] (lock_acquire+0xe4/0x210) [ 23.632080] r10:00000000 r9:00000000 r8:60000013 r7:c07cb7b4 r6:00000001 r5:00000000 [ 23.640350] r4:00000000 [ 23.643005] [] (lock_acquire) from [] (mutex_lock_nested+0x74/0x450) [ 23.651458] r10:ecc0bf80 r9:edbe7dcc r8:ed4b8000 r7:c0fd7a5c r6:c0415d40 r5:00000000 [ 23.659729] r4:c07cb780 [ 23.662384] [] (mutex_lock_nested) from [] (coresight_enable+0x1c/0x1b4) [ 23.671234] r10:ecc0bf80 r9:edbe7dcc r8:ed733c00 r7:00000000 r6:ed733c00 r5:00000002 [ 23.679473] r4:ed762140 [ 23.682128] [] (coresight_enable) from [] (enable_source_store+0x58/0x68) [ 23.691070] r7:00000000 r6:ed733c00 r5:00000002 r4:ed762160 [ 23.697052] [] (enable_source_store) from [] (dev_attr_store+0x20/0x2c) [ 23.705780] r5:edbe7dc0 r4:c0415fd8 [ 23.709533] [] (dev_attr_store) from [] (sysfs_kf_write+0x50/0x54) [ 23.717834] r5:edbe7dc0 r4:c030b338 [ 23.721618] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xc4/0x1c0) [ 23.730163] r7:00000000 r6:00000000 r5:00000002 r4:edbe7dc0 [ 23.736145] [] (kernfs_fop_write) from [] (__vfs_write+0x34/0xe4) [ 23.744323] r10:00000000 r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:00000002 r5:c01d4ae4 [ 23.752593] r4:ee385a40 [ 23.755249] [] (__vfs_write) from [] (vfs_write+0x98/0x174) [ 23.762908] r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:000ab0d8 r5:00000002 r4:ee385a40 [ 23.771057] [] (vfs_write) from [] (SyS_write+0x4c/0xa8) [ 23.778442] r8:c0010964 r7:00000002 r6:000ab0d8 r5:ee385a40 r4:ee385a40 [ 23.785522] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x1c) [ 23.793457] r7:00000004 r6:00000001 r5:000ab0d8 r4:00000002 [ 23.799652] coresight-etb10 54162000.etb: ETB enabled [ 23.805084] coresight-funnel 54164000.funnel: FUNNEL inport 0 enabled [ 23.811859] coresight-replicator 44000000.ocp:replicator: REPLICATOR enabled [ 23.819335] coresight-funnel 54158000.funnel: FUNNEL inport 0 enabled [ 23.826110] coresight-etm3x 5414c000.ptm: ETM tracing enabled The locking in coresight_unregister() is not required as the only customers of the function are drivers themselves when an initialisation failure has been encoutered. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 4236a02cdab2..41b42018b660 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -713,12 +713,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - mutex_lock(&coresight_mutex); - kfree(csdev->conns); device_unregister(&csdev->dev); - - mutex_unlock(&coresight_mutex); } EXPORT_SYMBOL_GPL(coresight_unregister); -- cgit v1.2.3 From fae54158792aec705620bdc3938d342879204f0c Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:57 -0700 Subject: coresight: coresight_unregister() function cleanup In its current form the code never frees csdev->refcnt allocated in coresight_register(). There is also a problem with csdev->conns that is freed before device_unregister() rather than in the device release function. This patch addresses both issues by moving kfree(csdev->conns) to coresight_device_release() and freeing csdev->refcnt, also in the same function. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 41b42018b660..8872db4410eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -481,6 +481,8 @@ static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev); + kfree(csdev->conns); + kfree(csdev->refcnt); kfree(csdev); } @@ -713,7 +715,6 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - kfree(csdev->conns); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); -- cgit v1.2.3 From f2dfab3568fc32afeac8b698481e80e7ab2dc658 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:58 -0700 Subject: coresight: release reference taken by 'bus_find_device()' The reference count taken by function bus_find_device() needs to be released if a child device is found, something this patch is adding. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8872db4410eb..a35ca54a76c9 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -570,6 +570,8 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) if (dev) { conn->child_dev = to_coresight_device(dev); + /* and put reference from 'bus_find_device()' */ + put_device(dev); } else { csdev->orphan = true; conn->child_dev = NULL; -- cgit v1.2.3 From ad725aee070caf8fa93d84d6fb78321f9642db18 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:59 -0700 Subject: coresight: remove csdev's link from topology In function 'coresight_unregister()', all references to the csdev that is being taken away need to be removed from the topology. Otherwise building the next coresight path from source to sink may use memory that has been released. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index a35ca54a76c9..7e6e9ff27dd1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -579,6 +579,50 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) } } +static int coresight_remove_match(struct device *dev, void *data) +{ + int i; + struct coresight_device *csdev, *iterator; + struct coresight_connection *conn; + + csdev = data; + iterator = to_coresight_device(dev); + + /* No need to check oneself */ + if (csdev == iterator) + return 0; + + /* + * Circle throuch all the connection of that component. If we find + * a connection whose name matches @csdev, remove it. + */ + for (i = 0; i < iterator->nr_outport; i++) { + conn = &iterator->conns[i]; + + if (conn->child_dev == NULL) + continue; + + if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { + iterator->orphan = true; + conn->child_dev = NULL; + /* No need to continue */ + break; + } + } + + /* + * Returning '0' ensures that all known component on the + * bus will be checked. + */ + return 0; +} + +static void coresight_remove_conns(struct coresight_device *csdev) +{ + bus_for_each_dev(&coresight_bustype, NULL, + csdev, coresight_remove_match); +} + /** * coresight_timeout - loop until a bit has changed to a specific state. * @addr: base address of the area of interest. @@ -717,6 +761,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { + /* Remove references of that device in the topology */ + coresight_remove_conns(csdev); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); -- cgit v1.2.3 From b15f0fb657e040401d875d11ae13b269af8a16e0 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:00 -0700 Subject: coresight: removing bind/unbind options from sysfs The coresight drivers have absolutely no control over bind and unbind operations triggered from sysfs. The operations simply can't be cancelled or denied event when one or several tracing sessions are under way. Since the memory associated to individual device is invariably freed, the end result is a kernel crash when the path from source to sink is travelled again as demonstrated here[1]. One solution could be to keep track of all the path (i.e tracing session) that get created and iterate through the elements of those path looking for the coresight device that is being removed. This proposition doesn't scale well since there is no upper bound on the amount of concurrent trace session that can be created. With the above in mind, this patch prevent devices from being unbounded from their driver by using the driver->suppress_bind_attr option. That way trace sessions can be managed without fearing to loose devices. Since device can't be removed anymore the xyz_remove() functions found in each driver is also removed. [1]. http://www.spinics.net/lists/arm-kernel/msg474952.html Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 11 +---------- drivers/hwtracing/coresight/coresight-etm3x.c | 13 +------------ drivers/hwtracing/coresight/coresight-etm4x.c | 13 +------------ drivers/hwtracing/coresight/coresight-funnel.c | 10 +--------- drivers/hwtracing/coresight/coresight-replicator-qcom.c | 11 +---------- drivers/hwtracing/coresight/coresight-replicator.c | 16 +--------------- drivers/hwtracing/coresight/coresight-tmc.c | 15 +-------------- drivers/hwtracing/coresight/coresight-tpiu.c | 10 +--------- 8 files changed, 8 insertions(+), 91 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 77d0f9c1118d..92969dae739d 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -489,15 +489,6 @@ err_misc_register: return ret; } -static int etb_remove(struct amba_device *adev) -{ - struct etb_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int etb_runtime_suspend(struct device *dev) { @@ -537,10 +528,10 @@ static struct amba_driver etb_driver = { .name = "coresight-etb10", .owner = THIS_MODULE, .pm = &etb_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etb_probe, - .remove = etb_remove, .id_table = etb_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d630b7ece735..5981fcc69960 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1877,17 +1877,6 @@ err_arch_supported: return ret; } -static int etm_remove(struct amba_device *adev) -{ - struct etm_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm_count == 0) - unregister_hotcpu_notifier(&etm_cpu_notifier); - - return 0; -} - #ifdef CONFIG_PM static int etm_runtime_suspend(struct device *dev) { @@ -1948,9 +1937,9 @@ static struct amba_driver etm_driver = { .name = "coresight-etm3x", .owner = THIS_MODULE, .pm = &etm_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etm_probe, - .remove = etm_remove, .id_table = etm_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index a6707642bb23..167004f9c42b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2684,17 +2684,6 @@ err_coresight_register: return ret; } -static int etm4_remove(struct amba_device *adev) -{ - struct etmv4_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm4_count == 0) - unregister_hotcpu_notifier(&etm4_cpu_notifier); - - return 0; -} - static struct amba_id etm4_ids[] = { { /* ETM 4.0 - Qualcomm */ .id = 0x0003b95d, @@ -2712,9 +2701,9 @@ static struct amba_id etm4_ids[] = { static struct amba_driver etm4x_driver = { .drv = { .name = "coresight-etm4x", + .suppress_bind_attrs = true, }, .probe = etm4_probe, - .remove = etm4_remove, .id_table = etm4_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 2e36bde7fcb4..25e8ea140a09 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -226,14 +226,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int funnel_remove(struct amba_device *adev) -{ - struct funnel_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int funnel_runtime_suspend(struct device *dev) { @@ -273,9 +265,9 @@ static struct amba_driver funnel_driver = { .name = "coresight-funnel", .owner = THIS_MODULE, .pm = &funnel_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = funnel_probe, - .remove = funnel_remove, .id_table = funnel_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 584059e9e866..444815179460 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -156,15 +156,6 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int replicator_remove(struct amba_device *adev) -{ - struct replicator_state *drvdata = amba_get_drvdata(adev); - - pm_runtime_disable(&adev->dev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -206,9 +197,9 @@ static struct amba_driver replicator_driver = { .drv = { .name = "coresight-replicator-qcom", .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = replicator_probe, - .remove = replicator_remove, .id_table = replicator_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 963ac197c253..b77d700a3f0e 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -127,20 +127,6 @@ out_disable_pm: return ret; } -static int replicator_remove(struct platform_device *pdev) -{ - struct replicator_drvdata *drvdata = platform_get_drvdata(pdev); - - coresight_unregister(drvdata->csdev); - pm_runtime_get_sync(&pdev->dev); - if (!IS_ERR(drvdata->atclk)) - clk_disable_unprepare(drvdata->atclk); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -175,11 +161,11 @@ static const struct of_device_id replicator_match[] = { static struct platform_driver replicator_driver = { .probe = replicator_probe, - .remove = replicator_remove, .driver = { .name = "coresight-replicator", .of_match_table = replicator_match, .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index a57c7ec1661f..570d96815f78 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -770,19 +770,6 @@ err_devm_kzalloc: return ret; } -static int tmc_remove(struct amba_device *adev) -{ - struct tmc_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) - dma_free_coherent(drvdata->dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); - - return 0; -} - static struct amba_id tmc_ids[] = { { .id = 0x0003b961, @@ -795,9 +782,9 @@ static struct amba_driver tmc_driver = { .drv = { .name = "coresight-tmc", .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, .probe = tmc_probe, - .remove = tmc_remove, .id_table = tmc_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 7214efd10db5..ca50e73d9df7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -172,14 +172,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int tpiu_remove(struct amba_device *adev) -{ - struct tpiu_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int tpiu_runtime_suspend(struct device *dev) { @@ -223,9 +215,9 @@ static struct amba_driver tpiu_driver = { .name = "coresight-tpiu", .owner = THIS_MODULE, .pm = &tpiu_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = tpiu_probe, - .remove = tpiu_remove, .id_table = tpiu_ids, }; -- cgit v1.2.3 From 52210c8745e418f82f3f0aeeee01d7bc4858812a Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:01 -0700 Subject: coresight: implementing 'cpu_id()' API Other than plainly parsing the device tree there is no way to know which CPU a tracer is affined to. As such adding an interface to lookup the CPU field enclosed in the etm_drvdata structure that was initialised at boot time. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 8 ++++++++ include/linux/coresight.h | 3 +++ 3 files changed, 19 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 5981fcc69960..aae80e14508d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -315,6 +315,13 @@ static void etm_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } +static int etm_cpu_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm_trace_id(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -421,6 +428,7 @@ static void etm_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm_source_ops = { + .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, .enable = etm_enable, .disable = etm_disable, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 167004f9c42b..b6ae9cb6ff57 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -63,6 +63,13 @@ static bool etm4_arch_supported(u8 arch) return true; } +static int etm4_cpu_id(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -262,6 +269,7 @@ static void etm4_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm4_source_ops = { + .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, .enable = etm4_enable, .disable = etm4_disable, diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a7cabfa23b55..bf62b265bf52 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -205,12 +205,15 @@ struct coresight_ops_link { /** * struct coresight_ops_source - basic operations for a source * Operations available for sources. + * @cpu_id: returns the value of the CPU number this component + * is associated to. * @trace_id: returns the value of the component's trace ID as known to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { + int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev); void (*disable)(struct coresight_device *csdev); -- cgit v1.2.3 From 00cd29b799e3449f0c68b1cc77cd4a5f95b42d17 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 13 Jan 2016 08:10:31 -0800 Subject: klist: fix starting point removed bug in klist iterators The starting node for a klist iteration is often passed in from somewhere way above the klist infrastructure, meaning there's no guarantee the node is still on the list. We've seen this in SCSI where we use bus_find_device() to iterate through a list of devices. In the face of heavy hotplug activity, the last device returned by bus_find_device() can be removed before the next call. This leads to Dec 3 13:22:02 localhost kernel: WARNING: CPU: 2 PID: 28073 at include/linux/kref.h:47 klist_iter_init_node+0x3d/0x50() Dec 3 13:22:02 localhost kernel: Modules linked in: scsi_debug x86_pkg_temp_thermal kvm_intel kvm irqbypass crc32c_intel joydev iTCO_wdt dcdbas ipmi_devintf acpi_power_meter iTCO_vendor_support ipmi_si imsghandler pcspkr wmi acpi_cpufreq tpm_tis tpm shpchp lpc_ich mfd_core nfsd nfs_acl lockd grace sunrpc tg3 ptp pps_core Dec 3 13:22:02 localhost kernel: CPU: 2 PID: 28073 Comm: cat Not tainted 4.4.0-rc1+ #2 Dec 3 13:22:02 localhost kernel: Hardware name: Dell Inc. PowerEdge R320/08VT7V, BIOS 2.0.22 11/19/2013 Dec 3 13:22:02 localhost kernel: ffffffff81a20e77 ffff880613acfd18 ffffffff81321eef 0000000000000000 Dec 3 13:22:02 localhost kernel: ffff880613acfd50 ffffffff8107ca52 ffff88061176b198 0000000000000000 Dec 3 13:22:02 localhost kernel: ffffffff814542b0 ffff880610cfb100 ffff88061176b198 ffff880613acfd60 Dec 3 13:22:02 localhost kernel: Call Trace: Dec 3 13:22:02 localhost kernel: [] dump_stack+0x44/0x55 Dec 3 13:22:02 localhost kernel: [] warn_slowpath_common+0x82/0xc0 Dec 3 13:22:02 localhost kernel: [] ? proc_scsi_show+0x20/0x20 Dec 3 13:22:02 localhost kernel: [] warn_slowpath_null+0x1a/0x20 Dec 3 13:22:02 localhost kernel: [] klist_iter_init_node+0x3d/0x50 Dec 3 13:22:02 localhost kernel: [] bus_find_device+0x51/0xb0 Dec 3 13:22:02 localhost kernel: [] scsi_seq_next+0x2d/0x40 [...] And an eventual crash. It can actually occur in any hotplug system which has a device finder and a starting device. We can fix this globally by making sure the starting node for klist_iter_init_node() is actually a member of the list before using it (and by starting from the beginning if it isn't). Reported-by: Ewan D. Milne Tested-by: Ewan D. Milne Cc: stable@vger.kernel.org Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- lib/klist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/klist.c b/lib/klist.c index d74cf7a29afd..0507fa5d84c5 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -282,9 +282,9 @@ void klist_iter_init_node(struct klist *k, struct klist_iter *i, struct klist_node *n) { i->i_klist = k; - i->i_cur = n; - if (n) - kref_get(&n->n_ref); + i->i_cur = NULL; + if (n && kref_get_unless_zero(&n->n_ref)) + i->i_cur = n; } EXPORT_SYMBOL_GPL(klist_iter_init_node); -- cgit v1.2.3 From 4a2e2b19f96acfc037a9773c7729d133ce1e7e3b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Dec 2015 17:25:15 +0200 Subject: stm class: Hide STM-specific options if STM is disabled If STM=n, it doesn't make sense to ask about STM_DUMMY and STM_SOURCE_CONSOLE support, which are not even built when enabled anyway. Hence hide these options if STM=n. Reported-by: Linus Torvalds Signed-off-by: Geert Uytterhoeven Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 83e9f591a54b..4c13762f2b4d 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -8,6 +8,8 @@ config STM Say Y here to enable System Trace Module device support. +if STM + config STM_DUMMY tristate "Dummy STM driver" help @@ -24,3 +26,5 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. + +endif -- cgit v1.2.3 From 8d0439369d98aa23c8e20a49b69d90b1efab2797 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Dec 2015 17:25:16 +0200 Subject: intel_th: INTEL_TH should depend on HAS_DMA If NO_DMA=y: ERROR: "dma_free_coherent" [drivers/hwtracing/intel_th/intel_th_msu.ko] undefined! ERROR: "dma_alloc_coherent" [drivers/hwtracing/intel_th/intel_th_msu.ko] undefined! ERROR: "dma_supported" [drivers/hwtracing/intel_th/intel_th.ko] undefined! Add a dependency on HAS_DMA to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index b7a9073d968b..90b08440938b 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -1,5 +1,6 @@ config INTEL_TH tristate "Intel(R) Trace Hub controller" + depends on HAS_DMA help Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that produce, switch and output trace data from multiple hardware and -- cgit v1.2.3 From 042d4460b5b4379a12f375045ff9065cf6758735 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Dec 2015 17:25:17 +0200 Subject: stm class: Select CONFIG_SRCU The newly added STM code uses SRCU, but does not ensure that this code is part of the kernel: drivers/built-in.o: In function `stm_source_link_show': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' drivers/built-in.o: In function `stm_source_link_drop': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' This adds a Kconfig 'select' statement like all the other SRCU using drivers have. Signed-off-by: Arnd Bergmann Fixes: 7bd1d4093c2f ("stm class: Introduce an abstraction for System Trace Module devices") Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 4c13762f2b4d..e0ac75395526 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -1,6 +1,7 @@ config STM tristate "System Trace Module devices" select CONFIGFS_FS + select SRCU help A System Trace Module (STM) is a device exporting data in System Trace Protocol (STP) format as defined by MIPI STP standards. -- cgit v1.2.3 From 4c127fd16e6b33ecb7badc091480c84ea9aebeb6 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:18 +0200 Subject: stm class: Fix locking in unbinding policy path Right now, if stm device removal has to unbind from a policy (that is, an stm device that has STP policy, gets removed), it will trigger a nested lock on the stm device's policy mutex. This patch fixes the problem by moving the locking from the policy unbinding to policy removal (configfs path), where it's actually needed; the other caller of the policy unbinding function already takes the mutex around the call. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 11ab6d01adf6..94d3abfb737a 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -272,13 +272,17 @@ void stp_policy_unbind(struct stp_policy *policy) { struct stm_device *stm = policy->stm; + /* + * stp_policy_release() will not call here if the policy is already + * unbound; other users should not either, as no link exists between + * this policy and anything else in that case + */ if (WARN_ON_ONCE(!policy->stm)) return; - mutex_lock(&stm->policy_mutex); - stm->policy = NULL; - mutex_unlock(&stm->policy_mutex); + lockdep_assert_held(&stm->policy_mutex); + stm->policy = NULL; policy->stm = NULL; stm_put_device(stm); @@ -287,8 +291,16 @@ void stp_policy_unbind(struct stp_policy *policy) static void stp_policy_release(struct config_item *item) { struct stp_policy *policy = to_stp_policy(item); + struct stm_device *stm = policy->stm; + /* a policy *can* be unbound and still exist in configfs tree */ + if (!stm) + return; + + mutex_lock(&stm->policy_mutex); stp_policy_unbind(policy); + mutex_unlock(&stm->policy_mutex); + kfree(policy); } -- cgit v1.2.3 From c74f7e8281add80bdfa0ad2998b8df287b13df73 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:19 +0200 Subject: stm class: Fix link list locking Currently, the list of stm_sources linked to an stm device is protected by a spinlock, which also means that sources' .unlink() method is called under this spinlock. However, this method may (and does) sleep, which means trouble. This patch slightly reworks locking around stm::link_list so that bits that might_sleep() are called with a mutex held instead. Modification of this list requires both mutex and spinlock to be held, while looking at the list can be done under either mutex or spinlock. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 38 +++++++++++++++++++++++++++++--------- drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b6445d9e5453..ddcb606acea6 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -641,6 +641,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; + mutex_init(&stm->link_mutex); spin_lock_init(&stm->link_lock); INIT_LIST_HEAD(&stm->link_list); @@ -671,11 +672,11 @@ void stm_unregister_device(struct stm_data *stm_data) struct stm_source_device *src, *iter; int i; - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { __stm_source_link_drop(src, stm); } - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); synchronize_srcu(&stm_source_srcu); @@ -694,6 +695,17 @@ void stm_unregister_device(struct stm_data *stm_data) } EXPORT_SYMBOL_GPL(stm_unregister_device); +/* + * stm::link_list access serialization uses a spinlock and a mutex; holding + * either of them guarantees that the list is stable; modification requires + * holding both of them. + * + * Lock ordering is as follows: + * stm::link_mutex + * stm::link_lock + * src::link_lock + */ + /** * stm_source_link_add() - connect an stm_source device to an stm device * @src: stm_source device @@ -710,6 +722,7 @@ static int stm_source_link_add(struct stm_source_device *src, char *id; int err; + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -719,6 +732,7 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); id = kstrdup(src->data->name, GFP_KERNEL); if (id) { @@ -756,6 +770,7 @@ fail_free_output: stm_put_device(stm); fail_detach: + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -764,6 +779,7 @@ fail_detach: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); return err; } @@ -776,13 +792,20 @@ fail_detach: * If @stm is @src::link, disconnect them from one another and put the * reference on the @stm device. * - * Caller must hold stm::link_lock. + * Caller must hold stm::link_mutex. */ static void __stm_source_link_drop(struct stm_source_device *src, struct stm_device *stm) { struct stm_device *link; + lockdep_assert_held(&stm->link_mutex); + + if (src->data->unlink) + src->data->unlink(src->data); + + /* for stm::link_list modification, we hold both mutex and spinlock */ + spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); if (WARN_ON_ONCE(link != stm)) { @@ -791,13 +814,13 @@ static void __stm_source_link_drop(struct stm_source_device *src, } stm_output_free(link, &src->output); - /* caller must hold stm::link_lock */ list_del_init(&src->link_entry); /* matches stm_find_device() from stm_source_link_store() */ stm_put_device(link); rcu_assign_pointer(src->link, NULL); spin_unlock(&src->link_lock); + spin_unlock(&stm->link_lock); } /** @@ -819,12 +842,9 @@ static void stm_source_link_drop(struct stm_source_device *src) stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) { - if (src->data->unlink) - src->data->unlink(src->data); - - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); __stm_source_link_drop(src, stm); - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 95ece0292c99..97ee02241440 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -45,6 +45,7 @@ struct stm_device { int major; unsigned int sw_nmasters; struct stm_data *data; + struct mutex link_mutex; spinlock_t link_lock; struct list_head link_list; /* master allocation */ -- cgit v1.2.3 From 7b3bb0e75395b2f3b0f95d9ae50581e989ba5e4c Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Tue, 22 Dec 2015 17:25:20 +0200 Subject: stm class: Fix an off-by-one in master array allocation Since both sw_start and sw_end are master indices, the size of array that holds them is sw_end - sw_start + 1, which the current code gets wrong, allocating one item less than required. This patch corrects the allocation size, avoiding potential slab corruption. Signed-off-by: Chunyan Zhang [alexander.shishkin@linux.intel.com: re-wrote the commit message] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index ddcb606acea6..40a8b79ab7db 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -618,7 +618,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (!stm_data->packet || !stm_data->sw_nchannels) return -EINVAL; - nmasters = stm_data->sw_end - stm_data->sw_start; + nmasters = stm_data->sw_end - stm_data->sw_start + 1; stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL); if (!stm) return -ENOMEM; -- cgit v1.2.3 From f08b18266c7116e2ec6885dd53a928f580060a71 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:21 +0200 Subject: stm class: Prevent user-controllable allocations Currently, the character device write method allocates a temporary buffer for user's data, but the user's data size is not sanitized and can cause arbitrarily large allocations via kzalloc() or an integer overflow that will then result in overwriting kernel memory. This patch trims the input buffer size to avoid these issues. Reported-by: Sasha Levin Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 40a8b79ab7db..aef8ddb24451 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -406,6 +406,9 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, char *kbuf; int err; + if (count + 1 > PAGE_SIZE) + count = PAGE_SIZE - 1; + /* * if no m/c have been assigned to this writer up to this * point, use "default" policy entry -- cgit v1.2.3 From 6396b912f1e97a0e9566c0d801923a0d0eb31749 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:22 +0200 Subject: intel_th: pci: Add Apollo Lake SOC support This adds Intel(R) Trace Hub PCI ID for Apollo Lake SOC. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 641e87936064..b5760730e11b 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -67,6 +67,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa126), .driver_data = (kernel_ulong_t)0, }, + { + /* Apollo Lake */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a8e), + .driver_data = (kernel_ulong_t)0, + }, { 0 }, }; -- cgit v1.2.3 From 3f040887a8be0c28a099a9106bc29443d4f79a7e Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:23 +0200 Subject: intel_th: pci: Add Broxton SOC support This adds Intel(R) Trace Hub PCI ID for Broxton SOC. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index b5760730e11b..09017073d7a4 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -72,6 +72,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a8e), .driver_data = (kernel_ulong_t)0, }, + { + /* Broxton */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0a80), + .driver_data = (kernel_ulong_t)0, + }, { 0 }, }; -- cgit v1.2.3 From f38e87e8c8d326ec6cddfc3f286b605d996a52a3 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 17 Nov 2015 15:55:37 -0800 Subject: misc: mic: Fix crash when MIC reset is invoked in RESET_FAILED state This patch fixes the following crash seen when MIC reset is invoked in RESET_FAILED state due to device_del being called a second time on an already deleted device: [] device_del+0x45/0x1d0 [] device_unregister+0x1e/0x60 [] scif_unregister_device+0x12/0x20 [scif_bus] [] cosm_stop+0xaa/0xe0 [mic_cosm] [] cosm_reset_trigger_work+0x14/0x20 [mic_cosm] The fix consists in realizing that because cosm_reset changes the state to MIC_RESETTING, cosm_stop needs the previous state, before it changed to MIC_RESETTING, to decide whether a hw_ops->stop had previously been issued. This is now provided in a new cosm_device member cdev->prev_state. Reviewed-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/bus/cosm_bus.h | 2 ++ drivers/misc/mic/cosm/cosm_main.c | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mic/bus/cosm_bus.h b/drivers/misc/mic/bus/cosm_bus.h index f7c57f266916..8b6341855dc3 100644 --- a/drivers/misc/mic/bus/cosm_bus.h +++ b/drivers/misc/mic/bus/cosm_bus.h @@ -30,6 +30,7 @@ * @attr_group: Pointer to list of sysfs attribute groups. * @sdev: Device for sysfs entries. * @state: MIC state. + * @prev_state: MIC state previous to MIC_RESETTING * @shutdown_status: MIC status reported by card for shutdown/crashes. * @shutdown_status_int: Internal shutdown status maintained by the driver * @cosm_mutex: Mutex for synchronizing access to data structures. @@ -55,6 +56,7 @@ struct cosm_device { const struct attribute_group **attr_group; struct device *sdev; u8 state; + u8 prev_state; u8 shutdown_status; u8 shutdown_status_int; struct mutex cosm_mutex; diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c index 4b4b356c797d..7005cb1e01d2 100644 --- a/drivers/misc/mic/cosm/cosm_main.c +++ b/drivers/misc/mic/cosm/cosm_main.c @@ -153,8 +153,10 @@ void cosm_stop(struct cosm_device *cdev, bool force) * stop(..) calls device_unregister and will crash the system if * called multiple times. */ - bool call_hw_ops = cdev->state != MIC_RESET_FAILED && - cdev->state != MIC_READY; + u8 state = cdev->state == MIC_RESETTING ? + cdev->prev_state : cdev->state; + bool call_hw_ops = state != MIC_RESET_FAILED && + state != MIC_READY; if (cdev->state != MIC_RESETTING) cosm_set_state(cdev, MIC_RESETTING); @@ -195,8 +197,11 @@ int cosm_reset(struct cosm_device *cdev) mutex_lock(&cdev->cosm_mutex); if (cdev->state != MIC_READY) { - cosm_set_state(cdev, MIC_RESETTING); - schedule_work(&cdev->reset_trigger_work); + if (cdev->state != MIC_RESETTING) { + cdev->prev_state = cdev->state; + cosm_set_state(cdev, MIC_RESETTING); + schedule_work(&cdev->reset_trigger_work); + } } else { dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__); rc = -EINVAL; -- cgit v1.2.3 From e6c0effa0e4dd17b4569bb62eaf6283033d475a4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Dec 2015 13:11:28 +0300 Subject: mic_virtio: fix a timeout loop After the loop we test "if (!retry)" to see if we timedout. The problem is "retry--" is a post-op so retry will be -1 at the end of the loop. I have fixed this by changing it to a pre-op instead. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/mic_virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index f6ed57d3125c..17764b2fbee5 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -337,7 +337,7 @@ static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, * rings have been re-assigned. */ mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - for (retry = 100; retry--;) { + for (retry = 100; --retry;) { if (!ioread8(&dc->used_address_updated)) break; msleep(100); -- cgit v1.2.3 From d897d7edee8eaf0a8f7924a9502fd68e72041f8c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:29 +0530 Subject: misc: mic: remove unneeded debug message >From the error path we are printing an error message with dev_err(). No need to print almost same message with dev_dbg(). Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 8118ac48c764..cd5208d1895d 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -450,21 +450,21 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = mic_x100_get_boot_addr(mdev); if (rc) - goto error; + goto done; /* load OS */ rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); if (rc < 0) { dev_err(&mdev->pdev->dev, "ramdisk request_firmware failed: %d %s\n", rc, mdev->cosm_dev->firmware); - goto error; + goto done; } if (mdev->bootaddr > mdev->aper.len - fw->size) { rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", __func__, __LINE__, rc, mdev->bootaddr); release_firmware(fw); - goto error; + goto done; } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); @@ -475,14 +475,13 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) if (rc) { dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - goto error; + goto done; } release_firmware(fw); /* load ramdisk */ if (mdev->cosm_dev->ramdisk) rc = mic_x100_load_ramdisk(mdev); -error: - dev_dbg(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); + done: return rc; } -- cgit v1.2.3 From ba1b5c44e2a9fed6f97fb10da3b4da164d4a26e4 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:30 +0530 Subject: misc: mic: return error properly If request_firmware() succeeds then rc becomes 0. After that if the test for strcmp() fails then we were jumping to label done: and returning rc. But rc being 0 we returned success whereas we have failed here and we were supposed to return an error. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index cd5208d1895d..317e25ff484c 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -468,8 +468,13 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); - if (!strcmp(mdev->cosm_dev->bootmode, "flash")) + if (!strcmp(mdev->cosm_dev->bootmode, "flash")) { + rc = -EINVAL; + dev_err(&mdev->pdev->dev, "%s %d rc %d\n", + __func__, __LINE__, rc); + release_firmware(fw); goto done; + } /* load command line */ rc = mic_x100_load_command_line(mdev, fw); if (rc) { -- cgit v1.2.3 From fbaa72d56424553f0aa434d8816a0da7a9071415 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:31 +0530 Subject: misc: mic: return error directly Instead of jumping to a label and then returning from there lets return directly. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 317e25ff484c..37fa89875e1e 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -450,14 +450,14 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = mic_x100_get_boot_addr(mdev); if (rc) - goto done; + return rc; /* load OS */ rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); if (rc < 0) { dev_err(&mdev->pdev->dev, "ramdisk request_firmware failed: %d %s\n", rc, mdev->cosm_dev->firmware); - goto done; + return rc; } if (mdev->bootaddr > mdev->aper.len - fw->size) { rc = -EINVAL; -- cgit v1.2.3 From 5fb437984b6c4793626be3936297fecece987a9f Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:32 +0530 Subject: misc: mic: use common error path Instead of calling release_firmware() on every error and then jumping lets have a common release_firmware() in the error path. This patch also fixes a memory leak where we missed release_firmware() if mic_x100_load_command_line() fails. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 37fa89875e1e..82a973c85b5d 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -463,8 +463,7 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", __func__, __LINE__, rc, mdev->bootaddr); - release_firmware(fw); - goto done; + goto error; } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); @@ -472,22 +471,24 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - release_firmware(fw); - goto done; + goto error; } /* load command line */ rc = mic_x100_load_command_line(mdev, fw); if (rc) { dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - goto done; + goto error; } release_firmware(fw); /* load ramdisk */ if (mdev->cosm_dev->ramdisk) rc = mic_x100_load_ramdisk(mdev); -done: + return rc; + +error: + release_firmware(fw); return rc; } -- cgit v1.2.3 From 9d403a998d509478f645a8e88282dcabbaded8b3 Mon Sep 17 00:00:00 2001 From: Matthias Lange Date: Fri, 29 Jan 2016 13:27:56 +0100 Subject: scripts: fix typo in ver_linux Signed-off-by: Matthias Lange Signed-off-by: Greg Kroah-Hartman --- scripts/ver_linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ver_linux b/scripts/ver_linux index 024a11ac8b97..0d8bd29b1bd6 100755 --- a/scripts/ver_linux +++ b/scripts/ver_linux @@ -1,6 +1,6 @@ #!/bin/sh # Before running this script please ensure that your PATH is -# typical as you use for compilation/istallation. I use +# typical as you use for compilation/installation. I use # /bin /sbin /usr/bin /usr/sbin /usr/local/bin, but it may # differ on your system. # -- cgit v1.2.3 From c7f340f844de848e6e15c964ad2ce597dbd31441 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 20 Nov 2015 15:23:50 +0530 Subject: misc: st_core: remove unreachable code The pr_debug() will never be executed. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 6e3af8b42cdd..dcdbd58672cc 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -632,7 +632,6 @@ long st_register(struct st_proto_s *new_proto) spin_unlock_irqrestore(&st_gdata->lock, flags); return err; } - pr_debug("done %s(%d) ", __func__, new_proto->chnl_id); } EXPORT_SYMBOL_GPL(st_register); -- cgit v1.2.3 From 46fd8c3426c2de74966480f83ebc8a1ed59b9840 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 30 Nov 2015 16:21:35 +0000 Subject: misc: pch_phub: allow build on MIPS platforms Allow the pch_phub driver to be build on MIPS platforms, in preparation for its use on the MIPS Boston board. Signed-off-by: Paul Burton Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 054fc10cb3b6..d8b11d7d569c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -470,7 +470,7 @@ config BMP085_SPI config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" select GENERIC_NET_UTILS - depends on PCI && (X86_32 || COMPILE_TEST) + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) help This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded -- cgit v1.2.3 From 47312a4a484f21e04d286367f16dd432cf646163 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Dec 2015 20:07:14 -0500 Subject: drivers/misc: make arm-charlcd.c explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/misc/Kconfig:config ARM_CHARLCD drivers/misc/Kconfig: bool "ARM Ltd. Character LCD Driver" ...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 this driver did not have a ".remove" function coded for non-modular drivers either. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Cc: Arnd Bergmann Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Reviewed-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/misc/arm-charlcd.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index c65b5ea5d5ef..b3176ee92b90 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -8,7 +8,6 @@ * Author: Linus Walleij */ #include -#include #include #include #include @@ -328,20 +327,6 @@ out_no_resource: return ret; } -static int __exit charlcd_remove(struct platform_device *pdev) -{ - struct charlcd *lcd = platform_get_drvdata(pdev); - - if (lcd) { - free_irq(lcd->irq, lcd); - iounmap(lcd->virtbase); - release_mem_region(lcd->phybase, lcd->physize); - kfree(lcd); - } - - return 0; -} - static int charlcd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -376,13 +361,8 @@ static struct platform_driver charlcd_driver = { .driver = { .name = DRIVERNAME, .pm = &charlcd_pm_ops, + .suppress_bind_attrs = true, .of_match_table = of_match_ptr(charlcd_match), }, - .remove = __exit_p(charlcd_remove), }; - -module_platform_driver_probe(charlcd_driver, charlcd_probe); - -MODULE_AUTHOR("Linus Walleij "); -MODULE_DESCRIPTION("ARM Character LCD Driver"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver_probe(charlcd_driver, charlcd_probe); -- cgit v1.2.3 From 50e6315dba721cbc24ccd6d7b299f1782f210a98 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 14 Dec 2015 14:29:23 +0000 Subject: misc/bmp085: Enable building as a module Commit 985087dbcb02 'misc: add support for bmp18x chips to the bmp085 driver' changed the BMP085 config symbol to a boolean. I see no reason why the shared code cannot be built as a module, so change it back to tristate. Fixes: 985087dbcb02 ("misc: add support for bmp18x chips to the bmp085 driver") Cc: Eric Andersson Signed-off-by: Ben Hutchings Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d8b11d7d569c..15579514d120 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -440,7 +440,7 @@ config ARM_CHARLCD still useful. config BMP085 - bool + tristate depends on SYSFS config BMP085_I2C -- cgit v1.2.3 From 8c99d8e6de0474bf5a7004e41c044ac4dcacbae1 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 28 Dec 2015 23:00:16 +0800 Subject: misc: apds990x, bh1770glc, lis3lv02d: use to_i2c_client Use to_i2c_client() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/apds990x.c | 8 ++++---- drivers/misc/bh1770glc.c | 8 ++++---- drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index a3e789b85cc8..dfb72ecfa604 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1215,7 +1215,7 @@ static int apds990x_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int apds990x_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_off(chip); @@ -1224,7 +1224,7 @@ static int apds990x_suspend(struct device *dev) static int apds990x_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); /* @@ -1240,7 +1240,7 @@ static int apds990x_resume(struct device *dev) #ifdef CONFIG_PM static int apds990x_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_off(chip); @@ -1249,7 +1249,7 @@ static int apds990x_runtime_suspend(struct device *dev) static int apds990x_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_on(chip); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 753d7ecdadaa..845466e45b95 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1323,7 +1323,7 @@ static int bh1770_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int bh1770_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_off(chip); @@ -1333,7 +1333,7 @@ static int bh1770_suspend(struct device *dev) static int bh1770_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); int ret = 0; @@ -1361,7 +1361,7 @@ static int bh1770_resume(struct device *dev) #ifdef CONFIG_PM static int bh1770_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_off(chip); @@ -1371,7 +1371,7 @@ static int bh1770_runtime_suspend(struct device *dev) static int bh1770_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_on(chip); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 0c3bb7e3ee80..14b7d539fed6 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -209,7 +209,7 @@ static int lis3lv02d_i2c_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int lis3lv02d_i2c_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); if (!lis3->pdata || !lis3->pdata->wakeup_flags) @@ -219,7 +219,7 @@ static int lis3lv02d_i2c_suspend(struct device *dev) static int lis3lv02d_i2c_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); /* @@ -238,7 +238,7 @@ static int lis3lv02d_i2c_resume(struct device *dev) #ifdef CONFIG_PM static int lis3_i2c_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); lis3lv02d_poweroff(lis3); @@ -247,7 +247,7 @@ static int lis3_i2c_runtime_suspend(struct device *dev) static int lis3_i2c_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); lis3lv02d_poweron(lis3); -- cgit v1.2.3 From 092462c2b52259edba80a6748acb3305f7f70423 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:11 +0800 Subject: misc: eeprom: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at24.c | 4 ++-- drivers/misc/eeprom/at25.c | 4 ++-- drivers/misc/eeprom/eeprom.c | 2 +- drivers/misc/eeprom/eeprom_93xx46.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 5d7c0900fa1b..d105c2564400 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -289,7 +289,7 @@ static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj, { struct at24_data *at24; - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + at24 = dev_get_drvdata(kobj_to_dev(kobj)); return at24_read(at24, buf, off, count); } @@ -420,7 +420,7 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, { struct at24_data *at24; - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + at24 = dev_get_drvdata(kobj_to_dev(kobj)); return at24_write(at24, buf, off, count); } diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index f850ef556bcc..3e9e5a28acaa 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -139,7 +139,7 @@ at25_bin_read(struct file *filp, struct kobject *kobj, struct device *dev; struct at25_data *at25; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); at25 = dev_get_drvdata(dev); return at25_ee_read(at25, buf, off, count); @@ -273,7 +273,7 @@ at25_bin_write(struct file *filp, struct kobject *kobj, struct device *dev; struct at25_data *at25; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); at25 = dev_get_drvdata(dev); return at25_ee_write(at25, buf, off, count); diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 7342fd637031..3d1d55157e5f 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -84,7 +84,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj)); struct eeprom_data *data = i2c_get_clientdata(client); u8 slice; diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index ff63f05edc76..473aa0a2eaf6 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -45,7 +45,7 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, int bits, ret; u16 cmd_addr; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); cmd_addr = OP_READ << edev->addrlen; @@ -190,7 +190,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj, struct device *dev; int i, ret, step = 1; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); /* only write even number of bytes on 16-bit devices */ -- cgit v1.2.3 From 85016ff33f359981a8c78a1eb67b04777207775c Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:09 +0800 Subject: misc: cxl: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Reviewed-by: Andrew Donnellan Acked-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/sysfs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 02006f7109a8..038af5d45145 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -386,8 +386,7 @@ static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct cxl_afu *afu = to_cxl_afu(container_of(kobj, - struct device, kobj)); + struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj)); return cxl_afu_read_err_buffer(afu, buf, off, count); } @@ -467,7 +466,7 @@ static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, loff_t off, size_t count) { struct afu_config_record *cr = to_cr(kobj); - struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj)); + struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj->parent)); u64 i, j, val; -- cgit v1.2.3 From 47679cde604d6977b390d5b0fc83dedf8a82f66d Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:08 +0800 Subject: misc: c2port: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/misc/c2port/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index cc8645b5369d..1922cb8f6b88 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -721,9 +721,7 @@ static ssize_t c2port_read_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { - struct c2port_device *c2dev = - dev_get_drvdata(container_of(kobj, - struct device, kobj)); + struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); ssize_t ret; /* Check the device and flash access status */ @@ -838,9 +836,7 @@ static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { - struct c2port_device *c2dev = - dev_get_drvdata(container_of(kobj, - struct device, kobj)); + struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); int ret; /* Check the device access status */ -- cgit v1.2.3 From 4020d07e917854c4300092c12503a382315ea844 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 7 Dec 2015 10:57:27 +0000 Subject: DT: nvmem: Add NXP LPC18xx EEPROM memory binding documentation Add the devicetree binding document for NXP LPC18xx EEPROM memory. Signed-off-by: Ariel D'Alessandro Acked-by: Stefan Wahren Acked-by: Rob Herring Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/lpc1857-eeprom.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt diff --git a/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt b/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt new file mode 100644 index 000000000000..809df68f6e14 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt @@ -0,0 +1,28 @@ +* NXP LPC18xx EEPROM memory NVMEM driver + +Required properties: + - compatible: Should be "nxp,lpc1857-eeprom" + - reg: Must contain an entry with the physical base address and length + for each entry in reg-names. + - reg-names: Must include the following entries. + - reg: EEPROM registers. + - mem: EEPROM address space. + - clocks: Must contain an entry for each entry in clock-names. + - clock-names: Must include the following entries. + - eeprom: EEPROM operating clock. + - resets: Should contain a reference to the reset controller asserting + the EEPROM in reset. + - interrupts: Should contain EEPROM interrupt. + +Example: + + eeprom: eeprom@4000e000 { + compatible = "nxp,lpc1857-eeprom"; + reg = <0x4000e000 0x1000>, + <0x20040000 0x4000>; + reg-names = "reg", "mem"; + clocks = <&ccu1 CLK_CPU_EEPROM>; + clock-names = "eeprom"; + resets = <&rgu 27>; + interrupts = <4>; + }; -- cgit v1.2.3 From f02f8aee211a1fc035cd3f87af8a39126eda1c20 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 7 Dec 2015 10:57:39 +0000 Subject: nvmem: NXP LPC18xx EEPROM memory NVMEM driver This commit adds support for NXP LPC18xx EEPROM memory found in NXP LPC185x/3x and LPC435x/3x/2x/1x devices. EEPROM size is 16384 bytes and it can be entirely read and written/erased with 1 word (4 bytes) granularity. The last page (128 bytes) contains the EEPROM initialization data and is not writable. Erase/program time is less than 3ms. The EEPROM device requires a ~1500 kHz clock (min 800 kHz, max 1600 kHz) that is generated dividing the system bus clock by the division factor, contained in the divider register (minus 1 encoded). EEPROM will be kept in Power Down mode except during read/write calls. Signed-off-by: Ariel D'Alessandro Acked-by: Stefan Wahren Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 9 ++ drivers/nvmem/Makefile | 2 + drivers/nvmem/lpc18xx_eeprom.c | 330 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/nvmem/lpc18xx_eeprom.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index bc4ea585b42e..6ff1b5051ae1 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -25,6 +25,15 @@ config NVMEM_IMX_OCOTP This driver can also be built as a module. If so, the module will be called nvmem-imx-ocotp. +config NVMEM_LPC18XX_EEPROM + tristate "NXP LPC18XX EEPROM Memory Support" + depends on ARCH_LPC18XX || COMPILE_TEST + help + Say Y here to include support for NXP LPC18xx EEPROM memory found in + NXP LPC185x/3x and LPC435x/3x/2x/1x devices. + To compile this driver as a module, choose M here: the module + will be called nvmem_lpc18xx_eeprom. + config NVMEM_MXS_OCOTP tristate "Freescale MXS On-Chip OTP Memory Support" depends on ARCH_MXS || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 95dde3f8f085..c14a55644c5f 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -8,6 +8,8 @@ nvmem_core-y := core.o # Devices obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o nvmem-imx-ocotp-y := imx-ocotp.o +obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o +nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o nvmem-mxs-ocotp-y := mxs-ocotp.o obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c new file mode 100644 index 000000000000..878fce789341 --- /dev/null +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -0,0 +1,330 @@ +/* + * NXP LPC18xx/LPC43xx EEPROM memory NVMEM driver + * + * Copyright (c) 2015 Ariel D'Alessandro + * + * 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 + +/* Registers */ +#define LPC18XX_EEPROM_AUTOPROG 0x00c +#define LPC18XX_EEPROM_AUTOPROG_WORD 0x1 + +#define LPC18XX_EEPROM_CLKDIV 0x014 + +#define LPC18XX_EEPROM_PWRDWN 0x018 +#define LPC18XX_EEPROM_PWRDWN_NO 0x0 +#define LPC18XX_EEPROM_PWRDWN_YES 0x1 + +#define LPC18XX_EEPROM_INTSTAT 0xfe0 +#define LPC18XX_EEPROM_INTSTAT_END_OF_PROG BIT(2) + +#define LPC18XX_EEPROM_INTSTATCLR 0xfe8 +#define LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST BIT(2) + +/* Fixed page size (bytes) */ +#define LPC18XX_EEPROM_PAGE_SIZE 0x80 + +/* EEPROM device requires a ~1500 kHz clock (min 800 kHz, max 1600 kHz) */ +#define LPC18XX_EEPROM_CLOCK_HZ 1500000 + +/* EEPROM requires 3 ms of erase/program time between each writing */ +#define LPC18XX_EEPROM_PROGRAM_TIME 3 + +struct lpc18xx_eeprom_dev { + struct clk *clk; + void __iomem *reg_base; + void __iomem *mem_base; + struct nvmem_device *nvmem; + unsigned reg_bytes; + unsigned val_bytes; +}; + +static struct regmap_config lpc18xx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static inline void lpc18xx_eeprom_writel(struct lpc18xx_eeprom_dev *eeprom, + u32 reg, u32 val) +{ + writel(val, eeprom->reg_base + reg); +} + +static inline u32 lpc18xx_eeprom_readl(struct lpc18xx_eeprom_dev *eeprom, + u32 reg) +{ + return readl(eeprom->reg_base + reg); +} + +static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom) +{ + unsigned long end; + u32 val; + + /* Wait until EEPROM program operation has finished */ + end = jiffies + msecs_to_jiffies(LPC18XX_EEPROM_PROGRAM_TIME * 10); + + while (time_is_after_jiffies(end)) { + val = lpc18xx_eeprom_readl(eeprom, LPC18XX_EEPROM_INTSTAT); + + if (val & LPC18XX_EEPROM_INTSTAT_END_OF_PROG) { + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_INTSTATCLR, + LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST); + return 0; + } + + usleep_range(LPC18XX_EEPROM_PROGRAM_TIME * USEC_PER_MSEC, + (LPC18XX_EEPROM_PROGRAM_TIME + 1) * USEC_PER_MSEC); + } + + return -ETIMEDOUT; +} + +static int lpc18xx_eeprom_gather_write(void *context, const void *reg, + size_t reg_size, const void *val, + size_t val_size) +{ + struct lpc18xx_eeprom_dev *eeprom = context; + unsigned int offset = *(u32 *)reg; + int ret; + + if (offset % lpc18xx_regmap_config.reg_stride) + return -EINVAL; + + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN, + LPC18XX_EEPROM_PWRDWN_NO); + + /* Wait 100 us while the EEPROM wakes up */ + usleep_range(100, 200); + + while (val_size) { + writel(*(u32 *)val, eeprom->mem_base + offset); + ret = lpc18xx_eeprom_busywait_until_prog(eeprom); + if (ret < 0) + return ret; + + val_size -= eeprom->val_bytes; + val += eeprom->val_bytes; + offset += eeprom->val_bytes; + } + + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN, + LPC18XX_EEPROM_PWRDWN_YES); + + return 0; +} + +static int lpc18xx_eeprom_write(void *context, const void *data, size_t count) +{ + struct lpc18xx_eeprom_dev *eeprom = context; + unsigned int offset = eeprom->reg_bytes; + + if (count <= offset) + return -EINVAL; + + return lpc18xx_eeprom_gather_write(context, data, eeprom->reg_bytes, + data + offset, count - offset); +} + +static int lpc18xx_eeprom_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct lpc18xx_eeprom_dev *eeprom = context; + unsigned int offset = *(u32 *)reg; + + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN, + LPC18XX_EEPROM_PWRDWN_NO); + + /* Wait 100 us while the EEPROM wakes up */ + usleep_range(100, 200); + + while (val_size) { + *(u32 *)val = readl(eeprom->mem_base + offset); + val_size -= eeprom->val_bytes; + val += eeprom->val_bytes; + offset += eeprom->val_bytes; + } + + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN, + LPC18XX_EEPROM_PWRDWN_YES); + + return 0; +} + +static struct regmap_bus lpc18xx_eeprom_bus = { + .write = lpc18xx_eeprom_write, + .gather_write = lpc18xx_eeprom_gather_write, + .read = lpc18xx_eeprom_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +static bool lpc18xx_eeprom_writeable_reg(struct device *dev, unsigned int reg) +{ + /* + * The last page contains the EEPROM initialization data and is not + * writable. + */ + return reg <= lpc18xx_regmap_config.max_register - + LPC18XX_EEPROM_PAGE_SIZE; +} + +static bool lpc18xx_eeprom_readable_reg(struct device *dev, unsigned int reg) +{ + return reg <= lpc18xx_regmap_config.max_register; +} + +static struct nvmem_config lpc18xx_nvmem_config = { + .name = "lpc18xx-eeprom", + .owner = THIS_MODULE, +}; + +static int lpc18xx_eeprom_probe(struct platform_device *pdev) +{ + struct lpc18xx_eeprom_dev *eeprom; + struct device *dev = &pdev->dev; + struct reset_control *rst; + unsigned long clk_rate; + struct regmap *regmap; + struct resource *res; + int ret; + + eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); + eeprom->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(eeprom->reg_base)) + return PTR_ERR(eeprom->reg_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + eeprom->mem_base = devm_ioremap_resource(dev, res); + if (IS_ERR(eeprom->mem_base)) + return PTR_ERR(eeprom->mem_base); + + eeprom->clk = devm_clk_get(&pdev->dev, "eeprom"); + if (IS_ERR(eeprom->clk)) { + dev_err(&pdev->dev, "failed to get eeprom clock\n"); + return PTR_ERR(eeprom->clk); + } + + ret = clk_prepare_enable(eeprom->clk); + if (ret < 0) { + dev_err(dev, "failed to prepare/enable eeprom clk: %d\n", ret); + return ret; + } + + rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get reset: %ld\n", PTR_ERR(rst)); + ret = PTR_ERR(rst); + goto err_clk; + } + + ret = reset_control_assert(rst); + if (ret < 0) { + dev_err(dev, "failed to assert reset: %d\n", ret); + goto err_clk; + } + + eeprom->val_bytes = lpc18xx_regmap_config.val_bits / BITS_PER_BYTE; + eeprom->reg_bytes = lpc18xx_regmap_config.reg_bits / BITS_PER_BYTE; + + /* + * Clock rate is generated by dividing the system bus clock by the + * division factor, contained in the divider register (minus 1 encoded). + */ + clk_rate = clk_get_rate(eeprom->clk); + clk_rate = DIV_ROUND_UP(clk_rate, LPC18XX_EEPROM_CLOCK_HZ) - 1; + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_CLKDIV, clk_rate); + + /* + * Writing a single word to the page will start the erase/program cycle + * automatically + */ + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_AUTOPROG, + LPC18XX_EEPROM_AUTOPROG_WORD); + + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN, + LPC18XX_EEPROM_PWRDWN_YES); + + lpc18xx_regmap_config.max_register = resource_size(res) - 1; + lpc18xx_regmap_config.writeable_reg = lpc18xx_eeprom_writeable_reg; + lpc18xx_regmap_config.readable_reg = lpc18xx_eeprom_readable_reg; + + regmap = devm_regmap_init(dev, &lpc18xx_eeprom_bus, eeprom, + &lpc18xx_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "regmap init failed: %ld\n", PTR_ERR(regmap)); + ret = PTR_ERR(regmap); + goto err_clk; + } + + lpc18xx_nvmem_config.dev = dev; + + eeprom->nvmem = nvmem_register(&lpc18xx_nvmem_config); + if (IS_ERR(eeprom->nvmem)) { + ret = PTR_ERR(eeprom->nvmem); + goto err_clk; + } + + platform_set_drvdata(pdev, eeprom); + + return 0; + +err_clk: + clk_disable_unprepare(eeprom->clk); + + return ret; +} + +static int lpc18xx_eeprom_remove(struct platform_device *pdev) +{ + struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev); + int ret; + + ret = nvmem_unregister(eeprom->nvmem); + if (ret < 0) + return ret; + + clk_disable_unprepare(eeprom->clk); + + return 0; +} + +static const struct of_device_id lpc18xx_eeprom_of_match[] = { + { .compatible = "nxp,lpc1857-eeprom" }, + { }, +}; +MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match); + +static struct platform_driver lpc18xx_eeprom_driver = { + .probe = lpc18xx_eeprom_probe, + .remove = lpc18xx_eeprom_remove, + .driver = { + .name = "lpc18xx-eeprom", + .of_match_table = lpc18xx_eeprom_of_match, + }, +}; + +module_platform_driver(lpc18xx_eeprom_driver); + +MODULE_AUTHOR("Ariel D'Alessandro "); +MODULE_DESCRIPTION("NXP LPC18xx EEPROM memory Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b3c9f95dec595d31cb79cceaf7ca85da21012eef Mon Sep 17 00:00:00 2001 From: Andrew-CT Chen Date: Mon, 7 Dec 2015 10:57:49 +0000 Subject: dt-bindings: add document of mediatek efuse driver Add Mediatek MT8173 EFUSE Devicetree binding file Signed-off-by: Andrew-CT Chen Reviewed-by: Sascha Hauer Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/mtk-efuse.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/mtk-efuse.txt diff --git a/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt new file mode 100644 index 000000000000..74cf52908a6c --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt @@ -0,0 +1,36 @@ += Mediatek MTK-EFUSE device tree bindings = + +This binding is intended to represent MTK-EFUSE which is found in most Mediatek SOCs. + +Required properties: +- compatible: should be "mediatek,mt8173-efuse" or "mediatek,efuse" +- reg: Should contain registers location and length + += Data cells = +Are child nodes of MTK-EFUSE, bindings of which as described in +bindings/nvmem/nvmem.txt + +Example: + + efuse: efuse@10206000 { + compatible = "mediatek,mt8173-efuse"; + reg = <0 0x10206000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells */ + thermal_calibration: calib@528 { + reg = <0x528 0xc>; + }; + }; + += Data consumers = +Are device nodes which consume nvmem data cells. + +For example: + + thermal { + ... + nvmem-cells = <&thermal_calibration>; + nvmem-cell-names = "calibration"; + }; -- cgit v1.2.3 From 4c7e4fe3776693ee4554ca1b3a2c728c1f8f361a Mon Sep 17 00:00:00 2001 From: Andrew-CT Chen Date: Mon, 7 Dec 2015 10:58:11 +0000 Subject: nvmem: mediatek: Add Mediatek EFUSE driver Add Mediatek EFUSE driver to access hardware data like thermal sensor calibration or HDMI impedance. Signed-off-by: Andrew-CT Chen Reviewed-by: Sascha Hauer Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 11 ++++++ drivers/nvmem/Makefile | 2 ++ drivers/nvmem/mtk-efuse.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 drivers/nvmem/mtk-efuse.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 6ff1b5051ae1..5bd18cc1a69c 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -45,6 +45,17 @@ config NVMEM_MXS_OCOTP This driver can also be built as a module. If so, the module will be called nvmem-mxs-ocotp. +config MTK_EFUSE + tristate "Mediatek SoCs EFUSE support" + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP_MMIO + help + This is a driver to access hardware related data like sensor + calibration, HDMI impedance etc. + + This driver can also be built as a module. If so, the module + will be called efuse-mtk. + config QCOM_QFPROM tristate "QCOM QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index c14a55644c5f..45ab1ae08fa9 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -12,6 +12,8 @@ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o nvmem-mxs-ocotp-y := mxs-ocotp.o +obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o +nvmem_mtk-efuse-y := mtk-efuse.o obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o nvmem_qfprom-y := qfprom.o obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c new file mode 100644 index 000000000000..7b35f5b630cd --- /dev/null +++ b/drivers/nvmem/mtk-efuse.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Andrew-CT Chen + * + * 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 +#include +#include +#include +#include + +static struct regmap_config mtk_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int mtk_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nvmem_device *nvmem; + struct nvmem_config *econfig; + struct regmap *regmap; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); + if (!econfig) + return -ENOMEM; + + mtk_regmap_config.max_register = resource_size(res) - 1; + + regmap = devm_regmap_init_mmio(dev, base, &mtk_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(regmap); + } + + econfig->dev = dev; + econfig->owner = THIS_MODULE; + nvmem = nvmem_register(econfig); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int mtk_efuse_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id mtk_efuse_of_match[] = { + { .compatible = "mediatek,mt8173-efuse",}, + { .compatible = "mediatek,efuse",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); + +static struct platform_driver mtk_efuse_driver = { + .probe = mtk_efuse_probe, + .remove = mtk_efuse_remove, + .driver = { + .name = "mediatek,efuse", + .of_match_table = mtk_efuse_of_match, + }, +}; +module_platform_driver(mtk_efuse_driver); +MODULE_AUTHOR("Andrew-CT Chen "); +MODULE_DESCRIPTION("Mediatek EFUSE driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 313a72ff983cc2e00ac4dcb791d40ebf2f9d5718 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 17 Nov 2015 09:12:41 +0000 Subject: nvmem: core: return error for non word aligned access nvmem providers have restrictions on register strides, so return error when users attempt to read/write buffers with sizes which are less than word size. Without this patch the userspace would continue to try as it does not get any error from the nvmem core, resulting in a hang or endless loop in userspace. Reported-by: Ariel D'Alessandro Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6fd4e5a5ef4a..9d11d9837312 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return 0; + if (count < nvmem->word_size) + return -EINVAL; + if (pos + count > nvmem->size) count = nvmem->size - pos; @@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return 0; + if (count < nvmem->word_size) + return -EINVAL; + if (pos + count > nvmem->size) count = nvmem->size - pos; -- cgit v1.2.3 From d16abd30b3faee8efab30ed6198d3696e0304b57 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Mon, 14 Dec 2015 09:43:21 +0000 Subject: nvmem: sunxi: trivial: fix code style this pacthset try to fix the code style for sunxi. Signed-off-by: Caesar Wang Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sunxi_sid.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index cfa3b85064dd..bc88b4084055 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -13,10 +13,8 @@ * 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 @@ -27,7 +25,6 @@ #include #include - static struct nvmem_config econfig = { .name = "sunxi-sid", .read_only = true, @@ -55,8 +52,8 @@ static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, } static int sunxi_sid_read(void *context, - const void *reg, size_t reg_size, - void *val, size_t val_size) + const void *reg, size_t reg_size, + void *val, size_t val_size) { struct sunxi_sid *sid = context; unsigned int offset = *(u32 *)reg; @@ -130,7 +127,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) if (IS_ERR(nvmem)) return PTR_ERR(nvmem); - randomness = kzalloc(sizeof(u8) * size, GFP_KERNEL); + randomness = kzalloc(sizeof(u8) * (size), GFP_KERNEL); if (!randomness) { ret = -EINVAL; goto err_unreg_nvmem; -- cgit v1.2.3 From c37ff3fbe06f44e8ec3f8077b3a3c468635a8868 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Mon, 14 Dec 2015 09:43:39 +0000 Subject: nvmem: rockchip: trivial: Make the driver more readability 1) Make the include file to sort from order 2) clean up the driver to make more readability Let's clean up such trivial details. Signed-off-by: Caesar Wang Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-efuse.c | 90 ++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index f55213424222..a009795111e9 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -14,16 +14,16 @@ * more details. */ -#include -#include -#include -#include +#include +#include #include #include #include -#include +#include +#include #include -#include +#include +#include #define EFUSE_A_SHIFT 6 #define EFUSE_A_MASK 0x3ff @@ -35,10 +35,10 @@ #define REG_EFUSE_CTRL 0x0000 #define REG_EFUSE_DOUT 0x0004 -struct rockchip_efuse_context { +struct rockchip_efuse_chip { struct device *dev; void __iomem *base; - struct clk *efuse_clk; + struct clk *clk; }; static int rockchip_efuse_write(void *context, const void *data, size_t count) @@ -52,34 +52,32 @@ static int rockchip_efuse_read(void *context, void *val, size_t val_size) { unsigned int offset = *(u32 *)reg; - struct rockchip_efuse_context *_context = context; - void __iomem *base = _context->base; - struct clk *clk = _context->efuse_clk; + struct rockchip_efuse_chip *efuse = context; u8 *buf = val; int ret; - ret = clk_prepare_enable(clk); + ret = clk_prepare_enable(efuse->clk); if (ret < 0) { - dev_err(_context->dev, "failed to prepare/enable efuse clk\n"); + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); return ret; } - writel(EFUSE_LOAD | EFUSE_PGENB, base + REG_EFUSE_CTRL); + writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL); udelay(1); while (val_size) { - writel(readl(base + REG_EFUSE_CTRL) & + writel(readl(efuse->base + REG_EFUSE_CTRL) & (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), - base + REG_EFUSE_CTRL); - writel(readl(base + REG_EFUSE_CTRL) | + efuse->base + REG_EFUSE_CTRL); + writel(readl(efuse->base + REG_EFUSE_CTRL) | ((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT), - base + REG_EFUSE_CTRL); + efuse->base + REG_EFUSE_CTRL); udelay(1); - writel(readl(base + REG_EFUSE_CTRL) | - EFUSE_STROBE, base + REG_EFUSE_CTRL); + writel(readl(efuse->base + REG_EFUSE_CTRL) | + EFUSE_STROBE, efuse->base + REG_EFUSE_CTRL); udelay(1); - *buf++ = readb(base + REG_EFUSE_DOUT); - writel(readl(base + REG_EFUSE_CTRL) & - (~EFUSE_STROBE), base + REG_EFUSE_CTRL); + *buf++ = readb(efuse->base + REG_EFUSE_DOUT); + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL); udelay(1); val_size -= 1; @@ -87,9 +85,9 @@ static int rockchip_efuse_read(void *context, } /* Switch to standby mode */ - writel(EFUSE_PGENB | EFUSE_CSB, base + REG_EFUSE_CTRL); + writel(EFUSE_PGENB | EFUSE_CSB, efuse->base + REG_EFUSE_CTRL); - clk_disable_unprepare(clk); + clk_disable_unprepare(efuse->clk); return 0; } @@ -114,48 +112,44 @@ static struct nvmem_config econfig = { }; static const struct of_device_id rockchip_efuse_match[] = { - { .compatible = "rockchip,rockchip-efuse",}, + { .compatible = "rockchip,rockchip-efuse", }, { /* sentinel */}, }; MODULE_DEVICE_TABLE(of, rockchip_efuse_match); static int rockchip_efuse_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; struct regmap *regmap; - void __iomem *base; - struct clk *clk; - struct rockchip_efuse_context *context; + struct rockchip_efuse_chip *efuse; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), + GFP_KERNEL); + if (!efuse) + return -ENOMEM; - context = devm_kzalloc(dev, sizeof(struct rockchip_efuse_context), - GFP_KERNEL); - if (IS_ERR(context)) - return PTR_ERR(context); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + efuse->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); - clk = devm_clk_get(dev, "pclk_efuse"); - if (IS_ERR(clk)) - return PTR_ERR(clk); + efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); + if (IS_ERR(efuse->clk)) + return PTR_ERR(efuse->clk); - context->dev = dev; - context->base = base; - context->efuse_clk = clk; + efuse->dev = &pdev->dev; rockchip_efuse_regmap_config.max_register = resource_size(res) - 1; - regmap = devm_regmap_init(dev, &rockchip_efuse_bus, - context, &rockchip_efuse_regmap_config); + regmap = devm_regmap_init(efuse->dev, &rockchip_efuse_bus, + efuse, &rockchip_efuse_regmap_config); if (IS_ERR(regmap)) { - dev_err(dev, "regmap init failed\n"); + dev_err(efuse->dev, "regmap init failed\n"); return PTR_ERR(regmap); } - econfig.dev = dev; + + econfig.dev = efuse->dev; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); -- cgit v1.2.3 From 2f9ba5b2f8420e0e5712080dafc773f7dd47c3df Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 14 Dec 2015 09:42:38 +0000 Subject: nvmem: core: return error for non word aligned access nvmem providers have restrictions on register strides, so return error when users attempt to read/write buffers with sizes which are less than word size. Without this patch the userspace would continue to try as it does not get any error from the nvmem core, resulting in a hang or endless loop in userspace. Reported-by: Ariel D'Alessandro Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6fd4e5a5ef4a..9d11d9837312 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return 0; + if (count < nvmem->word_size) + return -EINVAL; + if (pos + count > nvmem->size) count = nvmem->size - pos; @@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return 0; + if (count < nvmem->word_size) + return -EINVAL; + if (pos + count > nvmem->size) count = nvmem->size - pos; -- cgit v1.2.3 From 3b2b9ead32142b4cf55ea2793e5e4f7b63c04818 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 14 Dec 2015 09:42:57 +0000 Subject: nvmem: qfprom: Specify LE device endianness The qfprom is a little endian device, but so far we've been relying on the regmap mmio bus handling this for us without explicitly stating that fact. After commit 4a98da2164cf (regmap-mmio: Use native endianness for read/write, 2015-10-29), the regmap mmio bus will read/write with the __raw_*() IO accessors, instead of using the readl/writel() APIs that do proper byte swapping for little endian devices. So if we're running on a big endian processor and haven't specified the endianness explicitly in the regmap config or in DT, we're going to switch from doing little endian byte swapping to big endian accesses without byte swapping, leading to some confusing results. Specify the endianness explicitly so that the regmap core properly byte swaps the accesses for us. Cc: Rajendra Nayak Cc: Kevin Hilman Cc: Tyler Baker Cc: Simon Arlott Cc: Mark Brown Signed-off-by: Stephen Boyd Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qfprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index afb67e7eeee4..3829e5fbf8c3 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -21,6 +21,7 @@ static struct regmap_config qfprom_regmap_config = { .reg_bits = 32, .val_bits = 8, .reg_stride = 1, + .val_format_endian = REGMAP_ENDIAN_LITTLE, }; static struct nvmem_config econfig = { -- cgit v1.2.3 From ddce57a6f0a2d8d1bfacfa77f06043bc760403c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Feb 2016 15:27:36 +0100 Subject: ALSA: dummy: Implement timer backend switching more safely Currently the selected timer backend is referred at any moment from the running PCM callbacks. When the backend is switched, it's possible to lead to inconsistency from the running backend. This was pointed by syzkaller fuzzer, and the commit [7ee96216c31a: ALSA: dummy: Disable switching timer backend via sysfs] disabled the dynamic switching for avoiding the crash. This patch improves the handling of timer backend switching. It keeps the reference to the selected backend during the whole operation of an opened stream so that it won't be changed by other streams. Together with this change, the hrtimer parameter is reenabled as writable now. NOTE: this patch also turned out to fix the still remaining race. Namely, ops was still replaced dynamically at dummy_pcm_open: static int dummy_pcm_open(struct snd_pcm_substream *substream) { .... dummy->timer_ops = &dummy_systimer_ops; if (hrtimer) dummy->timer_ops = &dummy_hrtimer_ops; Since dummy->timer_ops is common among all streams, and when the replacement happens during accesses of other streams, it may lead to a crash. This was actually triggered by syzkaller fuzzer and KASAN. This patch rewrites the code not to use the ops shared by all streams any longer, too. BugLink: http://lkml.kernel.org/r/CACT4Y+aZ+xisrpuM6cOXbL21DuM0yVxPYXf4cD4Md9uw0C3dBQ@mail.gmail.com Reported-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/drivers/dummy.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index bde33308f0d6..c0f8f613f1f1 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -87,7 +87,7 @@ MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver."); module_param(fake_buffer, bool, 0444); MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations."); #ifdef CONFIG_HIGH_RES_TIMERS -module_param(hrtimer, bool, 0444); +module_param(hrtimer, bool, 0644); MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); #endif @@ -109,6 +109,9 @@ struct dummy_timer_ops { snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); }; +#define get_dummy_ops(substream) \ + (*(const struct dummy_timer_ops **)(substream)->runtime->private_data) + struct dummy_model { const char *name; int (*playback_constraints)(struct snd_pcm_runtime *runtime); @@ -137,7 +140,6 @@ struct snd_dummy { int iobox; struct snd_kcontrol *cd_volume_ctl; struct snd_kcontrol *cd_switch_ctl; - const struct dummy_timer_ops *timer_ops; }; /* @@ -231,6 +233,8 @@ static struct dummy_model *dummy_models[] = { */ struct dummy_systimer_pcm { + /* ops must be the first item */ + const struct dummy_timer_ops *timer_ops; spinlock_t lock; struct timer_list timer; unsigned long base_time; @@ -366,6 +370,8 @@ static const struct dummy_timer_ops dummy_systimer_ops = { */ struct dummy_hrtimer_pcm { + /* ops must be the first item */ + const struct dummy_timer_ops *timer_ops; ktime_t base_time; ktime_t period_time; atomic_t running; @@ -492,31 +498,25 @@ static const struct dummy_timer_ops dummy_hrtimer_ops = { static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - return dummy->timer_ops->start(substream); + return get_dummy_ops(substream)->start(substream); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - return dummy->timer_ops->stop(substream); + return get_dummy_ops(substream)->stop(substream); } return -EINVAL; } static int dummy_pcm_prepare(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - - return dummy->timer_ops->prepare(substream); + return get_dummy_ops(substream)->prepare(substream); } static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - - return dummy->timer_ops->pointer(substream); + return get_dummy_ops(substream)->pointer(substream); } static struct snd_pcm_hardware dummy_pcm_hardware = { @@ -562,17 +562,19 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) struct snd_dummy *dummy = snd_pcm_substream_chip(substream); struct dummy_model *model = dummy->model; struct snd_pcm_runtime *runtime = substream->runtime; + const struct dummy_timer_ops *ops; int err; - dummy->timer_ops = &dummy_systimer_ops; + ops = &dummy_systimer_ops; #ifdef CONFIG_HIGH_RES_TIMERS if (hrtimer) - dummy->timer_ops = &dummy_hrtimer_ops; + ops = &dummy_hrtimer_ops; #endif - err = dummy->timer_ops->create(substream); + err = ops->create(substream); if (err < 0) return err; + get_dummy_ops(substream) = ops; runtime->hw = dummy->pcm_hw; if (substream->pcm->device & 1) { @@ -594,7 +596,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) err = model->capture_constraints(substream->runtime); } if (err < 0) { - dummy->timer_ops->free(substream); + get_dummy_ops(substream)->free(substream); return err; } return 0; @@ -602,8 +604,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) static int dummy_pcm_close(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - dummy->timer_ops->free(substream); + get_dummy_ops(substream)->free(substream); return 0; } -- cgit v1.2.3 From 445f82492f2256b9a7c09292b458e152e7fae7f1 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 27 Dec 2015 18:46:04 +0800 Subject: vme: vme_ca91cx42.c: use to_pci_dev() Use to_pci_dev() instead of open-coding it. Signed-off-by: Geliang Tang Acked-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- drivers/vme/bridges/vme_ca91cx42.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index b79a74a98a23..5fbeab38889e 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -202,7 +202,7 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) bridge = ca91cx42_bridge->driver_priv; /* Need pdev */ - pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); + pdev = to_pci_dev(ca91cx42_bridge->parent); INIT_LIST_HEAD(&ca91cx42_bridge->vme_error_handlers); @@ -293,8 +293,7 @@ static void ca91cx42_irq_set(struct vme_bridge *ca91cx42_bridge, int level, iowrite32(tmp, bridge->base + LINT_EN); if ((state == 0) && (sync != 0)) { - pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, - dev); + pdev = to_pci_dev(ca91cx42_bridge->parent); synchronize_irq(pdev->irq); } @@ -518,7 +517,7 @@ static int ca91cx42_alloc_resource(struct vme_master_resource *image, dev_err(ca91cx42_bridge->parent, "Dev entry NULL\n"); return -EINVAL; } - pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); + pdev = to_pci_dev(ca91cx42_bridge->parent); existing_size = (unsigned long long)(image->bus_resource.end - image->bus_resource.start); @@ -1519,7 +1518,7 @@ static void *ca91cx42_alloc_consistent(struct device *parent, size_t size, struct pci_dev *pdev; /* Find pci_dev container of dev */ - pdev = container_of(parent, struct pci_dev, dev); + pdev = to_pci_dev(parent); return pci_alloc_consistent(pdev, size, dma); } @@ -1530,7 +1529,7 @@ static void ca91cx42_free_consistent(struct device *parent, size_t size, struct pci_dev *pdev; /* Find pci_dev container of dev */ - pdev = container_of(parent, struct pci_dev, dev); + pdev = to_pci_dev(parent); pci_free_consistent(pdev, size, vaddr, dma); } -- cgit v1.2.3 From 20af74ef140f0fbc477f90817c58241107782e10 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 27 Dec 2015 18:46:05 +0800 Subject: devres: use to_pci_dev() Use to_pci_dev() instead of open-coding it. Signed-off-by: Geliang Tang Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- lib/devres.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/devres.c b/lib/devres.c index 8c85672639d3..cb1464c411a2 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -236,7 +236,7 @@ struct pcim_iomap_devres { static void pcim_iomap_release(struct device *gendev, void *res) { - struct pci_dev *dev = container_of(gendev, struct pci_dev, dev); + struct pci_dev *dev = to_pci_dev(gendev); struct pcim_iomap_devres *this = res; int i; -- cgit v1.2.3 From 59fd1214561921343305a0e9dc218bf3d40068f3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 8 Feb 2016 08:47:48 +0100 Subject: x86/mm/numa: Fix 32-bit memblock range truncation bug on 32-bit NUMA kernels The following commit: a0acda917284 ("acpi, numa, mem_hotplug: mark all nodes the kernel resides un-hotpluggable") Introduced numa_clear_kernel_node_hotplug(), which function is executed during early bootup, and which marks all currently reserved memblock regions as hot-memory-unswappable as well. y14sg1 reported that when running 32-bit NUMA kernels, the grsecurity/PAX kernel patch flagged a size overflow in this function: PAX: size overflow detected in function x86_numa_init arch/x86/mm/numa.c:691 [...] ... the reason for the overflow is that memblock_clear_hotplug() takes physical addresses as arguments, while the start/end variables used by numa_clear_kernel_node_hotplug() are 'unsigned long', which is 32-bit on PAE kernels, but which has 64-bit physical addresses. So on 32-bit PAE kernels that have physical memory above the 4GB boundary, we truncate a 64-bit physical address range to 32 bits and pass it to memblock_clear_hotplug(), which at minimum prevents the original memory-hotplug bugfix from working, but might have other side effects as well. The fix is to use the proper type to handle physical addresses, phys_addr_t. Reported-by: y14sg1 Cc: Andrew Morton Cc: Brad Spengler Cc: Chen Tang Cc: "H. Peter Anvin" Cc: Lai Jiangshan Cc: Linus Torvalds Cc: PaX Team Cc: Taku Izumi Cc: Tang Chen Cc: Thomas Gleixner Cc: Wen Congyang Cc: Yasuaki Ishimatsu Cc: Zhang Yanfei Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/mm/numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index c3b3f653ed0c..d04f8094bc23 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -469,7 +469,7 @@ static void __init numa_clear_kernel_node_hotplug(void) { int i, nid; nodemask_t numa_kernel_nodes = NODE_MASK_NONE; - unsigned long start, end; + phys_addr_t start, end; struct memblock_region *r; /* -- cgit v1.2.3 From b00663124c76f69e71d118d778842471e85c6f11 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 6 Feb 2016 02:36:35 +0300 Subject: mmc: mmc_spi: add checks for dma mapping error There is no checks for dma mapping errors in mmc_spi. Tha patch fixes that and by the way it adds dma_unmap_single(ones_dma) that was left on a failure path mmc_spi_probe(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_spi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 1c1b45ef3faf..3446097a43c0 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -925,6 +925,10 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, dma_addr = dma_map_page(dma_dev, sg_page(sg), 0, PAGE_SIZE, dir); + if (dma_mapping_error(dma_dev, dma_addr)) { + data->error = -EFAULT; + break; + } if (direction == DMA_TO_DEVICE) t->tx_dma = dma_addr + sg->offset; else @@ -1393,10 +1397,12 @@ static int mmc_spi_probe(struct spi_device *spi) host->dma_dev = dev; host->ones_dma = dma_map_single(dev, ones, MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); + if (dma_mapping_error(dev, host->ones_dma)) + goto fail_ones_dma; host->data_dma = dma_map_single(dev, host->data, sizeof(*host->data), DMA_BIDIRECTIONAL); - - /* REVISIT in theory those map operations can fail... */ + if (dma_mapping_error(dev, host->data_dma)) + goto fail_data_dma; dma_sync_single_for_cpu(host->dma_dev, host->data_dma, sizeof(*host->data), @@ -1462,6 +1468,11 @@ fail_glue_init: if (host->dma_dev) dma_unmap_single(host->dma_dev, host->data_dma, sizeof(*host->data), DMA_BIDIRECTIONAL); +fail_data_dma: + if (host->dma_dev) + dma_unmap_single(host->dma_dev, host->ones_dma, + MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); +fail_ones_dma: kfree(host->data); fail_nobuf1: -- cgit v1.2.3 From 07e7716c746dd4e43211903eac954a18192d3d14 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 8 Feb 2016 15:17:57 +0100 Subject: mmc: pxamci: fix the device-tree probe deferral path When the gpio driver is probed after the mmc one, the read/write gpio and card detection one return -EPROBE_DEFER. Unfortunately, the memory region remains requested, and upon the next probe, the probe will fail anyway with -EBUSY. Fix this by releasing the memory resource upon probe failure. More broadly, this patch uses devm_*() primitives whenever possible in the probe function. Signed-off-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 28a057fae0a1..da824772bbb4 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -86,7 +86,7 @@ struct pxamci_host { static inline void pxamci_init_ocr(struct pxamci_host *host) { #ifdef CONFIG_REGULATOR - host->vcc = regulator_get_optional(mmc_dev(host->mmc), "vmmc"); + host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc"); if (IS_ERR(host->vcc)) host->vcc = NULL; @@ -654,12 +654,8 @@ static int pxamci_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!r || irq < 0) - return -ENXIO; - - r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); - if (!r) - return -EBUSY; + if (irq < 0) + return irq; mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); if (!mmc) { @@ -695,7 +691,7 @@ static int pxamci_probe(struct platform_device *pdev) host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; - host->clk = clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; @@ -727,9 +723,9 @@ static int pxamci_probe(struct platform_device *pdev) host->irq = irq; host->imask = MMC_I_MASK_ALL; - host->base = ioremap(r->start, SZ_4K); - if (!host->base) { - ret = -ENOMEM; + host->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); goto out; } @@ -742,7 +738,8 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); + ret = devm_request_irq(&pdev->dev, host->irq, pxamci_irq, 0, + DRIVER_NAME, host); if (ret) goto out; @@ -833,14 +830,9 @@ out: dma_release_channel(host->dma_chan_rx); if (host->dma_chan_tx) dma_release_channel(host->dma_chan_tx); - if (host->base) - iounmap(host->base); - if (host->clk) - clk_put(host->clk); } if (mmc) mmc_free_host(mmc); - release_resource(r); return ret; } @@ -859,9 +851,6 @@ static int pxamci_remove(struct platform_device *pdev) gpio_ro = host->pdata->gpio_card_ro; gpio_power = host->pdata->gpio_power; } - if (host->vcc) - regulator_put(host->vcc); - if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); @@ -870,16 +859,10 @@ static int pxamci_remove(struct platform_device *pdev) END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); - free_irq(host->irq, host); dmaengine_terminate_all(host->dma_chan_rx); dmaengine_terminate_all(host->dma_chan_tx); dma_release_channel(host->dma_chan_rx); dma_release_channel(host->dma_chan_tx); - iounmap(host->base); - - clk_put(host->clk); - - release_resource(host->res); mmc_free_host(mmc); } -- cgit v1.2.3 From 902c136fe4f72dfc2a616ad755c72f1ee407f79a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Feb 2016 10:45:36 +0530 Subject: ASoC: Intel: Revert "ASoC: Intel: fix ACPI probe regression with Atom DPCM driver" This reverts commit dc901a354171 ("ASoC: Intel: fix ACPI probe regression with Atom DPCM driver") as the fix prevented the probe on HSW/BDW if Atom-DPCM was selected Acked-by: Jie Yang Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 668fdeee195e..3b9332e7a094 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,10 +1,5 @@ snd-soc-sst-dsp-objs := sst-dsp.o -ifneq ($(CONFIG_SND_SST_IPC_ACPI),) -snd-soc-sst-acpi-objs := sst-match-acpi.o -else snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o -endif - snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o -- cgit v1.2.3 From 2dcffcee23a2bd491a8c4041db3a8041b23fa4eb Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Feb 2016 10:45:37 +0530 Subject: ASoC: Intel: Create independent acpi match module The ACPI match module is common to all three drivers, HSW, SKL and Atom-DPCM driver. But Atom-DPCM driver does not use common sst code so we cannot include the common SST module in Atom-DPCM driver. So the solution is to have a independent sst-match-acpi module which helps in matching for all the three drivers. Now all driver can be inbuilt in a single image This patch really fixes the regression introduced by the commit 95f098014815 ("ASoC: Intel: Move apci find machine routines") Acked-by: Jie Yang Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 9 +++++++++ sound/soc/intel/common/Makefile | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 803f95e40679..af7aabbc0977 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -30,11 +30,15 @@ config SND_SST_IPC_ACPI config SND_SOC_INTEL_SST tristate select SND_SOC_INTEL_SST_ACPI if ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI depends on (X86 || COMPILE_TEST) config SND_SOC_INTEL_SST_ACPI tristate +config SND_SOC_INTEL_SST_MATCH + tristate + config SND_SOC_INTEL_HASWELL tristate @@ -97,6 +101,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5640 audio codec. @@ -109,6 +114,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH select SND_SOC_RT5651 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5651 audio codec. @@ -121,6 +127,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH select SND_SOC_RT5670 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5672 audio codec. @@ -133,6 +140,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5645/5650 audio codec. @@ -145,6 +153,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH select SND_SOC_TS3A227E select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with MAX98090 audio codec it also can support TI jack chip as aux device. diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 3b9332e7a094..fbbb25c2ceed 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,8 +1,10 @@ snd-soc-sst-dsp-objs := sst-dsp.o -snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o +snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o +obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o -- cgit v1.2.3 From cfffcc66a89ab6d9961b2cde6cdab2ba056451ad Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 8 Feb 2016 10:45:38 +0530 Subject: ASoC: Intel: Load the atom DPCM driver only DPCM driver is recommended for BYT, CHT based platforms, so if CONFIG_SND_SST_IPC_ACPI is selected then don't compile the BYT Device IDs in common ACPI driver to avoid probe conflicts. Signed-off-by: Pierre-Louis Bossart Acked-by: Jie Yang Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 4 ++-- sound/soc/intel/common/sst-acpi.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index af7aabbc0977..7d7c872c280d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL_MACH config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n) + depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 @@ -73,7 +73,7 @@ config SND_SOC_INTEL_BYT_RT5640_MACH config SND_SOC_INTEL_BYT_MAX98090_MACH tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y + depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 7a85c576dad3..2c5eda14d510 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -215,6 +215,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; +#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) static struct sst_acpi_mach baytrail_machines[] = { { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, @@ -231,11 +232,14 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = { .sst_id = SST_DEV_ID_BYT, .resindex_dma_base = -1, }; +#endif static const struct acpi_device_id sst_acpi_match[] = { { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, +#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, +#endif { } }; MODULE_DEVICE_TABLE(acpi, sst_acpi_match); -- cgit v1.2.3 From 8ceffd229f0ef130530c79654e95b5fa007ae639 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Feb 2016 10:45:39 +0530 Subject: ASoC: Intel: Add module tags for common match module The match module lacked module license and description, so add it Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-match-acpi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index dd077e116d25..3b4539d21492 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -41,3 +41,6 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) return NULL; } EXPORT_SYMBOL_GPL(sst_acpi_find_machine); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); -- cgit v1.2.3 From 415e3d3e90ce9e18727e8843ae343eda5a58fad6 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 3 Feb 2016 02:11:03 +0100 Subject: unix: correctly track in-flight fds in sending process user_struct The commit referenced in the Fixes tag incorrectly accounted the number of in-flight fds over a unix domain socket to the original opener of the file-descriptor. This allows another process to arbitrary deplete the original file-openers resource limit for the maximum of open files. Instead the sending processes and its struct cred should be credited. To do so, we add a reference counted struct user_struct pointer to the scm_fp_list and use it to account for the number of inflight unix fds. Fixes: 712f4aad406bb1 ("unix: properly account for FDs passed over unix sockets") Reported-by: David Herrmann Cc: David Herrmann Cc: Willy Tarreau Cc: Linus Torvalds Suggested-by: Linus Torvalds Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/af_unix.h | 4 ++-- include/net/scm.h | 1 + net/core/scm.c | 7 +++++++ net/unix/af_unix.c | 4 ++-- net/unix/garbage.c | 8 ++++---- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 2a91a0561a47..9b4c418bebd8 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -6,8 +6,8 @@ #include #include -void unix_inflight(struct file *fp); -void unix_notinflight(struct file *fp); +void unix_inflight(struct user_struct *user, struct file *fp); +void unix_notinflight(struct user_struct *user, struct file *fp); void unix_gc(void); void wait_for_unix_gc(void); struct sock *unix_get_socket(struct file *filp); diff --git a/include/net/scm.h b/include/net/scm.h index 262532d111f5..59fa93c01d2a 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -21,6 +21,7 @@ struct scm_creds { struct scm_fp_list { short count; short max; + struct user_struct *user; struct file *fp[SCM_MAX_FD]; }; diff --git a/net/core/scm.c b/net/core/scm.c index 14596fb37172..2696aefdc148 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -87,6 +87,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) *fplp = fpl; fpl->count = 0; fpl->max = SCM_MAX_FD; + fpl->user = NULL; } fpp = &fpl->fp[fpl->count]; @@ -107,6 +108,10 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) *fpp++ = file; fpl->count++; } + + if (!fpl->user) + fpl->user = get_uid(current_user()); + return num; } @@ -119,6 +124,7 @@ void __scm_destroy(struct scm_cookie *scm) scm->fp = NULL; for (i=fpl->count-1; i>=0; i--) fput(fpl->fp[i]); + free_uid(fpl->user); kfree(fpl); } } @@ -336,6 +342,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) for (i = 0; i < fpl->count; i++) get_file(fpl->fp[i]); new_fpl->max = new_fpl->count; + new_fpl->user = get_uid(fpl->user); } return new_fpl; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 49d5093eb055..29be035f9c65 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1496,7 +1496,7 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb) UNIXCB(skb).fp = NULL; for (i = scm->fp->count-1; i >= 0; i--) - unix_notinflight(scm->fp->fp[i]); + unix_notinflight(scm->fp->user, scm->fp->fp[i]); } static void unix_destruct_scm(struct sk_buff *skb) @@ -1561,7 +1561,7 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) return -ENOMEM; for (i = scm->fp->count - 1; i >= 0; i--) - unix_inflight(scm->fp->fp[i]); + unix_inflight(scm->fp->user, scm->fp->fp[i]); return max_level; } diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 8fcdc2283af5..6a0d48525fcf 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -116,7 +116,7 @@ struct sock *unix_get_socket(struct file *filp) * descriptor if it is for an AF_UNIX socket. */ -void unix_inflight(struct file *fp) +void unix_inflight(struct user_struct *user, struct file *fp) { struct sock *s = unix_get_socket(fp); @@ -133,11 +133,11 @@ void unix_inflight(struct file *fp) } unix_tot_inflight++; } - fp->f_cred->user->unix_inflight++; + user->unix_inflight++; spin_unlock(&unix_gc_lock); } -void unix_notinflight(struct file *fp) +void unix_notinflight(struct user_struct *user, struct file *fp) { struct sock *s = unix_get_socket(fp); @@ -152,7 +152,7 @@ void unix_notinflight(struct file *fp) list_del_init(&u->link); unix_tot_inflight--; } - fp->f_cred->user->unix_inflight--; + user->unix_inflight--; spin_unlock(&unix_gc_lock); } -- cgit v1.2.3 From 44c3d0c1c0a880354e9de5d94175742e2c7c9683 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 2 Feb 2016 17:55:01 -0800 Subject: ipv6: fix a lockdep splat Silence lockdep false positive about rcu_dereference() being used in the wrong context. First one should use rcu_dereference_protected() as we own the spinlock. Second one should be a normal assignation, as no barrier is needed. Fixes: 18367681a10bd ("ipv6 flowlabel: Convert np->ipv6_fl_list to RCU.") Reported-by: Dave Jones Signed-off-by: Eric Dumazet Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/ip6_flowlabel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 1f9ebe3cbb4a..dc2db4f7b182 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -540,12 +540,13 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) } spin_lock_bh(&ip6_sk_fl_lock); for (sflp = &np->ipv6_fl_list; - (sfl = rcu_dereference(*sflp)) != NULL; + (sfl = rcu_dereference_protected(*sflp, + lockdep_is_held(&ip6_sk_fl_lock))) != NULL; sflp = &sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) np->flow_label &= ~IPV6_FLOWLABEL_MASK; - *sflp = rcu_dereference(sfl->next); + *sflp = sfl->next; spin_unlock_bh(&ip6_sk_fl_lock); fl_release(sfl->fl); kfree_rcu(sfl, rcu); -- cgit v1.2.3 From cd474ba0d6048aeefe6f1066a6bfb5eac36a2a81 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 10:21:23 -0500 Subject: drm/amdgpu: add pcie cap module parameters (v2) Allows the user to force the supported pcie gen and lane config on both the asic and the chipset. Useful for debugging pcie problems and for virtualization where we may not be able to query the pcie bridge caps. Default to: gen: chipset 1/2, asic 1/2/3 lanes: 1/2/4/8/16 v2: fix bare metal case Reviewed-by: monk liu Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 147 ++++++++++++++++------------- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++ 3 files changed, 92 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 82edf95b7740..4021c8a97fc7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -87,6 +87,8 @@ extern int amdgpu_sched_jobs; extern int amdgpu_sched_hw_submission; extern int amdgpu_enable_semaphores; extern int amdgpu_powerplay; +extern unsigned amdgpu_pcie_gen_cap; +extern unsigned amdgpu_pcie_lane_cap; #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000 #define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 65531463f88e..85991cee62e6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1933,80 +1933,97 @@ retry: return r; } +#define AMDGPU_DEFAULT_PCIE_GEN_MASK 0x30007 /* gen: chipset 1/2, asic 1/2/3 */ +#define AMDGPU_DEFAULT_PCIE_MLW_MASK 0x2f0000 /* 1/2/4/8/16 lanes */ + void amdgpu_get_pcie_info(struct amdgpu_device *adev) { u32 mask; int ret; - if (pci_is_root_bus(adev->pdev->bus)) - return; + if (amdgpu_pcie_gen_cap) + adev->pm.pcie_gen_mask = amdgpu_pcie_gen_cap; - if (amdgpu_pcie_gen2 == 0) - return; + if (amdgpu_pcie_lane_cap) + adev->pm.pcie_mlw_mask = amdgpu_pcie_lane_cap; - if (adev->flags & AMD_IS_APU) + /* covers APUs as well */ + if (pci_is_root_bus(adev->pdev->bus)) { + if (adev->pm.pcie_gen_mask == 0) + adev->pm.pcie_gen_mask = AMDGPU_DEFAULT_PCIE_GEN_MASK; + if (adev->pm.pcie_mlw_mask == 0) + adev->pm.pcie_mlw_mask = AMDGPU_DEFAULT_PCIE_MLW_MASK; return; + } - ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask); - if (!ret) { - adev->pm.pcie_gen_mask = (CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN1 | - CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN2 | - CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN3); - - if (mask & DRM_PCIE_SPEED_25) - adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN1; - if (mask & DRM_PCIE_SPEED_50) - adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN2; - if (mask & DRM_PCIE_SPEED_80) - adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3; - } - ret = drm_pcie_get_max_link_width(adev->ddev, &mask); - if (!ret) { - switch (mask) { - case 32: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X32 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X16 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 16: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X16 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 12: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 8: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 4: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 2: - adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | - CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); - break; - case 1: - adev->pm.pcie_mlw_mask = CAIL_PCIE_LINK_WIDTH_SUPPORT_X1; - break; - default: - break; + if (adev->pm.pcie_gen_mask == 0) { + ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask); + if (!ret) { + adev->pm.pcie_gen_mask = (CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN1 | + CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN2 | + CAIL_ASIC_PCIE_LINK_SPEED_SUPPORT_GEN3); + + if (mask & DRM_PCIE_SPEED_25) + adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN1; + if (mask & DRM_PCIE_SPEED_50) + adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN2; + if (mask & DRM_PCIE_SPEED_80) + adev->pm.pcie_gen_mask |= CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3; + } else { + adev->pm.pcie_gen_mask = AMDGPU_DEFAULT_PCIE_GEN_MASK; + } + } + if (adev->pm.pcie_mlw_mask == 0) { + ret = drm_pcie_get_max_link_width(adev->ddev, &mask); + if (!ret) { + switch (mask) { + case 32: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X32 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X16 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 16: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X16 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 12: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X12 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 8: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X8 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 4: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X4 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 2: + adev->pm.pcie_mlw_mask = (CAIL_PCIE_LINK_WIDTH_SUPPORT_X2 | + CAIL_PCIE_LINK_WIDTH_SUPPORT_X1); + break; + case 1: + adev->pm.pcie_mlw_mask = CAIL_PCIE_LINK_WIDTH_SUPPORT_X1; + break; + default: + break; + } + } else { + adev->pm.pcie_mlw_mask = AMDGPU_DEFAULT_PCIE_MLW_MASK; } } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 9c1af8976bef..9ef1db87cf26 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -83,6 +83,8 @@ int amdgpu_sched_jobs = 32; int amdgpu_sched_hw_submission = 2; int amdgpu_enable_semaphores = 0; int amdgpu_powerplay = -1; +unsigned amdgpu_pcie_gen_cap = 0; +unsigned amdgpu_pcie_lane_cap = 0; MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes"); module_param_named(vramlimit, amdgpu_vram_limit, int, 0600); @@ -170,6 +172,12 @@ MODULE_PARM_DESC(powerplay, "Powerplay component (1 = enable, 0 = disable, -1 = module_param_named(powerplay, amdgpu_powerplay, int, 0444); #endif +MODULE_PARM_DESC(pcie_gen_cap, "PCIE Gen Caps (0: autodetect (default))"); +module_param_named(pcie_gen_cap, amdgpu_pcie_gen_cap, uint, 0444); + +MODULE_PARM_DESC(pcie_lane_cap, "PCIE Lane Caps (0: autodetect (default))"); +module_param_named(pcie_lane_cap, amdgpu_pcie_lane_cap, uint, 0444); + static struct pci_device_id pciidlist[] = { #ifdef CONFIG_DRM_AMDGPU_CIK /* Kaveri */ -- cgit v1.2.3 From 76ecb2c75bc772050f2e0462b9cf0163cc43046e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 10:33:59 -0500 Subject: drm/amdgpu/cik: don't mess with aspm if gpu is root bus Pcie registers may not be available in a virtualized environment. Reviewed-by: monk liu Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/cik.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c index fd9c9588ef46..5c978e064f47 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik.c +++ b/drivers/gpu/drm/amd/amdgpu/cik.c @@ -1762,6 +1762,9 @@ static void cik_program_aspm(struct amdgpu_device *adev) if (amdgpu_aspm == 0) return; + if (pci_is_root_bus(adev->pdev->bus)) + return; + /* XXX double check APUs */ if (adev->flags & AMD_IS_APU) return; -- cgit v1.2.3 From 50171ebecf87521056db2b3d5654c4348f32c9bd Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 10:44:04 -0500 Subject: drm/amdgpu/dpm/ci: switch over to the common pcie caps interface We already query this at driver init, so use that info. Also handles virtualization cases. Reviewed-by: monk liu Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/ci_dpm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index 8b4731d4e10e..474ca02b0949 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -31,6 +31,7 @@ #include "ci_dpm.h" #include "gfx_v7_0.h" #include "atom.h" +#include "amd_pcie.h" #include #include "smu/smu_7_0_1_d.h" @@ -5835,18 +5836,16 @@ static int ci_dpm_init(struct amdgpu_device *adev) u8 frev, crev; struct ci_power_info *pi; int ret; - u32 mask; pi = kzalloc(sizeof(struct ci_power_info), GFP_KERNEL); if (pi == NULL) return -ENOMEM; adev->pm.dpm.priv = pi; - ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask); - if (ret) - pi->sys_pcie_mask = 0; - else - pi->sys_pcie_mask = mask; + pi->sys_pcie_mask = + (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_MASK) >> + CAIL_PCIE_LINK_SPEED_SUPPORT_SHIFT; + pi->force_pcie_gen = AMDGPU_PCIE_GEN_INVALID; pi->pcie_gen_performance.max = AMDGPU_PCIE_GEN1; -- cgit v1.2.3 From b6df77fc5c42041a11ff094e5595d1e7379c917f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:26:56 -0500 Subject: drm/amdgpu: handle uvd pg flags properly Don't attempt to start/stop the uvd block if pg is disabled. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c | 5 ++++- drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c | 3 +++ drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index 5e9f73af83a8..9cb528740473 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -848,7 +848,10 @@ static int uvd_v4_2_set_powergating_state(void *handle, * revisit this when there is a cleaner line between * the smc and the hw blocks */ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + return 0; if (state == AMD_PG_STATE_GATE) { uvd_v4_2_stop(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index 38864f562981..b4623deac8ef 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -789,6 +789,9 @@ static int uvd_v5_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + return 0; + if (state == AMD_PG_STATE_GATE) { uvd_v5_0_stop(adev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index 3d5913926436..c41eda78f0b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -1030,6 +1030,9 @@ static int uvd_v6_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + return 0; + if (state == AMD_PG_STATE_GATE) { uvd_v6_0_stop(adev); return 0; -- cgit v1.2.3 From 808a934fd47c1c4a1670069cbe2fae7c23068b14 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:29:45 -0500 Subject: drm/amdgpu: handle vce pg flags properly Don't attempt to start/stop the vce block if pg is disabled. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/vce_v2_0.c | 3 +++ drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c index 52ac7a8f1e58..d3ce6085ccd2 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c @@ -608,6 +608,9 @@ static int vce_v2_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_VCE)) + return 0; + if (state == AMD_PG_STATE_GATE) /* XXX do we need a vce_v2_0_stop()? */ return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index e99af81e4aec..797d12c31475 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -728,6 +728,9 @@ static int vce_v3_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_VCE)) + return 0; + if (state == AMD_PG_STATE_GATE) /* XXX do we need a vce_v3_0_stop()? */ return 0; -- cgit v1.2.3 From 0fd4af9e328c0f694d21a646232a7a62da7ec4ae Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:31:32 -0500 Subject: drm/amdgpu: clean up vce pg flags for cz/st It was already disabled elsewhere, make it offical. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/vi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index 89f5a1ff6f43..0d14d108a6c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -1457,8 +1457,7 @@ static int vi_common_early_init(void *handle) case CHIP_STONEY: adev->has_uvd = true; adev->cg_flags = 0; - /* Disable UVD pg */ - adev->pg_flags = /* AMDGPU_PG_SUPPORT_UVD | */AMDGPU_PG_SUPPORT_VCE; + adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0x1; break; default: -- cgit v1.2.3 From 35e5912d0801184b57119383da003263a21eeed1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:33:56 -0500 Subject: drm/amdgpu: be consistent with uvd cg flags Don't do anything if the uvd cg flags are not set. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c | 3 +++ drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index 9cb528740473..c982524d9287 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -830,6 +830,9 @@ static int uvd_v4_2_set_clockgating_state(void *handle, bool gate = false; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) + return 0; + if (state == AMD_CG_STATE_GATE) gate = true; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index b4623deac8ef..aad1ab596bdc 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -774,6 +774,11 @@ static int uvd_v5_0_process_interrupt(struct amdgpu_device *adev, static int uvd_v5_0_set_clockgating_state(void *handle, enum amd_clockgating_state state) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) + return 0; + return 0; } -- cgit v1.2.3 From d4fdc08e251316e2e0710d02e65b4576ce7963d2 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:40:32 -0500 Subject: drm/amd/powerplay/cz: disable uvd pg Not working reliably yet. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c index 0874ab42ee95..8fc9e01a7e50 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c @@ -247,6 +247,8 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr) phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableVoltageIsland); + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_UVDPowerGating); return 0; } -- cgit v1.2.3 From 67a0a0fd11524bd9943635168f8380b9906fb389 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:42:24 -0500 Subject: drm/amd/powerplay/cz: disable vce pg Not working reliably yet. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c index 8fc9e01a7e50..80af87fa0bf0 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c @@ -249,6 +249,9 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr) phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDPowerGating); + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_VCEPowerGating); + return 0; } -- cgit v1.2.3 From 3d5afb41f82f55e6912678ea24d637b84c160d65 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:47:38 -0500 Subject: drm/amd/powerplay/tonga: disable uvd pg Not working reliably yet. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c index 44a925006479..7518caae7bed 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c @@ -4615,6 +4615,9 @@ int tonga_hwmgr_backend_init(struct pp_hwmgr *hwmgr) data->vddc_phase_shed_control = 0; + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_UVDPowerGating); + if (0 == result) { struct cgs_system_info sys_info = {0}; -- cgit v1.2.3 From f997e6f21308f0627c46caed0315ee005ef4775a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Feb 2016 23:48:51 -0500 Subject: drm/amd/powerplay/tonga: disable vce pg Not working reliably yet. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c index 7518caae7bed..69c81c14d89c 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c @@ -4617,6 +4617,8 @@ int tonga_hwmgr_backend_init(struct pp_hwmgr *hwmgr) phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDPowerGating); + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_VCEPowerGating); if (0 == result) { struct cgs_system_info sys_info = {0}; -- cgit v1.2.3 From 08d334087617ed9662d40db776c5d2c0a614315a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Feb 2016 10:34:28 -0500 Subject: drm/amdgpu: add a cgs interface to fetch cg and pg flags Needed to pass the cg and pg info to powerplay. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c | 6 ++++++ drivers/gpu/drm/amd/include/cgs_common.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index a081dda9fa2f..7a4b101e10c6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -795,6 +795,12 @@ static int amdgpu_cgs_query_system_info(void *cgs_device, case CGS_SYSTEM_INFO_PCIE_MLW: sys_info->value = adev->pm.pcie_mlw_mask; break; + case CGS_SYSTEM_INFO_CG_FLAGS: + sys_info->value = adev->cg_flags; + break; + case CGS_SYSTEM_INFO_PG_FLAGS: + sys_info->value = adev->pg_flags; + break; default: return -ENODEV; } diff --git a/drivers/gpu/drm/amd/include/cgs_common.h b/drivers/gpu/drm/amd/include/cgs_common.h index 713aec954692..aec38fc3834f 100644 --- a/drivers/gpu/drm/amd/include/cgs_common.h +++ b/drivers/gpu/drm/amd/include/cgs_common.h @@ -109,6 +109,8 @@ enum cgs_system_info_id { CGS_SYSTEM_INFO_ADAPTER_BDF_ID = 1, CGS_SYSTEM_INFO_PCIE_GEN_INFO, CGS_SYSTEM_INFO_PCIE_MLW, + CGS_SYSTEM_INFO_CG_FLAGS, + CGS_SYSTEM_INFO_PG_FLAGS, CGS_SYSTEM_INFO_ID_MAXIMUM, }; -- cgit v1.2.3 From b118af7012f9bd4bdbda12681ce66f91aabffd3f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Feb 2016 10:37:29 -0500 Subject: drm/amdgpu: remove unused cg defines Leftover from radeon. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 4021c8a97fc7..73a72eee4dc3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -134,15 +134,6 @@ extern unsigned amdgpu_pcie_lane_cap; #define AMDGPU_RESET_VCE (1 << 13) #define AMDGPU_RESET_VCE1 (1 << 14) -/* CG block flags */ -#define AMDGPU_CG_BLOCK_GFX (1 << 0) -#define AMDGPU_CG_BLOCK_MC (1 << 1) -#define AMDGPU_CG_BLOCK_SDMA (1 << 2) -#define AMDGPU_CG_BLOCK_UVD (1 << 3) -#define AMDGPU_CG_BLOCK_VCE (1 << 4) -#define AMDGPU_CG_BLOCK_HDP (1 << 5) -#define AMDGPU_CG_BLOCK_BIF (1 << 6) - /* CG flags */ #define AMDGPU_CG_SUPPORT_GFX_MGCG (1 << 0) #define AMDGPU_CG_SUPPORT_GFX_MGLS (1 << 1) -- cgit v1.2.3 From e3b04bc790ecd6d08d4699bc60b4f5a76f7f7b6b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Feb 2016 10:56:22 -0500 Subject: drma/dmgpu: move cg and pg flags into shared headers So they can be used by powerplay. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 32 ------- drivers/gpu/drm/amd/amdgpu/cik.c | 154 +++++++++++++++---------------- drivers/gpu/drm/amd/amdgpu/cik_sdma.c | 4 +- drivers/gpu/drm/amd/amdgpu/cz_dpm.c | 6 +- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 70 +++++++------- drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 10 +- drivers/gpu/drm/amd/amdgpu/kv_dpm.c | 8 +- drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c | 6 +- drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c | 4 +- drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c | 6 +- drivers/gpu/drm/amd/amdgpu/vce_v2_0.c | 4 +- drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 6 +- drivers/gpu/drm/amd/include/amd_shared.h | 32 +++++++ 13 files changed, 171 insertions(+), 171 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 73a72eee4dc3..6808facaf6af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -134,38 +134,6 @@ extern unsigned amdgpu_pcie_lane_cap; #define AMDGPU_RESET_VCE (1 << 13) #define AMDGPU_RESET_VCE1 (1 << 14) -/* CG flags */ -#define AMDGPU_CG_SUPPORT_GFX_MGCG (1 << 0) -#define AMDGPU_CG_SUPPORT_GFX_MGLS (1 << 1) -#define AMDGPU_CG_SUPPORT_GFX_CGCG (1 << 2) -#define AMDGPU_CG_SUPPORT_GFX_CGLS (1 << 3) -#define AMDGPU_CG_SUPPORT_GFX_CGTS (1 << 4) -#define AMDGPU_CG_SUPPORT_GFX_CGTS_LS (1 << 5) -#define AMDGPU_CG_SUPPORT_GFX_CP_LS (1 << 6) -#define AMDGPU_CG_SUPPORT_GFX_RLC_LS (1 << 7) -#define AMDGPU_CG_SUPPORT_MC_LS (1 << 8) -#define AMDGPU_CG_SUPPORT_MC_MGCG (1 << 9) -#define AMDGPU_CG_SUPPORT_SDMA_LS (1 << 10) -#define AMDGPU_CG_SUPPORT_SDMA_MGCG (1 << 11) -#define AMDGPU_CG_SUPPORT_BIF_LS (1 << 12) -#define AMDGPU_CG_SUPPORT_UVD_MGCG (1 << 13) -#define AMDGPU_CG_SUPPORT_VCE_MGCG (1 << 14) -#define AMDGPU_CG_SUPPORT_HDP_LS (1 << 15) -#define AMDGPU_CG_SUPPORT_HDP_MGCG (1 << 16) - -/* PG flags */ -#define AMDGPU_PG_SUPPORT_GFX_PG (1 << 0) -#define AMDGPU_PG_SUPPORT_GFX_SMG (1 << 1) -#define AMDGPU_PG_SUPPORT_GFX_DMG (1 << 2) -#define AMDGPU_PG_SUPPORT_UVD (1 << 3) -#define AMDGPU_PG_SUPPORT_VCE (1 << 4) -#define AMDGPU_PG_SUPPORT_CP (1 << 5) -#define AMDGPU_PG_SUPPORT_GDS (1 << 6) -#define AMDGPU_PG_SUPPORT_RLC_SMU_HS (1 << 7) -#define AMDGPU_PG_SUPPORT_SDMA (1 << 8) -#define AMDGPU_PG_SUPPORT_ACP (1 << 9) -#define AMDGPU_PG_SUPPORT_SAMU (1 << 10) - /* GFX current status */ #define AMDGPU_GFX_NORMAL_MODE 0x00000000L #define AMDGPU_GFX_SAFE_MODE 0x00000001L diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c index 5c978e064f47..155965ed14a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik.c +++ b/drivers/gpu/drm/amd/amdgpu/cik.c @@ -2335,72 +2335,72 @@ static int cik_common_early_init(void *handle) switch (adev->asic_type) { case CHIP_BONAIRE: adev->cg_flags = - AMDGPU_CG_SUPPORT_GFX_MGCG | - AMDGPU_CG_SUPPORT_GFX_MGLS | - /*AMDGPU_CG_SUPPORT_GFX_CGCG |*/ - AMDGPU_CG_SUPPORT_GFX_CGLS | - AMDGPU_CG_SUPPORT_GFX_CGTS | - AMDGPU_CG_SUPPORT_GFX_CGTS_LS | - AMDGPU_CG_SUPPORT_GFX_CP_LS | - AMDGPU_CG_SUPPORT_MC_LS | - AMDGPU_CG_SUPPORT_MC_MGCG | - AMDGPU_CG_SUPPORT_SDMA_MGCG | - AMDGPU_CG_SUPPORT_SDMA_LS | - AMDGPU_CG_SUPPORT_BIF_LS | - AMDGPU_CG_SUPPORT_VCE_MGCG | - AMDGPU_CG_SUPPORT_UVD_MGCG | - AMDGPU_CG_SUPPORT_HDP_LS | - AMDGPU_CG_SUPPORT_HDP_MGCG; + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_MGLS | + /*AMD_CG_SUPPORT_GFX_CGCG |*/ + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_CGTS | + AMD_CG_SUPPORT_GFX_CGTS_LS | + AMD_CG_SUPPORT_GFX_CP_LS | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_SDMA_MGCG | + AMD_CG_SUPPORT_SDMA_LS | + AMD_CG_SUPPORT_BIF_LS | + AMD_CG_SUPPORT_VCE_MGCG | + AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_MGCG; adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0x14; break; case CHIP_HAWAII: adev->cg_flags = - AMDGPU_CG_SUPPORT_GFX_MGCG | - AMDGPU_CG_SUPPORT_GFX_MGLS | - /*AMDGPU_CG_SUPPORT_GFX_CGCG |*/ - AMDGPU_CG_SUPPORT_GFX_CGLS | - AMDGPU_CG_SUPPORT_GFX_CGTS | - AMDGPU_CG_SUPPORT_GFX_CP_LS | - AMDGPU_CG_SUPPORT_MC_LS | - AMDGPU_CG_SUPPORT_MC_MGCG | - AMDGPU_CG_SUPPORT_SDMA_MGCG | - AMDGPU_CG_SUPPORT_SDMA_LS | - AMDGPU_CG_SUPPORT_BIF_LS | - AMDGPU_CG_SUPPORT_VCE_MGCG | - AMDGPU_CG_SUPPORT_UVD_MGCG | - AMDGPU_CG_SUPPORT_HDP_LS | - AMDGPU_CG_SUPPORT_HDP_MGCG; + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_MGLS | + /*AMD_CG_SUPPORT_GFX_CGCG |*/ + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_CGTS | + AMD_CG_SUPPORT_GFX_CP_LS | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_SDMA_MGCG | + AMD_CG_SUPPORT_SDMA_LS | + AMD_CG_SUPPORT_BIF_LS | + AMD_CG_SUPPORT_VCE_MGCG | + AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_MGCG; adev->pg_flags = 0; adev->external_rev_id = 0x28; break; case CHIP_KAVERI: adev->cg_flags = - AMDGPU_CG_SUPPORT_GFX_MGCG | - AMDGPU_CG_SUPPORT_GFX_MGLS | - /*AMDGPU_CG_SUPPORT_GFX_CGCG |*/ - AMDGPU_CG_SUPPORT_GFX_CGLS | - AMDGPU_CG_SUPPORT_GFX_CGTS | - AMDGPU_CG_SUPPORT_GFX_CGTS_LS | - AMDGPU_CG_SUPPORT_GFX_CP_LS | - AMDGPU_CG_SUPPORT_SDMA_MGCG | - AMDGPU_CG_SUPPORT_SDMA_LS | - AMDGPU_CG_SUPPORT_BIF_LS | - AMDGPU_CG_SUPPORT_VCE_MGCG | - AMDGPU_CG_SUPPORT_UVD_MGCG | - AMDGPU_CG_SUPPORT_HDP_LS | - AMDGPU_CG_SUPPORT_HDP_MGCG; + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_MGLS | + /*AMD_CG_SUPPORT_GFX_CGCG |*/ + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_CGTS | + AMD_CG_SUPPORT_GFX_CGTS_LS | + AMD_CG_SUPPORT_GFX_CP_LS | + AMD_CG_SUPPORT_SDMA_MGCG | + AMD_CG_SUPPORT_SDMA_LS | + AMD_CG_SUPPORT_BIF_LS | + AMD_CG_SUPPORT_VCE_MGCG | + AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_MGCG; adev->pg_flags = - /*AMDGPU_PG_SUPPORT_GFX_PG | - AMDGPU_PG_SUPPORT_GFX_SMG | - AMDGPU_PG_SUPPORT_GFX_DMG |*/ - AMDGPU_PG_SUPPORT_UVD | - /*AMDGPU_PG_SUPPORT_VCE | - AMDGPU_PG_SUPPORT_CP | - AMDGPU_PG_SUPPORT_GDS | - AMDGPU_PG_SUPPORT_RLC_SMU_HS | - AMDGPU_PG_SUPPORT_ACP | - AMDGPU_PG_SUPPORT_SAMU |*/ + /*AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_GFX_SMG | + AMD_PG_SUPPORT_GFX_DMG |*/ + AMD_PG_SUPPORT_UVD | + /*AMD_PG_SUPPORT_VCE | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_GDS | + AMD_PG_SUPPORT_RLC_SMU_HS | + AMD_PG_SUPPORT_ACP | + AMD_PG_SUPPORT_SAMU |*/ 0; if (adev->pdev->device == 0x1312 || adev->pdev->device == 0x1316 || @@ -2412,29 +2412,29 @@ static int cik_common_early_init(void *handle) case CHIP_KABINI: case CHIP_MULLINS: adev->cg_flags = - AMDGPU_CG_SUPPORT_GFX_MGCG | - AMDGPU_CG_SUPPORT_GFX_MGLS | - /*AMDGPU_CG_SUPPORT_GFX_CGCG |*/ - AMDGPU_CG_SUPPORT_GFX_CGLS | - AMDGPU_CG_SUPPORT_GFX_CGTS | - AMDGPU_CG_SUPPORT_GFX_CGTS_LS | - AMDGPU_CG_SUPPORT_GFX_CP_LS | - AMDGPU_CG_SUPPORT_SDMA_MGCG | - AMDGPU_CG_SUPPORT_SDMA_LS | - AMDGPU_CG_SUPPORT_BIF_LS | - AMDGPU_CG_SUPPORT_VCE_MGCG | - AMDGPU_CG_SUPPORT_UVD_MGCG | - AMDGPU_CG_SUPPORT_HDP_LS | - AMDGPU_CG_SUPPORT_HDP_MGCG; + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_MGLS | + /*AMD_CG_SUPPORT_GFX_CGCG |*/ + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_CGTS | + AMD_CG_SUPPORT_GFX_CGTS_LS | + AMD_CG_SUPPORT_GFX_CP_LS | + AMD_CG_SUPPORT_SDMA_MGCG | + AMD_CG_SUPPORT_SDMA_LS | + AMD_CG_SUPPORT_BIF_LS | + AMD_CG_SUPPORT_VCE_MGCG | + AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_MGCG; adev->pg_flags = - /*AMDGPU_PG_SUPPORT_GFX_PG | - AMDGPU_PG_SUPPORT_GFX_SMG | */ - AMDGPU_PG_SUPPORT_UVD | - /*AMDGPU_PG_SUPPORT_VCE | - AMDGPU_PG_SUPPORT_CP | - AMDGPU_PG_SUPPORT_GDS | - AMDGPU_PG_SUPPORT_RLC_SMU_HS | - AMDGPU_PG_SUPPORT_SAMU |*/ + /*AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_GFX_SMG | */ + AMD_PG_SUPPORT_UVD | + /*AMD_PG_SUPPORT_VCE | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_GDS | + AMD_PG_SUPPORT_RLC_SMU_HS | + AMD_PG_SUPPORT_SAMU |*/ 0; if (adev->asic_type == CHIP_KABINI) { if (adev->rev_id == 0) diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index 5f712ceddf08..c55ecf0ea845 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -885,7 +885,7 @@ static void cik_enable_sdma_mgcg(struct amdgpu_device *adev, { u32 orig, data; - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_SDMA_MGCG)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_MGCG)) { WREG32(mmSDMA0_CLK_CTRL + SDMA0_REGISTER_OFFSET, 0x00000100); WREG32(mmSDMA0_CLK_CTRL + SDMA1_REGISTER_OFFSET, 0x00000100); } else { @@ -906,7 +906,7 @@ static void cik_enable_sdma_mgls(struct amdgpu_device *adev, { u32 orig, data; - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_SDMA_LS)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_LS)) { orig = data = RREG32(mmSDMA0_POWER_CNTL + SDMA0_REGISTER_OFFSET); data |= 0x100; if (orig != data) diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c index 4dd17f2dd905..9056355309d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c @@ -445,13 +445,13 @@ static int cz_dpm_init(struct amdgpu_device *adev) pi->gfx_pg_threshold = 500; pi->caps_fps = true; /* uvd */ - pi->caps_uvd_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_UVD) ? true : false; + pi->caps_uvd_pg = (adev->pg_flags & AMD_PG_SUPPORT_UVD) ? true : false; pi->caps_uvd_dpm = true; /* vce */ - pi->caps_vce_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_VCE) ? true : false; + pi->caps_vce_pg = (adev->pg_flags & AMD_PG_SUPPORT_VCE) ? true : false; pi->caps_vce_dpm = true; /* acp */ - pi->caps_acp_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_ACP) ? true : false; + pi->caps_acp_pg = (adev->pg_flags & AMD_PG_SUPPORT_ACP) ? true : false; pi->caps_acp_dpm = true; pi->caps_stable_power_state = false; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 6c76139de1c9..7732059ae30f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -4109,7 +4109,7 @@ static void gfx_v7_0_enable_cgcg(struct amdgpu_device *adev, bool enable) orig = data = RREG32(mmRLC_CGCG_CGLS_CTRL); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_CGCG)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGCG)) { gfx_v7_0_enable_gui_idle_interrupt(adev, true); tmp = gfx_v7_0_halt_rlc(adev); @@ -4147,9 +4147,9 @@ static void gfx_v7_0_enable_mgcg(struct amdgpu_device *adev, bool enable) { u32 data, orig, tmp = 0; - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_MGCG)) { - if (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_MGLS) { - if (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_CP_LS) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_MGCG)) { + if (adev->cg_flags & AMD_CG_SUPPORT_GFX_MGLS) { + if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CP_LS) { orig = data = RREG32(mmCP_MEM_SLP_CNTL); data |= CP_MEM_SLP_CNTL__CP_MEM_LS_EN_MASK; if (orig != data) @@ -4176,14 +4176,14 @@ static void gfx_v7_0_enable_mgcg(struct amdgpu_device *adev, bool enable) gfx_v7_0_update_rlc(adev, tmp); - if (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_CGTS) { + if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGTS) { orig = data = RREG32(mmCGTS_SM_CTRL_REG); data &= ~CGTS_SM_CTRL_REG__SM_MODE_MASK; data |= (0x2 << CGTS_SM_CTRL_REG__SM_MODE__SHIFT); data |= CGTS_SM_CTRL_REG__SM_MODE_ENABLE_MASK; data &= ~CGTS_SM_CTRL_REG__OVERRIDE_MASK; - if ((adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_MGLS) && - (adev->cg_flags & AMDGPU_CG_SUPPORT_GFX_CGTS_LS)) + if ((adev->cg_flags & AMD_CG_SUPPORT_GFX_MGLS) && + (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGTS_LS)) data &= ~CGTS_SM_CTRL_REG__LS_OVERRIDE_MASK; data &= ~CGTS_SM_CTRL_REG__ON_MONITOR_ADD_MASK; data |= CGTS_SM_CTRL_REG__ON_MONITOR_ADD_EN_MASK; @@ -4249,7 +4249,7 @@ static void gfx_v7_0_enable_sclk_slowdown_on_pu(struct amdgpu_device *adev, u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_RLC_SMU_HS)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_RLC_SMU_HS)) data |= RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PU_ENABLE_MASK; else data &= ~RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PU_ENABLE_MASK; @@ -4263,7 +4263,7 @@ static void gfx_v7_0_enable_sclk_slowdown_on_pd(struct amdgpu_device *adev, u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_RLC_SMU_HS)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_RLC_SMU_HS)) data |= RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PD_ENABLE_MASK; else data &= ~RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PD_ENABLE_MASK; @@ -4276,7 +4276,7 @@ static void gfx_v7_0_enable_cp_pg(struct amdgpu_device *adev, bool enable) u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_CP)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_CP)) data &= ~0x8000; else data |= 0x8000; @@ -4289,7 +4289,7 @@ static void gfx_v7_0_enable_gds_pg(struct amdgpu_device *adev, bool enable) u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_GDS)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GDS)) data &= ~0x2000; else data |= 0x2000; @@ -4370,7 +4370,7 @@ static void gfx_v7_0_enable_gfx_cgpg(struct amdgpu_device *adev, { u32 data, orig; - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_PG)) { + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)) { orig = data = RREG32(mmRLC_PG_CNTL); data |= RLC_PG_CNTL__GFX_POWER_GATING_ENABLE_MASK; if (orig != data) @@ -4442,7 +4442,7 @@ static void gfx_v7_0_enable_gfx_static_mgpg(struct amdgpu_device *adev, u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_SMG)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_SMG)) data |= RLC_PG_CNTL__STATIC_PER_CU_PG_ENABLE_MASK; else data &= ~RLC_PG_CNTL__STATIC_PER_CU_PG_ENABLE_MASK; @@ -4456,7 +4456,7 @@ static void gfx_v7_0_enable_gfx_dynamic_mgpg(struct amdgpu_device *adev, u32 data, orig; orig = data = RREG32(mmRLC_PG_CNTL); - if (enable && (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_DMG)) + if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_DMG)) data |= RLC_PG_CNTL__DYN_PER_CU_PG_ENABLE_MASK; else data &= ~RLC_PG_CNTL__DYN_PER_CU_PG_ENABLE_MASK; @@ -4623,15 +4623,15 @@ static void gfx_v7_0_get_csb_buffer(struct amdgpu_device *adev, static void gfx_v7_0_init_pg(struct amdgpu_device *adev) { - if (adev->pg_flags & (AMDGPU_PG_SUPPORT_GFX_PG | - AMDGPU_PG_SUPPORT_GFX_SMG | - AMDGPU_PG_SUPPORT_GFX_DMG | - AMDGPU_PG_SUPPORT_CP | - AMDGPU_PG_SUPPORT_GDS | - AMDGPU_PG_SUPPORT_RLC_SMU_HS)) { + if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_GFX_SMG | + AMD_PG_SUPPORT_GFX_DMG | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_GDS | + AMD_PG_SUPPORT_RLC_SMU_HS)) { gfx_v7_0_enable_sclk_slowdown_on_pu(adev, true); gfx_v7_0_enable_sclk_slowdown_on_pd(adev, true); - if (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_PG) { + if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) { gfx_v7_0_init_gfx_cgpg(adev); gfx_v7_0_enable_cp_pg(adev, true); gfx_v7_0_enable_gds_pg(adev, true); @@ -4643,14 +4643,14 @@ static void gfx_v7_0_init_pg(struct amdgpu_device *adev) static void gfx_v7_0_fini_pg(struct amdgpu_device *adev) { - if (adev->pg_flags & (AMDGPU_PG_SUPPORT_GFX_PG | - AMDGPU_PG_SUPPORT_GFX_SMG | - AMDGPU_PG_SUPPORT_GFX_DMG | - AMDGPU_PG_SUPPORT_CP | - AMDGPU_PG_SUPPORT_GDS | - AMDGPU_PG_SUPPORT_RLC_SMU_HS)) { + if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_GFX_SMG | + AMD_PG_SUPPORT_GFX_DMG | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_GDS | + AMD_PG_SUPPORT_RLC_SMU_HS)) { gfx_v7_0_update_gfx_pg(adev, false); - if (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_PG) { + if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) { gfx_v7_0_enable_cp_pg(adev, false); gfx_v7_0_enable_gds_pg(adev, false); } @@ -5527,14 +5527,14 @@ static int gfx_v7_0_set_powergating_state(void *handle, if (state == AMD_PG_STATE_GATE) gate = true; - if (adev->pg_flags & (AMDGPU_PG_SUPPORT_GFX_PG | - AMDGPU_PG_SUPPORT_GFX_SMG | - AMDGPU_PG_SUPPORT_GFX_DMG | - AMDGPU_PG_SUPPORT_CP | - AMDGPU_PG_SUPPORT_GDS | - AMDGPU_PG_SUPPORT_RLC_SMU_HS)) { + if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_GFX_SMG | + AMD_PG_SUPPORT_GFX_DMG | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_GDS | + AMD_PG_SUPPORT_RLC_SMU_HS)) { gfx_v7_0_update_gfx_pg(adev, gate); - if (adev->pg_flags & AMDGPU_PG_SUPPORT_GFX_PG) { + if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) { gfx_v7_0_enable_cp_pg(adev, gate); gfx_v7_0_enable_gds_pg(adev, gate); } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 8aa2991ab379..b8060795b27b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -792,7 +792,7 @@ static void gmc_v7_0_enable_mc_ls(struct amdgpu_device *adev, for (i = 0; i < ARRAY_SIZE(mc_cg_registers); i++) { orig = data = RREG32(mc_cg_registers[i]); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_MC_LS)) + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_MC_LS)) data |= mc_cg_ls_en[i]; else data &= ~mc_cg_ls_en[i]; @@ -809,7 +809,7 @@ static void gmc_v7_0_enable_mc_mgcg(struct amdgpu_device *adev, for (i = 0; i < ARRAY_SIZE(mc_cg_registers); i++) { orig = data = RREG32(mc_cg_registers[i]); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_MC_MGCG)) + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_MC_MGCG)) data |= mc_cg_en[i]; else data &= ~mc_cg_en[i]; @@ -825,7 +825,7 @@ static void gmc_v7_0_enable_bif_mgls(struct amdgpu_device *adev, orig = data = RREG32_PCIE(ixPCIE_CNTL2); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_BIF_LS)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_BIF_LS)) { data = REG_SET_FIELD(data, PCIE_CNTL2, SLV_MEM_LS_EN, 1); data = REG_SET_FIELD(data, PCIE_CNTL2, MST_MEM_LS_EN, 1); data = REG_SET_FIELD(data, PCIE_CNTL2, REPLAY_MEM_LS_EN, 1); @@ -848,7 +848,7 @@ static void gmc_v7_0_enable_hdp_mgcg(struct amdgpu_device *adev, orig = data = RREG32(mmHDP_HOST_PATH_CNTL); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_HDP_MGCG)) + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_MGCG)) data = REG_SET_FIELD(data, HDP_HOST_PATH_CNTL, CLOCK_GATING_DIS, 0); else data = REG_SET_FIELD(data, HDP_HOST_PATH_CNTL, CLOCK_GATING_DIS, 1); @@ -864,7 +864,7 @@ static void gmc_v7_0_enable_hdp_ls(struct amdgpu_device *adev, orig = data = RREG32(mmHDP_MEM_POWER_LS); - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_HDP_LS)) + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS)) data = REG_SET_FIELD(data, HDP_MEM_POWER_LS, LS_ENABLE, 1); else data = REG_SET_FIELD(data, HDP_MEM_POWER_LS, LS_ENABLE, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c index 7e9154c7f1db..654d76723bc3 100644 --- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c @@ -2859,11 +2859,11 @@ static int kv_dpm_init(struct amdgpu_device *adev) pi->voltage_drop_t = 0; pi->caps_sclk_throttle_low_notification = false; pi->caps_fps = false; /* true? */ - pi->caps_uvd_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_UVD) ? true : false; + pi->caps_uvd_pg = (adev->pg_flags & AMD_PG_SUPPORT_UVD) ? true : false; pi->caps_uvd_dpm = true; - pi->caps_vce_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_VCE) ? true : false; - pi->caps_samu_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_SAMU) ? true : false; - pi->caps_acp_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_ACP) ? true : false; + pi->caps_vce_pg = (adev->pg_flags & AMD_PG_SUPPORT_VCE) ? true : false; + pi->caps_samu_pg = (adev->pg_flags & AMD_PG_SUPPORT_SAMU) ? true : false; + pi->caps_acp_pg = (adev->pg_flags & AMD_PG_SUPPORT_ACP) ? true : false; pi->caps_stable_p_state = false; ret = kv_parse_sys_info_table(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index c982524d9287..fbd3767671bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -611,7 +611,7 @@ static void uvd_v4_2_enable_mgcg(struct amdgpu_device *adev, { u32 orig, data; - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) { data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL); data = 0xfff; WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data); @@ -830,7 +830,7 @@ static int uvd_v4_2_set_clockgating_state(void *handle, bool gate = false; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) + if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) return 0; if (state == AMD_CG_STATE_GATE) @@ -853,7 +853,7 @@ static int uvd_v4_2_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD)) return 0; if (state == AMD_PG_STATE_GATE) { diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index aad1ab596bdc..57f1c5bf3bf1 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -776,7 +776,7 @@ static int uvd_v5_0_set_clockgating_state(void *handle, { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) + if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) return 0; return 0; @@ -794,7 +794,7 @@ static int uvd_v5_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD)) return 0; if (state == AMD_PG_STATE_GATE) { diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index c41eda78f0b7..0b365b7651ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -532,7 +532,7 @@ static int uvd_v6_0_start(struct amdgpu_device *adev) uvd_v6_0_mc_resume(adev); /* Set dynamic clock gating in S/W control mode */ - if (adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG) { + if (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG) { if (adev->flags & AMD_IS_APU) cz_set_uvd_clock_gating_branches(adev, false); else @@ -1000,7 +1000,7 @@ static int uvd_v6_0_set_clockgating_state(void *handle, struct amdgpu_device *adev = (struct amdgpu_device *)handle; bool enable = (state == AMD_CG_STATE_GATE) ? true : false; - if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_UVD_MGCG)) + if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) return 0; if (enable) { @@ -1030,7 +1030,7 @@ static int uvd_v6_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_UVD)) + if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD)) return 0; if (state == AMD_PG_STATE_GATE) { diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c index d3ce6085ccd2..a822edacfa95 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c @@ -373,7 +373,7 @@ static void vce_v2_0_enable_mgcg(struct amdgpu_device *adev, bool enable) { bool sw_cg = false; - if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_VCE_MGCG)) { + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) { if (sw_cg) vce_v2_0_set_sw_cg(adev, true); else @@ -608,7 +608,7 @@ static int vce_v2_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_VCE)) + if (!(adev->pg_flags & AMD_PG_SUPPORT_VCE)) return 0; if (state == AMD_PG_STATE_GATE) diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index 797d12c31475..d662fa9f9091 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -277,7 +277,7 @@ static int vce_v3_0_start(struct amdgpu_device *adev) WREG32_P(mmVCE_STATUS, 0, ~1); /* Set Clock-Gating off */ - if (adev->cg_flags & AMDGPU_CG_SUPPORT_VCE_MGCG) + if (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG) vce_v3_0_set_vce_sw_clock_gating(adev, false); if (r) { @@ -676,7 +676,7 @@ static int vce_v3_0_set_clockgating_state(void *handle, bool enable = (state == AMD_CG_STATE_GATE) ? true : false; int i; - if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_VCE_MGCG)) + if (!(adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) return 0; mutex_lock(&adev->grbm_idx_mutex); @@ -728,7 +728,7 @@ static int vce_v3_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMDGPU_PG_SUPPORT_VCE)) + if (!(adev->pg_flags & AMD_PG_SUPPORT_VCE)) return 0; if (state == AMD_PG_STATE_GATE) diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 1195d06f55bc..dbf7e6413cab 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -85,6 +85,38 @@ enum amd_powergating_state { AMD_PG_STATE_UNGATE, }; +/* CG flags */ +#define AMD_CG_SUPPORT_GFX_MGCG (1 << 0) +#define AMD_CG_SUPPORT_GFX_MGLS (1 << 1) +#define AMD_CG_SUPPORT_GFX_CGCG (1 << 2) +#define AMD_CG_SUPPORT_GFX_CGLS (1 << 3) +#define AMD_CG_SUPPORT_GFX_CGTS (1 << 4) +#define AMD_CG_SUPPORT_GFX_CGTS_LS (1 << 5) +#define AMD_CG_SUPPORT_GFX_CP_LS (1 << 6) +#define AMD_CG_SUPPORT_GFX_RLC_LS (1 << 7) +#define AMD_CG_SUPPORT_MC_LS (1 << 8) +#define AMD_CG_SUPPORT_MC_MGCG (1 << 9) +#define AMD_CG_SUPPORT_SDMA_LS (1 << 10) +#define AMD_CG_SUPPORT_SDMA_MGCG (1 << 11) +#define AMD_CG_SUPPORT_BIF_LS (1 << 12) +#define AMD_CG_SUPPORT_UVD_MGCG (1 << 13) +#define AMD_CG_SUPPORT_VCE_MGCG (1 << 14) +#define AMD_CG_SUPPORT_HDP_LS (1 << 15) +#define AMD_CG_SUPPORT_HDP_MGCG (1 << 16) + +/* PG flags */ +#define AMD_PG_SUPPORT_GFX_PG (1 << 0) +#define AMD_PG_SUPPORT_GFX_SMG (1 << 1) +#define AMD_PG_SUPPORT_GFX_DMG (1 << 2) +#define AMD_PG_SUPPORT_UVD (1 << 3) +#define AMD_PG_SUPPORT_VCE (1 << 4) +#define AMD_PG_SUPPORT_CP (1 << 5) +#define AMD_PG_SUPPORT_GDS (1 << 6) +#define AMD_PG_SUPPORT_RLC_SMU_HS (1 << 7) +#define AMD_PG_SUPPORT_SDMA (1 << 8) +#define AMD_PG_SUPPORT_ACP (1 << 9) +#define AMD_PG_SUPPORT_SAMU (1 << 10) + enum amd_pm_state_type { /* not used for dpm */ POWER_STATE_TYPE_DEFAULT, -- cgit v1.2.3 From 52b52a87814b4016bb324c0d1b45eb6e6f4cea3b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Feb 2016 11:11:51 -0500 Subject: drm/amdgpu/tonga: plumb pg flags through to powerplay Enable vce and uvd pg based on single set of pg flags. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c index 69c81c14d89c..980d3bf8ea76 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c @@ -4451,6 +4451,7 @@ int tonga_hwmgr_backend_init(struct pp_hwmgr *hwmgr) pp_atomctrl_gpio_pin_assignment gpio_pin_assignment; struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable); phw_tonga_ulv_parm *ulv; + struct cgs_system_info sys_info = {0}; PP_ASSERT_WITH_CODE((NULL != hwmgr), "Invalid Parameter!", return -1;); @@ -4619,10 +4620,19 @@ int tonga_hwmgr_backend_init(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_UVDPowerGating); phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEPowerGating); + sys_info.size = sizeof(struct cgs_system_info); + sys_info.info_id = CGS_SYSTEM_INFO_PG_FLAGS; + result = cgs_query_system_info(hwmgr->device, &sys_info); + if (!result) { + if (sys_info.value & AMD_PG_SUPPORT_UVD) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_UVDPowerGating); + if (sys_info.value & AMD_PG_SUPPORT_VCE) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_VCEPowerGating); + } if (0 == result) { - struct cgs_system_info sys_info = {0}; - data->is_tlu_enabled = 0; hwmgr->platform_descriptor.hardwareActivityPerformanceLevels = TONGA_MAX_HARDWARE_POWERLEVELS; -- cgit v1.2.3 From db5cffcd2bca5fafc4912446605101ec368d4d5f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Feb 2016 11:23:28 -0500 Subject: drm/amdgpu/cz: plumb pg flags through to powerplay Enable vce and uvd pg based on single set of pg flags. Reviewed-by: Eric Huang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c index 80af87fa0bf0..cf01177ca3b5 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c @@ -174,6 +174,8 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr) { struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); uint32_t i; + struct cgs_system_info sys_info = {0}; + int result; cz_hwmgr->gfx_ramp_step = 256*25/100; @@ -251,6 +253,17 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_UVDPowerGating); phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEPowerGating); + sys_info.size = sizeof(struct cgs_system_info); + sys_info.info_id = CGS_SYSTEM_INFO_PG_FLAGS; + result = cgs_query_system_info(hwmgr->device, &sys_info); + if (!result) { + if (sys_info.value & AMD_PG_SUPPORT_UVD) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_UVDPowerGating); + if (sys_info.value & AMD_PG_SUPPORT_VCE) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_VCEPowerGating); + } return 0; } -- cgit v1.2.3 From 117159f0b9d392fb433a7871426fad50317f06f7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Feb 2016 17:36:25 +0100 Subject: ALSA: timer: Fix wrong instance passed to slave callbacks In snd_timer_notify1(), the wrong timer instance was passed for slave ccallback function. This leads to the access to the wrong data when an incompatible master is handled (e.g. the master is the sequencer timer and the slave is a user timer), as spotted by syzkaller fuzzer. This patch fixes that wrong assignment. BugLink: http://lkml.kernel.org/r/CACT4Y+Y_Bm+7epAb=8Wi=AaWd+DYS7qawX52qxdCfOfY49vozQ@mail.gmail.com Reported-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 9b513a05765a..dea932ac6165 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -422,7 +422,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) spin_lock_irqsave(&timer->lock, flags); list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) - ts->ccallback(ti, event + 100, &tstamp, resolution); + ts->ccallback(ts, event + 100, &tstamp, resolution); spin_unlock_irqrestore(&timer->lock, flags); } -- cgit v1.2.3 From 5070fb14a0154f075c8b418e5bc58a620ae85a45 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 8 Feb 2016 09:14:37 +0100 Subject: ARM: 8517/1: ICST: avoid arithmetic overflow in icst_hz() When trying to set the ICST 307 clock to 25174000 Hz I ran into this arithmetic error: the icst_hz_to_vco() correctly figure out DIVIDE=2, RDW=100 and VDW=99 yielding a frequency of 25174000 Hz out of the VCO. (I replicated the icst_hz() function in a spreadsheet to verify this.) However, when I called icst_hz() on these VCO settings it would instead return 4122709 Hz. This causes an error in the common clock driver for ICST as the common clock framework will call .round_rate() on the clock which will utilize icst_hz_to_vco() followed by icst_hz() suggesting the erroneous frequency, and then the clock gets set to this. The error did not manifest in the old clock framework since this high frequency was only used by the CLCD, which calls clk_set_rate() without first calling clk_round_rate() and since the old clock framework would not call clk_round_rate() before setting the frequency, the correct values propagated into the VCO. After some experimenting I figured out that it was due to a simple arithmetic overflow: the divisor for 24Mhz reference frequency as reference becomes 24000000*2*(99+8)=0x132212400 and the "1" in bit 32 overflows and is lost. But introducing an explicit 64-by-32 bit do_div() and casting the divisor into (u64) we get the right frequency back, and the right frequency gets set. Tested on the ARM Versatile. Cc: stable@vger.kernel.org Cc: linux-clk@vger.kernel.org Cc: Pawel Moll Signed-off-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/common/icst.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm/common/icst.c b/arch/arm/common/icst.c index 2dc6da70ae59..d3c0e69df259 100644 --- a/arch/arm/common/icst.c +++ b/arch/arm/common/icst.c @@ -16,7 +16,7 @@ */ #include #include - +#include #include /* @@ -29,7 +29,11 @@ EXPORT_SYMBOL(icst525_s2div); unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco) { - return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); + u64 dividend = p->ref * 2 * (u64)(vco.v + 8); + u32 divisor = (vco.r + 2) * p->s2div[vco.s]; + + do_div(dividend, divisor); + return (unsigned long)dividend; } EXPORT_SYMBOL(icst_hz); -- cgit v1.2.3 From 4ba6a2b28f111e4c9621487612056d10f3f4a6ca Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 8 Feb 2016 16:09:08 +0900 Subject: scatterlist: fix a typo in comment block of sg_miter_stop() Fix the doubled "started" and tidy up the following sentences. Signed-off-by: Masahiro Yamada Signed-off-by: Linus Torvalds --- lib/scatterlist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/scatterlist.c b/lib/scatterlist.c index bafa9933fa76..004fc70fc56a 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -598,9 +598,9 @@ EXPORT_SYMBOL(sg_miter_next); * * Description: * Stops mapping iterator @miter. @miter should have been started - * started using sg_miter_start(). A stopped iteration can be - * resumed by calling sg_miter_next() on it. This is useful when - * resources (kmap) need to be released during iteration. + * using sg_miter_start(). A stopped iteration can be resumed by + * calling sg_miter_next() on it. This is useful when resources (kmap) + * need to be released during iteration. * * Context: * Preemption disabled if the SG_MITER_ATOMIC is set. Don't care -- cgit v1.2.3 From 85f4f39c80e9350da8a351e6ea36367fa9fb87a5 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:10 +0800 Subject: pch_phub: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 9a17a9bab8d6..15bb0c8cdda3 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -503,8 +503,7 @@ static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj, int err; ssize_t rom_size; - struct pch_phub_reg *chip = - dev_get_drvdata(container_of(kobj, struct device, kobj)); + struct pch_phub_reg *chip = dev_get_drvdata(kobj_to_dev(kobj)); ret = mutex_lock_interruptible(&pch_phub_mutex); if (ret) { @@ -567,8 +566,7 @@ static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj, unsigned int addr_offset; int ret; ssize_t rom_size; - struct pch_phub_reg *chip = - dev_get_drvdata(container_of(kobj, struct device, kobj)); + struct pch_phub_reg *chip = dev_get_drvdata(kobj_to_dev(kobj)); ret = mutex_lock_interruptible(&pch_phub_mutex); if (ret) -- cgit v1.2.3 From 6908b45eafc480e9da8196f987b1d0f6c887a5d7 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:12 +0800 Subject: GenWQE: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index 6ab31eff0536..c24c9b7c1dd3 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -278,7 +278,7 @@ static umode_t genwqe_is_visible(struct kobject *kobj, struct attribute *attr, int n) { unsigned int j; - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct genwqe_dev *cd = dev_get_drvdata(dev); umode_t mode = attr->mode; -- cgit v1.2.3 From 7b46a105dbb7b67ea1f2f63a0b3ec29ab64e1bd3 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 4 Jan 2016 16:57:28 +0100 Subject: omap_hdq: fix usecount handling hdq_usecount was set to zero after a successful read, so omap_hdq_put could not properly free resources which leads e.g. to increasing usecounts in lsmod output Signed-off-by: Andreas Kemnade Acked-by: Evgeniy Polyakov Reviewed-by: Vignesh R Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 0e2f43bccf1f..a2eec97d5064 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -618,7 +618,6 @@ static u8 omap_w1_read_byte(void *_hdq) hdq_disable_interrupt(hdq_data, OMAP_HDQ_CTRL_STATUS, ~OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); - hdq_data->hdq_usecount = 0; /* Write followed by a read, release the module */ if (hdq_data->init_trans) { -- cgit v1.2.3 From 987a9f128b8ad8e7019229300e8d91d37ca55933 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 17 Nov 2015 16:13:55 -0800 Subject: spmi: pmic-arb: Support more than 128 peripherals Add support for more than 128 peripherals by taking a lazy caching approach to the mapping tables. Instead of reading and caching the tables at boot given some fixed size, read them and cache them on an as needed basis. We still assume a max size of 512 peripherals, trading off some space for simplicity. Based on a patch by Gilad Avidov and Sagar Dharia . Cc: Gilad Avidov Cc: Sagar Dharia Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 153 +++++++++++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 42 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index be822f7a9ce6..aca282d45421 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -10,6 +10,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include #include #include #include @@ -47,9 +48,9 @@ #define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) #define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) -#define SPMI_MAPPING_TABLE_LEN 255 #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ -#define PPID_TO_CHAN_TABLE_SZ BIT(12) /* PPID is 12bit chan is 1byte*/ +#define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */ +#define PMIC_ARB_CHAN_VALID BIT(15) /* Ownership Table */ #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) @@ -85,9 +86,7 @@ enum pmic_arb_cmd_op_code { }; /* Maximum number of support PMIC peripherals */ -#define PMIC_ARB_MAX_PERIPHS 256 -#define PMIC_ARB_MAX_CHNL 128 -#define PMIC_ARB_PERIPH_ID_VALID (1 << 15) +#define PMIC_ARB_MAX_PERIPHS 512 #define PMIC_ARB_TIMEOUT_US 100 #define PMIC_ARB_MAX_TRANS_BYTES (8) @@ -125,18 +124,22 @@ struct spmi_pmic_arb_dev { void __iomem *wr_base; void __iomem *intr; void __iomem *cnfg; + void __iomem *core; + resource_size_t core_size; raw_spinlock_t lock; u8 channel; int irq; u8 ee; - u8 min_apid; - u8 max_apid; - u32 mapping_table[SPMI_MAPPING_TABLE_LEN]; + u16 min_apid; + u16 max_apid; + u32 *mapping_table; + DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); struct irq_domain *domain; struct spmi_controller *spmic; - u16 apid_to_ppid[256]; + u16 *apid_to_ppid; const struct pmic_arb_ver_ops *ver_ops; - u8 *ppid_to_chan; + u16 *ppid_to_chan; + u16 last_channel; }; /** @@ -158,7 +161,8 @@ struct spmi_pmic_arb_dev { */ struct pmic_arb_ver_ops { /* spmi commands (read_cmd, write_cmd, cmd) functionality */ - u32 (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr); + int (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr, + u32 *offset); u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); /* Interrupts controller functionality (offset of PIC registers) */ @@ -212,7 +216,14 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); u32 status = 0; u32 timeout = PMIC_ARB_TIMEOUT_US; - u32 offset = dev->ver_ops->offset(dev, sid, addr) + PMIC_ARB_STATUS; + u32 offset; + int rc; + + rc = dev->ver_ops->offset(dev, sid, addr, &offset); + if (rc) + return rc; + + offset += PMIC_ARB_STATUS; while (timeout--) { status = readl_relaxed(base + offset); @@ -257,7 +268,11 @@ pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) unsigned long flags; u32 cmd; int rc; - u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, 0); + u32 offset; + + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, &offset); + if (rc) + return rc; cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); @@ -297,7 +312,11 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u8 bc = len - 1; u32 cmd; int rc; - u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr); + u32 offset; + + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset); + if (rc) + return rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { dev_err(&ctrl->dev, @@ -344,7 +363,11 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u8 bc = len - 1; u32 cmd; int rc; - u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr); + u32 offset; + + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset); + if (rc) + return rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { dev_err(&ctrl->dev, @@ -614,6 +637,10 @@ static int search_mapping_table(struct spmi_pmic_arb_dev *pa, u32 data; for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { + if (!test_and_set_bit(index, pa->mapping_table_valid)) + mapping_table[index] = readl_relaxed(pa->cnfg + + SPMI_MAPPING_TABLE_REG(index)); + data = mapping_table[index]; if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) { @@ -701,18 +728,61 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, } /* v1 offset per ee */ -static u32 pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr) +static int +pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset) { - return 0x800 + 0x80 * pa->channel; + *offset = 0x800 + 0x80 * pa->channel; + return 0; } +static u16 pmic_arb_find_chan(struct spmi_pmic_arb_dev *pa, u16 ppid) +{ + u32 regval, offset; + u16 chan; + u16 id; + + /* + * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. + * ppid_to_chan is an in-memory invert of that table. + */ + for (chan = pa->last_channel; ; chan++) { + offset = PMIC_ARB_REG_CHNL(chan); + if (offset >= pa->core_size) + break; + + regval = readl_relaxed(pa->core + offset); + if (!regval) + continue; + + id = (regval >> 8) & PMIC_ARB_PPID_MASK; + pa->ppid_to_chan[id] = chan | PMIC_ARB_CHAN_VALID; + if (id == ppid) { + chan |= PMIC_ARB_CHAN_VALID; + break; + } + } + pa->last_channel = chan & ~PMIC_ARB_CHAN_VALID; + + return chan; +} + + /* v2 offset per ppid (chan) and per ee */ -static u32 pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr) +static int +pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset) { u16 ppid = (sid << 8) | (addr >> 8); - u8 chan = pa->ppid_to_chan[ppid]; + u16 chan; - return 0x1000 * pa->ee + 0x8000 * chan; + chan = pa->ppid_to_chan[ppid]; + if (!(chan & PMIC_ARB_CHAN_VALID)) + chan = pmic_arb_find_chan(pa, ppid); + if (!(chan & PMIC_ARB_CHAN_VALID)) + return -ENODEV; + chan &= ~PMIC_ARB_CHAN_VALID; + + *offset = 0x1000 * pa->ee + 0x8000 * chan; + return 0; } static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) @@ -797,7 +867,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) struct resource *res; void __iomem *core; u32 channel, ee, hw_ver; - int err, i; + int err; bool is_v1; ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); @@ -808,6 +878,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pa->spmic = ctrl; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + pa->core_size = resource_size(res); core = devm_ioremap_resource(&ctrl->dev, res); if (IS_ERR(core)) { err = PTR_ERR(core); @@ -825,10 +896,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pa->wr_base = core; pa->rd_base = core; } else { - u8 chan; - u16 ppid; - u32 regval; - + pa->core = core; pa->ver_ops = &pmic_arb_v2; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -847,24 +915,14 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) goto err_put_ctrl; } - pa->ppid_to_chan = devm_kzalloc(&ctrl->dev, - PPID_TO_CHAN_TABLE_SZ, GFP_KERNEL); + pa->ppid_to_chan = devm_kcalloc(&ctrl->dev, + PMIC_ARB_MAX_PPID, + sizeof(*pa->ppid_to_chan), + GFP_KERNEL); if (!pa->ppid_to_chan) { err = -ENOMEM; goto err_put_ctrl; } - /* - * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. - * ppid_to_chan is an in-memory invert of that table. - */ - for (chan = 0; chan < PMIC_ARB_MAX_CHNL; ++chan) { - regval = readl_relaxed(core + PMIC_ARB_REG_CHNL(chan)); - if (!regval) - continue; - - ppid = (regval >> 8) & 0xFFF; - pa->ppid_to_chan[ppid] = chan; - } } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); @@ -915,9 +973,20 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pa->ee = ee; - for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i) - pa->mapping_table[i] = readl_relaxed( - pa->cnfg + SPMI_MAPPING_TABLE_REG(i)); + pa->apid_to_ppid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS, + sizeof(*pa->apid_to_ppid), + GFP_KERNEL); + if (!pa->apid_to_ppid) { + err = -ENOMEM; + goto err_put_ctrl; + } + + pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1, + sizeof(*pa->mapping_table), GFP_KERNEL); + if (!pa->mapping_table) { + err = -ENOMEM; + goto err_put_ctrl; + } /* Initialize max_apid/min_apid to the opposite bounds, during * the irq domain translation, we are sure to update these */ -- cgit v1.2.3 From a116eaf1be4ed4f91cc85d17b2f000f08d85c2d9 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Thu, 5 Nov 2015 10:39:00 +0100 Subject: char/nvram: set array of const as const Some array of const char are not set as const. This patch fix that. Signed-off-by: LABBE Corentin Signed-off-by: Greg Kroah-Hartman --- drivers/char/nvram.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 01292328a456..678fa97e41fb 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -496,12 +496,12 @@ static void pc_set_checksum(void) #ifdef CONFIG_PROC_FS -static char *floppy_types[] = { +static const char * const floppy_types[] = { "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", "3.5'' 2.88M", "3.5'' 2.88M" }; -static char *gfx_types[] = { +static const char * const gfx_types[] = { "EGA, VGA, ... (with BIOS)", "CGA (40 cols)", "CGA (80 cols)", @@ -602,7 +602,7 @@ static void atari_set_checksum(void) static struct { unsigned char val; - char *name; + const char *name; } boot_prefs[] = { { 0x80, "TOS" }, { 0x40, "ASV" }, @@ -611,7 +611,7 @@ static struct { { 0x00, "unspecified" } }; -static char *languages[] = { +static const char * const languages[] = { "English (US)", "German", "French", @@ -623,7 +623,7 @@ static char *languages[] = { "Swiss (German)" }; -static char *dateformat[] = { +static const char * const dateformat[] = { "MM%cDD%cYY", "DD%cMM%cYY", "YY%cMM%cDD", @@ -634,7 +634,7 @@ static char *dateformat[] = { "7 (undefined)" }; -static char *colors[] = { +static const char * const colors[] = { "2", "4", "16", "256", "65536", "??", "??", "??" }; -- cgit v1.2.3 From 202cdb6f889bf45e1cb4883e1727e9909152b777 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 22 Dec 2015 00:11:11 +0530 Subject: drivers: char: raw: Removed unnecessary braces Removed braces from single statement if condition.Fixed checkpatch.pl warning. Signed-off-by: Bhumika Goyal Signed-off-by: Greg Kroah-Hartman --- drivers/char/raw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 9b9809b709a5..e83b2adc014a 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -334,10 +334,8 @@ static int __init raw_init(void) cdev_init(&raw_cdev, &raw_fops); ret = cdev_add(&raw_cdev, dev, max_raw_minors); - if (ret) { + if (ret) goto error_region; - } - raw_class = class_create(THIS_MODULE, "raw"); if (IS_ERR(raw_class)) { printk(KERN_ERR "Error creating raw class.\n"); -- cgit v1.2.3 From 468623bb835c215028947820c5c36afa409de633 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 3 Feb 2016 14:06:12 -0500 Subject: firmware: simplify dev_*() print messages for generic helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify a few of the *generic* shared dev_warn() and dev_dbg() print messages for three reasons: 0) Historically firmware_class code was added to help get device driver firmware binaries but these days request_firmware*() helpers are being repurposed for general *system data* needed by the kernel. 1) This will also help generalize shared code as much as possible later in the future in consideration for a new extensible firmware API which will enable to separate usermode helper code out as much as possible. 2) Kees Cook pointed out the the prints already have the device associated as dev_*() helpers are used, that should help identify the user and case in which the helpers are used. That should provide enough context and simplifies the messages further. v4: generalize debug/warn messages even further as suggested by Kees Cook. Cc: Rusty Russell Cc: Andrew Morton Cc: David Howells Cc: Casey Schaufler Cc: Ming Lei Cc: Takashi Iwai Cc: VojtÄ›ch Pavlík Cc: Kyle McMartin Cc: Matthew Garrett Cc: linux-kernel@vger.kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: Mimi Zohar Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b9250e564ebf..ce88355eb128 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -353,15 +353,15 @@ static int fw_get_filesystem_firmware(struct device *device, rc = fw_read_file_contents(file, buf); fput(file); if (rc) - dev_warn(device, "firmware, attempted to load %s, but failed with error %d\n", - path, rc); + dev_warn(device, "loading %s failed with error %d\n", + path, rc); else break; } __putname(path); if (!rc) { - dev_dbg(device, "firmware: direct-loading firmware %s\n", + dev_dbg(device, "direct-loading %s\n", buf->fw_id); mutex_lock(&fw_lock); set_bit(FW_STATUS_DONE, &buf->status); @@ -1051,7 +1051,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, } if (fw_get_builtin_firmware(firmware, name)) { - dev_dbg(device, "firmware: using built-in firmware %s\n", name); + dev_dbg(device, "using built-in %s\n", name); return 0; /* assigned */ } -- cgit v1.2.3 From 3b9ab374a1e6d3cd6d16231ec6fe11fe2c49a72a Mon Sep 17 00:00:00 2001 From: Bamvor Jian Zhang Date: Fri, 8 Jan 2016 15:50:48 +0800 Subject: ppdev: convert to y2038 safe The y2038 issue for ppdev is changes of timeval in the ioctl (PPSETTIME and PPGETTIME). The size of struct timeval changes from 8bytes to 16bytes due to the changes of time_t. It lead to the changes of the command of ioctl, e.g. for PPGETTIME, We have: on 32-bit (old): 0x80087095 on 32-bit (new): 0x80107095 on 64-bit : 0x80107095 This patch define these two ioctl commands to support the 32bit and 64bit time_t application at the same time. And, introduce pp_set_timeout to remove some duplicated code. Signed-off-by: Bamvor Jian Zhang Reviewed-by: Arnd Bergmann Tested-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 75 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index ae0b42b66e55..c03d998731ea 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -98,6 +98,13 @@ struct pp_struct { #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) static DEFINE_MUTEX(pp_do_mutex); + +/* define fixed sized ioctl cmd for y2038 migration */ +#define PPGETTIME32 _IOR(PP_IOCTL, 0x95, s32[2]) +#define PPSETTIME32 _IOW(PP_IOCTL, 0x96, s32[2]) +#define PPGETTIME64 _IOR(PP_IOCTL, 0x95, s64[2]) +#define PPSETTIME64 _IOW(PP_IOCTL, 0x96, s64[2]) + static inline void pp_enable_irq (struct pp_struct *pp) { struct parport *port = pp->pdev->port; @@ -322,6 +329,22 @@ static enum ieee1284_phase init_phase (int mode) return IEEE1284_PH_FWD_IDLE; } +static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) +{ + long to_jiffies; + + if ((tv_sec < 0) || (tv_usec < 0)) + return -EINVAL; + + to_jiffies = usecs_to_jiffies(tv_usec); + to_jiffies += tv_sec * HZ; + if (to_jiffies <= 0) + return -EINVAL; + + pdev->timeout = to_jiffies; + return 0; +} + static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = iminor(file_inode(file)); @@ -495,9 +518,10 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) unsigned char reg; unsigned char mask; int mode; + s32 time32[2]; + s64 time64[2]; + struct timespec64 ts; int ret; - struct timeval par_timeout; - long to_jiffies; case PPRSTATUS: reg = parport_read_status (port); @@ -592,29 +616,40 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) atomic_sub (ret, &pp->irqc); return 0; - case PPSETTIME: - if (copy_from_user (&par_timeout, argp, sizeof(struct timeval))) { + case PPSETTIME32: + if (copy_from_user(time32, argp, sizeof(time32))) return -EFAULT; - } - /* Convert to jiffies, place in pp->pdev->timeout */ - if ((par_timeout.tv_sec < 0) || (par_timeout.tv_usec < 0)) { - return -EINVAL; - } - to_jiffies = ROUND_UP(par_timeout.tv_usec, 1000000/HZ); - to_jiffies += par_timeout.tv_sec * (long)HZ; - if (to_jiffies <= 0) { + + return pp_set_timeout(pp->pdev, time32[0], time32[1]); + + case PPSETTIME64: + if (copy_from_user(time64, argp, sizeof(time64))) + return -EFAULT; + + return pp_set_timeout(pp->pdev, time64[0], time64[1]); + + case PPGETTIME32: + jiffies_to_timespec64(pp->pdev->timeout, &ts); + time32[0] = ts.tv_sec; + time32[1] = ts.tv_nsec / NSEC_PER_USEC; + if ((time32[0] < 0) || (time32[1] < 0)) return -EINVAL; - } - pp->pdev->timeout = to_jiffies; + + if (copy_to_user(argp, time32, sizeof(time32))) + return -EFAULT; + return 0; - case PPGETTIME: - to_jiffies = pp->pdev->timeout; - memset(&par_timeout, 0, sizeof(par_timeout)); - par_timeout.tv_sec = to_jiffies / HZ; - par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ); - if (copy_to_user (argp, &par_timeout, sizeof(struct timeval))) + case PPGETTIME64: + jiffies_to_timespec64(pp->pdev->timeout, &ts); + time64[0] = ts.tv_sec; + time64[1] = ts.tv_nsec / NSEC_PER_USEC; + if ((time64[0] < 0) || (time64[1] < 0)) + return -EINVAL; + + if (copy_to_user(argp, time64, sizeof(time64))) return -EFAULT; + return 0; default: -- cgit v1.2.3 From 17a3596f2122b4d88dc0efe297aa2de7be1bb31c Mon Sep 17 00:00:00 2001 From: Bamvor Jian Zhang Date: Fri, 8 Jan 2016 15:50:49 +0800 Subject: ppdev: add support for compat ioctl The arg of ioctl in ppdev is the pointer of integer except the timeval in PPSETTIME, PPGETTIME. Different size of timeval is already supported by the previous patches. So, it is safe to add compat support. Signed-off-by: Bamvor Jian Zhang Reviewed-by: Arnd Bergmann Tested-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index c03d998731ea..9e98d0153148 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -69,6 +69,7 @@ #include #include #include +#include #define PP_VERSION "ppdev: user-space parallel port driver" #define CHRDEV "ppdev" @@ -670,6 +671,14 @@ static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } +#ifdef CONFIG_COMPAT +static long pp_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static int pp_open (struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); @@ -779,6 +788,9 @@ static const struct file_operations pp_fops = { .write = pp_write, .poll = pp_poll, .unlocked_ioctl = pp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pp_compat_ioctl, +#endif .open = pp_open, .release = pp_release, }; -- cgit v1.2.3 From 6a4f555aa1990ec80e8cdc92719f1349792363db Mon Sep 17 00:00:00 2001 From: Bamvor Jian Zhang Date: Fri, 8 Jan 2016 15:50:50 +0800 Subject: fs/compat: remove useless compat ioctl for parport device Compat ioctl is already introduced in drivers/char/ppdev.c in order to fix y2038 issue for PP[GS]ETTIME. There is no need to define these here. Suggested-by: Arnd Bergmann Tested-by: Sudip Mukherjee Signed-off-by: Bamvor Jian Zhang Signed-off-by: Greg Kroah-Hartman --- fs/compat_ioctl.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a5b8eb69a8f4..31608b3b8f51 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1040,28 +1040,6 @@ COMPATIBLE_IOCTL(PPPIOCGL2TPSTATS) /* PPPOX */ COMPATIBLE_IOCTL(PPPOEIOCSFWD) COMPATIBLE_IOCTL(PPPOEIOCDFWD) -/* ppdev */ -COMPATIBLE_IOCTL(PPSETMODE) -COMPATIBLE_IOCTL(PPRSTATUS) -COMPATIBLE_IOCTL(PPRCONTROL) -COMPATIBLE_IOCTL(PPWCONTROL) -COMPATIBLE_IOCTL(PPFCONTROL) -COMPATIBLE_IOCTL(PPRDATA) -COMPATIBLE_IOCTL(PPWDATA) -COMPATIBLE_IOCTL(PPCLAIM) -COMPATIBLE_IOCTL(PPRELEASE) -COMPATIBLE_IOCTL(PPYIELD) -COMPATIBLE_IOCTL(PPEXCL) -COMPATIBLE_IOCTL(PPDATADIR) -COMPATIBLE_IOCTL(PPNEGOT) -COMPATIBLE_IOCTL(PPWCTLONIRQ) -COMPATIBLE_IOCTL(PPCLRIRQ) -COMPATIBLE_IOCTL(PPSETPHASE) -COMPATIBLE_IOCTL(PPGETMODES) -COMPATIBLE_IOCTL(PPGETMODE) -COMPATIBLE_IOCTL(PPGETPHASE) -COMPATIBLE_IOCTL(PPGETFLAGS) -COMPATIBLE_IOCTL(PPSETFLAGS) /* Big A */ /* sparc only */ /* Big Q for sound/OSS */ -- cgit v1.2.3 From 10a3fbf18d99caeeecd2b28445d9104deab11ed7 Mon Sep 17 00:00:00 2001 From: Chen Feng Date: Wed, 16 Dec 2015 17:03:15 +0800 Subject: firmware: Change the page arrary alloc to vmalloc No need to use use continuous memory, it may be fail when memory deeply fragmented. Signed-off-by: Chen Feng Signed-off-by: Xia Qing Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index ce88355eb128..a7f4aa3f6b32 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -257,7 +257,7 @@ static void __fw_free_buf(struct kref *ref) vunmap(buf->data); for (i = 0; i < buf->nr_pages; i++) __free_page(buf->pages[i]); - kfree(buf->pages); + vfree(buf->pages); } else #endif vfree(buf->data); @@ -660,7 +660,7 @@ static ssize_t firmware_loading_store(struct device *dev, if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) { for (i = 0; i < fw_buf->nr_pages; i++) __free_page(fw_buf->pages[i]); - kfree(fw_buf->pages); + vfree(fw_buf->pages); fw_buf->pages = NULL; fw_buf->page_array_size = 0; fw_buf->nr_pages = 0; @@ -770,8 +770,7 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) buf->page_array_size * 2); struct page **new_pages; - new_pages = kmalloc(new_array_size * sizeof(void *), - GFP_KERNEL); + new_pages = vmalloc(new_array_size * sizeof(void *)); if (!new_pages) { fw_load_abort(fw_priv); return -ENOMEM; @@ -780,7 +779,7 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) buf->page_array_size * sizeof(void *)); memset(&new_pages[buf->page_array_size], 0, sizeof(void *) * (new_array_size - buf->page_array_size)); - kfree(buf->pages); + vfree(buf->pages); buf->pages = new_pages; buf->page_array_size = new_array_size; } -- cgit v1.2.3 From 9cf7490360bf2c46a16b7525f899e4970c5fc144 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 2 Feb 2016 19:31:12 -0800 Subject: tcp: do not drop syn_recv on all icmp reports Petr Novopashenniy reported that ICMP redirects on SYN_RECV sockets were leading to RST. This is of course incorrect. A specific list of ICMP messages should be able to drop a SYN_RECV. For instance, a REDIRECT on SYN_RECV shall be ignored, as we do not hold a dst per SYN_RECV pseudo request. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111751 Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Reported-by: Petr Novopashenniy Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_ipv4.c | 11 ++++++++--- net/ipv6/tcp_ipv6.c | 5 +++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index f6f8f032c73e..ae6468f5c9f3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -447,7 +447,7 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); void tcp_v4_mtu_reduced(struct sock *sk); -void tcp_req_err(struct sock *sk, u32 seq); +void tcp_req_err(struct sock *sk, u32 seq, bool abort); int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); struct sock *tcp_create_openreq_child(const struct sock *sk, struct request_sock *req, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a4d523709ab3..7f6ff037adaf 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -311,7 +311,7 @@ static void do_redirect(struct sk_buff *skb, struct sock *sk) /* handle ICMP messages on TCP_NEW_SYN_RECV request sockets */ -void tcp_req_err(struct sock *sk, u32 seq) +void tcp_req_err(struct sock *sk, u32 seq, bool abort) { struct request_sock *req = inet_reqsk(sk); struct net *net = sock_net(sk); @@ -323,7 +323,7 @@ void tcp_req_err(struct sock *sk, u32 seq) if (seq != tcp_rsk(req)->snt_isn) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); - } else { + } else if (abort) { /* * Still in SYN_RECV, just remove it silently. * There is no good way to pass the error to the newly @@ -383,7 +383,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) } seq = ntohl(th->seq); if (sk->sk_state == TCP_NEW_SYN_RECV) - return tcp_req_err(sk, seq); + return tcp_req_err(sk, seq, + type == ICMP_PARAMETERPROB || + type == ICMP_TIME_EXCEEDED || + (type == ICMP_DEST_UNREACH && + (code == ICMP_NET_UNREACH || + code == ICMP_HOST_UNREACH))); bh_lock_sock(sk); /* If too many ICMPs get dropped on busy diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 006396e31cb0..1a5a70fb8551 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -327,6 +327,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct tcp_sock *tp; __u32 seq, snd_una; struct sock *sk; + bool fatal; int err; sk = __inet6_lookup_established(net, &tcp_hashinfo, @@ -345,8 +346,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return; } seq = ntohl(th->seq); + fatal = icmpv6_err_convert(type, code, &err); if (sk->sk_state == TCP_NEW_SYN_RECV) - return tcp_req_err(sk, seq); + return tcp_req_err(sk, seq, fatal); bh_lock_sock(sk); if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) @@ -400,7 +402,6 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - icmpv6_err_convert(type, code, &err); /* Might be for an request_sock */ switch (sk->sk_state) { -- cgit v1.2.3 From 5f74f82ea34c0da80ea0b49192bb5ea06e063593 Mon Sep 17 00:00:00 2001 From: Hans Westgaard Ry Date: Wed, 3 Feb 2016 09:26:57 +0100 Subject: net:Add sysctl_max_skb_frags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devices may have limits on the number of fragments in an skb they support. Current codebase uses a constant as maximum for number of fragments one skb can hold and use. When enabling scatter/gather and running traffic with many small messages the codebase uses the maximum number of fragments and may thereby violate the max for certain devices. The patch introduces a global variable as max number of fragments. Signed-off-by: Hans Westgaard Ry Reviewed-by: HÃ¥kon Bugge Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 1 + net/core/skbuff.c | 2 ++ net/core/sysctl_net_core.c | 10 ++++++++++ net/ipv4/tcp.c | 4 ++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 11f935c1a090..4ce9ff7086f4 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -299,6 +299,7 @@ struct sk_buff; #else #define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1) #endif +extern int sysctl_max_skb_frags; typedef struct skb_frag_struct skb_frag_t; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b2df375ec9c2..5bf88f58bee7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -79,6 +79,8 @@ struct kmem_cache *skbuff_head_cache __read_mostly; static struct kmem_cache *skbuff_fclone_cache __read_mostly; +int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS; +EXPORT_SYMBOL(sysctl_max_skb_frags); /** * skb_panic - private function for out-of-line support diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 95b6139d710c..a6beb7b6ae55 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -26,6 +26,7 @@ static int zero = 0; static int one = 1; static int min_sndbuf = SOCK_MIN_SNDBUF; static int min_rcvbuf = SOCK_MIN_RCVBUF; +static int max_skb_frags = MAX_SKB_FRAGS; static int net_msg_warn; /* Unused, but still a sysctl */ @@ -392,6 +393,15 @@ static struct ctl_table net_core_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "max_skb_frags", + .data = &sysctl_max_skb_frags, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &max_skb_frags, + }, { } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 19746b3fcbbe..0c36ef4a3f86 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -940,7 +940,7 @@ new_segment: i = skb_shinfo(skb)->nr_frags; can_coalesce = skb_can_coalesce(skb, i, page, offset); - if (!can_coalesce && i >= MAX_SKB_FRAGS) { + if (!can_coalesce && i >= sysctl_max_skb_frags) { tcp_mark_push(tp, skb); goto new_segment; } @@ -1213,7 +1213,7 @@ new_segment: if (!skb_can_coalesce(skb, i, pfrag->page, pfrag->offset)) { - if (i == MAX_SKB_FRAGS || !sg) { + if (i == sysctl_max_skb_frags || !sg) { tcp_mark_push(tp, skb); goto new_segment; } -- cgit v1.2.3 From b7d987295c74500b733a0ba07f9a9bcc4074fa83 Mon Sep 17 00:00:00 2001 From: Siva Reddy Kallam Date: Wed, 3 Feb 2016 14:09:38 +0530 Subject: tg3: Fix for tg3 transmit queue 0 timed out when too many gso_segs tg3_tso_bug() can hit a condition where the entire tx ring is not big enough to segment the GSO packet. For example, if MSS is very small, gso_segs can exceed the tx ring size. When we hit the condition, it will cause tx timeout. tg3_tso_bug() is called to handle TSO and DMA hardware bugs. For TSO bugs, if tg3_tso_bug() cannot succeed, we have to drop the packet. For DMA bugs, we can still fall back to linearize the SKB and let the hardware transmit the TSO packet. This patch adds a function tg3_tso_bug_gso_check() to check if there are enough tx descriptors for GSO before calling tg3_tso_bug(). The caller will then handle the error appropriately - drop or lineraize the SKB. v2: Corrected patch description to avoid confusion. Signed-off-by: Siva Reddy Kallam Signed-off-by: Michael Chan Acked-by: Prashant Sreedharan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 49eea8981332..3010080cfeee 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -7831,6 +7831,14 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi, return ret; } +static bool tg3_tso_bug_gso_check(struct tg3_napi *tnapi, struct sk_buff *skb) +{ + /* Check if we will never have enough descriptors, + * as gso_segs can be more than current ring size + */ + return skb_shinfo(skb)->gso_segs < tnapi->tx_pending / 3; +} + static netdev_tx_t tg3_start_xmit(struct sk_buff *, struct net_device *); /* Use GSO to workaround all TSO packets that meet HW bug conditions @@ -7934,14 +7942,19 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) * vlan encapsulated. */ if (skb->protocol == htons(ETH_P_8021Q) || - skb->protocol == htons(ETH_P_8021AD)) - return tg3_tso_bug(tp, tnapi, txq, skb); + skb->protocol == htons(ETH_P_8021AD)) { + if (tg3_tso_bug_gso_check(tnapi, skb)) + return tg3_tso_bug(tp, tnapi, txq, skb); + goto drop; + } if (!skb_is_gso_v6(skb)) { if (unlikely((ETH_HLEN + hdr_len) > 80) && - tg3_flag(tp, TSO_BUG)) - return tg3_tso_bug(tp, tnapi, txq, skb); - + tg3_flag(tp, TSO_BUG)) { + if (tg3_tso_bug_gso_check(tnapi, skb)) + return tg3_tso_bug(tp, tnapi, txq, skb); + goto drop; + } ip_csum = iph->check; ip_tot_len = iph->tot_len; iph->check = 0; @@ -8073,7 +8086,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) if (would_hit_hwbug) { tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i); - if (mss) { + if (mss && tg3_tso_bug_gso_check(tnapi, skb)) { /* If it's a TSO packet, do GSO instead of * allocating and copying to a large linear SKB */ -- cgit v1.2.3 From ca7f41a4957b872577807169bd7464b36aae9b9c Mon Sep 17 00:00:00 2001 From: Sandeep Pillai Date: Wed, 3 Feb 2016 14:40:44 +0530 Subject: enic: increment devcmd2 result ring in case of timeout Firmware posts the devcmd result in result ring. In case of timeout, driver does not increment the current result pointer and firmware could post the result after timeout has occurred. During next devcmd, driver would be reading the result of previous devcmd. Fix this by incrementing result even in case of timeout. Fixes: 373fb0873d43 ("enic: add devcmd2") Signed-off-by: Sandeep Pillai Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic.h | 2 +- drivers/net/ethernet/cisco/enic/vnic_dev.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 1671fa3332c2..7ba6d530b0c0 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -33,7 +33,7 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "2.3.0.12" +#define DRV_VERSION "2.3.0.20" #define DRV_COPYRIGHT "Copyright 2008-2013 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index 1ffd1050860b..1fdf5fe12a95 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -298,7 +298,8 @@ static 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 = dc2c->result + dc2c->next_result; + struct devcmd2_result *result; + u8 color; unsigned int i; int delay, err; u32 fetch_index, new_posted; @@ -336,13 +337,17 @@ static int _vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, 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++) { - if (result->color == dc2c->color) { - dc2c->next_result++; - if (dc2c->next_result == dc2c->result_size) { - dc2c->next_result = 0; - dc2c->color = dc2c->color ? 0 : 1; - } + if (result->color == color) { if (result->error) { err = result->error; if (err != ERR_ECMDUNKNOWN || -- cgit v1.2.3 From 7a84bd46647ff181eb2659fdc99590e6f16e501d Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 3 Feb 2016 23:33:30 +0800 Subject: sctp: translate network order to host order when users get a hmacid Commit ed5a377d87dc ("sctp: translate host order to network order when setting a hmacid") corrected the hmacid byte-order when setting a hmacid. but the same issue also exists on getting a hmacid. We fix it by changing hmacids to host order when users get them with getsockopt. Fixes: Commit ed5a377d87dc ("sctp: translate host order to network order when setting a hmacid") Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- net/sctp/socket.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 5ca2ebfe0be8..e878da0949db 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5538,6 +5538,7 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len, struct sctp_hmac_algo_param *hmacs; __u16 data_len = 0; u32 num_idents; + int i; if (!ep->auth_enable) return -EACCES; @@ -5555,8 +5556,12 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len, return -EFAULT; if (put_user(num_idents, &p->shmac_num_idents)) return -EFAULT; - if (copy_to_user(p->shmac_idents, hmacs->hmac_ids, data_len)) - return -EFAULT; + for (i = 0; i < num_idents; i++) { + __u16 hmacid = ntohs(hmacs->hmac_ids[i]); + + if (copy_to_user(&p->shmac_idents[i], &hmacid, sizeof(__u16))) + return -EFAULT; + } return 0; } -- cgit v1.2.3 From 08ff924e7fa7b826396f5ef1cb15656db7fb6545 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 4 Feb 2016 01:17:12 +0900 Subject: selinux: nlmsgtab: add SOCK_DESTROY to the netlink mapping tables Without this, using SOCK_DESTROY in enforcing mode results in: SELinux: unrecognized netlink message type=21 for sclass=32 Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- security/selinux/nlmsgtab.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 2bbb41822d8e..8495b9368190 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -83,6 +83,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE }, }; static struct nlmsg_perm nlmsg_xfrm_perms[] = -- cgit v1.2.3 From 8a5fd56431fe1682e870bd6ab0c276e74befbeb9 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 4 Feb 2016 14:40:40 +0100 Subject: locking/lockdep: Fix stack trace caching logic check_prev_add() caches saved stack trace in static trace variable to avoid duplicate save_trace() calls in dependencies involving trylocks. But that caching logic contains a bug. We may not save trace on first iteration due to early return from check_prev_add(). Then on the second iteration when we actually need the trace we don't save it because we think that we've already saved it. Let check_prev_add() itself control when stack is saved. There is another bug. Trace variable is protected by graph lock. But we can temporary release graph lock during printing. Fix this by invalidating cached stack trace when we release graph lock. Signed-off-by: Dmitry Vyukov Cc: Andrew Morton Cc: Linus Torvalds Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: glider@google.com Cc: kcc@google.com Cc: peter@hurleysoftware.com Cc: sasha.levin@oracle.com Link: http://lkml.kernel.org/r/1454593240-121647-1-git-send-email-dvyukov@google.com Signed-off-by: Ingo Molnar --- kernel/locking/lockdep.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 60ace56618f6..c7710e4092ef 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -1822,7 +1822,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next, */ static int check_prev_add(struct task_struct *curr, struct held_lock *prev, - struct held_lock *next, int distance, int trylock_loop) + struct held_lock *next, int distance, int *stack_saved) { struct lock_list *entry; int ret; @@ -1883,8 +1883,11 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, } } - if (!trylock_loop && !save_trace(&trace)) - return 0; + if (!*stack_saved) { + if (!save_trace(&trace)) + return 0; + *stack_saved = 1; + } /* * Ok, all validations passed, add the new lock @@ -1907,6 +1910,8 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, * Debugging printouts: */ if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) { + /* We drop graph lock, so another thread can overwrite trace. */ + *stack_saved = 0; graph_unlock(); printk("\n new dependency: "); print_lock_name(hlock_class(prev)); @@ -1929,7 +1934,7 @@ static int check_prevs_add(struct task_struct *curr, struct held_lock *next) { int depth = curr->lockdep_depth; - int trylock_loop = 0; + int stack_saved = 0; struct held_lock *hlock; /* @@ -1956,7 +1961,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next) */ if (hlock->read != 2 && hlock->check) { if (!check_prev_add(curr, hlock, next, - distance, trylock_loop)) + distance, &stack_saved)) return 0; /* * Stop after the first non-trylock entry, @@ -1979,7 +1984,6 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next) if (curr->held_locks[depth].irq_context != curr->held_locks[depth-1].irq_context) break; - trylock_loop = 1; } return 1; out_bug: -- cgit v1.2.3 From f5a952c08e842cb06eb5c65947ced9b5128a62ee Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Wed, 3 Feb 2016 21:35:29 +0200 Subject: of: of_mdio: Add marvell, 88e1145 to whitelist of PHY compatibilities. Commit ae461131960b ("of: of_mdio: Add a whitelist of PHY compatibilities.") missed one compatible string used in in-tree DTBs: in OCTEON, for selected boards, the kernel DTB pruning code will overwrite the DTB compatible string with "marvell,88e1145", which is missing from the whitelist. Add it. The patch fixes broken networking on EdgeRouter Lite. Fixes: ae461131960b ("of: of_mdio: Add a whitelist of PHY compatibilities.") Signed-off-by: Aaro Koskinen Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 5648317d355f..39c4be41ef83 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -154,6 +154,7 @@ static const struct of_device_id whitelist_phys[] = { { .compatible = "marvell,88E1111", }, { .compatible = "marvell,88e1116", }, { .compatible = "marvell,88e1118", }, + { .compatible = "marvell,88e1145", }, { .compatible = "marvell,88e1149r", }, { .compatible = "marvell,88e1310", }, { .compatible = "marvell,88E1510", }, -- cgit v1.2.3 From ed8b1d6d2c741ab26d60d499d7fbb7ac801f0f51 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Feb 2016 12:02:32 +0100 Subject: ALSA: timer: Fix race between stop and interrupt A slave timer element also unlinks at snd_timer_stop() but it takes only slave_active_lock. When a slave is assigned to a master, however, this may become a race against the master's interrupt handling, eventually resulting in a list corruption. The actual bug could be seen with a syzkaller fuzzer test case in BugLink below. As a fix, we need to take timeri->timer->lock when timer isn't NULL, i.e. assigned to a master, while the assignment to a master itself is protected by slave_active_lock. BugLink: http://lkml.kernel.org/r/CACT4Y+Y_Bm+7epAb=8Wi=AaWd+DYS7qawX52qxdCfOfY49vozQ@mail.gmail.com Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index dea932ac6165..a0405b0078c6 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -518,9 +518,13 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) spin_unlock_irqrestore(&slave_active_lock, flags); return -EBUSY; } + if (timeri->timer) + spin_lock(&timeri->timer->lock); timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); + if (timeri->timer) + spin_unlock(&timeri->timer->lock); spin_unlock_irqrestore(&slave_active_lock, flags); goto __end; } -- cgit v1.2.3 From 2ebab40eb74a0225d5dfba72bfae317dd948fa2d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Feb 2016 10:23:52 +0100 Subject: ALSA: hda - Fix bad dereference of jack object The hda_jack_tbl entries are managed by snd_array for allowing multiple jacks. It's good per se, but the problem is that struct hda_jack_callback keeps the hda_jack_tbl pointer. Since snd_array doesn't preserve each pointer at resizing the array, we can't keep the original pointer but have to deduce the pointer at each time via snd_array_entry() instead. Actually, this resulted in the deference to the wrong pointer on codecs that have many pins such as CS4208. This patch replaces the pointer to the NID value as the search key. As an unexpected good side effect, this even simplifies the code, as only NID is needed in most cases. Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- sound/pci/hda/hda_jack.c | 2 +- sound/pci/hda/hda_jack.h | 2 +- sound/pci/hda/patch_ca0132.c | 5 ++++- sound/pci/hda/patch_hdmi.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_sigmatel.c | 6 +++--- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 30c8efe0f80a..7ca5b89f088a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4028,9 +4028,9 @@ static void pin_power_callback(struct hda_codec *codec, struct hda_jack_callback *jack, bool on) { - if (jack && jack->tbl->nid) + if (jack && jack->nid) sync_power_state_change(codec, - set_pin_power_jack(codec, jack->tbl->nid, on)); + set_pin_power_jack(codec, jack->nid, on)); } /* callback only doing power up -- called at first */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index c945e257d368..a33234e04d4f 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -259,7 +259,7 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, if (!callback) return ERR_PTR(-ENOMEM); callback->func = func; - callback->tbl = jack; + callback->nid = jack->nid; callback->next = jack->callback; jack->callback = callback; } diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 858708a044f5..e9814c0168ea 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -21,7 +21,7 @@ struct hda_jack_callback; typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *); struct hda_jack_callback { - struct hda_jack_tbl *tbl; + hda_nid_t nid; hda_jack_callback_fn func; unsigned int private_data; /* arbitrary data */ struct hda_jack_callback *next; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4ef2259f88ca..9ceb2bc36e68 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4427,13 +4427,16 @@ static void ca0132_process_dsp_response(struct hda_codec *codec, static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { struct ca0132_spec *spec = codec->spec; + struct hda_jack_tbl *tbl; /* Delay enabling the HP amp, to let the mic-detection * state machine run. */ cancel_delayed_work_sync(&spec->unsol_hp_work); schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); - cb->tbl->block_report = 1; + tbl = snd_hda_jack_tbl_get(codec, cb->nid); + if (tbl) + tbl->block_report = 1; } static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2191e2359315..8ee78dbd4c60 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1194,7 +1194,7 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid) static void jack_callback(struct hda_codec *codec, struct hda_jack_callback *jack) { - check_presence_and_report(codec, jack->tbl->nid); + check_presence_and_report(codec, jack->nid); } static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b43c0f7994eb..efd4980cffb8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -282,7 +282,7 @@ static void alc_update_knob_master(struct hda_codec *codec, uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return; - val = snd_hda_codec_read(codec, jack->tbl->nid, 0, + val = snd_hda_codec_read(codec, jack->nid, 0, AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); val &= HDA_AMP_VOLMASK; uctl->value.integer.value[0] = val; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 2c7c5eb8b1e9..37b70f8e878f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -493,9 +493,9 @@ static void jack_update_power(struct hda_codec *codec, if (!spec->num_pwrs) return; - if (jack && jack->tbl->nid) { - stac_toggle_power_map(codec, jack->tbl->nid, - snd_hda_jack_detect(codec, jack->tbl->nid), + if (jack && jack->nid) { + stac_toggle_power_map(codec, jack->nid, + snd_hda_jack_detect(codec, jack->nid), true); return; } -- cgit v1.2.3 From b8cb3750ce94d7610934465263850dcf40736bca Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 7 Feb 2016 15:14:15 +0100 Subject: ALSA: firewire-digi00x: Drop bogus const type qualifier on dot_scrt() sound/firewire/digi00x/amdtp-dot.c:67: warning: type qualifiers ignored on function return type Drop the bogus "const" type qualifier on the return type of dot_scrt() to fix this. Signed-off-by: Geert Uytterhoeven Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/amdtp-dot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index b02a5e8cad44..0ac92aba5bc1 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -63,7 +63,7 @@ struct amdtp_dot { #define BYTE_PER_SAMPLE (4) #define MAGIC_DOT_BYTE (2) #define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) -static const u8 dot_scrt(const u8 idx, const unsigned int off) +static u8 dot_scrt(const u8 idx, const unsigned int off) { /* * the length of the added pattern only depends on the lower nibble -- cgit v1.2.3 From 4dff5c7b7093b19c19d3a100f8a3ad87cb7cd9e7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Feb 2016 17:26:58 +0100 Subject: ALSA: timer: Fix race at concurrent reads snd_timer_user_read() has a potential race among parallel reads, as qhead and qused are updated outside the critical section due to copy_to_user() calls. Move them into the critical section, and also sanitize the relevant code a bit. Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index a0405b0078c6..dca817fc7894 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1933,6 +1933,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, { struct snd_timer_user *tu; long result = 0, unit; + int qhead; int err = 0; tu = file->private_data; @@ -1944,7 +1945,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { err = -EAGAIN; - break; + goto _error; } set_current_state(TASK_INTERRUPTIBLE); @@ -1959,42 +1960,37 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, if (tu->disconnected) { err = -ENODEV; - break; + goto _error; } if (signal_pending(current)) { err = -ERESTARTSYS; - break; + goto _error; } } + qhead = tu->qhead++; + tu->qhead %= tu->queue_size; spin_unlock_irq(&tu->qlock); - if (err < 0) - goto _error; if (tu->tread) { - if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], - sizeof(struct snd_timer_tread))) { + if (copy_to_user(buffer, &tu->tqueue[qhead], + sizeof(struct snd_timer_tread))) err = -EFAULT; - goto _error; - } } else { - if (copy_to_user(buffer, &tu->queue[tu->qhead++], - sizeof(struct snd_timer_read))) { + if (copy_to_user(buffer, &tu->queue[qhead], + sizeof(struct snd_timer_read))) err = -EFAULT; - goto _error; - } } - tu->qhead %= tu->queue_size; - - result += unit; - buffer += unit; - spin_lock_irq(&tu->qlock); tu->qused--; + if (err < 0) + goto _error; + result += unit; + buffer += unit; } - spin_unlock_irq(&tu->qlock); _error: + spin_unlock_irq(&tu->qlock); return result > 0 ? result : err; } -- cgit v1.2.3 From 461547f3158978c180d74484d58e82be9b8e7357 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 9 Feb 2016 02:49:54 -0800 Subject: flow_dissector: Fix unaligned access in __skb_flow_dissector when used by eth_get_headlen This patch fixes an issue with unaligned accesses when using eth_get_headlen on a page that was DMA aligned instead of being IP aligned. The fact is when trying to check the length we don't need to be looking at the flow label so we can reorder the checks to first check if we are supposed to gather the flow label and then make the call to actually get it. v2: Updated path so that either STOP_AT_FLOW_LABEL or KEY_FLOW_LABEL can cause us to check for the flow label. Reported-by: Sowmini Varadhan Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index d79699c9d1b9..eab81bc80e5c 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -208,7 +208,6 @@ ip: case htons(ETH_P_IPV6): { const struct ipv6hdr *iph; struct ipv6hdr _iph; - __be32 flow_label; ipv6: iph = __skb_header_pointer(skb, nhoff, sizeof(_iph), data, hlen, &_iph); @@ -230,8 +229,12 @@ ipv6: key_control->addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; } - flow_label = ip6_flowlabel(iph); - if (flow_label) { + if ((dissector_uses_key(flow_dissector, + FLOW_DISSECTOR_KEY_FLOW_LABEL) || + (flags & FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL)) && + ip6_flowlabel(iph)) { + __be32 flow_label = ip6_flowlabel(iph); + if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_FLOW_LABEL)) { key_tags = skb_flow_dissector_target(flow_dissector, -- cgit v1.2.3 From 3af5a67c86a30f8cd8bfd6202709be21cedd2756 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 8 Feb 2016 09:46:31 -0800 Subject: MIPS: Fix early CM probing Commit c014d164f21d ("MIPS: Add platform callback before initializing the L2 cache") added a platform_early_l2_init function in order to allow platforms to probe for the CM before L2 initialisation is performed, so that CM GCRs are available to mips_sc_probe. That commit actually fails to do anything useful, since it checks mips_cm_revision to determine whether it should call mips_cm_probe but the result of mips_cm_revision will always be 0 until mips_cm_probe has been called. Thus the "early" mips_cm_probe call never occurs. Fix this & drop the useless weak platform_early_l2_init function by simply calling mips_cm_probe from setup_arch. For platforms that don't select CONFIG_MIPS_CM this will be a no-op, and for those that do it removes the requirement for them to call mips_cm_probe manually (although doing so isn't harmful for now). Signed-off-by: Paul Burton Reviewed-by: Alexander Sverdlin Cc: Andrzej Hajda Cc: Aaro Koskinen Cc: Masahiro Yamada Cc: Rob Herring Cc: Peter Hurley Cc: Leonid Yegoshin Cc: Jaedon Shin Cc: James Hogan Cc: Jonas Gorski Cc: Markos Chandras Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12475/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/setup.c | 1 + arch/mips/mm/sc-mips.c | 10 ---------- arch/mips/mti-malta/malta-init.c | 8 -------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 569a7d5242dd..5fdaf8bdcd2e 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -782,6 +782,7 @@ static inline void prefill_possible_map(void) {} void __init setup_arch(char **cmdline_p) { cpu_probe(); + mips_cm_probe(); prom_init(); setup_early_fdc_console(); diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c index 3bd0597d9c3d..249647578e58 100644 --- a/arch/mips/mm/sc-mips.c +++ b/arch/mips/mm/sc-mips.c @@ -181,10 +181,6 @@ static int __init mips_sc_probe_cm3(void) return 1; } -void __weak platform_early_l2_init(void) -{ -} - static inline int __init mips_sc_probe(void) { struct cpuinfo_mips *c = ¤t_cpu_data; @@ -194,12 +190,6 @@ static inline int __init mips_sc_probe(void) /* Mark as not present until probe completed */ c->scache.flags |= MIPS_CACHE_NOT_PRESENT; - /* - * Do we need some platform specific probing before - * we configure L2? - */ - platform_early_l2_init(); - if (mips_cm_revision() >= CM_REV_CM3) return mips_sc_probe_cm3(); diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c index 571148c5fd0b..dc2c5214809d 100644 --- a/arch/mips/mti-malta/malta-init.c +++ b/arch/mips/mti-malta/malta-init.c @@ -293,7 +293,6 @@ mips_pci_controller: console_config(); #endif /* Early detection of CMP support */ - mips_cm_probe(); mips_cpc_probe(); if (!register_cps_smp_ops()) @@ -304,10 +303,3 @@ mips_pci_controller: return; register_up_smp_ops(); } - -void platform_early_l2_init(void) -{ - /* L2 configuration lives in the CM3 */ - if (mips_cm_revision() >= CM_REV_CM3) - mips_cm_probe(); -} -- cgit v1.2.3 From b64e86cdf6a9d772c47b8e594dd173b86270fd1b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 10 Jul 2013 16:54:34 -0400 Subject: scripts: add "prune-kernel" script to clean up old kernel images Long ago, Dave Jones complained about CONFIG_LOCALVERSION_AUTO: "I don't use the auto config, because I end up filling up /boot unless I go through and clean them out by hand every time I install a new one (which I do probably a dozen or so times a day). Is there some easy way to prune old builds I'm missing?" To which Bruce replied: "I run this by hand every now and then. I'm probably doing it all wrong" And if he is running it wrong, then so am I - because I've been using this script ever since. It is true that CONFIG_LOCALVERSION_AUTO easily ends up filling your /boot partition if you don't clean up old versions regularly, and this script helps make that easier. Checked with Bruce to see that it's fine to add this to the kernel scripts. Maybe people will come up with enhancements, but more importantly, this way I won't misplace this script whenever I install a new machine and start doing custom kernels for it. Signed-off-by: Linus Torvalds --- scripts/prune-kernel | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 scripts/prune-kernel diff --git a/scripts/prune-kernel b/scripts/prune-kernel new file mode 100755 index 000000000000..ab5034e1d081 --- /dev/null +++ b/scripts/prune-kernel @@ -0,0 +1,20 @@ +#!/bin/bash + +# because I use CONFIG_LOCALVERSION_AUTO, not the same version again and +# again, /boot and /lib/modules/ eventually fill up. +# Dumb script to purge that stuff: + +for f in "$@" +do + if rpm -qf "/lib/modules/$f" >/dev/null; then + echo "keeping $f (installed from rpm)" + elif [ $(uname -r) = "$f" ]; then + echo "keeping $f (running kernel) " + else + echo "removing $f" + rm -f "/boot/initramfs-$f.img" "/boot/System.map-$f" + rm -f "/boot/vmlinuz-$f" "/boot/config-$f" + rm -rf "/lib/modules/$f" + new-kernel-pkg --remove $f + fi +done -- cgit v1.2.3 From 041bd12e272c53a35c54c13875839bcb98c999ce Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 9 Feb 2016 16:11:26 -0500 Subject: Revert "workqueue: make sure delayed work run in local cpu" This reverts commit 874bbfe600a660cba9c776b3957b1ce393151b76. Workqueue used to implicity guarantee that work items queued without explicit CPU specified are put on the local CPU. Recent changes in timer broke the guarantee and led to vmstat breakage which was fixed by 176bed1de5bf ("vmstat: explicitly schedule per-cpu work on the CPU we need it to run on"). vmstat is the most likely to expose the issue and it's quite possible that there are other similar problems which are a lot more difficult to trigger. As a preventive measure, 874bbfe600a6 ("workqueue: make sure delayed work run in local cpu") was applied to restore the local CPU guarnatee. Unfortunately, the change exposed a bug in timer code which got fixed by 22b886dd1018 ("timers: Use proper base migration in add_timer_on()"). Due to code restructuring, the commit couldn't be backported beyond certain point and stable kernels which only had 874bbfe600a6 started crashing. The local CPU guarantee was accidental more than anything else and we want to get rid of it anyway. As, with the vmstat case fixed, 874bbfe600a6 is causing more problems than it's fixing, it has been decided to take the chance and officially break the guarantee by reverting the commit. A debug feature will be added to force foreign CPU assignment to expose cases relying on the guarantee and fixes for the individual cases will be backported to stable as necessary. Signed-off-by: Tejun Heo Fixes: 874bbfe600a6 ("workqueue: make sure delayed work run in local cpu") Link: http://lkml.kernel.org/g/20160120211926.GJ10810@quack.suse.cz Cc: stable@vger.kernel.org Cc: Mike Galbraith Cc: Henrique de Moraes Holschuh Cc: Daniel Bilik Cc: Jan Kara Cc: Shaohua Li Cc: Sasha Levin Cc: Ben Hutchings Cc: Thomas Gleixner Cc: Daniel Bilik Cc: Jiri Slaby Cc: Michal Hocko --- kernel/workqueue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index dc7faad63646..5e63d3b719ae 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1464,13 +1464,13 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, timer_stats_timer_set_start_info(&dwork->timer); dwork->wq = wq; - /* timer isn't guaranteed to run in this cpu, record earlier */ - if (cpu == WORK_CPU_UNBOUND) - cpu = raw_smp_processor_id(); dwork->cpu = cpu; timer->expires = jiffies + delay; - add_timer_on(timer, cpu); + if (unlikely(cpu != WORK_CPU_UNBOUND)) + add_timer_on(timer, cpu); + else + add_timer(timer); } /** -- cgit v1.2.3 From 42bbe400fbef6283a28227ea262a1145b27e957f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 8 Feb 2016 18:58:34 +0530 Subject: thermal: of: use for_each_available_child_of_node for child iterator Use for_each_available_child_of_node() for iterating over each available child instead of iterating over each child and then checking their status. Signed-off-by: Laxman Dewangan Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index be4eedcb839a..9043f8f91852 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -475,14 +475,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, sensor_np = of_node_get(dev->of_node); - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { struct of_phandle_args sensor_specs; int ret, id; - /* Check whether child is enabled or not */ - if (!of_device_is_available(child)) - continue; - /* For now, thermal framework supports only 1 sensor per zone */ ret = of_parse_phandle_with_args(child, "thermal-sensors", "#thermal-sensor-cells", @@ -881,16 +877,12 @@ int __init of_parse_thermal_zones(void) return 0; /* Run successfully on systems without thermal DT */ } - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { struct thermal_zone_device *zone; struct thermal_zone_params *tzp; int i, mask = 0; u32 prop; - /* Check whether child is enabled or not */ - if (!of_device_is_available(child)) - continue; - tz = thermal_of_build_thermal_zone(child); if (IS_ERR(tz)) { pr_err("failed to build thermal zone %s: %ld\n", @@ -968,13 +960,9 @@ void of_thermal_destroy_zones(void) return; } - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { struct thermal_zone_device *zone; - /* Check whether child is enabled or not */ - if (!of_device_is_available(child)) - continue; - zone = thermal_zone_get_zone_by_name(child->name); if (IS_ERR(zone)) continue; -- cgit v1.2.3 From 8b477ea56383dc8b838f1f8b506e4571c14ceb30 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Jan 2016 02:45:08 +0000 Subject: thermal: rcar: enable to use thermal-zone on DT This patch enables to use thermal-zone on DT if it was calles as "renesas,rcar-thermal-gen2". Previous style (= non thermal-zone) is still supported by "renesas,rcar-thermal" to keep compatibility for "git bisect". Signed-off-by: Kuninori Morimoto Signed-off-by: Eduardo Valentin --- .../devicetree/bindings/thermal/rcar-thermal.txt | 37 +++++++++++++++++- drivers/thermal/rcar_thermal.c | 45 +++++++++++++++++++--- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt index 332e625f6ed0..e5ee3f159893 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt @@ -1,8 +1,9 @@ * Renesas R-Car Thermal Required properties: -- compatible : "renesas,thermal-", "renesas,rcar-thermal" - as fallback. +- compatible : "renesas,thermal-", + "renesas,rcar-gen2-thermal" (with thermal-zone) or + "renesas,rcar-thermal" (without thermal-zone) as fallback. Examples with soctypes are: - "renesas,thermal-r8a73a4" (R-Mobile APE6) - "renesas,thermal-r8a7779" (R-Car H1) @@ -36,3 +37,35 @@ thermal@e61f0000 { 0xe61f0300 0x38>; interrupts = <0 69 IRQ_TYPE_LEVEL_HIGH>; }; + +Example (with thermal-zone): + +thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + + thermal-sensors = <&thermal>; + + trips { + cpu-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + cooling-maps { + }; + }; +}; + +thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7790", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>; + interrupts = <0 69 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp5_clks R8A7790_CLK_THERMAL>; + power-domains = <&cpg_clocks>; + #thermal-sensor-cells = <0>; +}; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 44b9c485157d..0e735acea33a 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -75,8 +76,10 @@ struct rcar_thermal_priv { #define rcar_has_irq_support(priv) ((priv)->common->base) #define rcar_id_to_shift(priv) ((priv)->id * 8) +#define USE_OF_THERMAL 1 static const struct of_device_id rcar_thermal_dt_ids[] = { { .compatible = "renesas,rcar-thermal", }, + { .compatible = "renesas,rcar-gen2-thermal", .data = (void *)USE_OF_THERMAL }, {}, }; MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); @@ -200,9 +203,9 @@ err_out_unlock: return ret; } -static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) +static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, + int *temp) { - struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); int tmp; int ret; @@ -226,6 +229,20 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) return 0; } +static int rcar_thermal_of_get_temp(void *data, int *temp) +{ + struct rcar_thermal_priv *priv = data; + + return rcar_thermal_get_current_temp(priv, temp); +} + +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + return rcar_thermal_get_current_temp(priv, temp); +} + static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { @@ -282,6 +299,10 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, return 0; } +static const struct thermal_zone_of_device_ops rcar_thermal_zone_of_ops = { + .get_temp = rcar_thermal_of_get_temp, +}; + static struct thermal_zone_device_ops rcar_thermal_zone_ops = { .get_temp = rcar_thermal_get_temp, .get_trip_type = rcar_thermal_get_trip_type, @@ -318,14 +339,20 @@ static void rcar_thermal_work(struct work_struct *work) priv = container_of(work, struct rcar_thermal_priv, work.work); - rcar_thermal_get_temp(priv->zone, &cctemp); + ret = rcar_thermal_get_current_temp(priv, &cctemp); + if (ret < 0) + return; + ret = rcar_thermal_update_temp(priv); if (ret < 0) return; rcar_thermal_irq_enable(priv); - rcar_thermal_get_temp(priv->zone, &nctemp); + ret = rcar_thermal_get_current_temp(priv, &nctemp); + if (ret < 0) + return; + if (nctemp != cctemp) thermal_zone_device_update(priv->zone); } @@ -403,6 +430,8 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct rcar_thermal_priv *priv; struct device *dev = &pdev->dev; struct resource *res, *irq; + const struct of_device_id *of_id = of_match_device(rcar_thermal_dt_ids, dev); + unsigned long of_data = (unsigned long)of_id->data; int mres = 0; int i; int ret = -ENODEV; @@ -463,7 +492,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) if (ret < 0) goto error_unregister; - priv->zone = thermal_zone_device_register("rcar_thermal", + if (of_data == USE_OF_THERMAL) + priv->zone = thermal_zone_of_sensor_register( + dev, i, priv, + &rcar_thermal_zone_of_ops); + else + priv->zone = thermal_zone_device_register( + "rcar_thermal", 1, 0, priv, &rcar_thermal_zone_ops, NULL, 0, idle); -- cgit v1.2.3 From d612c64d1f4d6b2464993dfeafd9ec319f774188 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 17:44:10 +0100 Subject: thermal: spear: use __maybe_unused for PM functions The spear thermal driver hides its suspend/resume function conditionally based on CONFIG_PM, but references them based on CONFIG_PM_SLEEP, so we get a warning if the former is set but the latter is not: thermal/spear_thermal.c:58:12: warning: 'spear_thermal_suspend' defined but not used [-Wunused-function] thermal/spear_thermal.c:75:12: warning: 'spear_thermal_resume' defined but not used [-Wunused-function] This removes the #ifdef and instead uses a __maybe_uninitialized annotation to avoid the warning and improve compile-time coverage. Signed-off-by: Arnd Bergmann Signed-off-by: Eduardo Valentin --- drivers/thermal/spear_thermal.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 534dd9136662..81b35aace9de 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -54,8 +54,7 @@ static struct thermal_zone_device_ops ops = { .get_temp = thermal_get_temp, }; -#ifdef CONFIG_PM -static int spear_thermal_suspend(struct device *dev) +static int __maybe_unused spear_thermal_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev); @@ -72,7 +71,7 @@ static int spear_thermal_suspend(struct device *dev) return 0; } -static int spear_thermal_resume(struct device *dev) +static int __maybe_unused spear_thermal_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev); @@ -94,7 +93,6 @@ static int spear_thermal_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(spear_thermal_pm_ops, spear_thermal_suspend, spear_thermal_resume); -- cgit v1.2.3 From 4d2f1794c07aae55b8f25f4d8aebcafc0d3e501d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 17:44:11 +0100 Subject: thermal: allow spear-thermal driver to be a module When the thermal subsystem is a loadable module, the spear driver fails to build: drivers/thermal/built-in.o: In function `spear_thermal_exit': spear_thermal.c:(.text+0xf8): undefined reference to `thermal_zone_device_unregister' drivers/thermal/built-in.o: In function `spear_thermal_probe': spear_thermal.c:(.text+0x230): undefined reference to `thermal_zone_device_register' This changes the symbol to a tristate, so Kconfig can track the dependency correctly. Signed-off-by: Arnd Bergmann Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 8cc4ac64a91c..0852fd028dc9 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -195,7 +195,7 @@ config IMX_THERMAL passive trip is crossed. config SPEAR_THERMAL - bool "SPEAr thermal sensor driver" + tristate "SPEAr thermal sensor driver" depends on PLAT_SPEAR || COMPILE_TEST depends on OF help -- cgit v1.2.3 From 26716ce124fce88f288f07738ef685d5dfe5c13f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 17:44:12 +0100 Subject: thermal: allow u8500-thermal driver to be a module When the thermal subsystem is a loadable module, the u8500 driver fails to build: drivers/thermal/built-in.o: In function `db8500_thermal_probe': db8500_thermal.c:(.text+0x96c): undefined reference to `thermal_zone_device_register' drivers/thermal/built-in.o: In function `db8500_thermal_work': db8500_thermal.c:(.text+0xab4): undefined reference to `thermal_zone_device_update' This changes the symbol to a tristate, so Kconfig can track the dependency correctly. Signed-off-by: Arnd Bergmann Signed-off-by: Eduardo Valentin --- drivers/mfd/db8500-prcmu.c | 3 +++ drivers/thermal/Kconfig | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index e6e4bacb09ee..12099b09a9a7 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2048,6 +2048,7 @@ int db8500_prcmu_config_hotmon(u8 low, u8 high) return 0; } +EXPORT_SYMBOL_GPL(db8500_prcmu_config_hotmon); static int config_hot_period(u16 val) { @@ -2074,11 +2075,13 @@ int db8500_prcmu_start_temp_sense(u16 cycles32k) return config_hot_period(cycles32k); } +EXPORT_SYMBOL_GPL(db8500_prcmu_start_temp_sense); int db8500_prcmu_stop_temp_sense(void) { return config_hot_period(0xFFFF); } +EXPORT_SYMBOL_GPL(db8500_prcmu_stop_temp_sense); static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3) { diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 0852fd028dc9..7c92c09be213 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -237,8 +237,8 @@ config DOVE_THERMAL framework. config DB8500_THERMAL - bool "DB8500 thermal management" - depends on ARCH_U8500 + tristate "DB8500 thermal management" + depends on MFD_DB8500_PRCMU default y help Adds DB8500 thermal management implementation according to the thermal -- cgit v1.2.3 From ef557180447fa9a7a0affd3abb21ecceb4b5e125 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Tue, 9 Feb 2016 17:59:38 -0500 Subject: workqueue: schedule WORK_CPU_UNBOUND work on wq_unbound_cpumask CPUs WORK_CPU_UNBOUND work items queued to a bound workqueue always run locally. This is a good thing normally, but not when the user has asked us to keep unbound work away from certain CPUs. Round robin these to wq_unbound_cpumask CPUs instead, as perturbation avoidance trumps performance. tj: Cosmetic and comment changes. WARN_ON_ONCE() dropped from empty (wq_unbound_cpumask AND cpu_online_mask). If we want that, it should be done when config changes. Signed-off-by: Mike Galbraith Signed-off-by: Tejun Heo --- kernel/workqueue.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5e63d3b719ae..054774605d2f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -301,7 +301,11 @@ static DEFINE_SPINLOCK(wq_mayday_lock); /* protects wq->maydays list */ static LIST_HEAD(workqueues); /* PR: list of all workqueues */ static bool workqueue_freezing; /* PL: have wqs started freezing? */ -static cpumask_var_t wq_unbound_cpumask; /* PL: low level cpumask for all unbound wqs */ +/* PL: allowable cpus for unbound wqs and work items */ +static cpumask_var_t wq_unbound_cpumask; + +/* CPU where unbound work was last round robin scheduled from this CPU */ +static DEFINE_PER_CPU(int, wq_rr_cpu_last); /* the per-cpu worker pools */ static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS], @@ -1298,6 +1302,32 @@ static bool is_chained_work(struct workqueue_struct *wq) return worker && worker->current_pwq->wq == wq; } +/* + * When queueing an unbound work item to a wq, prefer local CPU if allowed + * by wq_unbound_cpumask. Otherwise, round robin among the allowed ones to + * avoid perturbing sensitive tasks. + */ +static int wq_select_unbound_cpu(int cpu) +{ + int new_cpu; + + if (cpumask_test_cpu(cpu, wq_unbound_cpumask)) + return cpu; + if (cpumask_empty(wq_unbound_cpumask)) + return cpu; + + new_cpu = __this_cpu_read(wq_rr_cpu_last); + new_cpu = cpumask_next_and(new_cpu, wq_unbound_cpumask, cpu_online_mask); + if (unlikely(new_cpu >= nr_cpu_ids)) { + new_cpu = cpumask_first_and(wq_unbound_cpumask, cpu_online_mask); + if (unlikely(new_cpu >= nr_cpu_ids)) + return cpu; + } + __this_cpu_write(wq_rr_cpu_last, new_cpu); + + return new_cpu; +} + static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) { @@ -1323,7 +1353,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, return; retry: if (req_cpu == WORK_CPU_UNBOUND) - cpu = raw_smp_processor_id(); + cpu = wq_select_unbound_cpu(raw_smp_processor_id()); /* pwq which will be used unless @work is executing elsewhere */ if (!(wq->flags & WQ_UNBOUND)) -- cgit v1.2.3 From f303fccb82928790ec58eea82722bd5c54d300b3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 9 Feb 2016 17:59:38 -0500 Subject: workqueue: implement "workqueue.debug_force_rr_cpu" debug feature Workqueue used to guarantee local execution for work items queued without explicit target CPU. The guarantee is gone now which can break some usages in subtle ways. To flush out those cases, this patch implements a debug feature which forces round-robin CPU selection for all such work items. The debug feature defaults to off and can be enabled with a kernel parameter. The default can be flipped with a debug config option. If you hit this commit during bisection, please refer to 041bd12e272c ("Revert "workqueue: make sure delayed work run in local cpu"") for more information and ping me. Signed-off-by: Tejun Heo --- Documentation/kernel-parameters.txt | 11 +++++++++++ kernel/workqueue.c | 23 +++++++++++++++++++++-- lib/Kconfig.debug | 15 +++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 87d40a72f6a1..cda2ead39093 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -4230,6 +4230,17 @@ bytes respectively. Such letter suffixes can also be entirely omitted. The default value of this parameter is determined by the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. + workqueue.debug_force_rr_cpu + Workqueue used to implicitly guarantee that work + items queued without explicit CPU specified are put + on the local CPU. This guarantee is no longer true + and while local CPU is still preferred work items + may be put on foreign CPUs. This debug option + forces round-robin CPU selection to flush out + usages which depend on the now broken guarantee. + When enabled, memory and cache locality will be + impacted. + x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of default x2apic cluster mode on platforms supporting x2apic. diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 054774605d2f..51d77e7c0989 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -307,6 +307,18 @@ static cpumask_var_t wq_unbound_cpumask; /* CPU where unbound work was last round robin scheduled from this CPU */ static DEFINE_PER_CPU(int, wq_rr_cpu_last); +/* + * Local execution of unbound work items is no longer guaranteed. The + * following always forces round-robin CPU selection on unbound work items + * to uncover usages which depend on it. + */ +#ifdef CONFIG_DEBUG_WQ_FORCE_RR_CPU +static bool wq_debug_force_rr_cpu = true; +#else +static bool wq_debug_force_rr_cpu = false; +#endif +module_param_named(debug_force_rr_cpu, wq_debug_force_rr_cpu, bool, 0644); + /* the per-cpu worker pools */ static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS], cpu_worker_pools); @@ -1309,10 +1321,17 @@ static bool is_chained_work(struct workqueue_struct *wq) */ static int wq_select_unbound_cpu(int cpu) { + static bool printed_dbg_warning; int new_cpu; - if (cpumask_test_cpu(cpu, wq_unbound_cpumask)) - return cpu; + if (likely(!wq_debug_force_rr_cpu)) { + if (cpumask_test_cpu(cpu, wq_unbound_cpumask)) + return cpu; + } else if (!printed_dbg_warning) { + pr_warn("workqueue: round-robin CPU selection forced, expect performance impact\n"); + printed_dbg_warning = true; + } + if (cpumask_empty(wq_unbound_cpumask)) return cpu; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ecb9e75614bf..8bfd1aca7a3d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1400,6 +1400,21 @@ config RCU_EQS_DEBUG endmenu # "RCU Debugging" +config DEBUG_WQ_FORCE_RR_CPU + bool "Force round-robin CPU selection for unbound work items" + depends on DEBUG_KERNEL + default n + help + Workqueue used to implicitly guarantee that work items queued + without explicit CPU specified are put on the local CPU. This + guarantee is no longer true and while local CPU is still + preferred work items may be put on foreign CPUs. Kernel + parameter "workqueue.debug_force_rr_cpu" is added to force + round-robin CPU selection to flush out usages which depend on the + now broken guarantee. This config option enables the debug + feature by default. When enabled, memory and cache locality will + be impacted. + config DEBUG_BLOCK_EXT_DEVT bool "Force extended block device numbers and spread them" depends on DEBUG_KERNEL -- cgit v1.2.3 From dc262dfaaeda7617ae0b15b5ce1252a6cd102b19 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 9 Feb 2016 09:32:42 -0800 Subject: Input: edt-ft5x06 - fix setting gain, offset, and threshold via device tree A recent patch broke parsing the gain, offset, and threshold parameters from device tree. Instead of setting the cached values and writing them to the correct registers during probe, it would write the values from DT into the register address variables and never write them to the chip during normal operation. Fixes: 2e23b7a96372 ("Input: edt-ft5x06 - use generic properties API") Signed-off-by: Philipp Zabel Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 0b0f8c17f3f7..23fbe382da8b 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -822,16 +822,22 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev, int error; error = device_property_read_u32(dev, "threshold", &val); - if (!error) - reg_addr->reg_threshold = val; + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, val); + tsdata->threshold = val; + } error = device_property_read_u32(dev, "gain", &val); - if (!error) - reg_addr->reg_gain = val; + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, val); + tsdata->gain = val; + } error = device_property_read_u32(dev, "offset", &val); - if (!error) - reg_addr->reg_offset = val; + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val); + tsdata->offset = val; + } } static void -- cgit v1.2.3 From 7008dafb02c858411d70b82c3aeb40f93b93c67a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 9 Feb 2016 09:35:33 -0800 Subject: Input: adp5589 - fix row 5 handling for adp5589 The adp5589 has row 5, don't skip it when creating the GPIO mapping. Otherwise the pin gets reserved as used and it is not possible to use it as a GPIO. Signed-off-by: Lars-Peter Clausen Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/adp5589-keys.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 4d446d5085aa..c01a1d648f9f 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -235,7 +235,7 @@ struct adp5589_kpad { unsigned short gpimapsize; unsigned extend_cfg; bool is_adp5585; - bool adp5585_support_row5; + bool support_row5; #ifdef CONFIG_GPIOLIB unsigned char gpiomap[ADP5589_MAXGPIO]; bool export_gpio; @@ -485,7 +485,7 @@ static int adp5589_build_gpiomap(struct adp5589_kpad *kpad, if (kpad->extend_cfg & C4_EXTEND_CFG) pin_used[kpad->var->c4_extend_cfg] = true; - if (!kpad->adp5585_support_row5) + if (!kpad->support_row5) pin_used[5] = true; for (i = 0; i < kpad->var->maxgpio; i++) @@ -884,12 +884,13 @@ static int adp5589_probe(struct i2c_client *client, switch (id->driver_data) { case ADP5585_02: - kpad->adp5585_support_row5 = true; + kpad->support_row5 = true; case ADP5585_01: kpad->is_adp5585 = true; kpad->var = &const_adp5585; break; case ADP5589: + kpad->support_row5 = true; kpad->var = &const_adp5589; break; } -- cgit v1.2.3 From ff84dabe3c6ebba517086e1161145d70ff129665 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 9 Feb 2016 10:32:53 -0800 Subject: Input: colibri-vf50-ts - add missing #include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/input/touchscreen/colibri-vf50-ts.c: In function ‘vf50_ts_probe’: drivers/input/touchscreen/colibri-vf50-ts.c:302: error: implicit declaration of function ‘of_property_read_u32’ Signed-off-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/colibri-vf50-ts.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 5d4903a402cc..69828d015d45 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From dec2b2849cfccf09822d6ce3f9bc84b8c8611152 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Sun, 7 Feb 2016 12:54:35 +0530 Subject: ARCv2: intc: Allow interruption by lowest priority interrupt ARC HS Cores support configurable multiple interrupt priorities of upto 16 levels. There is processor "interrupt preemption threshhold" in STATUS32.E[4:1] And several places need to set this up: 1. seed value as kernel is booting 2. seed value for user space programs 3. Arg to SLEEP instruction in idle task (what interrupt prio can wake) 4. Per-IRQ line prioirty (i.e. what is the priority of interrupt raised by a peripheral or timer or perf counter... Currently above sites use the highest priority 0. This can be potential problem when multiple priorities are supported. e.g. user space could only be interrupted by P0 interrupt, not others... So turn this over and instead make default interruption level to be the lowest priority possible 15. This should be fine even if there are fewer priority levels configured (say two: P0 HIGH, P1 LOW) This feature also effectively disables FIRQ feature if present in hardware config. With old code, a P0 interrupt would be FIRQ, needing special handling (ISR or Register Banks) which is NOT supported yet. Now it not be P0 (P15 or whatever is lowest prio) so FIRQ is not triggered. Signed-off-by: Vineet Gupta --- arch/arc/include/asm/irqflags-arcv2.h | 7 ++++-- arch/arc/kernel/intc-arcv2.c | 41 ++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/arch/arc/include/asm/irqflags-arcv2.h b/arch/arc/include/asm/irqflags-arcv2.h index 258b0e5ad332..1fc18ee06cf2 100644 --- a/arch/arc/include/asm/irqflags-arcv2.h +++ b/arch/arc/include/asm/irqflags-arcv2.h @@ -30,8 +30,11 @@ /* Was Intr taken in User Mode */ #define AUX_IRQ_ACT_BIT_U 31 -/* 0 is highest level, but taken by FIRQs, if present in design */ -#define ARCV2_IRQ_DEF_PRIO 0 +/* + * User space should be interruptable even by lowest prio interrupt + * Safe even if actual interrupt priorities is fewer or even one + */ +#define ARCV2_IRQ_DEF_PRIO 15 /* seed value for status register */ #define ISA_INIT_STATUS_BITS (STATUS_IE_MASK | STATUS_AD_MASK | \ diff --git a/arch/arc/kernel/intc-arcv2.c b/arch/arc/kernel/intc-arcv2.c index 0394f9f61b46..942526322ae7 100644 --- a/arch/arc/kernel/intc-arcv2.c +++ b/arch/arc/kernel/intc-arcv2.c @@ -14,6 +14,8 @@ #include #include +static int irq_prio; + /* * Early Hardware specific Interrupt setup * -Called very early (start_kernel -> setup_arch -> setup_processor) @@ -24,6 +26,14 @@ void arc_init_IRQ(void) { unsigned int tmp; + struct irq_build { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:3, firq:1, prio:4, exts:8, irqs:8, ver:8; +#else + unsigned int ver:8, irqs:8, exts:8, prio:4, firq:1, pad:3; +#endif + } irq_bcr; + struct aux_irq_ctrl { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int res3:18, save_idx_regs:1, res2:1, @@ -46,28 +56,25 @@ void arc_init_IRQ(void) WRITE_AUX(AUX_IRQ_CTRL, ictrl); - /* setup status32, don't enable intr yet as kernel doesn't want */ - tmp = read_aux_reg(0xa); - tmp |= ISA_INIT_STATUS_BITS; - tmp &= ~STATUS_IE_MASK; - asm volatile("flag %0 \n"::"r"(tmp)); - /* * ARCv2 core intc provides multiple interrupt priorities (upto 16). * Typical builds though have only two levels (0-high, 1-low) * Linux by default uses lower prio 1 for most irqs, reserving 0 for * NMI style interrupts in future (say perf) - * - * Read the intc BCR to confirm that Linux default priority is avail - * in h/w - * - * Note: - * IRQ_BCR[27..24] contains N-1 (for N priority levels) and prio level - * is 0 based. */ - tmp = (read_aux_reg(ARC_REG_IRQ_BCR) >> 24 ) & 0xF; - if (ARCV2_IRQ_DEF_PRIO > tmp) - panic("Linux default irq prio incorrect\n"); + + READ_BCR(ARC_REG_IRQ_BCR, irq_bcr); + + irq_prio = irq_bcr.prio; /* Encoded as N-1 for N levels */ + pr_info("archs-intc\t: %d priority levels (default %d)%s\n", + irq_prio + 1, irq_prio, + irq_bcr.firq ? " FIRQ (not used)":""); + + /* setup status32, don't enable intr yet as kernel doesn't want */ + tmp = read_aux_reg(0xa); + tmp |= STATUS_AD_MASK | (irq_prio << 1); + tmp &= ~STATUS_IE_MASK; + asm volatile("flag %0 \n"::"r"(tmp)); } static void arcv2_irq_mask(struct irq_data *data) @@ -86,7 +93,7 @@ void arcv2_irq_enable(struct irq_data *data) { /* set default priority */ write_aux_reg(AUX_IRQ_SELECT, data->irq); - write_aux_reg(AUX_IRQ_PRIORITY, ARCV2_IRQ_DEF_PRIO); + write_aux_reg(AUX_IRQ_PRIORITY, irq_prio); /* * hw auto enables (linux unmask) all by default -- cgit v1.2.3 From cb54ad6cddb606add2481b82901d69670b480d1b Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 6 Jan 2016 22:55:00 -0500 Subject: misc: eeprom_93xx46: Fix 16-bit read and write accesses. Compatible at93xx46 devices from both Microchip and Atmel expect a word-based address, regardless of whether the device is strapped for 8- or 16-bit operation. However, the offset parameter passed in when reading or writing at a specific location is always specified in terms of bytes. This commit fixes 16-bit read and write accesses by shifting the offset parameter to account for this difference between a byte offset and a word-based address. Signed-off-by: Cory Tusar Tested-by: Chris Healy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 473aa0a2eaf6..da3c081c2639 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -54,7 +54,7 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, cmd_addr |= off & 0x7f; bits = 10; } else { - cmd_addr |= off & 0x3f; + cmd_addr |= (off >> 1) & 0x3f; bits = 9; } @@ -155,7 +155,7 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, bits = 10; data_len = 1; } else { - cmd_addr |= off & 0x3f; + cmd_addr |= (off >> 1) & 0x3f; bits = 9; data_len = 2; } -- cgit v1.2.3 From 862a4a7836ebe77beabcda844d03e1eab3e1cf19 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 6 Jan 2016 22:55:01 -0500 Subject: Documentation: devicetree: Add DT bindings to eeprom_93xx46 driver. This commit documents bindings to be added to the eeprom_93xx46 driver which will allow: - Device word size and read-only attributes to be specified. - A device-specific compatible string for use with Atmel AT93C46D EEPROMs. - Specifying a GPIO line to function as a 'select' or 'enable' signal prior to accessing the EEPROM. Signed-off-by: Cory Tusar Acked-by: Rob Herring Tested-by: Chris Healy Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/misc/eeprom-93xx46.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/eeprom-93xx46.txt diff --git a/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt b/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt new file mode 100644 index 000000000000..a8ebb4621f79 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/eeprom-93xx46.txt @@ -0,0 +1,25 @@ +EEPROMs (SPI) compatible with Microchip Technology 93xx46 family. + +Required properties: +- compatible : shall be one of: + "atmel,at93c46d" + "eeprom-93xx46" +- data-size : number of data bits per word (either 8 or 16) + +Optional properties: +- read-only : parameter-less property which disables writes to the EEPROM +- select-gpios : if present, specifies the GPIO that will be asserted prior to + each access to the EEPROM (e.g. for SPI bus multiplexing) + +Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt +apply. In particular, "reg" and "spi-max-frequency" properties must be given. + +Example: + eeprom@0 { + compatible = "eeprom-93xx46"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cs-high; + data-size = <8>; + select-gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>; + }; -- cgit v1.2.3 From c074abe02e5e3479b2dfd109fa2620d22d351c34 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 6 Jan 2016 22:55:02 -0500 Subject: misc: eeprom_93xx46: Implement eeprom_93xx46 DT bindings. This commit implements bindings in the eeprom_93xx46 driver allowing device word size and read-only attributes to be specified via devicetree. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index da3c081c2639..f0c30b366218 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -294,12 +296,58 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev, } static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); +static const struct of_device_id eeprom_93xx46_of_table[] = { + { .compatible = "eeprom-93xx46", }, + {} +}; +MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); + +static int eeprom_93xx46_probe_dt(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + struct eeprom_93xx46_platform_data *pd; + u32 tmp; + int ret; + + pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + ret = of_property_read_u32(np, "data-size", &tmp); + if (ret < 0) { + dev_err(&spi->dev, "data-size property not found\n"); + return ret; + } + + if (tmp == 8) { + pd->flags |= EE_ADDR8; + } else if (tmp == 16) { + pd->flags |= EE_ADDR16; + } else { + dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); + return -EINVAL; + } + + if (of_property_read_bool(np, "read-only")) + pd->flags |= EE_READONLY; + + spi->dev.platform_data = pd; + + return 0; +} + static int eeprom_93xx46_probe(struct spi_device *spi) { struct eeprom_93xx46_platform_data *pd; struct eeprom_93xx46_dev *edev; int err; + if (spi->dev.of_node) { + err = eeprom_93xx46_probe_dt(spi); + if (err < 0) + return err; + } + pd = spi->dev.platform_data; if (!pd) { dev_err(&spi->dev, "missing platform data\n"); @@ -370,6 +418,7 @@ static int eeprom_93xx46_remove(struct spi_device *spi) static struct spi_driver eeprom_93xx46_driver = { .driver = { .name = "93xx46", + .of_match_table = of_match_ptr(eeprom_93xx46_of_table), }, .probe = eeprom_93xx46_probe, .remove = eeprom_93xx46_remove, -- cgit v1.2.3 From ececdc021ba8f48ce87f476dd412e2ed50f9cd8e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 8 Feb 2016 15:34:21 +0100 Subject: lkdtm: mark execute_location as noinline The kernel sometimes fails to link when lkdrm is built-in and compiled with clang: relocation truncated to fit: R_ARM_THM_CALL against `.bss' The reason here is that a relocation from .text to .bss fails to generate a trampoline because .bss is not an executable section. Marking the function 'noinline' turns the relative branch to .bss into an absolute branch to the function argument, and that works fine. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 11fdadc68e53..5c1351b19029 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -335,7 +335,7 @@ static noinline void corrupt_stack(void) memset((void *)data, 0, 64); } -static void execute_location(void *dst) +static void noinline execute_location(void *dst) { void (*func)(void) = dst; -- cgit v1.2.3 From 4ddbdbb90d2ec7d7db93c98936faf3dad75f479b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 9 Feb 2016 00:00:17 +0200 Subject: mei: wd: drop AGAIN the watchdog code from the core mei driver The file wd.c was remove from the driver by commit commit fdd9b8655933 ("mei: wd: drop the watchdog code from the core mei driver") Unfortunately it came back by mistake in rebasing in the commit commit 06ee536bcb15 ("mei: fill file pointer in read cb for fixed address client") Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/wd.c | 391 -------------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 drivers/misc/mei/wd.c diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c deleted file mode 100644 index 7d9b4ee42f65..000000000000 --- a/drivers/misc/mei/wd.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2012, 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 "mei_dev.h" -#include "hbm.h" -#include "client.h" - -static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; -static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; - -/* - * AMT Watchdog Device - */ -#define INTEL_AMT_WATCHDOG_ID "INTCAMT" - -/* UUIDs for AMT F/W clients */ -const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, - 0x9D, 0xA9, 0x15, 0x14, 0xCB, - 0x32, 0xAB); - -static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) -{ - dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); - memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); - memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); -} - -/** - * mei_wd_host_init - connect to the watchdog client - * - * @dev: the device structure - * @me_cl: me client - * - * Return: -ENOTTY if wd client cannot be found - * -EIO if write has failed - * 0 on success - */ -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - mei_cl_init(cl, dev); - - dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; - dev->wd_state = MEI_WD_IDLE; - - ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); - if (ret < 0) { - dev_info(dev->dev, "wd: failed link client\n"); - return ret; - } - - ret = mei_cl_connect(cl, me_cl, NULL); - if (ret) { - dev_err(dev->dev, "wd: failed to connect = %d\n", ret); - mei_cl_unlink(cl); - return ret; - } - - ret = mei_watchdog_register(dev); - if (ret) { - mei_cl_disconnect(cl); - mei_cl_unlink(cl); - } - return ret; -} - -/** - * mei_wd_send - sends watch dog message to fw. - * - * @dev: the device structure - * - * Return: 0 if success, - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ENODEV on flow control failure - */ -int mei_wd_send(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - struct mei_msg_hdr hdr; - int ret; - - hdr.host_addr = cl->host_client_id; - hdr.me_addr = mei_cl_me_id(cl); - hdr.msg_complete = 1; - hdr.reserved = 0; - hdr.internal = 0; - - if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_START_MSG_SIZE; - else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_STOP_MSG_SIZE; - else { - dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); - return -EINVAL; - } - - ret = mei_write_message(dev, &hdr, dev->wd_data); - if (ret) { - dev_err(dev->dev, "wd: write message failed\n"); - return ret; - } - - ret = mei_cl_flow_ctrl_reduce(cl); - if (ret) { - dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); - return ret; - } - - return 0; -} - -/** - * mei_wd_stop - sends watchdog stop message to fw. - * - * @dev: the device structure - * - * Return: 0 if success - * on error: - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ETIME on message timeout - */ -int mei_wd_stop(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - if (!mei_cl_is_connected(cl) || - dev->wd_state != MEI_WD_RUNNING) - return 0; - - memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); - - dev->wd_state = MEI_WD_STOPPING; - - ret = mei_cl_flow_ctrl_creds(cl, NULL); - if (ret < 0) - goto err; - - if (ret && mei_hbuf_acquire(dev)) { - ret = mei_wd_send(dev); - if (ret) - goto err; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - - mutex_unlock(&dev->device_lock); - - ret = wait_event_timeout(dev->wait_stop_wd, - dev->wd_state == MEI_WD_IDLE, - msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); - mutex_lock(&dev->device_lock); - if (dev->wd_state != MEI_WD_IDLE) { - /* timeout */ - ret = -ETIME; - dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); - goto err; - } - dev_dbg(dev->dev, "wd: stop completed after %u msec\n", - MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); - return 0; -err: - return ret; -} - -/** - * mei_wd_ops_start - wd start command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_start(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int err = -ENODEV; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", - mei_dev_state_str(dev->dev_state)); - goto end_unlock; - } - - if (!mei_cl_is_connected(cl)) { - cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); - goto end_unlock; - } - - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - err = 0; -end_unlock: - mutex_unlock(&dev->device_lock); - return err; -} - -/** - * mei_wd_ops_stop - wd stop command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_stop(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - mutex_unlock(&dev->device_lock); - - return 0; -} - -/** - * mei_wd_ops_ping - wd ping command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_ping(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int ret; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (!mei_cl_is_connected(cl)) { - cl_err(dev, cl, "wd: not connected.\n"); - ret = -ENODEV; - goto end; - } - - dev->wd_state = MEI_WD_RUNNING; - - ret = mei_cl_flow_ctrl_creds(cl, NULL); - if (ret < 0) - goto end; - - /* Check if we can send the ping to HW*/ - if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(dev->dev, "wd: sending ping\n"); - - ret = mei_wd_send(dev); - if (ret) - goto end; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - -end: - mutex_unlock(&dev->device_lock); - return ret; -} - -/** - * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. - * - * @wd_dev: watchdog device struct - * @timeout: timeout value to set - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, - unsigned int timeout) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - /* Check Timeout value */ - if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) - return -EINVAL; - - mutex_lock(&dev->device_lock); - - dev->wd_timeout = timeout; - wd_dev->timeout = timeout; - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - mutex_unlock(&dev->device_lock); - - return 0; -} - -/* - * Watchdog Device structs - */ -static const struct watchdog_ops wd_ops = { - .owner = THIS_MODULE, - .start = mei_wd_ops_start, - .stop = mei_wd_ops_stop, - .ping = mei_wd_ops_ping, - .set_timeout = mei_wd_ops_set_timeout, -}; -static const struct watchdog_info wd_info = { - .identity = INTEL_AMT_WATCHDOG_ID, - .options = WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_ALARMONLY, -}; - -static struct watchdog_device amt_wd_dev = { - .info = &wd_info, - .ops = &wd_ops, - .timeout = MEI_WD_DEFAULT_TIMEOUT, - .min_timeout = MEI_WD_MIN_TIMEOUT, - .max_timeout = MEI_WD_MAX_TIMEOUT, -}; - - -int mei_watchdog_register(struct mei_device *dev) -{ - - int ret; - - amt_wd_dev.parent = dev->dev; - /* unlock to perserve correct locking order */ - mutex_unlock(&dev->device_lock); - ret = watchdog_register_device(&amt_wd_dev); - mutex_lock(&dev->device_lock); - if (ret) { - dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", - ret); - return ret; - } - - dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); - watchdog_set_drvdata(&amt_wd_dev, dev); - return 0; -} - -void mei_watchdog_unregister(struct mei_device *dev) -{ - if (watchdog_get_drvdata(&amt_wd_dev) == NULL) - return; - - watchdog_set_drvdata(&amt_wd_dev, NULL); - watchdog_unregister_device(&amt_wd_dev); -} - -- cgit v1.2.3 From ef39830c35838696013a2f6c16f6728c4a9dcefd Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:11 -0800 Subject: misc: mic: Remove MIC X100 host virtio functionality This patch deletes the virtio functionality from the MIC X100 host driver. A subsequent patch will re-enable this functionality by consolidating the hardware independent logic in a new Virtio over PCIe (VOP) driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/Makefile | 2 - drivers/misc/mic/host/mic_boot.c | 2 - drivers/misc/mic/host/mic_debugfs.c | 190 --------- drivers/misc/mic/host/mic_device.h | 6 - drivers/misc/mic/host/mic_fops.c | 222 ---------- drivers/misc/mic/host/mic_fops.h | 32 -- drivers/misc/mic/host/mic_main.c | 48 +-- drivers/misc/mic/host/mic_virtio.c | 811 ------------------------------------ drivers/misc/mic/host/mic_virtio.h | 155 ------- 9 files changed, 4 insertions(+), 1464 deletions(-) delete mode 100644 drivers/misc/mic/host/mic_fops.c delete mode 100644 drivers/misc/mic/host/mic_fops.h delete mode 100644 drivers/misc/mic/host/mic_virtio.c delete mode 100644 drivers/misc/mic/host/mic_virtio.h diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 004d3db0f990..f3b502333ded 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -9,5 +9,3 @@ mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o mic_host-objs += mic_boot.o mic_host-objs += mic_debugfs.o -mic_host-objs += mic_fops.o -mic_host-objs += mic_virtio.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 7845564dff64..3df305f6282c 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -28,7 +28,6 @@ #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" -#include "mic_virtio.h" static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) { @@ -423,7 +422,6 @@ static void _mic_stop(struct cosm_device *cdev, bool force) * will be the first to be registered and the last to be * unregistered. */ - mic_virtio_reset_devices(mdev); scif_unregister_device(mdev->scdev); mic_free_dma_chans(mdev); mbus_unregister_device(mdev->dma_mbdev); diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 10581600777a..0a9daba8bb5d 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -26,7 +26,6 @@ #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" -#include "mic_virtio.h" /* Debugfs parent dir */ static struct dentry *mic_dbg; @@ -100,190 +99,6 @@ static const struct file_operations post_code_ops = { .release = mic_post_code_debug_release }; -static int mic_dp_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - struct mic_device_desc *d; - struct mic_device_ctrl *dc; - struct mic_vqconfig *vqconfig; - __u32 *features; - __u8 *config; - struct mic_bootparam *bootparam = mdev->dp; - int i, j; - - seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); - seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); - seq_printf(s, "Bootparam: node_id %d\n", - bootparam->node_id); - seq_printf(s, "Bootparam: c2h_scif_db %d\n", - bootparam->c2h_scif_db); - seq_printf(s, "Bootparam: h2c_scif_db %d\n", - bootparam->h2c_scif_db); - seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", - bootparam->scif_host_dma_addr); - seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", - bootparam->scif_card_dma_addr); - - - for (i = sizeof(*bootparam); i < MIC_DP_SIZE; - i += mic_total_desc_size(d)) { - d = mdev->dp + i; - dc = (void *)d + mic_aligned_desc_size(d); - - /* end of list */ - if (d->type == 0) - break; - - if (d->type == -1) - continue; - - seq_printf(s, "Type %d ", d->type); - seq_printf(s, "Num VQ %d ", d->num_vq); - seq_printf(s, "Feature Len %d\n", d->feature_len); - seq_printf(s, "Config Len %d ", d->config_len); - seq_printf(s, "Shutdown Status %d\n", d->status); - - for (j = 0; j < d->num_vq; j++) { - vqconfig = mic_vq_config(d) + j; - seq_printf(s, "vqconfig[%d]: ", j); - seq_printf(s, "address 0x%llx ", vqconfig->address); - seq_printf(s, "num %d ", vqconfig->num); - seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); - } - - features = (__u32 *)mic_vq_features(d); - seq_printf(s, "Features: Host 0x%x ", features[0]); - seq_printf(s, "Guest 0x%x\n", features[1]); - - config = mic_vq_configspace(d); - for (j = 0; j < d->config_len; j++) - seq_printf(s, "config[%d]=%d\n", j, config[j]); - - seq_puts(s, "Device control:\n"); - seq_printf(s, "Config Change %d ", dc->config_change); - seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); - seq_printf(s, "Guest Ack %d ", dc->guest_ack); - seq_printf(s, "Host ack %d\n", dc->host_ack); - seq_printf(s, "Used address updated %d ", - dc->used_address_updated); - seq_printf(s, "Vdev 0x%llx\n", dc->vdev); - seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); - seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); - } - - return 0; -} - -static int mic_dp_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mic_dp_show, inode->i_private); -} - -static int mic_dp_debug_release(struct inode *inode, struct file *file) -{ - return single_release(inode, file); -} - -static const struct file_operations dp_ops = { - .owner = THIS_MODULE, - .open = mic_dp_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = mic_dp_debug_release -}; - -static int mic_vdev_info_show(struct seq_file *s, void *unused) -{ - struct mic_device *mdev = s->private; - struct list_head *pos, *tmp; - struct mic_vdev *mvdev; - int i, j; - - mutex_lock(&mdev->mic_mutex); - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - mvdev = list_entry(pos, struct mic_vdev, list); - seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", - mvdev->virtio_id, - mic_vdevup(mvdev) ? "UP" : "DOWN", - mvdev->in_bytes, - mvdev->out_bytes); - for (i = 0; i < MIC_MAX_VRINGS; i++) { - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct mic_vringh *mvr = &mvdev->mvr[i]; - struct vringh *vrh = &mvr->vrh; - int num = vrh->vring.num; - if (!num) - continue; - desc = vrh->vring.desc; - seq_printf(s, "vring i %d avail_idx %d", - i, mvr->vring.info->avail_idx & (num - 1)); - seq_printf(s, " vring i %d avail_idx %d\n", - i, mvr->vring.info->avail_idx); - seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); - seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); - seq_printf(s, " completed %d\n", vrh->completed); - for (j = 0; j < num; j++) { - seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); - seq_printf(s, " flags 0x%x next %d\n", - desc->flags, desc->next); - desc++; - } - avail = vrh->vring.avail; - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx) & (num - 1)); - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); - used = vrh->vring.used; - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx) & (num - 1)); - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "used ring[%d] id %d len %d\n", - j, vringh32_to_cpu(vrh, - used->ring[j].id), - vringh32_to_cpu(vrh, - used->ring[j].len)); - } - } - mutex_unlock(&mdev->mic_mutex); - - return 0; -} - -static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mic_vdev_info_show, inode->i_private); -} - -static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) -{ - return single_release(inode, file); -} - -static const struct file_operations vdev_info_ops = { - .owner = THIS_MODULE, - .open = mic_vdev_info_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = mic_vdev_info_debug_release -}; - static int mic_msi_irq_info_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; @@ -367,11 +182,6 @@ void mic_create_debug_dir(struct mic_device *mdev) debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, &post_code_ops); - debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); - - debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, - &vdev_info_ops); - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, &msi_irq_info_ops); } diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 461184a12fbb..8460de122929 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -64,9 +64,6 @@ extern struct cosm_hw_ops cosm_hw_ops; * @bootaddr: MIC boot address. * @dp: virtio device page * @dp_dma_addr: virtio device page DMA address. - * @name: name for the misc char device - * @miscdev: registered misc char device - * @vdev_list: list of virtio devices. * @dma_mbdev: MIC BUS DMA device. * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available @@ -91,9 +88,6 @@ struct mic_device { u32 bootaddr; void *dp; dma_addr_t dp_dma_addr; - char name[16]; - struct miscdevice miscdev; - struct list_head vdev_list; struct mbus_device *dma_mbdev; struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c deleted file mode 100644 index 8cc1d90cd949..000000000000 --- a/drivers/misc/mic/host/mic_fops.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#include -#include - -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_fops.h" -#include "mic_virtio.h" - -int mic_open(struct inode *inode, struct file *f) -{ - struct mic_vdev *mvdev; - struct mic_device *mdev = container_of(f->private_data, - struct mic_device, miscdev); - - mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); - if (!mvdev) - return -ENOMEM; - - init_waitqueue_head(&mvdev->waitq); - INIT_LIST_HEAD(&mvdev->list); - mvdev->mdev = mdev; - mvdev->virtio_id = -1; - - f->private_data = mvdev; - return 0; -} - -int mic_release(struct inode *inode, struct file *f) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - - if (-1 != mvdev->virtio_id) - mic_virtio_del_device(mvdev); - f->private_data = NULL; - kfree(mvdev); - return 0; -} - -long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - void __user *argp = (void __user *)arg; - int ret; - - switch (cmd) { - case MIC_VIRTIO_ADD_DEVICE: - { - ret = mic_virtio_add_device(mvdev, argp); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - break; - } - case MIC_VIRTIO_COPY_DESC: - { - struct mic_copy_desc copy; - - ret = mic_vdev_inited(mvdev); - if (ret) - return ret; - - if (copy_from_user(©, argp, sizeof(copy))) - return -EFAULT; - - dev_dbg(mic_dev(mvdev), - "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", - __func__, __LINE__, copy.iovcnt, copy.vr_idx, - copy.update_used); - - ret = mic_virtio_copy_desc(mvdev, ©); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - if (copy_to_user( - &((struct mic_copy_desc __user *)argp)->out_len, - ©.out_len, sizeof(copy.out_len))) { - dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", - __func__, __LINE__, -EFAULT); - return -EFAULT; - } - break; - } - case MIC_VIRTIO_CONFIG_CHANGE: - { - ret = mic_vdev_inited(mvdev); - if (ret) - return ret; - - ret = mic_virtio_config_change(mvdev, argp); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - break; - } - default: - return -ENOIOCTLCMD; - }; - return 0; -} - -/* - * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and - * not when previously enqueued buffers may be available. This means that - * in the card->host (TX) path, when userspace is unblocked by poll it - * must drain all available descriptors or it can stall. - */ -unsigned int mic_poll(struct file *f, poll_table *wait) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - int mask = 0; - - poll_wait(f, &mvdev->waitq, wait); - - if (mic_vdev_inited(mvdev)) { - mask = POLLERR; - } else if (mvdev->poll_wake) { - mvdev->poll_wake = 0; - mask = POLLIN | POLLOUT; - } - - return mask; -} - -static inline int -mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, - unsigned long *size, unsigned long *pa) -{ - struct mic_device *mdev = mvdev->mdev; - unsigned long start = MIC_DP_SIZE; - int i; - - /* - * MMAP interface is as follows: - * offset region - * 0x0 virtio device_page - * 0x1000 first vring - * 0x1000 + size of 1st vring second vring - * .... - */ - if (!offset) { - *pa = virt_to_phys(mdev->dp); - *size = MIC_DP_SIZE; - return 0; - } - - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - if (offset == start) { - *pa = virt_to_phys(mvr->vring.va); - *size = mvr->vring.len; - return 0; - } - start += mvr->vring.len; - } - return -1; -} - -/* - * Maps the device page and virtio rings to user space for readonly access. - */ -int -mic_mmap(struct file *f, struct vm_area_struct *vma) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; - int i, err; - - err = mic_vdev_inited(mvdev); - if (err) - return err; - - if (vma->vm_flags & VM_WRITE) - return -EACCES; - - while (size_rem) { - i = mic_query_offset(mvdev, offset, &size, &pa); - if (i < 0) - return -EINVAL; - err = remap_pfn_range(vma, vma->vm_start + offset, - pa >> PAGE_SHIFT, size, vma->vm_page_prot); - if (err) - return err; - dev_dbg(mic_dev(mvdev), - "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", - __func__, __LINE__, mvdev->virtio_id, size, offset, - pa, vma->vm_start + offset); - size_rem -= size; - offset += size; - } - return 0; -} diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h deleted file mode 100644 index dc3893dff667..000000000000 --- a/drivers/misc/mic/host/mic_fops.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#ifndef _MIC_FOPS_H_ -#define _MIC_FOPS_H_ - -int mic_open(struct inode *inode, struct file *filp); -int mic_release(struct inode *inode, struct file *filp); -ssize_t mic_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos); -long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -int mic_mmap(struct file *f, struct vm_area_struct *vma); -unsigned int mic_poll(struct file *f, poll_table *wait); - -#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 153894e7ed5b..400def2106e7 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -27,8 +27,6 @@ #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" -#include "mic_fops.h" -#include "mic_virtio.h" static const char mic_driver_name[] = "mic"; @@ -57,17 +55,6 @@ MODULE_DEVICE_TABLE(pci, mic_pci_tbl); /* ID allocator for MIC devices */ static struct ida g_mic_ida; -/* Base device node number for MIC devices */ -static dev_t g_mic_devno; - -static const struct file_operations mic_fops = { - .open = mic_open, - .release = mic_release, - .unlocked_ioctl = mic_ioctl, - .poll = mic_poll, - .mmap = mic_mmap, - .owner = THIS_MODULE, -}; /* Initialize the device page */ static int mic_dp_init(struct mic_device *mdev) @@ -169,7 +156,6 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mic_ops_init(mdev); mutex_init(&mdev->mic_mutex); mdev->irq_info.next_avail_src = 0; - INIT_LIST_HEAD(&mdev->vdev_list); } /** @@ -259,30 +245,15 @@ static int mic_probe(struct pci_dev *pdev, goto smpt_uninit; } mic_bootparam_init(mdev); - mic_create_debug_dir(mdev); - mdev->miscdev.minor = MISC_DYNAMIC_MINOR; - snprintf(mdev->name, sizeof(mdev->name), "mic%d", mdev->id); - mdev->miscdev.name = mdev->name; - mdev->miscdev.fops = &mic_fops; - mdev->miscdev.parent = &mdev->pdev->dev; - rc = misc_register(&mdev->miscdev); - if (rc) { - dev_err(&pdev->dev, "misc_register err id %d rc %d\n", - mdev->id, rc); - goto cleanup_debug_dir; - } - mdev->cosm_dev = cosm_register_device(&mdev->pdev->dev, &cosm_hw_ops); if (IS_ERR(mdev->cosm_dev)) { rc = PTR_ERR(mdev->cosm_dev); dev_err(&pdev->dev, "cosm_add_device failed rc %d\n", rc); - goto misc_dereg; + goto cleanup_debug_dir; } return 0; -misc_dereg: - misc_deregister(&mdev->miscdev); cleanup_debug_dir: mic_delete_debug_dir(mdev); mic_dp_uninit(mdev); @@ -323,7 +294,6 @@ static void mic_remove(struct pci_dev *pdev) return; cosm_unregister_device(mdev->cosm_dev); - misc_deregister(&mdev->miscdev); mic_delete_debug_dir(mdev); mic_dp_uninit(mdev); mic_smpt_uninit(mdev); @@ -347,26 +317,17 @@ static int __init mic_init(void) { int ret; - ret = alloc_chrdev_region(&g_mic_devno, 0, - MIC_MAX_NUM_DEVS, mic_driver_name); - if (ret) { - pr_err("alloc_chrdev_region failed ret %d\n", ret); - goto error; - } - mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); if (ret) { pr_err("pci_register_driver failed ret %d\n", ret); - goto cleanup_chrdev; + goto cleanup_debugfs; } - return ret; -cleanup_chrdev: + return 0; +cleanup_debugfs: ida_destroy(&g_mic_ida); mic_exit_debugfs(); - unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); -error: return ret; } @@ -375,7 +336,6 @@ static void __exit mic_exit(void) pci_unregister_driver(&mic_driver); ida_destroy(&g_mic_ida); mic_exit_debugfs(); - unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); } module_init(mic_init); diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c deleted file mode 100644 index 58b107a24a8b..000000000000 --- a/drivers/misc/mic/host/mic_virtio.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#include -#include -#include -#include -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" -#include "mic_virtio.h" - -/* - * Size of the internal buffer used during DMA's as an intermediate buffer - * for copy to/from user. - */ -#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) - -static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst, - dma_addr_t src, size_t len) -{ - int err = 0; - struct dma_async_tx_descriptor *tx; - struct dma_chan *mic_ch = mdev->dma_ch[0]; - - if (!mic_ch) { - err = -EBUSY; - goto error; - } - - tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len, - DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } else { - dma_cookie_t cookie = tx->tx_submit(tx); - - err = dma_submit_error(cookie); - if (err) - goto error; - err = dma_sync_wait(mic_ch, cookie); - } -error: - if (err) - dev_err(&mdev->pdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; -} - -/* - * Initiates the copies across the PCIe bus from card memory to a user - * space buffer. When transfers are done using DMA, source/destination - * addresses and transfer length must follow the alignment requirements of - * the MIC DMA engine. - */ -static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct mic_device *mdev = mvdev->mdev; - void __iomem *dbuf = mdev->aper.va + daddr; - struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; - size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align; - size_t dma_offset; - size_t partlen; - int err; - - dma_offset = daddr - round_down(daddr, dma_alignment); - daddr -= dma_offset; - len += dma_offset; - - while (len) { - partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); - - err = mic_sync_dma(mdev, mvr->buf_da, daddr, - ALIGN(partlen, dma_alignment)); - if (err) - goto err; - - if (copy_to_user(ubuf, mvr->buf + dma_offset, - partlen - dma_offset)) { - err = -EFAULT; - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - mvdev->in_bytes_dma += partlen; - mvdev->in_bytes += partlen; - len -= partlen; - dma_offset = 0; - } - return 0; -err: - dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -/* - * Initiates copies across the PCIe bus from a user space buffer to card - * memory. When transfers are done using DMA, source/destination addresses - * and transfer length must follow the alignment requirements of the MIC - * DMA engine. - */ -static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct mic_device *mdev = mvdev->mdev; - void __iomem *dbuf = mdev->aper.va + daddr; - struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; - size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align; - size_t partlen; - int err; - - if (daddr & (dma_alignment - 1)) { - mvdev->tx_dst_unaligned += len; - goto memcpy; - } else if (ALIGN(len, dma_alignment) > dlen) { - mvdev->tx_len_unaligned += len; - goto memcpy; - } - - while (len) { - partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); - - if (copy_from_user(mvr->buf, ubuf, partlen)) { - err = -EFAULT; - goto err; - } - err = mic_sync_dma(mdev, daddr, mvr->buf_da, - ALIGN(partlen, dma_alignment)); - if (err) - goto err; - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - mvdev->out_bytes_dma += partlen; - mvdev->out_bytes += partlen; - len -= partlen; - } -memcpy: - /* - * We are copying to IO below and should ideally use something - * like copy_from_user_toio(..) if it existed. - */ - if (copy_from_user((void __force *)dbuf, ubuf, len)) { - err = -EFAULT; - goto err; - } - mvdev->out_bytes += len; - return 0; -err: - dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -#define MIC_VRINGH_READ true - -/* The function to call to notify the card about added buffers */ -static void mic_notify(struct vringh *vrh) -{ - struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); - struct mic_vdev *mvdev = mvrh->mvdev; - s8 db = mvdev->dc->h2c_vdev_db; - - if (db != -1) - mvdev->mdev->ops->send_intr(mvdev->mdev, db); -} - -/* Determine the total number of bytes consumed in a VRINGH KIOV */ -static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) -{ - int i; - u32 total = iov->consumed; - - for (i = 0; i < iov->i; i++) - total += iov->iov[i].iov_len; - return total; -} - -/* - * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. - * This API is heavily based on the vringh_iov_xfer(..) implementation - * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) - * and vringh_iov_push_kern(..) directly is because there is no - * way to override the VRINGH xfer(..) routines as of v3.10. - */ -static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, - void __user *ubuf, size_t len, bool read, int vr_idx, - size_t *out_len) -{ - int ret = 0; - size_t partlen, tot_len = 0; - - while (len && iov->i < iov->used) { - partlen = min(iov->iov[iov->i].iov_len, len); - if (read) - ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, - (u64)iov->iov[iov->i].iov_base, - iov->iov[iov->i].iov_len, - vr_idx); - else - ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, - (u64)iov->iov[iov->i].iov_base, - iov->iov[iov->i].iov_len, - vr_idx); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= partlen; - ubuf += partlen; - tot_len += partlen; - iov->consumed += partlen; - iov->iov[iov->i].iov_len -= partlen; - iov->iov[iov->i].iov_base += partlen; - if (!iov->iov[iov->i].iov_len) { - /* Fix up old iov element then increment. */ - iov->iov[iov->i].iov_len = iov->consumed; - iov->iov[iov->i].iov_base -= iov->consumed; - - iov->consumed = 0; - iov->i++; - } - } - *out_len = tot_len; - return ret; -} - -/* - * Use the standard VRINGH infrastructure in the kernel to fetch new - * descriptors, initiate the copies and update the used ring. - */ -static int _mic_virtio_copy(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - int ret = 0; - u32 iovcnt = copy->iovcnt; - struct iovec iov; - struct iovec __user *u_iov = copy->iov; - void __user *ubuf = NULL; - struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; - struct vringh_kiov *riov = &mvr->riov; - struct vringh_kiov *wiov = &mvr->wiov; - struct vringh *vrh = &mvr->vrh; - u16 *head = &mvr->head; - struct mic_vring *vr = &mvr->vring; - size_t len = 0, out_len; - - copy->out_len = 0; - /* Fetch a new IOVEC if all previous elements have been processed */ - if (riov->i == riov->used && wiov->i == wiov->used) { - ret = vringh_getdesc_kern(vrh, riov, wiov, - head, GFP_KERNEL); - /* Check if there are available descriptors */ - if (ret <= 0) - return ret; - } - while (iovcnt) { - if (!len) { - /* Copy over a new iovec from user space. */ - ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); - if (ret) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len = iov.iov_len; - ubuf = iov.iov_base; - } - /* Issue all the read descriptors first */ - ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, - copy->vr_idx, &out_len); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - /* Issue the write descriptors next */ - ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, - copy->vr_idx, &out_len); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - if (!len) { - /* One user space iovec is now completed */ - iovcnt--; - u_iov++; - } - /* Exit loop if all elements in KIOVs have been processed. */ - if (riov->i == riov->used && wiov->i == wiov->used) - break; - } - /* - * Update the used ring if a descriptor was available and some data was - * copied in/out and the user asked for a used ring update. - */ - if (*head != USHRT_MAX && copy->out_len && copy->update_used) { - u32 total = 0; - - /* Determine the total data consumed */ - total += mic_vringh_iov_consumed(riov); - total += mic_vringh_iov_consumed(wiov); - vringh_complete_kern(vrh, *head, total); - *head = USHRT_MAX; - if (vringh_need_notify_kern(vrh) > 0) - vringh_notify(vrh); - vringh_kiov_cleanup(riov); - vringh_kiov_cleanup(wiov); - /* Update avail idx for user space */ - vr->info->avail_idx = vrh->last_avail_idx; - } - return ret; -} - -static inline int mic_verify_copy_args(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - if (copy->vr_idx >= mvdev->dd->num_vq) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - return 0; -} - -/* Copy a specified number of virtio descriptors in a chain */ -int mic_virtio_copy_desc(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - int err; - struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; - - err = mic_verify_copy_args(mvdev, copy); - if (err) - return err; - - mutex_lock(&mvr->vr_mutex); - if (!mic_vdevup(mvdev)) { - err = -ENODEV; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = _mic_virtio_copy(mvdev, copy); - if (err) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - } -err: - mutex_unlock(&mvr->vr_mutex); - return err; -} - -static void mic_virtio_init_post(struct mic_vdev *mvdev) -{ - struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); - int i; - - for (i = 0; i < mvdev->dd->num_vq; i++) { - if (!le64_to_cpu(vqconfig[i].used_address)) { - dev_warn(mic_dev(mvdev), "used_address zero??\n"); - continue; - } - mvdev->mvr[i].vrh.vring.used = - (void __force *)mvdev->mdev->aper.va + - le64_to_cpu(vqconfig[i].used_address); - } - - mvdev->dc->used_address_updated = 0; - - dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", - __func__, mvdev->virtio_id); -} - -static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) -{ - int i; - - dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", - __func__, mvdev->dd->status, mvdev->virtio_id); - - for (i = 0; i < mvdev->dd->num_vq; i++) - /* - * Avoid lockdep false positive. The + 1 is for the mic - * mutex which is held in the reset devices code path. - */ - mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); - - /* 0 status means "reset" */ - mvdev->dd->status = 0; - mvdev->dc->vdev_reset = 0; - mvdev->dc->host_ack = 1; - - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct vringh *vrh = &mvdev->mvr[i].vrh; - mvdev->mvr[i].vring.info->avail_idx = 0; - vrh->completed = 0; - vrh->last_avail_idx = 0; - vrh->last_used_idx = 0; - } - - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_unlock(&mvdev->mvr[i].vr_mutex); -} - -void mic_virtio_reset_devices(struct mic_device *mdev) -{ - struct list_head *pos, *tmp; - struct mic_vdev *mvdev; - - dev_dbg(&mdev->pdev->dev, "%s\n", __func__); - - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - mvdev = list_entry(pos, struct mic_vdev, list); - mic_virtio_device_reset(mvdev); - mvdev->poll_wake = 1; - wake_up(&mvdev->waitq); - } -} - -void mic_bh_handler(struct work_struct *work) -{ - struct mic_vdev *mvdev = container_of(work, struct mic_vdev, - virtio_bh_work); - - if (mvdev->dc->used_address_updated) - mic_virtio_init_post(mvdev); - - if (mvdev->dc->vdev_reset) - mic_virtio_device_reset(mvdev); - - mvdev->poll_wake = 1; - wake_up(&mvdev->waitq); -} - -static irqreturn_t mic_virtio_intr_handler(int irq, void *data) -{ - struct mic_vdev *mvdev = data; - struct mic_device *mdev = mvdev->mdev; - - mdev->ops->intr_workarounds(mdev); - schedule_work(&mvdev->virtio_bh_work); - return IRQ_HANDLED; -} - -int mic_virtio_config_change(struct mic_vdev *mvdev, - void __user *argp) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int ret = 0, retry, i; - struct mic_bootparam *bootparam = mvdev->mdev->dp; - s8 db = bootparam->h2c_config_db; - - mutex_lock(&mvdev->mdev->mic_mutex); - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); - - if (db == -1 || mvdev->dd->type == -1) { - ret = -EIO; - goto exit; - } - - if (copy_from_user(mic_vq_configspace(mvdev->dd), - argp, mvdev->dd->config_len)) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EFAULT); - ret = -EFAULT; - goto exit; - } - mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; - mvdev->mdev->ops->send_intr(mvdev->mdev, db); - - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, - mvdev->dc->guest_ack, msecs_to_jiffies(100)); - if (ret) - break; - } - - dev_dbg(mic_dev(mvdev), - "%s %d retry: %d\n", __func__, __LINE__, retry); - mvdev->dc->config_change = 0; - mvdev->dc->guest_ack = 0; -exit: - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_unlock(&mvdev->mvr[i].vr_mutex); - mutex_unlock(&mvdev->mdev->mic_mutex); - return ret; -} - -static int mic_copy_dp_entry(struct mic_vdev *mvdev, - void __user *argp, - __u8 *type, - struct mic_device_desc **devpage) -{ - struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc dd, *dd_config, *devp; - struct mic_vqconfig *vqconfig; - int ret = 0, i; - bool slot_found = false; - - if (copy_from_user(&dd, argp, sizeof(dd))) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EFAULT); - return -EFAULT; - } - - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || - dd.num_vq > MIC_MAX_VRINGS) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - - dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); - if (dd_config == NULL) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -ENOMEM); - return -ENOMEM; - } - if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { - ret = -EFAULT; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - - vqconfig = mic_vq_config(dd_config); - for (i = 0; i < dd.num_vq; i++) { - if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - } - - /* Find the first free device page entry */ - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE - mic_total_desc_size(dd_config); - i += mic_total_desc_size(devp)) { - devp = mdev->dp + i; - if (devp->type == 0 || devp->type == -1) { - slot_found = true; - break; - } - } - if (!slot_found) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - /* - * Save off the type before doing the memcpy. Type will be set in the - * end after completing all initialization for the new device. - */ - *type = dd_config->type; - dd_config->type = 0; - memcpy(devp, dd_config, mic_desc_size(dd_config)); - - *devpage = devp; -exit: - kfree(dd_config); - return ret; -} - -static void mic_init_device_ctrl(struct mic_vdev *mvdev, - struct mic_device_desc *devpage) -{ - struct mic_device_ctrl *dc; - - dc = (void *)devpage + mic_aligned_desc_size(devpage); - - dc->config_change = 0; - dc->guest_ack = 0; - dc->vdev_reset = 0; - dc->host_ack = 0; - dc->used_address_updated = 0; - dc->c2h_vdev_db = -1; - dc->h2c_vdev_db = -1; - mvdev->dc = dc; -} - -int mic_virtio_add_device(struct mic_vdev *mvdev, - void __user *argp) -{ - struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc *dd = NULL; - struct mic_vqconfig *vqconfig; - int vr_size, i, j, ret; - u8 type = 0; - s8 db; - char irqname[10]; - struct mic_bootparam *bootparam = mdev->dp; - u16 num; - dma_addr_t vr_addr; - - mutex_lock(&mdev->mic_mutex); - - ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); - if (ret) { - mutex_unlock(&mdev->mic_mutex); - return ret; - } - - mic_init_device_ctrl(mvdev, dd); - - mvdev->dd = dd; - mvdev->virtio_id = type; - vqconfig = mic_vq_config(dd); - INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); - - for (i = 0; i < dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - struct mic_vring *vr = &mvdev->mvr[i].vring; - num = le16_to_cpu(vqconfig[i].num); - mutex_init(&mvr->vr_mutex); - vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + - sizeof(struct _mic_vring_info)); - vr->va = (void *) - __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); - if (!vr->va) { - ret = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vr->len = vr_size; - vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); - vr->info->magic = cpu_to_le32(MIC_MAGIC + mvdev->virtio_id + i); - vr_addr = mic_map_single(mdev, vr->va, vr_size); - if (mic_map_error(vr_addr)) { - free_pages((unsigned long)vr->va, get_order(vr_size)); - ret = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vqconfig[i].address = cpu_to_le64(vr_addr); - - vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); - ret = vringh_init_kern(&mvr->vrh, - *(u32 *)mic_vq_features(mvdev->dd), num, false, - vr->vr.desc, vr->vr.avail, vr->vr.used); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vringh_kiov_init(&mvr->riov, NULL, 0); - vringh_kiov_init(&mvr->wiov, NULL, 0); - mvr->head = USHRT_MAX; - mvr->mvdev = mvdev; - mvr->vrh.notify = mic_notify; - dev_dbg(&mdev->pdev->dev, - "%s %d index %d va %p info %p vr_size 0x%x\n", - __func__, __LINE__, i, vr->va, vr->info, vr_size); - mvr->buf = (void *)__get_free_pages(GFP_KERNEL, - get_order(MIC_INT_DMA_BUF_SIZE)); - mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf, - MIC_INT_DMA_BUF_SIZE); - } - - snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, - mvdev->virtio_id); - mvdev->virtio_db = mic_next_db(mdev); - mvdev->virtio_cookie = mic_request_threaded_irq(mdev, - mic_virtio_intr_handler, - NULL, irqname, mvdev, - mvdev->virtio_db, MIC_INTR_DB); - if (IS_ERR(mvdev->virtio_cookie)) { - ret = PTR_ERR(mvdev->virtio_cookie); - dev_dbg(&mdev->pdev->dev, "request irq failed\n"); - goto err; - } - - mvdev->dc->c2h_vdev_db = mvdev->virtio_db; - - list_add_tail(&mvdev->list, &mdev->vdev_list); - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - dd->type = type; - - dev_dbg(&mdev->pdev->dev, "Added virtio device id %d\n", dd->type); - - db = bootparam->h2c_config_db; - if (db != -1) - mdev->ops->send_intr(mdev, db); - mutex_unlock(&mdev->mic_mutex); - return 0; -err: - vqconfig = mic_vq_config(dd); - for (j = 0; j < i; j++) { - struct mic_vringh *mvr = &mvdev->mvr[j]; - mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), - mvr->vring.len); - free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); - } - mutex_unlock(&mdev->mic_mutex); - return ret; -} - -void mic_virtio_del_device(struct mic_vdev *mvdev) -{ - struct list_head *pos, *tmp; - struct mic_vdev *tmp_mvdev; - struct mic_device *mdev = mvdev->mdev; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int i, ret, retry; - struct mic_vqconfig *vqconfig; - struct mic_bootparam *bootparam = mdev->dp; - s8 db; - - mutex_lock(&mdev->mic_mutex); - db = bootparam->h2c_config_db; - if (db == -1) - goto skip_hot_remove; - dev_dbg(&mdev->pdev->dev, - "Requesting hot remove id %d\n", mvdev->virtio_id); - mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; - mdev->ops->send_intr(mdev, db); - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, - mvdev->dc->guest_ack, msecs_to_jiffies(100)); - if (ret) - break; - } - dev_dbg(&mdev->pdev->dev, - "Device id %d config_change %d guest_ack %d retry %d\n", - mvdev->virtio_id, mvdev->dc->config_change, - mvdev->dc->guest_ack, retry); - mvdev->dc->config_change = 0; - mvdev->dc->guest_ack = 0; -skip_hot_remove: - mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); - flush_work(&mvdev->virtio_bh_work); - vqconfig = mic_vq_config(mvdev->dd); - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - - mic_unmap_single(mvdev->mdev, mvr->buf_da, - MIC_INT_DMA_BUF_SIZE); - free_pages((unsigned long)mvr->buf, - get_order(MIC_INT_DMA_BUF_SIZE)); - vringh_kiov_cleanup(&mvr->riov); - vringh_kiov_cleanup(&mvr->wiov); - mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), - mvr->vring.len); - free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); - } - - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - tmp_mvdev = list_entry(pos, struct mic_vdev, list); - if (tmp_mvdev == mvdev) { - list_del(pos); - dev_dbg(&mdev->pdev->dev, - "Removing virtio device id %d\n", - mvdev->virtio_id); - break; - } - } - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - mvdev->dd->type = -1; - mutex_unlock(&mdev->mic_mutex); -} diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h deleted file mode 100644 index a80631f2790d..000000000000 --- a/drivers/misc/mic/host/mic_virtio.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#ifndef MIC_VIRTIO_H -#define MIC_VIRTIO_H - -#include -#include - -/* - * Note on endianness. - * 1. Host can be both BE or LE - * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail - * rings and ioreadXX/iowriteXX to access used ring. - * 3. Device page exposed by host to guest contains LE values. Guest - * accesses these using ioreadXX/iowriteXX etc. This way in general we - * obey the virtio spec according to which guest works with native - * endianness and host is aware of guest endianness and does all - * required endianness conversion. - * 4. Data provided from user space to guest (in ADD_DEVICE and - * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be - * in guest endianness. - */ - -/** - * struct mic_vringh - Virtio ring host information. - * - * @vring: The MIC vring used for setting up user space mappings. - * @vrh: The host VRINGH used for accessing the card vrings. - * @riov: The VRINGH read kernel IOV. - * @wiov: The VRINGH write kernel IOV. - * @vr_mutex: Mutex for synchronizing access to the VRING. - * @buf: Temporary kernel buffer used to copy in/out data - * from/to the card via DMA. - * @buf_da: dma address of buf. - * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). - * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). - */ -struct mic_vringh { - struct mic_vring vring; - struct vringh vrh; - struct vringh_kiov riov; - struct vringh_kiov wiov; - struct mutex vr_mutex; - void *buf; - dma_addr_t buf_da; - struct mic_vdev *mvdev; - u16 head; -}; - -/** - * struct mic_vdev - Host information for a card Virtio device. - * - * @virtio_id - Virtio device id. - * @waitq - Waitqueue to allow ring3 apps to poll. - * @mdev - Back pointer to host MIC device. - * @poll_wake - Used for waking up threads blocked in poll. - * @out_bytes - Debug stats for number of bytes copied from host to card. - * @in_bytes - Debug stats for number of bytes copied from card to host. - * @out_bytes_dma - Debug stats for number of bytes copied from host to card - * using DMA. - * @in_bytes_dma - Debug stats for number of bytes copied from card to host - * using DMA. - * @tx_len_unaligned - Debug stats for number of bytes copied to the card where - * the transfer length did not have the required DMA alignment. - * @tx_dst_unaligned - Debug stats for number of bytes copied where the - * destination address on the card did not have the required DMA alignment. - * @mvr - Store per VRING data structures. - * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. - * @dd - Virtio device descriptor. - * @dc - Virtio device control fields. - * @list - List of Virtio devices. - * @virtio_db - The doorbell used by the card to interrupt the host. - * @virtio_cookie - The cookie returned while requesting interrupts. - */ -struct mic_vdev { - int virtio_id; - wait_queue_head_t waitq; - struct mic_device *mdev; - int poll_wake; - unsigned long out_bytes; - unsigned long in_bytes; - unsigned long out_bytes_dma; - unsigned long in_bytes_dma; - unsigned long tx_len_unaligned; - unsigned long tx_dst_unaligned; - struct mic_vringh mvr[MIC_MAX_VRINGS]; - struct work_struct virtio_bh_work; - struct mic_device_desc *dd; - struct mic_device_ctrl *dc; - struct list_head list; - int virtio_db; - struct mic_irq *virtio_cookie; -}; - -void mic_virtio_uninit(struct mic_device *mdev); -int mic_virtio_add_device(struct mic_vdev *mvdev, - void __user *argp); -void mic_virtio_del_device(struct mic_vdev *mvdev); -int mic_virtio_config_change(struct mic_vdev *mvdev, - void __user *argp); -int mic_virtio_copy_desc(struct mic_vdev *mvdev, - struct mic_copy_desc *request); -void mic_virtio_reset_devices(struct mic_device *mdev); -void mic_bh_handler(struct work_struct *work); - -/* Helper API to obtain the MIC PCIe device */ -static inline struct device *mic_dev(struct mic_vdev *mvdev) -{ - return &mvdev->mdev->pdev->dev; -} - -/* Helper API to check if a virtio device is initialized */ -static inline int mic_vdev_inited(struct mic_vdev *mvdev) -{ - /* Device has not been created yet */ - if (!mvdev->dd || !mvdev->dd->type) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - - /* Device has been removed/deleted */ - if (mvdev->dd->type == -1) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -ENODEV); - return -ENODEV; - } - - return 0; -} - -/* Helper API to check if a virtio device is running */ -static inline bool mic_vdevup(struct mic_vdev *mvdev) -{ - return !!mvdev->dd->status; -} -#endif -- cgit v1.2.3 From b73c295833cc660c1acdf2601920e2abdb6a29c8 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:12 -0800 Subject: misc: mic: Remove MIC X100 card virtio functionality This patch deletes the virtio functionality from the MIC X100 card driver. A subsequent patch will re-enable this functionality by consolidating the hardware independent logic in a new Virtio over PCIe (VOP) driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/Makefile | 1 - drivers/misc/mic/card/mic_device.c | 9 +- drivers/misc/mic/card/mic_virtio.c | 634 ------------------------------------- drivers/misc/mic/card/mic_virtio.h | 76 ----- 4 files changed, 1 insertion(+), 719 deletions(-) delete mode 100644 drivers/misc/mic/card/mic_virtio.c delete mode 100644 drivers/misc/mic/card/mic_virtio.h diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile index 69d58bef92ce..6e9675e12a09 100644 --- a/drivers/misc/mic/card/Makefile +++ b/drivers/misc/mic/card/Makefile @@ -8,4 +8,3 @@ obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o mic_card-y += mic_x100.o mic_card-y += mic_device.o mic_card-y += mic_debugfs.o -mic_card-y += mic_virtio.o diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index d0edaf7e0cd5..ff03c633541c 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -34,7 +34,6 @@ #include #include "../common/mic_dev.h" #include "mic_device.h" -#include "mic_virtio.h" static struct mic_driver *g_drv; @@ -309,9 +308,6 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = -ENODEV; goto irq_uninit; } - rc = mic_devices_init(mdrv); - if (rc) - goto dma_free; bootparam = mdrv->dp; node_id = ioread8(&bootparam->node_id); mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, @@ -321,13 +317,11 @@ int __init mic_driver_init(struct mic_driver *mdrv) mdrv->num_dma_ch, true); if (IS_ERR(mdrv->scdev)) { rc = PTR_ERR(mdrv->scdev); - goto device_uninit; + goto dma_free; } mic_create_card_debug_dir(mdrv); done: return rc; -device_uninit: - mic_devices_uninit(mdrv); dma_free: mic_free_dma_chans(mdrv); irq_uninit: @@ -348,7 +342,6 @@ void mic_driver_uninit(struct mic_driver *mdrv) { mic_delete_card_debug_dir(mdrv); scif_unregister_device(mdrv->scdev); - mic_devices_uninit(mdrv); mic_free_dma_chans(mdrv); mic_uninit_irq(); mic_dp_uninit(); diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c deleted file mode 100644 index 17764b2fbee5..000000000000 --- a/drivers/misc/mic/card/mic_virtio.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Adapted from: - * - * virtio for kvm on s390 - * - * Copyright IBM Corp. 2008 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (version 2 only) - * as published by the Free Software Foundation. - * - * Author(s): Christian Borntraeger - * - * Intel MIC Card driver. - * - */ -#include -#include -#include - -#include "../common/mic_dev.h" -#include "mic_virtio.h" - -#define VIRTIO_SUBCODE_64 0x0D00 - -#define MIC_MAX_VRINGS 4 -struct mic_vdev { - struct virtio_device vdev; - struct mic_device_desc __iomem *desc; - struct mic_device_ctrl __iomem *dc; - struct mic_device *mdev; - void __iomem *vr[MIC_MAX_VRINGS]; - int used_size[MIC_MAX_VRINGS]; - struct completion reset_done; - struct mic_irq *virtio_cookie; - int c2h_vdev_db; -}; - -static struct mic_irq *virtio_config_cookie; -#define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev) - -/* Helper API to obtain the parent of the virtio device */ -static inline struct device *mic_dev(struct mic_vdev *mvdev) -{ - return mvdev->vdev.dev.parent; -} - -/* This gets the device's feature bits. */ -static u64 mic_get_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - u32 features = 0; - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - u8 __iomem *in_features = mic_vq_features(desc); - int feature_len = ioread8(&desc->feature_len); - - bits = min_t(unsigned, feature_len, sizeof(features)) * 8; - for (i = 0; i < bits; i++) - if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) - features |= BIT(i); - - return features; -} - -static int mic_finalize_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - u8 feature_len = ioread8(&desc->feature_len); - /* Second half of bitmap is features we accept. */ - u8 __iomem *out_features = - mic_vq_features(desc) + feature_len; - - /* Give virtio_ring a chance to accept features. */ - vring_transport_features(vdev); - - /* Make sure we don't have any features > 32 bits! */ - BUG_ON((u32)vdev->features != vdev->features); - - memset_io(out_features, 0, feature_len); - bits = min_t(unsigned, feature_len, - sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) { - if (__virtio_test_bit(vdev, i)) - iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); - } - - return 0; -} - -/* - * Reading and writing elements in config space - */ -static void mic_get(struct virtio_device *vdev, unsigned int offset, - void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len); -} - -static void mic_set(struct virtio_device *vdev, unsigned int offset, - const void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_toio(mic_vq_configspace(desc) + offset, buf, len); -} - -/* - * The operations to get and set the status word just access the status - * field of the device descriptor. set_status also interrupts the host - * to tell about status changes. - */ -static u8 mic_get_status(struct virtio_device *vdev) -{ - return ioread8(&to_micvdev(vdev)->desc->status); -} - -static void mic_set_status(struct virtio_device *vdev, u8 status) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - if (!status) - return; - iowrite8(status, &mvdev->desc->status); - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); -} - -/* Inform host on a virtio device reset and wait for ack from host */ -static void mic_reset_inform_host(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_device_ctrl __iomem *dc = mvdev->dc; - int retry; - - iowrite8(0, &dc->host_ack); - iowrite8(1, &dc->vdev_reset); - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - - /* Wait till host completes all card accesses and acks the reset */ - for (retry = 100; retry--;) { - if (ioread8(&dc->host_ack)) - break; - msleep(100); - }; - - dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); - - /* Reset status to 0 in case we timed out */ - iowrite8(0, &mvdev->desc->status); -} - -static void mic_reset(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - - dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n", - __func__, vdev->id.device); - - mic_reset_inform_host(vdev); - complete_all(&mvdev->reset_done); -} - -/* - * The virtio_ring code calls this API when it wants to notify the Host. - */ -static bool mic_notify(struct virtqueue *vq) -{ - struct mic_vdev *mvdev = vq->priv; - - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - return true; -} - -static void mic_del_vq(struct virtqueue *vq, int n) -{ - struct mic_vdev *mvdev = to_micvdev(vq->vdev); - struct vring *vr = (struct vring *)(vq + 1); - - free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n])); - vring_del_virtqueue(vq); - mic_card_unmap(mvdev->mdev, mvdev->vr[n]); - mvdev->vr[n] = NULL; -} - -static void mic_del_vqs(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct virtqueue *vq, *n; - int idx = 0; - - dev_dbg(mic_dev(mvdev), "%s\n", __func__); - - list_for_each_entry_safe(vq, n, &vdev->vqs, list) - mic_del_vq(vq, idx++); -} - -/* - * This routine will assign vring's allocated in host/io memory. Code in - * virtio_ring.c however continues to access this io memory as if it were local - * memory without io accessors. - */ -static struct virtqueue *mic_find_vq(struct virtio_device *vdev, - unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_vqconfig __iomem *vqconfig; - struct mic_vqconfig config; - struct virtqueue *vq; - void __iomem *va; - struct _mic_vring_info __iomem *info; - void *used; - int vr_size, _vr_size, err, magic; - struct vring *vr; - u8 type = ioread8(&mvdev->desc->type); - - if (index >= ioread8(&mvdev->desc->num_vq)) - return ERR_PTR(-ENOENT); - - if (!name) - return ERR_PTR(-ENOENT); - - /* First assign the vring's allocated in host memory */ - vqconfig = mic_vq_config(mvdev->desc) + index; - memcpy_fromio(&config, vqconfig, sizeof(config)); - _vr_size = vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN); - vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); - va = mic_card_map(mvdev->mdev, le64_to_cpu(config.address), vr_size); - if (!va) - return ERR_PTR(-ENOMEM); - mvdev->vr[index] = va; - memset_io(va, 0x0, _vr_size); - vq = vring_new_virtqueue(index, le16_to_cpu(config.num), - MIC_VIRTIO_RING_ALIGN, vdev, false, - (void __force *)va, mic_notify, callback, - name); - if (!vq) { - err = -ENOMEM; - goto unmap; - } - info = va + _vr_size; - magic = ioread32(&info->magic); - - if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { - err = -EIO; - goto unmap; - } - - /* Allocate and reassign used ring now */ - mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(config.num)); - used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(mvdev->used_size[index])); - if (!used) { - err = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto del_vq; - } - iowrite64(virt_to_phys(used), &vqconfig->used_address); - - /* - * To reassign the used ring here we are directly accessing - * struct vring_virtqueue which is a private data structure - * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in - * vring_new_virtqueue() would ensure that - * (&vq->vring == (struct vring *) (&vq->vq + 1)); - */ - vr = (struct vring *)(vq + 1); - vr->used = used; - - vq->priv = mvdev; - return vq; -del_vq: - vring_del_virtqueue(vq); -unmap: - mic_card_unmap(mvdev->mdev, mvdev->vr[index]); - return ERR_PTR(err); -} - -static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[]) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_device_ctrl __iomem *dc = mvdev->dc; - int i, err, retry; - - /* We must have this many virtqueues. */ - if (nvqs > ioread8(&mvdev->desc->num_vq)) - return -ENOENT; - - for (i = 0; i < nvqs; ++i) { - dev_dbg(mic_dev(mvdev), "%s: %d: %s\n", - __func__, i, names[i]); - vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]); - if (IS_ERR(vqs[i])) { - err = PTR_ERR(vqs[i]); - goto error; - } - } - - iowrite8(1, &dc->used_address_updated); - /* - * Send an interrupt to the host to inform it that used - * rings have been re-assigned. - */ - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - for (retry = 100; --retry;) { - if (!ioread8(&dc->used_address_updated)) - break; - msleep(100); - }; - - dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); - if (!retry) { - err = -ENODEV; - goto error; - } - - return 0; -error: - mic_del_vqs(vdev); - return err; -} - -/* - * The config ops structure as defined by virtio config - */ -static struct virtio_config_ops mic_vq_config_ops = { - .get_features = mic_get_features, - .finalize_features = mic_finalize_features, - .get = mic_get, - .set = mic_set, - .get_status = mic_get_status, - .set_status = mic_set_status, - .reset = mic_reset, - .find_vqs = mic_find_vqs, - .del_vqs = mic_del_vqs, -}; - -static irqreturn_t -mic_virtio_intr_handler(int irq, void *data) -{ - struct mic_vdev *mvdev = data; - struct virtqueue *vq; - - mic_ack_interrupt(mvdev->mdev); - list_for_each_entry(vq, &mvdev->vdev.vqs, list) - vring_interrupt(0, vq); - - return IRQ_HANDLED; -} - -static void mic_virtio_release_dev(struct device *_d) -{ - /* - * No need for a release method similar to virtio PCI. - * Provide an empty one to avoid getting a warning from core. - */ -} - -/* - * adds a new device and register it with virtio - * appropriate drivers are loaded by the device model - */ -static int mic_add_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_vdev *mvdev; - int ret; - int virtio_db; - u8 type = ioread8(&d->type); - - mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); - if (!mvdev) { - dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n", - offset, type); - return -ENOMEM; - } - - mvdev->mdev = &mdrv->mdev; - mvdev->vdev.dev.parent = mdrv->dev; - mvdev->vdev.dev.release = mic_virtio_release_dev; - mvdev->vdev.id.device = type; - mvdev->vdev.config = &mic_vq_config_ops; - mvdev->desc = d; - mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d); - init_completion(&mvdev->reset_done); - - virtio_db = mic_next_card_db(); - mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, - NULL, "virtio intr", mvdev, virtio_db); - if (IS_ERR(mvdev->virtio_cookie)) { - ret = PTR_ERR(mvdev->virtio_cookie); - goto kfree; - } - iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db); - mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db); - - ret = register_virtio_device(&mvdev->vdev); - if (ret) { - dev_err(mic_dev(mvdev), - "Failed to register mic device %u type %u\n", - offset, type); - goto free_irq; - } - iowrite64((u64)mvdev, &mvdev->dc->vdev); - dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n", - __func__, offset, type, mvdev); - - return 0; - -free_irq: - mic_free_card_irq(mvdev->virtio_cookie, mvdev); -kfree: - kfree(mvdev); - return ret; -} - -/* - * match for a mic device with a specific desc pointer - */ -static int mic_match_desc(struct device *dev, void *data) -{ - struct virtio_device *vdev = dev_to_virtio(dev); - struct mic_vdev *mvdev = to_micvdev(vdev); - - return mvdev->desc == (void __iomem *)data; -} - -static void mic_handle_config_change(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + mic_aligned_desc_size(d); - struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); - - if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) - return; - - dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); - virtio_config_changed(&mvdev->vdev); - iowrite8(1, &dc->guest_ack); -} - -/* - * removes a virtio device if a hot remove event has been - * requested by the host. - */ -static int mic_remove_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + mic_aligned_desc_size(d); - struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); - u8 status; - int ret = -1; - - if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { - dev_dbg(mdrv->dev, - "%s %d config_change %d type %d mvdev %p\n", - __func__, __LINE__, - ioread8(&dc->config_change), ioread8(&d->type), mvdev); - - status = ioread8(&d->status); - reinit_completion(&mvdev->reset_done); - unregister_virtio_device(&mvdev->vdev); - mic_free_card_irq(mvdev->virtio_cookie, mvdev); - if (status & VIRTIO_CONFIG_S_DRIVER_OK) - wait_for_completion(&mvdev->reset_done); - kfree(mvdev); - iowrite8(1, &dc->guest_ack); - dev_dbg(mdrv->dev, "%s %d guest_ack %d\n", - __func__, __LINE__, ioread8(&dc->guest_ack)); - ret = 0; - } - - return ret; -} - -#define REMOVE_DEVICES true - -static void mic_scan_devices(struct mic_driver *mdrv, bool remove) -{ - s8 type; - unsigned int i; - struct mic_device_desc __iomem *d; - struct mic_device_ctrl __iomem *dc; - struct device *dev; - int ret; - - for (i = sizeof(struct mic_bootparam); i < MIC_DP_SIZE; - i += mic_total_desc_size(d)) { - d = mdrv->dp + i; - dc = (void __iomem *)d + mic_aligned_desc_size(d); - /* - * This read barrier is paired with the corresponding write - * barrier on the host which is inserted before adding or - * removing a virtio device descriptor, by updating the type. - */ - rmb(); - type = ioread8(&d->type); - - /* end of list */ - if (type == 0) - break; - - if (type == -1) - continue; - - /* device already exists */ - dev = device_find_child(mdrv->dev, (void __force *)d, - mic_match_desc); - if (dev) { - if (remove) - iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); - put_device(dev); - mic_handle_config_change(d, i, mdrv); - ret = mic_remove_device(d, i, mdrv); - if (!ret && !remove) - iowrite8(-1, &d->type); - if (remove) { - iowrite8(0, &dc->config_change); - iowrite8(0, &dc->guest_ack); - } - continue; - } - - /* new device */ - dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); - if (!remove) - mic_add_device(d, i, mdrv); - } -} - -/* - * mic_hotplug_device tries to find changes in the device page. - */ -static void mic_hotplug_devices(struct work_struct *work) -{ - struct mic_driver *mdrv = container_of(work, - struct mic_driver, hotplug_work); - - mic_scan_devices(mdrv, !REMOVE_DEVICES); -} - -/* - * Interrupt handler for hot plug/config changes etc. - */ -static irqreturn_t -mic_extint_handler(int irq, void *data) -{ - struct mic_driver *mdrv = (struct mic_driver *)data; - - dev_dbg(mdrv->dev, "%s %d hotplug work\n", - __func__, __LINE__); - mic_ack_interrupt(&mdrv->mdev); - schedule_work(&mdrv->hotplug_work); - return IRQ_HANDLED; -} - -/* - * Init function for virtio - */ -int mic_devices_init(struct mic_driver *mdrv) -{ - int rc; - struct mic_bootparam __iomem *bootparam; - int config_db; - - INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices); - mic_scan_devices(mdrv, !REMOVE_DEVICES); - - config_db = mic_next_card_db(); - virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL, - "virtio_config_intr", mdrv, - config_db); - if (IS_ERR(virtio_config_cookie)) { - rc = PTR_ERR(virtio_config_cookie); - goto exit; - } - - bootparam = mdrv->dp; - iowrite8(config_db, &bootparam->h2c_config_db); - return 0; -exit: - return rc; -} - -/* - * Uninit function for virtio - */ -void mic_devices_uninit(struct mic_driver *mdrv) -{ - struct mic_bootparam __iomem *bootparam = mdrv->dp; - iowrite8(-1, &bootparam->h2c_config_db); - mic_free_card_irq(virtio_config_cookie, mdrv); - flush_work(&mdrv->hotplug_work); - mic_scan_devices(mdrv, REMOVE_DEVICES); -} diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h deleted file mode 100644 index d0407ba53bb7..000000000000 --- a/drivers/misc/mic/card/mic_virtio.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * 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. - * - * 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - * - */ -#ifndef __MIC_CARD_VIRTIO_H -#define __MIC_CARD_VIRTIO_H - -#include -#include "mic_device.h" - -/* - * 64 bit I/O access - */ -#ifndef ioread64 -#define ioread64 readq -#endif -#ifndef iowrite64 -#define iowrite64 writeq -#endif - -static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc) -{ - return sizeof(*desc) - + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) - + ioread8(&desc->feature_len) * 2 - + ioread8(&desc->config_len); -} - -static inline struct mic_vqconfig __iomem * -mic_vq_config(struct mic_device_desc __iomem *desc) -{ - return (struct mic_vqconfig __iomem *)(desc + 1); -} - -static inline __u8 __iomem * -mic_vq_features(struct mic_device_desc __iomem *desc) -{ - return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq)); -} - -static inline __u8 __iomem * -mic_vq_configspace(struct mic_device_desc __iomem *desc) -{ - return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2; -} -static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc) -{ - return mic_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); -} - -int mic_devices_init(struct mic_driver *mdrv); -void mic_devices_uninit(struct mic_driver *mdrv); - -#endif -- cgit v1.2.3 From a19ddd6fd260d6b92de38acb1b87940045697627 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:13 -0800 Subject: misc: mic: MIC VOP Bus The Virtio Over PCIe (VOP) bus abstracts the low level hardware details like interrupts and mapping remote memory so that the same VOP driver can work without changes with different MIC host or card drivers as long as the hardware bus operations are implemented. The VOP driver registers itself on the VOP bus. The base PCIe drivers implement the bus ops and register VOP devices on the bus, resulting in the VOP driver being probed with the VOP devices. This allows the VOP functionality to be shared between multiple generations of Intel MIC products. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 17 ++++ drivers/misc/mic/bus/Makefile | 1 + drivers/misc/mic/bus/vop_bus.c | 203 +++++++++++++++++++++++++++++++++++++++++ drivers/misc/mic/bus/vop_bus.h | 140 ++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 drivers/misc/mic/bus/vop_bus.c create mode 100644 drivers/misc/mic/bus/vop_bus.h diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 40677df7f996..840f7ef1a141 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -32,6 +32,23 @@ config SCIF_BUS OS and tools for MIC to use with this driver are available from . +comment "VOP Bus Driver" + +config VOP_BUS + tristate "VOP Bus Driver" + depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS + help + This option is selected by any driver which registers a + device or driver on the VOP Bus, such as CONFIG_INTEL_MIC_HOST + and CONFIG_INTEL_MIC_CARD. + + If you are building a host/card kernel with an Intel MIC device + then say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . + comment "Intel MIC Host Driver" config INTEL_MIC_HOST diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile index 761842b0d0bb..8758a7daa52c 100644 --- a/drivers/misc/mic/bus/Makefile +++ b/drivers/misc/mic/bus/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o obj-$(CONFIG_SCIF_BUS) += scif_bus.o obj-$(CONFIG_MIC_COSM) += cosm_bus.o +obj-$(CONFIG_VOP_BUS) += vop_bus.o diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c new file mode 100644 index 000000000000..303da222f5b6 --- /dev/null +++ b/drivers/misc/mic/bus/vop_bus.c @@ -0,0 +1,203 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) Bus driver. + */ +#include +#include +#include +#include + +#include "vop_bus.h" + +static ssize_t device_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "0x%04x\n", dev->id.device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t vendor_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "0x%04x\n", dev->id.vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t modalias_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "vop:d%08Xv%08X\n", + dev->id.device, dev->id.vendor); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *vop_dev_attrs[] = { + &dev_attr_device.attr, + &dev_attr_vendor.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vop_dev); + +static inline int vop_id_match(const struct vop_device *dev, + const struct vop_device_id *id) +{ + if (id->device != dev->id.device && id->device != VOP_DEV_ANY_ID) + return 0; + + return id->vendor == VOP_DEV_ANY_ID || id->vendor == dev->id.vendor; +} + +/* + * This looks through all the IDs a driver claims to support. If any of them + * match, we return 1 and the kernel will call vop_dev_probe(). + */ +static int vop_dev_match(struct device *dv, struct device_driver *dr) +{ + unsigned int i; + struct vop_device *dev = dev_to_vop(dv); + const struct vop_device_id *ids; + + ids = drv_to_vop(dr)->id_table; + for (i = 0; ids[i].device; i++) + if (vop_id_match(dev, &ids[i])) + return 1; + return 0; +} + +static int vop_uevent(struct device *dv, struct kobj_uevent_env *env) +{ + struct vop_device *dev = dev_to_vop(dv); + + return add_uevent_var(env, "MODALIAS=vop:d%08Xv%08X", + dev->id.device, dev->id.vendor); +} + +static int vop_dev_probe(struct device *d) +{ + struct vop_device *dev = dev_to_vop(d); + struct vop_driver *drv = drv_to_vop(dev->dev.driver); + + return drv->probe(dev); +} + +static int vop_dev_remove(struct device *d) +{ + struct vop_device *dev = dev_to_vop(d); + struct vop_driver *drv = drv_to_vop(dev->dev.driver); + + drv->remove(dev); + return 0; +} + +static struct bus_type vop_bus = { + .name = "vop_bus", + .match = vop_dev_match, + .dev_groups = vop_dev_groups, + .uevent = vop_uevent, + .probe = vop_dev_probe, + .remove = vop_dev_remove, +}; + +int vop_register_driver(struct vop_driver *driver) +{ + driver->driver.bus = &vop_bus; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_GPL(vop_register_driver); + +void vop_unregister_driver(struct vop_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(vop_unregister_driver); + +static void vop_release_dev(struct device *d) +{ + put_device(d); +} + +struct vop_device * +vop_register_device(struct device *pdev, int id, + const struct dma_map_ops *dma_ops, + struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, + struct dma_chan *chan) +{ + int ret; + struct vop_device *vdev; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return ERR_PTR(-ENOMEM); + + vdev->dev.parent = pdev; + vdev->id.device = id; + vdev->id.vendor = VOP_DEV_ANY_ID; + vdev->dev.archdata.dma_ops = (struct dma_map_ops *)dma_ops; + vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; + dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); + vdev->dev.release = vop_release_dev; + vdev->hw_ops = hw_ops; + vdev->dev.bus = &vop_bus; + vdev->dnode = dnode; + vdev->aper = aper; + vdev->dma_ch = chan; + vdev->index = dnode - 1; + dev_set_name(&vdev->dev, "vop-dev%u", vdev->index); + /* + * device_register() causes the bus infrastructure to look for a + * matching driver. + */ + ret = device_register(&vdev->dev); + if (ret) + goto free_vdev; + return vdev; +free_vdev: + kfree(vdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(vop_register_device); + +void vop_unregister_device(struct vop_device *dev) +{ + device_unregister(&dev->dev); +} +EXPORT_SYMBOL_GPL(vop_unregister_device); + +static int __init vop_init(void) +{ + return bus_register(&vop_bus); +} + +static void __exit vop_exit(void) +{ + bus_unregister(&vop_bus); +} + +core_initcall(vop_init); +module_exit(vop_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) VOP Bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/vop_bus.h b/drivers/misc/mic/bus/vop_bus.h new file mode 100644 index 000000000000..fff7a865d721 --- /dev/null +++ b/drivers/misc/mic/bus/vop_bus.h @@ -0,0 +1,140 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio over PCIe Bus driver. + */ +#ifndef _VOP_BUS_H_ +#define _VOP_BUS_H_ +/* + * Everything a vop driver needs to work with any particular vop + * implementation. + */ +#include +#include + +#include "../common/mic_dev.h" + +struct vop_device_id { + u32 device; + u32 vendor; +}; + +#define VOP_DEV_TRNSP 1 +#define VOP_DEV_ANY_ID 0xffffffff +/* + * Size of the internal buffer used during DMA's as an intermediate buffer + * for copy to/from user. Must be an integral number of pages. + */ +#define VOP_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) + +/** + * vop_device - representation of a device using vop + * @hw_ops: the hardware ops supported by this device. + * @id: the device type identification (used to match it with a driver). + * @dev: underlying device. + * @dnode - The destination node which this device will communicate with. + * @aper: Aperture memory window + * @dma_ch - DMA channel + * @index: unique position on the vop bus + */ +struct vop_device { + struct vop_hw_ops *hw_ops; + struct vop_device_id id; + struct device dev; + u8 dnode; + struct mic_mw *aper; + struct dma_chan *dma_ch; + int index; +}; + +/** + * vop_driver - operations for a vop I/O driver + * @driver: underlying device driver (populate name and owner). + * @id_table: the ids serviced by this driver. + * @probe: the function to call when a device is found. Returns 0 or -errno. + * @remove: the function to call when a device is removed. + */ +struct vop_driver { + struct device_driver driver; + const struct vop_device_id *id_table; + int (*probe)(struct vop_device *dev); + void (*remove)(struct vop_device *dev); +}; + +/** + * vop_hw_ops - Hardware operations for accessing a VOP device on the VOP bus. + * + * @next_db: Obtain the next available doorbell. + * @request_irq: Request an interrupt on a particular doorbell. + * @free_irq: Free an interrupt requested previously. + * @ack_interrupt: acknowledge an interrupt in the ISR. + * @get_remote_dp: Get access to the virtio device page used by the remote + * node to add/remove/configure virtio devices. + * @get_dp: Get access to the virtio device page used by the self + * node to add/remove/configure virtio devices. + * @send_intr: Send an interrupt to the peer node on a specified doorbell. + * @ioremap: Map a buffer with the specified DMA address and length. + * @iounmap: Unmap a buffer previously mapped. + * @dma_filter: The DMA filter function to use for obtaining access to + * a DMA channel on the peer node. + */ +struct vop_hw_ops { + int (*next_db)(struct vop_device *vpdev); + struct mic_irq *(*request_irq)(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, + int intr_src); + void (*free_irq)(struct vop_device *vpdev, + struct mic_irq *cookie, void *data); + void (*ack_interrupt)(struct vop_device *vpdev, int num); + void __iomem * (*get_remote_dp)(struct vop_device *vpdev); + void * (*get_dp)(struct vop_device *vpdev); + void (*send_intr)(struct vop_device *vpdev, int db); + void __iomem * (*ioremap)(struct vop_device *vpdev, + dma_addr_t pa, size_t len); + void (*iounmap)(struct vop_device *vpdev, void __iomem *va); +}; + +struct vop_device * +vop_register_device(struct device *pdev, int id, + const struct dma_map_ops *dma_ops, + struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, + struct dma_chan *chan); +void vop_unregister_device(struct vop_device *dev); +int vop_register_driver(struct vop_driver *drv); +void vop_unregister_driver(struct vop_driver *drv); + +/* + * module_vop_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_vop_driver(__vop_driver) \ + module_driver(__vop_driver, vop_register_driver, \ + vop_unregister_driver) + +static inline struct vop_device *dev_to_vop(struct device *dev) +{ + return container_of(dev, struct vop_device, dev); +} + +static inline struct vop_driver *drv_to_vop(struct device_driver *drv) +{ + return container_of(drv, struct vop_driver, driver); +} +#endif /* _VOP_BUS_H */ -- cgit v1.2.3 From 26909e26f4b51a9f7f8838eee775a65563388c0d Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:14 -0800 Subject: misc: mic: Add data structures for the VOP driver This patch adds VOP driver data structures used in subsequent patches. These data structures are refactored from similar data structures used in the virtio parts of previous MIC host and card drivers. Signed-off-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.h | 170 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_main.h diff --git a/drivers/misc/mic/vop/vop_main.h b/drivers/misc/mic/vop/vop_main.h new file mode 100644 index 000000000000..ba47ec7a6386 --- /dev/null +++ b/drivers/misc/mic/vop/vop_main.h @@ -0,0 +1,170 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#ifndef _VOP_MAIN_H_ +#define _VOP_MAIN_H_ + +#include +#include +#include +#include + +#include +#include "../common/mic_dev.h" + +#include "../bus/vop_bus.h" + +/* + * Note on endianness. + * 1. Host can be both BE or LE + * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail + * rings and ioreadXX/iowriteXX to access used ring. + * 3. Device page exposed by host to guest contains LE values. Guest + * accesses these using ioreadXX/iowriteXX etc. This way in general we + * obey the virtio spec according to which guest works with native + * endianness and host is aware of guest endianness and does all + * required endianness conversion. + * 4. Data provided from user space to guest (in ADD_DEVICE and + * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be + * in guest endianness. + */ + +/* + * vop_info - Allocated per invocation of VOP probe + * + * @vpdev: VOP device + * @hotplug_work: Handle virtio device creation, deletion and configuration + * @cookie: Cookie received upon requesting a virtio configuration interrupt + * @h2c_config_db: The doorbell used by the peer to indicate a config change + * @vdev_list: List of "active" virtio devices injected in the peer node + * @vop_mutex: Synchronize access to the device page as well as serialize + * creation/deletion of virtio devices on the peer node + * @dp: Peer device page information + * @dbg: Debugfs entry + * @dma_ch: The DMA channel used by this transport for data transfers. + * @name: Name for this transport used in misc device creation. + * @miscdev: The misc device registered. + */ +struct vop_info { + struct vop_device *vpdev; + struct work_struct hotplug_work; + struct mic_irq *cookie; + int h2c_config_db; + struct list_head vdev_list; + struct mutex vop_mutex; + void __iomem *dp; + struct dentry *dbg; + struct dma_chan *dma_ch; + char name[16]; + struct miscdevice miscdev; +}; + +/** + * struct vop_vringh - Virtio ring host information. + * + * @vring: The VOP vring used for setting up user space mappings. + * @vrh: The host VRINGH used for accessing the card vrings. + * @riov: The VRINGH read kernel IOV. + * @wiov: The VRINGH write kernel IOV. + * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). + * @vr_mutex: Mutex for synchronizing access to the VRING. + * @buf: Temporary kernel buffer used to copy in/out data + * from/to the card via DMA. + * @buf_da: dma address of buf. + * @vdev: Back pointer to VOP virtio device for vringh_notify(..). + */ +struct vop_vringh { + struct mic_vring vring; + struct vringh vrh; + struct vringh_kiov riov; + struct vringh_kiov wiov; + u16 head; + struct mutex vr_mutex; + void *buf; + dma_addr_t buf_da; + struct vop_vdev *vdev; +}; + +/** + * struct vop_vdev - Host information for a card Virtio device. + * + * @virtio_id - Virtio device id. + * @waitq - Waitqueue to allow ring3 apps to poll. + * @vpdev - pointer to VOP bus device. + * @poll_wake - Used for waking up threads blocked in poll. + * @out_bytes - Debug stats for number of bytes copied from host to card. + * @in_bytes - Debug stats for number of bytes copied from card to host. + * @out_bytes_dma - Debug stats for number of bytes copied from host to card + * using DMA. + * @in_bytes_dma - Debug stats for number of bytes copied from card to host + * using DMA. + * @tx_len_unaligned - Debug stats for number of bytes copied to the card where + * the transfer length did not have the required DMA alignment. + * @tx_dst_unaligned - Debug stats for number of bytes copied where the + * destination address on the card did not have the required DMA alignment. + * @vvr - Store per VRING data structures. + * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. + * @dd - Virtio device descriptor. + * @dc - Virtio device control fields. + * @list - List of Virtio devices. + * @virtio_db - The doorbell used by the card to interrupt the host. + * @virtio_cookie - The cookie returned while requesting interrupts. + * @vi: Transport information. + * @vdev_mutex: Mutex synchronizing virtio device injection, + * removal and data transfers. + * @destroy: Track if a virtio device is being destroyed. + * @deleted: The virtio device has been deleted. + */ +struct vop_vdev { + int virtio_id; + wait_queue_head_t waitq; + struct vop_device *vpdev; + int poll_wake; + unsigned long out_bytes; + unsigned long in_bytes; + unsigned long out_bytes_dma; + unsigned long in_bytes_dma; + unsigned long tx_len_unaligned; + unsigned long tx_dst_unaligned; + unsigned long rx_dst_unaligned; + struct vop_vringh vvr[MIC_MAX_VRINGS]; + struct work_struct virtio_bh_work; + struct mic_device_desc *dd; + struct mic_device_ctrl *dc; + struct list_head list; + int virtio_db; + struct mic_irq *virtio_cookie; + struct vop_info *vi; + struct mutex vdev_mutex; + struct completion destroy; + bool deleted; +}; + +/* Helper API to check if a virtio device is running */ +static inline bool vop_vdevup(struct vop_vdev *vdev) +{ + return !!vdev->dd->status; +} + +void vop_init_debugfs(struct vop_info *vi); +void vop_exit_debugfs(struct vop_info *vi); +int vop_host_init(struct vop_info *vi); +void vop_host_uninit(struct vop_info *vi); +#endif -- cgit v1.2.3 From 61e9c905df78c253752971e200f0ac6d8667dda6 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:15 -0800 Subject: misc: mic: Enable VOP host side functionality This patch moves virtio functionality from the MIC host driver into a separate hardware independent Virtio Over PCIe (VOP) driver. This functionality was introduced in commit f69bcbf3b4c4 ("Intel MIC Host Driver Changes for Virtio Devices.") in drivers/misc/mic/host/mic_virtio.c. Apart from being moved into a separate driver the functionality is essentially unchanged. See the above mentioned commit for a description of this functionality. Signed-off-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 1164 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1164 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_vringh.c diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c new file mode 100644 index 000000000000..c1dd000f042b --- /dev/null +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -0,0 +1,1164 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_dev.h" + +#include +#include "vop_main.h" + +/* Helper API to obtain the VOP PCIe device */ +static inline struct device *vop_dev(struct vop_vdev *vdev) +{ + return vdev->vpdev->dev.parent; +} + +/* Helper API to check if a virtio device is initialized */ +static inline int vop_vdev_inited(struct vop_vdev *vdev) +{ + if (!vdev) + return -EINVAL; + /* Device has not been created yet */ + if (!vdev->dd || !vdev->dd->type) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + /* Device has been removed/deleted */ + if (vdev->dd->type == -1) { + dev_dbg(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, -ENODEV); + return -ENODEV; + } + return 0; +} + +static void _vop_notify(struct vringh *vrh) +{ + struct vop_vringh *vvrh = container_of(vrh, struct vop_vringh, vrh); + struct vop_vdev *vdev = vvrh->vdev; + struct vop_device *vpdev = vdev->vpdev; + s8 db = vdev->dc->h2c_vdev_db; + + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); +} + +static void vop_virtio_init_post(struct vop_vdev *vdev) +{ + struct mic_vqconfig *vqconfig = mic_vq_config(vdev->dd); + struct vop_device *vpdev = vdev->vpdev; + int i, used_size; + + for (i = 0; i < vdev->dd->num_vq; i++) { + used_size = PAGE_ALIGN(sizeof(u16) * 3 + + sizeof(struct vring_used_elem) * + le16_to_cpu(vqconfig->num)); + if (!le64_to_cpu(vqconfig[i].used_address)) { + dev_warn(vop_dev(vdev), "used_address zero??\n"); + continue; + } + vdev->vvr[i].vrh.vring.used = + (void __force *)vpdev->hw_ops->ioremap( + vpdev, + le64_to_cpu(vqconfig[i].used_address), + used_size); + } + + vdev->dc->used_address_updated = 0; + + dev_info(vop_dev(vdev), "%s: device type %d LINKUP\n", + __func__, vdev->virtio_id); +} + +static inline void vop_virtio_device_reset(struct vop_vdev *vdev) +{ + int i; + + dev_dbg(vop_dev(vdev), "%s: status %d device type %d RESET\n", + __func__, vdev->dd->status, vdev->virtio_id); + + for (i = 0; i < vdev->dd->num_vq; i++) + /* + * Avoid lockdep false positive. The + 1 is for the vop + * mutex which is held in the reset devices code path. + */ + mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); + + /* 0 status means "reset" */ + vdev->dd->status = 0; + vdev->dc->vdev_reset = 0; + vdev->dc->host_ack = 1; + + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vringh *vrh = &vdev->vvr[i].vrh; + + vdev->vvr[i].vring.info->avail_idx = 0; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + } + + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_unlock(&vdev->vvr[i].vr_mutex); +} + +static void vop_virtio_reset_devices(struct vop_info *vi) +{ + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + vop_virtio_device_reset(vdev); + vdev->poll_wake = 1; + wake_up(&vdev->waitq); + } +} + +static void vop_bh_handler(struct work_struct *work) +{ + struct vop_vdev *vdev = container_of(work, struct vop_vdev, + virtio_bh_work); + + if (vdev->dc->used_address_updated) + vop_virtio_init_post(vdev); + + if (vdev->dc->vdev_reset) + vop_virtio_device_reset(vdev); + + vdev->poll_wake = 1; + wake_up(&vdev->waitq); +} + +static irqreturn_t _vop_virtio_intr_handler(int irq, void *data) +{ + struct vop_vdev *vdev = data; + struct vop_device *vpdev = vdev->vpdev; + + vpdev->hw_ops->ack_interrupt(vpdev, vdev->virtio_db); + schedule_work(&vdev->virtio_bh_work); + return IRQ_HANDLED; +} + +static int vop_virtio_config_change(struct vop_vdev *vdev, void *argp) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int ret = 0, retry, i; + struct vop_device *vpdev = vdev->vpdev; + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + s8 db = bootparam->h2c_config_db; + + mutex_lock(&vi->vop_mutex); + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); + + if (db == -1 || vdev->dd->type == -1) { + ret = -EIO; + goto exit; + } + + memcpy(mic_vq_configspace(vdev->dd), argp, vdev->dd->config_len); + vdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; + vpdev->hw_ops->send_intr(vpdev, db); + + for (retry = 100; retry--;) { + ret = wait_event_timeout(wake, vdev->dc->guest_ack, + msecs_to_jiffies(100)); + if (ret) + break; + } + + dev_dbg(vop_dev(vdev), + "%s %d retry: %d\n", __func__, __LINE__, retry); + vdev->dc->config_change = 0; + vdev->dc->guest_ack = 0; +exit: + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_unlock(&vdev->vvr[i].vr_mutex); + mutex_unlock(&vi->vop_mutex); + return ret; +} + +static int vop_copy_dp_entry(struct vop_vdev *vdev, + struct mic_device_desc *argp, __u8 *type, + struct mic_device_desc **devpage) +{ + struct vop_device *vpdev = vdev->vpdev; + struct mic_device_desc *devp; + struct mic_vqconfig *vqconfig; + int ret = 0, i; + bool slot_found = false; + + vqconfig = mic_vq_config(argp); + for (i = 0; i < argp->num_vq; i++) { + if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + } + + /* Find the first free device page entry */ + for (i = sizeof(struct mic_bootparam); + i < MIC_DP_SIZE - mic_total_desc_size(argp); + i += mic_total_desc_size(devp)) { + devp = vpdev->hw_ops->get_dp(vpdev) + i; + if (devp->type == 0 || devp->type == -1) { + slot_found = true; + break; + } + } + if (!slot_found) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + /* + * Save off the type before doing the memcpy. Type will be set in the + * end after completing all initialization for the new device. + */ + *type = argp->type; + argp->type = 0; + memcpy(devp, argp, mic_desc_size(argp)); + + *devpage = devp; +exit: + return ret; +} + +static void vop_init_device_ctrl(struct vop_vdev *vdev, + struct mic_device_desc *devpage) +{ + struct mic_device_ctrl *dc; + + dc = (void *)devpage + mic_aligned_desc_size(devpage); + + dc->config_change = 0; + dc->guest_ack = 0; + dc->vdev_reset = 0; + dc->host_ack = 0; + dc->used_address_updated = 0; + dc->c2h_vdev_db = -1; + dc->h2c_vdev_db = -1; + vdev->dc = dc; +} + +static int vop_virtio_add_device(struct vop_vdev *vdev, + struct mic_device_desc *argp) +{ + struct vop_info *vi = vdev->vi; + struct vop_device *vpdev = vi->vpdev; + struct mic_device_desc *dd = NULL; + struct mic_vqconfig *vqconfig; + int vr_size, i, j, ret; + u8 type = 0; + s8 db = -1; + char irqname[16]; + struct mic_bootparam *bootparam; + u16 num; + dma_addr_t vr_addr; + + bootparam = vpdev->hw_ops->get_dp(vpdev); + init_waitqueue_head(&vdev->waitq); + INIT_LIST_HEAD(&vdev->list); + vdev->vpdev = vpdev; + + ret = vop_copy_dp_entry(vdev, argp, &type, &dd); + if (ret) { + kfree(vdev); + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + return ret; + } + + vop_init_device_ctrl(vdev, dd); + + vdev->dd = dd; + vdev->virtio_id = type; + vqconfig = mic_vq_config(dd); + INIT_WORK(&vdev->virtio_bh_work, vop_bh_handler); + + for (i = 0; i < dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + struct mic_vring *vr = &vdev->vvr[i].vring; + + num = le16_to_cpu(vqconfig[i].num); + mutex_init(&vvr->vr_mutex); + vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); + vr->va = (void *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vr_size)); + if (!vr->va) { + ret = -ENOMEM; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vr->len = vr_size; + vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); + vr->info->magic = cpu_to_le32(MIC_MAGIC + vdev->virtio_id + i); + vr_addr = dma_map_single(&vpdev->dev, vr->va, vr_size, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&vpdev->dev, vr_addr)) { + free_pages((unsigned long)vr->va, get_order(vr_size)); + ret = -ENOMEM; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vqconfig[i].address = cpu_to_le64(vr_addr); + + vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); + ret = vringh_init_kern(&vvr->vrh, + *(u32 *)mic_vq_features(vdev->dd), + num, false, vr->vr.desc, vr->vr.avail, + vr->vr.used); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vringh_kiov_init(&vvr->riov, NULL, 0); + vringh_kiov_init(&vvr->wiov, NULL, 0); + vvr->head = USHRT_MAX; + vvr->vdev = vdev; + vvr->vrh.notify = _vop_notify; + dev_dbg(&vpdev->dev, + "%s %d index %d va %p info %p vr_size 0x%x\n", + __func__, __LINE__, i, vr->va, vr->info, vr_size); + vvr->buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(VOP_INT_DMA_BUF_SIZE)); + vvr->buf_da = dma_map_single(&vpdev->dev, + vvr->buf, VOP_INT_DMA_BUF_SIZE, + DMA_BIDIRECTIONAL); + } + + snprintf(irqname, sizeof(irqname), "vop%dvirtio%d", vpdev->index, + vdev->virtio_id); + vdev->virtio_db = vpdev->hw_ops->next_db(vpdev); + vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, + _vop_virtio_intr_handler, irqname, vdev, + vdev->virtio_db); + if (IS_ERR(vdev->virtio_cookie)) { + ret = PTR_ERR(vdev->virtio_cookie); + dev_dbg(&vpdev->dev, "request irq failed\n"); + goto err; + } + + vdev->dc->c2h_vdev_db = vdev->virtio_db; + + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + dd->type = type; + argp->type = type; + + if (bootparam) { + db = bootparam->h2c_config_db; + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); + } + dev_dbg(&vpdev->dev, "Added virtio id %d db %d\n", dd->type, db); + return 0; +err: + vqconfig = mic_vq_config(dd); + for (j = 0; j < i; j++) { + struct vop_vringh *vvr = &vdev->vvr[j]; + + dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[j].address), + vvr->vring.len, DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->vring.va, + get_order(vvr->vring.len)); + } + return ret; +} + +static void vop_dev_remove(struct vop_info *pvi, struct mic_device_ctrl *devp, + struct vop_device *vpdev) +{ + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + s8 db; + int ret, retry; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + + devp->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; + db = bootparam->h2c_config_db; + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); + else + goto done; + for (retry = 15; retry--;) { + ret = wait_event_timeout(wake, devp->guest_ack, + msecs_to_jiffies(1000)); + if (ret) + break; + } +done: + devp->config_change = 0; + devp->guest_ack = 0; +} + +static void vop_virtio_del_device(struct vop_vdev *vdev) +{ + struct vop_info *vi = vdev->vi; + struct vop_device *vpdev = vdev->vpdev; + int i; + struct mic_vqconfig *vqconfig; + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + + if (!bootparam) + goto skip_hot_remove; + vop_dev_remove(vi, vdev->dc, vpdev); +skip_hot_remove: + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); + flush_work(&vdev->virtio_bh_work); + vqconfig = mic_vq_config(vdev->dd); + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + + dma_unmap_single(&vpdev->dev, + vvr->buf_da, VOP_INT_DMA_BUF_SIZE, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->buf, + get_order(VOP_INT_DMA_BUF_SIZE)); + vringh_kiov_cleanup(&vvr->riov); + vringh_kiov_cleanup(&vvr->wiov); + dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[i].address), + vvr->vring.len, DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->vring.va, + get_order(vvr->vring.len)); + } + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + vdev->dd->type = -1; +} + +/* + * vop_sync_dma - Wrapper for synchronous DMAs. + * + * @dev - The address of the pointer to the device instance used + * for DMA registration. + * @dst - destination DMA address. + * @src - source DMA address. + * @len - size of the transfer. + * + * Return DMA_SUCCESS on success + */ +static int vop_sync_dma(struct vop_vdev *vdev, dma_addr_t dst, dma_addr_t src, + size_t len) +{ + int err = 0; + struct dma_device *ddev; + struct dma_async_tx_descriptor *tx; + struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); + struct dma_chan *vop_ch = vi->dma_ch; + + if (!vop_ch) { + err = -EBUSY; + goto error; + } + ddev = vop_ch->device; + tx = ddev->device_prep_dma_memcpy(vop_ch, dst, src, len, + DMA_PREP_FENCE); + if (!tx) { + err = -ENOMEM; + goto error; + } else { + dma_cookie_t cookie; + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + err = -ENOMEM; + goto error; + } + dma_async_issue_pending(vop_ch); + err = dma_sync_wait(vop_ch, cookie); + } +error: + if (err) + dev_err(&vi->vpdev->dev, "%s %d err %d\n", + __func__, __LINE__, err); + return err; +} + +#define VOP_USE_DMA true + +/* + * Initiates the copies across the PCIe bus from card memory to a user + * space buffer. When transfers are done using DMA, source/destination + * addresses and transfer length must follow the alignment requirements of + * the MIC DMA engine. + */ +static int vop_virtio_copy_to_user(struct vop_vdev *vdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) +{ + struct vop_device *vpdev = vdev->vpdev; + void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len); + struct vop_vringh *vvr = &vdev->vvr[vr_idx]; + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + size_t dma_alignment = 1 << vi->dma_ch->device->copy_align; + bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); + size_t dma_offset, partlen; + int err; + + if (!VOP_USE_DMA) { + if (copy_to_user(ubuf, (void __force *)dbuf, len)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + vdev->in_bytes += len; + err = 0; + goto err; + } + + dma_offset = daddr - round_down(daddr, dma_alignment); + daddr -= dma_offset; + len += dma_offset; + /* + * X100 uses DMA addresses as seen by the card so adding + * the aperture base is not required for DMA. However x200 + * requires DMA addresses to be an offset into the bar so + * add the aperture base for x200. + */ + if (x200) + daddr += vpdev->aper->pa; + while (len) { + partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); + err = vop_sync_dma(vdev, vvr->buf_da, daddr, + ALIGN(partlen, dma_alignment)); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + if (copy_to_user(ubuf, vvr->buf + dma_offset, + partlen - dma_offset)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + vdev->in_bytes_dma += partlen; + vdev->in_bytes += partlen; + len -= partlen; + dma_offset = 0; + } + err = 0; +err: + vpdev->hw_ops->iounmap(vpdev, dbuf); + dev_dbg(vop_dev(vdev), + "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n", + __func__, ubuf, dbuf, len, vr_idx); + return err; +} + +/* + * Initiates copies across the PCIe bus from a user space buffer to card + * memory. When transfers are done using DMA, source/destination addresses + * and transfer length must follow the alignment requirements of the MIC + * DMA engine. + */ +static int vop_virtio_copy_from_user(struct vop_vdev *vdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) +{ + struct vop_device *vpdev = vdev->vpdev; + void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len); + struct vop_vringh *vvr = &vdev->vvr[vr_idx]; + struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); + size_t dma_alignment = 1 << vi->dma_ch->device->copy_align; + bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); + size_t partlen; + bool dma = VOP_USE_DMA; + int err = 0; + + if (daddr & (dma_alignment - 1)) { + vdev->tx_dst_unaligned += len; + dma = false; + } else if (ALIGN(len, dma_alignment) > dlen) { + vdev->tx_len_unaligned += len; + dma = false; + } + + if (!dma) + goto memcpy; + + /* + * X100 uses DMA addresses as seen by the card so adding + * the aperture base is not required for DMA. However x200 + * requires DMA addresses to be an offset into the bar so + * add the aperture base for x200. + */ + if (x200) + daddr += vpdev->aper->pa; + while (len) { + partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); + + if (copy_from_user(vvr->buf, ubuf, partlen)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = vop_sync_dma(vdev, daddr, vvr->buf_da, + ALIGN(partlen, dma_alignment)); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + vdev->out_bytes_dma += partlen; + vdev->out_bytes += partlen; + len -= partlen; + } +memcpy: + /* + * We are copying to IO below and should ideally use something + * like copy_from_user_toio(..) if it existed. + */ + if (copy_from_user((void __force *)dbuf, ubuf, len)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + vdev->out_bytes += len; + err = 0; +err: + vpdev->hw_ops->iounmap(vpdev, dbuf); + dev_dbg(vop_dev(vdev), + "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n", + __func__, ubuf, dbuf, len, vr_idx); + return err; +} + +#define MIC_VRINGH_READ true + +/* Determine the total number of bytes consumed in a VRINGH KIOV */ +static inline u32 vop_vringh_iov_consumed(struct vringh_kiov *iov) +{ + int i; + u32 total = iov->consumed; + + for (i = 0; i < iov->i; i++) + total += iov->iov[i].iov_len; + return total; +} + +/* + * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. + * This API is heavily based on the vringh_iov_xfer(..) implementation + * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) + * and vringh_iov_push_kern(..) directly is because there is no + * way to override the VRINGH xfer(..) routines as of v3.10. + */ +static int vop_vringh_copy(struct vop_vdev *vdev, struct vringh_kiov *iov, + void __user *ubuf, size_t len, bool read, int vr_idx, + size_t *out_len) +{ + int ret = 0; + size_t partlen, tot_len = 0; + + while (len && iov->i < iov->used) { + struct kvec *kiov = &iov->iov[iov->i]; + + partlen = min(kiov->iov_len, len); + if (read) + ret = vop_virtio_copy_to_user(vdev, ubuf, partlen, + (u64)kiov->iov_base, + kiov->iov_len, + vr_idx); + else + ret = vop_virtio_copy_from_user(vdev, ubuf, partlen, + (u64)kiov->iov_base, + kiov->iov_len, + vr_idx); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= partlen; + ubuf += partlen; + tot_len += partlen; + iov->consumed += partlen; + kiov->iov_len -= partlen; + kiov->iov_base += partlen; + if (!kiov->iov_len) { + /* Fix up old iov element then increment. */ + kiov->iov_len = iov->consumed; + kiov->iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } + } + *out_len = tot_len; + return ret; +} + +/* + * Use the standard VRINGH infrastructure in the kernel to fetch new + * descriptors, initiate the copies and update the used ring. + */ +static int _vop_virtio_copy(struct vop_vdev *vdev, struct mic_copy_desc *copy) +{ + int ret = 0; + u32 iovcnt = copy->iovcnt; + struct iovec iov; + struct iovec __user *u_iov = copy->iov; + void __user *ubuf = NULL; + struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + struct vringh_kiov *riov = &vvr->riov; + struct vringh_kiov *wiov = &vvr->wiov; + struct vringh *vrh = &vvr->vrh; + u16 *head = &vvr->head; + struct mic_vring *vr = &vvr->vring; + size_t len = 0, out_len; + + copy->out_len = 0; + /* Fetch a new IOVEC if all previous elements have been processed */ + if (riov->i == riov->used && wiov->i == wiov->used) { + ret = vringh_getdesc_kern(vrh, riov, wiov, + head, GFP_KERNEL); + /* Check if there are available descriptors */ + if (ret <= 0) + return ret; + } + while (iovcnt) { + if (!len) { + /* Copy over a new iovec from user space. */ + ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); + if (ret) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len = iov.iov_len; + ubuf = iov.iov_base; + } + /* Issue all the read descriptors first */ + ret = vop_vringh_copy(vdev, riov, ubuf, len, + MIC_VRINGH_READ, copy->vr_idx, &out_len); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + /* Issue the write descriptors next */ + ret = vop_vringh_copy(vdev, wiov, ubuf, len, + !MIC_VRINGH_READ, copy->vr_idx, &out_len); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + if (!len) { + /* One user space iovec is now completed */ + iovcnt--; + u_iov++; + } + /* Exit loop if all elements in KIOVs have been processed. */ + if (riov->i == riov->used && wiov->i == wiov->used) + break; + } + /* + * Update the used ring if a descriptor was available and some data was + * copied in/out and the user asked for a used ring update. + */ + if (*head != USHRT_MAX && copy->out_len && copy->update_used) { + u32 total = 0; + + /* Determine the total data consumed */ + total += vop_vringh_iov_consumed(riov); + total += vop_vringh_iov_consumed(wiov); + vringh_complete_kern(vrh, *head, total); + *head = USHRT_MAX; + if (vringh_need_notify_kern(vrh) > 0) + vringh_notify(vrh); + vringh_kiov_cleanup(riov); + vringh_kiov_cleanup(wiov); + /* Update avail idx for user space */ + vr->info->avail_idx = vrh->last_avail_idx; + } + return ret; +} + +static inline int vop_verify_copy_args(struct vop_vdev *vdev, + struct mic_copy_desc *copy) +{ + if (!vdev || copy->vr_idx >= vdev->dd->num_vq) + return -EINVAL; + return 0; +} + +/* Copy a specified number of virtio descriptors in a chain */ +static int vop_virtio_copy_desc(struct vop_vdev *vdev, + struct mic_copy_desc *copy) +{ + int err; + struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + + err = vop_verify_copy_args(vdev, copy); + if (err) + return err; + + mutex_lock(&vvr->vr_mutex); + if (!vop_vdevup(vdev)) { + err = -ENODEV; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = _vop_virtio_copy(vdev, copy); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + } +err: + mutex_unlock(&vvr->vr_mutex); + return err; +} + +static int vop_open(struct inode *inode, struct file *f) +{ + struct vop_vdev *vdev; + struct vop_info *vi = container_of(f->private_data, + struct vop_info, miscdev); + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + vdev->vi = vi; + mutex_init(&vdev->vdev_mutex); + f->private_data = vdev; + init_completion(&vdev->destroy); + complete(&vdev->destroy); + return 0; +} + +static int vop_release(struct inode *inode, struct file *f) +{ + struct vop_vdev *vdev = f->private_data, *vdev_tmp; + struct vop_info *vi = vdev->vi; + struct list_head *pos, *tmp; + bool found = false; + + mutex_lock(&vdev->vdev_mutex); + if (vdev->deleted) + goto unlock; + mutex_lock(&vi->vop_mutex); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev_tmp = list_entry(pos, struct vop_vdev, list); + if (vdev == vdev_tmp) { + vop_virtio_del_device(vdev); + list_del(pos); + found = true; + break; + } + } + mutex_unlock(&vi->vop_mutex); +unlock: + mutex_unlock(&vdev->vdev_mutex); + if (!found) + wait_for_completion(&vdev->destroy); + f->private_data = NULL; + kfree(vdev); + return 0; +} + +static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct vop_vdev *vdev = f->private_data; + struct vop_info *vi = vdev->vi; + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MIC_VIRTIO_ADD_DEVICE: + { + struct mic_device_desc dd, *dd_config; + + if (copy_from_user(&dd, argp, sizeof(dd))) + return -EFAULT; + + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || + dd.num_vq > MIC_MAX_VRINGS) + return -EINVAL; + + dd_config = kzalloc(mic_desc_size(&dd), GFP_KERNEL); + if (!dd_config) + return -ENOMEM; + if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { + ret = -EFAULT; + goto free_ret; + } + mutex_lock(&vdev->vdev_mutex); + mutex_lock(&vi->vop_mutex); + ret = vop_virtio_add_device(vdev, dd_config); + if (ret) + goto unlock_ret; + list_add_tail(&vdev->list, &vi->vdev_list); +unlock_ret: + mutex_unlock(&vi->vop_mutex); + mutex_unlock(&vdev->vdev_mutex); +free_ret: + kfree(dd_config); + return ret; + } + case MIC_VIRTIO_COPY_DESC: + { + struct mic_copy_desc copy; + + mutex_lock(&vdev->vdev_mutex); + ret = vop_vdev_inited(vdev); + if (ret) + goto _unlock_ret; + + if (copy_from_user(©, argp, sizeof(copy))) { + ret = -EFAULT; + goto _unlock_ret; + } + + ret = vop_virtio_copy_desc(vdev, ©); + if (ret < 0) + goto _unlock_ret; + if (copy_to_user( + &((struct mic_copy_desc __user *)argp)->out_len, + ©.out_len, sizeof(copy.out_len))) + ret = -EFAULT; +_unlock_ret: + mutex_unlock(&vdev->vdev_mutex); + return ret; + } + case MIC_VIRTIO_CONFIG_CHANGE: + { + void *buf; + + mutex_lock(&vdev->vdev_mutex); + ret = vop_vdev_inited(vdev); + if (ret) + goto __unlock_ret; + buf = kzalloc(vdev->dd->config_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto __unlock_ret; + } + if (copy_from_user(buf, argp, vdev->dd->config_len)) { + ret = -EFAULT; + goto done; + } + ret = vop_virtio_config_change(vdev, buf); +done: + kfree(buf); +__unlock_ret: + mutex_unlock(&vdev->vdev_mutex); + return ret; + } + default: + return -ENOIOCTLCMD; + }; + return 0; +} + +/* + * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and + * not when previously enqueued buffers may be available. This means that + * in the card->host (TX) path, when userspace is unblocked by poll it + * must drain all available descriptors or it can stall. + */ +static unsigned int vop_poll(struct file *f, poll_table *wait) +{ + struct vop_vdev *vdev = f->private_data; + int mask = 0; + + mutex_lock(&vdev->vdev_mutex); + if (vop_vdev_inited(vdev)) { + mask = POLLERR; + goto done; + } + poll_wait(f, &vdev->waitq, wait); + if (vop_vdev_inited(vdev)) { + mask = POLLERR; + } else if (vdev->poll_wake) { + vdev->poll_wake = 0; + mask = POLLIN | POLLOUT; + } +done: + mutex_unlock(&vdev->vdev_mutex); + return mask; +} + +static inline int +vop_query_offset(struct vop_vdev *vdev, unsigned long offset, + unsigned long *size, unsigned long *pa) +{ + struct vop_device *vpdev = vdev->vpdev; + unsigned long start = MIC_DP_SIZE; + int i; + + /* + * MMAP interface is as follows: + * offset region + * 0x0 virtio device_page + * 0x1000 first vring + * 0x1000 + size of 1st vring second vring + * .... + */ + if (!offset) { + *pa = virt_to_phys(vpdev->hw_ops->get_dp(vpdev)); + *size = MIC_DP_SIZE; + return 0; + } + + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + + if (offset == start) { + *pa = virt_to_phys(vvr->vring.va); + *size = vvr->vring.len; + return 0; + } + start += vvr->vring.len; + } + return -1; +} + +/* + * Maps the device page and virtio rings to user space for readonly access. + */ +static int vop_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct vop_vdev *vdev = f->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; + int i, err; + + err = vop_vdev_inited(vdev); + if (err) + goto ret; + if (vma->vm_flags & VM_WRITE) { + err = -EACCES; + goto ret; + } + while (size_rem) { + i = vop_query_offset(vdev, offset, &size, &pa); + if (i < 0) { + err = -EINVAL; + goto ret; + } + err = remap_pfn_range(vma, vma->vm_start + offset, + pa >> PAGE_SHIFT, size, + vma->vm_page_prot); + if (err) + goto ret; + size_rem -= size; + offset += size; + } +ret: + return err; +} + +static const struct file_operations vop_fops = { + .open = vop_open, + .release = vop_release, + .unlocked_ioctl = vop_ioctl, + .poll = vop_poll, + .mmap = vop_mmap, + .owner = THIS_MODULE, +}; + +int vop_host_init(struct vop_info *vi) +{ + int rc; + struct miscdevice *mdev; + struct vop_device *vpdev = vi->vpdev; + + INIT_LIST_HEAD(&vi->vdev_list); + vi->dma_ch = vpdev->dma_ch; + mdev = &vi->miscdev; + mdev->minor = MISC_DYNAMIC_MINOR; + snprintf(vi->name, sizeof(vi->name), "vop_virtio%d", vpdev->index); + mdev->name = vi->name; + mdev->fops = &vop_fops; + mdev->parent = &vpdev->dev; + + rc = misc_register(mdev); + if (rc) + dev_err(&vpdev->dev, "%s failed rc %d\n", __func__, rc); + return rc; +} + +void vop_host_uninit(struct vop_info *vi) +{ + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + + mutex_lock(&vi->vop_mutex); + vop_virtio_reset_devices(vi); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + list_del(pos); + reinit_completion(&vdev->destroy); + mutex_unlock(&vi->vop_mutex); + mutex_lock(&vdev->vdev_mutex); + vop_virtio_del_device(vdev); + vdev->deleted = true; + mutex_unlock(&vdev->vdev_mutex); + complete(&vdev->destroy); + mutex_lock(&vi->vop_mutex); + } + mutex_unlock(&vi->vop_mutex); + misc_deregister(&vi->miscdev); +} -- cgit v1.2.3 From c1becd2849681e8be1043d2ce9034a9d9bb3d540 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 8 Feb 2016 15:48:16 -0800 Subject: misc: mic: Enable VOP card side functionality This patch moves virtio functionality from the MIC card driver into a separate hardware independent Virtio Over PCIe (VOP) driver. This functionality was introduced in commit 2141c7c5ee67 ("Intel MIC Card Driver Changes for Virtio Devices.") in drivers/misc/mic/card/mic_virtio.c. Apart from being moved into a separate driver the functionality is essentially unchanged. See the above mentioned commit for a description of this functionality. Signed-off-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.c | 755 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 755 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_main.c diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c new file mode 100644 index 000000000000..1a2b67f3183d --- /dev/null +++ b/drivers/misc/mic/vop/vop_main.c @@ -0,0 +1,755 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Adapted from: + * + * virtio for kvm on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include +#include +#include + +#include "vop_main.h" + +#define VOP_MAX_VRINGS 4 + +/* + * _vop_vdev - Allocated per virtio device instance injected by the peer. + * + * @vdev: Virtio device + * @desc: Virtio device page descriptor + * @dc: Virtio device control + * @vpdev: VOP device which is the parent for this virtio device + * @vr: Buffer for accessing the VRING + * @used: Buffer for used + * @used_size: Size of the used buffer + * @reset_done: Track whether VOP reset is complete + * @virtio_cookie: Cookie returned upon requesting a interrupt + * @c2h_vdev_db: The doorbell used by the guest to interrupt the host + * @h2c_vdev_db: The doorbell used by the host to interrupt the guest + * @dnode: The destination node + */ +struct _vop_vdev { + struct virtio_device vdev; + struct mic_device_desc __iomem *desc; + struct mic_device_ctrl __iomem *dc; + struct vop_device *vpdev; + void __iomem *vr[VOP_MAX_VRINGS]; + dma_addr_t used[VOP_MAX_VRINGS]; + int used_size[VOP_MAX_VRINGS]; + struct completion reset_done; + struct mic_irq *virtio_cookie; + int c2h_vdev_db; + int h2c_vdev_db; + int dnode; +}; + +#define to_vopvdev(vd) container_of(vd, struct _vop_vdev, vdev) + +#define _vop_aligned_desc_size(d) __mic_align(_vop_desc_size(d), 8) + +/* Helper API to obtain the parent of the virtio device */ +static inline struct device *_vop_dev(struct _vop_vdev *vdev) +{ + return vdev->vdev.dev.parent; +} + +static inline unsigned _vop_desc_size(struct mic_device_desc __iomem *desc) +{ + return sizeof(*desc) + + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) + + ioread8(&desc->feature_len) * 2 + + ioread8(&desc->config_len); +} + +static inline struct mic_vqconfig __iomem * +_vop_vq_config(struct mic_device_desc __iomem *desc) +{ + return (struct mic_vqconfig __iomem *)(desc + 1); +} + +static inline u8 __iomem * +_vop_vq_features(struct mic_device_desc __iomem *desc) +{ + return (u8 __iomem *)(_vop_vq_config(desc) + ioread8(&desc->num_vq)); +} + +static inline u8 __iomem * +_vop_vq_configspace(struct mic_device_desc __iomem *desc) +{ + return _vop_vq_features(desc) + ioread8(&desc->feature_len) * 2; +} + +static inline unsigned +_vop_total_desc_size(struct mic_device_desc __iomem *desc) +{ + return _vop_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); +} + +/* This gets the device's feature bits. */ +static u64 vop_get_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + u32 features = 0; + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + u8 __iomem *in_features = _vop_vq_features(desc); + int feature_len = ioread8(&desc->feature_len); + + bits = min_t(unsigned, feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) + if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) + features |= BIT(i); + + return features; +} + +static int vop_finalize_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + u8 feature_len = ioread8(&desc->feature_len); + /* Second half of bitmap is features we accept. */ + u8 __iomem *out_features = + _vop_vq_features(desc) + feature_len; + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + memset_io(out_features, 0, feature_len); + bits = min_t(unsigned, feature_len, + sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (__virtio_test_bit(vdev, i)) + iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), + &out_features[i / 8]); + } + return 0; +} + +/* + * Reading and writing elements in config space + */ +static void vop_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_fromio(buf, _vop_vq_configspace(desc) + offset, len); +} + +static void vop_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_toio(_vop_vq_configspace(desc) + offset, buf, len); +} + +/* + * The operations to get and set the status word just access the status + * field of the device descriptor. set_status also interrupts the host + * to tell about status changes. + */ +static u8 vop_get_status(struct virtio_device *vdev) +{ + return ioread8(&to_vopvdev(vdev)->desc->status); +} + +static void vop_set_status(struct virtio_device *dev, u8 status) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + + if (!status) + return; + iowrite8(status, &vdev->desc->status); + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); +} + +/* Inform host on a virtio device reset and wait for ack from host */ +static void vop_reset_inform_host(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct mic_device_ctrl __iomem *dc = vdev->dc; + struct vop_device *vpdev = vdev->vpdev; + int retry; + + iowrite8(0, &dc->host_ack); + iowrite8(1, &dc->vdev_reset); + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + + /* Wait till host completes all card accesses and acks the reset */ + for (retry = 100; retry--;) { + if (ioread8(&dc->host_ack)) + break; + msleep(100); + }; + + dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); + + /* Reset status to 0 in case we timed out */ + iowrite8(0, &vdev->desc->status); +} + +static void vop_reset(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + + dev_dbg(_vop_dev(vdev), "%s: virtio id %d\n", + __func__, dev->id.device); + + vop_reset_inform_host(dev); + complete_all(&vdev->reset_done); +} + +/* + * The virtio_ring code calls this API when it wants to notify the Host. + */ +static bool vop_notify(struct virtqueue *vq) +{ + struct _vop_vdev *vdev = vq->priv; + struct vop_device *vpdev = vdev->vpdev; + + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + return true; +} + +static void vop_del_vq(struct virtqueue *vq, int n) +{ + struct _vop_vdev *vdev = to_vopvdev(vq->vdev); + struct vring *vr = (struct vring *)(vq + 1); + struct vop_device *vpdev = vdev->vpdev; + + dma_unmap_single(&vpdev->dev, vdev->used[n], + vdev->used_size[n], DMA_BIDIRECTIONAL); + free_pages((unsigned long)vr->used, get_order(vdev->used_size[n])); + vring_del_virtqueue(vq); + vpdev->hw_ops->iounmap(vpdev, vdev->vr[n]); + vdev->vr[n] = NULL; +} + +static void vop_del_vqs(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct virtqueue *vq, *n; + int idx = 0; + + dev_dbg(_vop_dev(vdev), "%s\n", __func__); + + list_for_each_entry_safe(vq, n, &dev->vqs, list) + vop_del_vq(vq, idx++); +} + +/* + * This routine will assign vring's allocated in host/io memory. Code in + * virtio_ring.c however continues to access this io memory as if it were local + * memory without io accessors. + */ +static struct virtqueue *vop_find_vq(struct virtio_device *dev, + unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + struct mic_vqconfig __iomem *vqconfig; + struct mic_vqconfig config; + struct virtqueue *vq; + void __iomem *va; + struct _mic_vring_info __iomem *info; + void *used; + int vr_size, _vr_size, err, magic; + struct vring *vr; + u8 type = ioread8(&vdev->desc->type); + + if (index >= ioread8(&vdev->desc->num_vq)) + return ERR_PTR(-ENOENT); + + if (!name) + return ERR_PTR(-ENOENT); + + /* First assign the vring's allocated in host memory */ + vqconfig = _vop_vq_config(vdev->desc) + index; + memcpy_fromio(&config, vqconfig, sizeof(config)); + _vr_size = vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN); + vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); + va = vpdev->hw_ops->ioremap(vpdev, le64_to_cpu(config.address), + vr_size); + if (!va) + return ERR_PTR(-ENOMEM); + vdev->vr[index] = va; + memset_io(va, 0x0, _vr_size); + vq = vring_new_virtqueue( + index, + le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN, + dev, + false, + (void __force *)va, vop_notify, callback, name); + if (!vq) { + err = -ENOMEM; + goto unmap; + } + info = va + _vr_size; + magic = ioread32(&info->magic); + + if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { + err = -EIO; + goto unmap; + } + + /* Allocate and reassign used ring now */ + vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + + sizeof(struct vring_used_elem) * + le16_to_cpu(config.num)); + used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vdev->used_size[index])); + if (!used) { + err = -ENOMEM; + dev_err(_vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto del_vq; + } + vdev->used[index] = dma_map_single(&vpdev->dev, used, + vdev->used_size[index], + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&vpdev->dev, vdev->used[index])) { + err = -ENOMEM; + dev_err(_vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto free_used; + } + writeq(vdev->used[index], &vqconfig->used_address); + /* + * To reassign the used ring here we are directly accessing + * struct vring_virtqueue which is a private data structure + * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in + * vring_new_virtqueue() would ensure that + * (&vq->vring == (struct vring *) (&vq->vq + 1)); + */ + vr = (struct vring *)(vq + 1); + vr->used = used; + + vq->priv = vdev; + return vq; +free_used: + free_pages((unsigned long)used, + get_order(vdev->used_size[index])); +del_vq: + vring_del_virtqueue(vq); +unmap: + vpdev->hw_ops->iounmap(vpdev, vdev->vr[index]); + return ERR_PTR(err); +} + +static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char * const names[]) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + struct mic_device_ctrl __iomem *dc = vdev->dc; + int i, err, retry; + + /* We must have this many virtqueues. */ + if (nvqs > ioread8(&vdev->desc->num_vq)) + return -ENOENT; + + for (i = 0; i < nvqs; ++i) { + dev_dbg(_vop_dev(vdev), "%s: %d: %s\n", + __func__, i, names[i]); + vqs[i] = vop_find_vq(dev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + err = PTR_ERR(vqs[i]); + goto error; + } + } + + iowrite8(1, &dc->used_address_updated); + /* + * Send an interrupt to the host to inform it that used + * rings have been re-assigned. + */ + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + for (retry = 100; --retry;) { + if (!ioread8(&dc->used_address_updated)) + break; + msleep(100); + }; + + dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); + if (!retry) { + err = -ENODEV; + goto error; + } + + return 0; +error: + vop_del_vqs(dev); + return err; +} + +/* + * The config ops structure as defined by virtio config + */ +static struct virtio_config_ops vop_vq_config_ops = { + .get_features = vop_get_features, + .finalize_features = vop_finalize_features, + .get = vop_get, + .set = vop_set, + .get_status = vop_get_status, + .set_status = vop_set_status, + .reset = vop_reset, + .find_vqs = vop_find_vqs, + .del_vqs = vop_del_vqs, +}; + +static irqreturn_t vop_virtio_intr_handler(int irq, void *data) +{ + struct _vop_vdev *vdev = data; + struct vop_device *vpdev = vdev->vpdev; + struct virtqueue *vq; + + vpdev->hw_ops->ack_interrupt(vpdev, vdev->h2c_vdev_db); + list_for_each_entry(vq, &vdev->vdev.vqs, list) + vring_interrupt(0, vq); + + return IRQ_HANDLED; +} + +static void vop_virtio_release_dev(struct device *_d) +{ + /* + * No need for a release method similar to virtio PCI. + * Provide an empty one to avoid getting a warning from core. + */ +} + +/* + * adds a new device and register it with virtio + * appropriate drivers are loaded by the device model + */ +static int _vop_add_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct vop_device *vpdev, + int dnode) +{ + struct _vop_vdev *vdev; + int ret; + u8 type = ioread8(&d->type); + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->vpdev = vpdev; + vdev->vdev.dev.parent = &vpdev->dev; + vdev->vdev.dev.release = vop_virtio_release_dev; + vdev->vdev.id.device = type; + vdev->vdev.config = &vop_vq_config_ops; + vdev->desc = d; + vdev->dc = (void __iomem *)d + _vop_aligned_desc_size(d); + vdev->dnode = dnode; + vdev->vdev.priv = (void *)(u64)dnode; + init_completion(&vdev->reset_done); + + vdev->h2c_vdev_db = vpdev->hw_ops->next_db(vpdev); + vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, + vop_virtio_intr_handler, "virtio intr", + vdev, vdev->h2c_vdev_db); + if (IS_ERR(vdev->virtio_cookie)) { + ret = PTR_ERR(vdev->virtio_cookie); + goto kfree; + } + iowrite8((u8)vdev->h2c_vdev_db, &vdev->dc->h2c_vdev_db); + vdev->c2h_vdev_db = ioread8(&vdev->dc->c2h_vdev_db); + + ret = register_virtio_device(&vdev->vdev); + if (ret) { + dev_err(_vop_dev(vdev), + "Failed to register vop device %u type %u\n", + offset, type); + goto free_irq; + } + writeq((u64)vdev, &vdev->dc->vdev); + dev_dbg(_vop_dev(vdev), "%s: registered vop device %u type %u vdev %p\n", + __func__, offset, type, vdev); + + return 0; + +free_irq: + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); +kfree: + kfree(vdev); + return ret; +} + +/* + * match for a vop device with a specific desc pointer + */ +static int vop_match_desc(struct device *dev, void *data) +{ + struct virtio_device *_dev = dev_to_virtio(dev); + struct _vop_vdev *vdev = to_vopvdev(_dev); + + return vdev->desc == (void __iomem *)data; +} + +static void _vop_handle_config_change(struct mic_device_desc __iomem *d, + unsigned int offset, + struct vop_device *vpdev) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + _vop_aligned_desc_size(d); + struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); + + if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) + return; + + dev_dbg(&vpdev->dev, "%s %d\n", __func__, __LINE__); + virtio_config_changed(&vdev->vdev); + iowrite8(1, &dc->guest_ack); +} + +/* + * removes a virtio device if a hot remove event has been + * requested by the host. + */ +static int _vop_remove_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct vop_device *vpdev) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + _vop_aligned_desc_size(d); + struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); + u8 status; + int ret = -1; + + if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { + dev_dbg(&vpdev->dev, + "%s %d config_change %d type %d vdev %p\n", + __func__, __LINE__, + ioread8(&dc->config_change), ioread8(&d->type), vdev); + status = ioread8(&d->status); + reinit_completion(&vdev->reset_done); + unregister_virtio_device(&vdev->vdev); + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); + iowrite8(-1, &dc->h2c_vdev_db); + if (status & VIRTIO_CONFIG_S_DRIVER_OK) + wait_for_completion(&vdev->reset_done); + kfree(vdev); + iowrite8(1, &dc->guest_ack); + dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n", + __func__, __LINE__, ioread8(&dc->guest_ack)); + iowrite8(-1, &d->type); + ret = 0; + } + return ret; +} + +#define REMOVE_DEVICES true + +static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, + bool remove, int dnode) +{ + s8 type; + unsigned int i; + struct mic_device_desc __iomem *d; + struct mic_device_ctrl __iomem *dc; + struct device *dev; + int ret; + + for (i = sizeof(struct mic_bootparam); + i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { + d = dp + i; + dc = (void __iomem *)d + _vop_aligned_desc_size(d); + /* + * This read barrier is paired with the corresponding write + * barrier on the host which is inserted before adding or + * removing a virtio device descriptor, by updating the type. + */ + rmb(); + type = ioread8(&d->type); + + /* end of list */ + if (type == 0) + break; + + if (type == -1) + continue; + + /* device already exists */ + dev = device_find_child(&vpdev->dev, (void __force *)d, + vop_match_desc); + if (dev) { + if (remove) + iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, + &dc->config_change); + put_device(dev); + _vop_handle_config_change(d, i, vpdev); + ret = _vop_remove_device(d, i, vpdev); + if (remove) { + iowrite8(0, &dc->config_change); + iowrite8(0, &dc->guest_ack); + } + continue; + } + + /* new device */ + dev_dbg(&vpdev->dev, "%s %d Adding new virtio device %p\n", + __func__, __LINE__, d); + if (!remove) + _vop_add_device(d, i, vpdev, dnode); + } +} + +static void vop_scan_devices(struct vop_info *vi, + struct vop_device *vpdev, bool remove) +{ + void __iomem *dp = vpdev->hw_ops->get_remote_dp(vpdev); + + if (!dp) + return; + mutex_lock(&vi->vop_mutex); + _vop_scan_devices(dp, vpdev, remove, vpdev->dnode); + mutex_unlock(&vi->vop_mutex); +} + +/* + * vop_hotplug_device tries to find changes in the device page. + */ +static void vop_hotplug_devices(struct work_struct *work) +{ + struct vop_info *vi = container_of(work, struct vop_info, + hotplug_work); + + vop_scan_devices(vi, vi->vpdev, !REMOVE_DEVICES); +} + +/* + * Interrupt handler for hot plug/config changes etc. + */ +static irqreturn_t vop_extint_handler(int irq, void *data) +{ + struct vop_info *vi = data; + struct mic_bootparam __iomem *bp; + struct vop_device *vpdev = vi->vpdev; + + bp = vpdev->hw_ops->get_remote_dp(vpdev); + dev_dbg(&vpdev->dev, "%s %d hotplug work\n", + __func__, __LINE__); + vpdev->hw_ops->ack_interrupt(vpdev, ioread8(&bp->h2c_config_db)); + schedule_work(&vi->hotplug_work); + return IRQ_HANDLED; +} + +static int vop_driver_probe(struct vop_device *vpdev) +{ + struct vop_info *vi; + int rc; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) { + rc = -ENOMEM; + goto exit; + } + dev_set_drvdata(&vpdev->dev, vi); + vi->vpdev = vpdev; + + mutex_init(&vi->vop_mutex); + INIT_WORK(&vi->hotplug_work, vop_hotplug_devices); + if (vpdev->dnode) { + rc = vop_host_init(vi); + if (rc < 0) + goto free; + } else { + struct mic_bootparam __iomem *bootparam; + + vop_scan_devices(vi, vpdev, !REMOVE_DEVICES); + + vi->h2c_config_db = vpdev->hw_ops->next_db(vpdev); + vi->cookie = vpdev->hw_ops->request_irq(vpdev, + vop_extint_handler, + "virtio_config_intr", + vi, vi->h2c_config_db); + if (IS_ERR(vi->cookie)) { + rc = PTR_ERR(vi->cookie); + goto free; + } + bootparam = vpdev->hw_ops->get_remote_dp(vpdev); + iowrite8(vi->h2c_config_db, &bootparam->h2c_config_db); + } + vop_init_debugfs(vi); + return 0; +free: + kfree(vi); +exit: + return rc; +} + +static void vop_driver_remove(struct vop_device *vpdev) +{ + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + + if (vpdev->dnode) { + vop_host_uninit(vi); + } else { + struct mic_bootparam __iomem *bootparam = + vpdev->hw_ops->get_remote_dp(vpdev); + if (bootparam) + iowrite8(-1, &bootparam->h2c_config_db); + vpdev->hw_ops->free_irq(vpdev, vi->cookie, vi); + flush_work(&vi->hotplug_work); + vop_scan_devices(vi, vpdev, REMOVE_DEVICES); + } + vop_exit_debugfs(vi); + kfree(vi); +} + +static struct vop_device_id id_table[] = { + { VOP_DEV_TRNSP, VOP_DEV_ANY_ID }, + { 0 }, +}; + +static struct vop_driver vop_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = vop_driver_probe, + .remove = vop_driver_remove, +}; + +module_vop_driver(vop_driver); + +MODULE_DEVICE_TABLE(mbus, id_table); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Virtio Over PCIe (VOP) driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8810df37762746657cfe84014a8f30758e8f366a Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:17 -0800 Subject: misc: mic: Enable VOP debugfs and driver build This patch moves the virtio specific debugfs hooks previously in mic_debugfs.c in the MIC host driver into the VOP driver. The Kconfig/Makefile is also updated to allow building the VOP driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 20 ++++ drivers/misc/mic/Makefile | 1 + drivers/misc/mic/vop/Makefile | 9 ++ drivers/misc/mic/vop/vop_debugfs.c | 232 +++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/misc/mic/vop/Makefile create mode 100644 drivers/misc/mic/vop/vop_debugfs.c diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 840f7ef1a141..b03bb17cae77 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -124,3 +124,23 @@ config MIC_COSM More information about the Intel MIC family as well as the Linux OS and tools for MIC to use with this driver are available from . + +comment "VOP Driver" + +config VOP + tristate "VOP Driver" + depends on 64BIT && PCI && X86 && VOP_BUS + select VHOST_RING + help + This enables VOP (Virtio over PCIe) Driver support for the Intel + Many Integrated Core (MIC) family of PCIe form factor coprocessor + devices. The VOP driver allows virtio drivers, e.g. net, console + and block drivers, on the card connect to user space virtio + devices on the host. + + If you are building a host kernel with an Intel MIC device then + say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index e288a1106738..f2b1323ff96c 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -8,3 +8,4 @@ obj-y += bus/ obj-$(CONFIG_SCIF) += scif/ obj-$(CONFIG_MIC_COSM) += cosm/ obj-$(CONFIG_MIC_COSM) += cosm_client/ +obj-$(CONFIG_VOP) += vop/ diff --git a/drivers/misc/mic/vop/Makefile b/drivers/misc/mic/vop/Makefile new file mode 100644 index 000000000000..78819c8999f1 --- /dev/null +++ b/drivers/misc/mic/vop/Makefile @@ -0,0 +1,9 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2016, Intel Corporation. +# +obj-m := vop.o + +vop-objs += vop_main.o +vop-objs += vop_debugfs.o +vop-objs += vop_vringh.o diff --git a/drivers/misc/mic/vop/vop_debugfs.c b/drivers/misc/mic/vop/vop_debugfs.c new file mode 100644 index 000000000000..ab43884e5cd7 --- /dev/null +++ b/drivers/misc/mic/vop/vop_debugfs.c @@ -0,0 +1,232 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * 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. + * + * 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. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include + +#include "vop_main.h" + +static int vop_dp_show(struct seq_file *s, void *pos) +{ + struct mic_device_desc *d; + struct mic_device_ctrl *dc; + struct mic_vqconfig *vqconfig; + __u32 *features; + __u8 *config; + struct vop_info *vi = s->private; + struct vop_device *vpdev = vi->vpdev; + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + int j, k; + + seq_printf(s, "Bootparam: magic 0x%x\n", + bootparam->magic); + seq_printf(s, "Bootparam: h2c_config_db %d\n", + bootparam->h2c_config_db); + seq_printf(s, "Bootparam: node_id %d\n", + bootparam->node_id); + seq_printf(s, "Bootparam: c2h_scif_db %d\n", + bootparam->c2h_scif_db); + seq_printf(s, "Bootparam: h2c_scif_db %d\n", + bootparam->h2c_scif_db); + seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", + bootparam->scif_host_dma_addr); + seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", + bootparam->scif_card_dma_addr); + + for (j = sizeof(*bootparam); + j < MIC_DP_SIZE; j += mic_total_desc_size(d)) { + d = (void *)bootparam + j; + dc = (void *)d + mic_aligned_desc_size(d); + + /* end of list */ + if (d->type == 0) + break; + + if (d->type == -1) + continue; + + seq_printf(s, "Type %d ", d->type); + seq_printf(s, "Num VQ %d ", d->num_vq); + seq_printf(s, "Feature Len %d\n", d->feature_len); + seq_printf(s, "Config Len %d ", d->config_len); + seq_printf(s, "Shutdown Status %d\n", d->status); + + for (k = 0; k < d->num_vq; k++) { + vqconfig = mic_vq_config(d) + k; + seq_printf(s, "vqconfig[%d]: ", k); + seq_printf(s, "address 0x%llx ", + vqconfig->address); + seq_printf(s, "num %d ", vqconfig->num); + seq_printf(s, "used address 0x%llx\n", + vqconfig->used_address); + } + + features = (__u32 *)mic_vq_features(d); + seq_printf(s, "Features: Host 0x%x ", features[0]); + seq_printf(s, "Guest 0x%x\n", features[1]); + + config = mic_vq_configspace(d); + for (k = 0; k < d->config_len; k++) + seq_printf(s, "config[%d]=%d\n", k, config[k]); + + seq_puts(s, "Device control:\n"); + seq_printf(s, "Config Change %d ", dc->config_change); + seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); + seq_printf(s, "Guest Ack %d ", dc->guest_ack); + seq_printf(s, "Host ack %d\n", dc->host_ack); + seq_printf(s, "Used address updated %d ", + dc->used_address_updated); + seq_printf(s, "Vdev 0x%llx\n", dc->vdev); + seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); + seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); + } + schedule_work(&vi->hotplug_work); + return 0; +} + +static int vop_dp_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vop_dp_show, inode->i_private); +} + +static int vop_dp_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations dp_ops = { + .owner = THIS_MODULE, + .open = vop_dp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = vop_dp_debug_release +}; + +static int vop_vdev_info_show(struct seq_file *s, void *unused) +{ + struct vop_info *vi = s->private; + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + int i, j; + + mutex_lock(&vi->vop_mutex); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + seq_printf(s, "VDEV type %d state %s in %ld out %ld in_dma %ld out_dma %ld\n", + vdev->virtio_id, + vop_vdevup(vdev) ? "UP" : "DOWN", + vdev->in_bytes, + vdev->out_bytes, + vdev->in_bytes_dma, + vdev->out_bytes_dma); + for (i = 0; i < MIC_MAX_VRINGS; i++) { + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; + struct vop_vringh *vvr = &vdev->vvr[i]; + struct vringh *vrh = &vvr->vrh; + int num = vrh->vring.num; + + if (!num) + continue; + desc = vrh->vring.desc; + seq_printf(s, "vring i %d avail_idx %d", + i, vvr->vring.info->avail_idx & (num - 1)); + seq_printf(s, " vring i %d avail_idx %d\n", + i, vvr->vring.info->avail_idx); + seq_printf(s, "vrh i %d weak_barriers %d", + i, vrh->weak_barriers); + seq_printf(s, " last_avail_idx %d last_used_idx %d", + vrh->last_avail_idx, vrh->last_used_idx); + seq_printf(s, " completed %d\n", vrh->completed); + for (j = 0; j < num; j++) { + seq_printf(s, "desc[%d] addr 0x%llx len %d", + j, desc->addr, desc->len); + seq_printf(s, " flags 0x%x next %d\n", + desc->flags, desc->next); + desc++; + } + avail = vrh->vring.avail; + seq_printf(s, "avail flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, avail->flags), + vringh16_to_cpu(vrh, + avail->idx) & (num - 1)); + seq_printf(s, "avail flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, avail->flags), + vringh16_to_cpu(vrh, avail->idx)); + for (j = 0; j < num; j++) + seq_printf(s, "avail ring[%d] %d\n", + j, avail->ring[j]); + used = vrh->vring.used; + seq_printf(s, "used flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, used->flags), + vringh16_to_cpu(vrh, used->idx) & (num - 1)); + seq_printf(s, "used flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, used->flags), + vringh16_to_cpu(vrh, used->idx)); + for (j = 0; j < num; j++) + seq_printf(s, "used ring[%d] id %d len %d\n", + j, vringh32_to_cpu(vrh, + used->ring[j].id), + vringh32_to_cpu(vrh, + used->ring[j].len)); + } + } + mutex_unlock(&vi->vop_mutex); + + return 0; +} + +static int vop_vdev_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vop_vdev_info_show, inode->i_private); +} + +static int vop_vdev_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations vdev_info_ops = { + .owner = THIS_MODULE, + .open = vop_vdev_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = vop_vdev_info_debug_release +}; + +void vop_init_debugfs(struct vop_info *vi) +{ + char name[16]; + + snprintf(name, sizeof(name), "%s%d", KBUILD_MODNAME, vi->vpdev->dnode); + vi->dbg = debugfs_create_dir(name, NULL); + if (!vi->dbg) { + pr_err("can't create debugfs dir vop\n"); + return; + } + debugfs_create_file("dp", 0444, vi->dbg, vi, &dp_ops); + debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vdev_info_ops); +} + +void vop_exit_debugfs(struct vop_info *vi) +{ + debugfs_remove_recursive(vi->dbg); +} -- cgit v1.2.3 From c74c9318a3a76c3714785bc06147dd207a9d0aa3 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:18 -0800 Subject: misc: mic: MIC host and card driver changes to enable VOP This patch modifies the MIC host and card drivers to start using the VOP driver. The MIC host and card drivers now implement the VOP bus operations and register a VOP device on the VOP bus. MIC driver stack documentation is also updated to include the new VOP driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- Documentation/mic/mic_overview.txt | 54 ++++++++-------- Documentation/mic/mpssd/mpss | 2 +- Documentation/mic/mpssd/mpssd.c | 2 +- drivers/misc/mic/Kconfig | 7 ++- drivers/misc/mic/card/mic_device.c | 84 ++++++++++++++++++++++++- drivers/misc/mic/card/mic_device.h | 3 + drivers/misc/mic/card/mic_x100.c | 1 + drivers/misc/mic/host/mic_boot.c | 123 ++++++++++++++++++++++++++++++++++++- drivers/misc/mic/host/mic_device.h | 3 + drivers/misc/mic/host/mic_main.c | 1 + 10 files changed, 247 insertions(+), 33 deletions(-) diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index 73f44fc3e715..074adbdf83a4 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt @@ -12,10 +12,19 @@ for the X100 devices. Since it is a PCIe card, it does not have the ability to host hardware devices for networking, storage and console. We provide these devices -on X100 coprocessors thus enabling a self-bootable equivalent environment -for applications. A key benefit of our solution is that it leverages -the standard virtio framework for network, disk and console devices, -though in our case the virtio framework is used across a PCIe bus. +on X100 coprocessors thus enabling a self-bootable equivalent +environment for applications. A key benefit of our solution is that it +leverages the standard virtio framework for network, disk and console +devices, though in our case the virtio framework is used across a PCIe +bus. A Virtio Over PCIe (VOP) driver allows creating user space +backends or devices on the host which are used to probe virtio drivers +for these devices on the MIC card. The existing VRINGH infrastructure +in the kernel is used to access virtio rings from the host. The card +VOP driver allows card virtio drivers to communicate with their user +space backends on the host via a device page. Ring 3 apps on the host +can add, remove and configure virtio devices. A thin MIC specific +virtio_config_ops is implemented which is borrowed heavily from +previous similar implementations in lguest and s390. MIC PCIe card has a dma controller with 8 channels. These channels are shared between the host s/w and the card s/w. 0 to 3 are used by host @@ -38,7 +47,6 @@ single threaded performance for the host compared to MIC, the ability of the host to initiate DMA's to/from the card using the MIC DMA engine and the fact that the virtio block storage backend can only be on the host. - | +----------+ | +----------+ | Card OS | | | Host OS | +----------+ | +----------+ @@ -47,27 +55,25 @@ the fact that the virtio block storage backend can only be on the host. | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | | Net | |Console | |Block | | |Net | |Console | |Block | | Driver| |Driver | |Driver| | |backend | |backend | |backend | - +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ + +---+---+ +---+----+ +--+---+ | +---------+ +----+---+ +--------+ | | | | | | | | | | |User | | | - | | | |------|------------|---------|------- - +-------------------+ |Kernel +--------------------------+ - | | | Virtio over PCIe IOCTLs | - | | +--------------------------+ -+-----------+ | | | +-----------+ -| MIC DMA | | +------+ | +------+ +------+ | | MIC DMA | -| Driver | | | SCIF | | | SCIF | | COSM | | | Driver | -+-----------+ | +------+ | +------+ +--+---+ | +-----------+ - | | | | | | | | -+---------------+ | +------+ | +--+---+ +--+---+ | +----------------+ -|MIC virtual Bus| | |SCIF | | |SCIF | | COSM | | |MIC virtual Bus | -+---------------+ | |HW Bus| | |HW Bus| | Bus | | +----------------+ - | | +------+ | +--+---+ +------+ | | - | | | | | | | | - | +-----------+---+ | | | +---------------+ | - | |Intel MIC | | | | |Intel MIC | | - +---|Card Driver | | | | |Host Driver | | - +------------+--------+ | +----+---------------+-----+ + | | | |------|------------|--+------|------- + +---------+---------+ |Kernel | + | | | + +---------+ +---+----+ +------+ | +------+ +------+ +--+---+ +-------+ + |MIC DMA | | VOP | | SCIF | | | SCIF | | COSM | | VOP | |MIC DMA| + +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ + | | | | | | | + +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ + |MIC | | VOP | |SCIF | | |SCIF | | COSM | | VOP | | MIC | + |HW Bus | | HW Bus| |HW Bus| | |HW Bus| | Bus | |HW Bus| |HW Bus | + +---------+ +--------+ +--+---+ | +--+---+ +------+ +------+ +-------+ + | | | | | | | + | +-----------+--+ | | | +---------------+ | + | |Intel MIC | | | | |Intel MIC | | + | |Card Driver | | | | |Host Driver | | + +---+--------------+------+ | +----+---------------+-----+ | | | +-------------------------------------------------------------+ | | diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss index 09ea90931649..5fcf9fa4b082 100755 --- a/Documentation/mic/mpssd/mpss +++ b/Documentation/mic/mpssd/mpss @@ -35,7 +35,7 @@ exec=/usr/sbin/mpssd sysfs="/sys/class/mic" -mic_modules="mic_host mic_x100_dma scif" +mic_modules="mic_host mic_x100_dma scif vop" start() { diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index aaeafa18d99b..518dece71578 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -926,7 +926,7 @@ add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd) char path[PATH_MAX]; int fd, err; - snprintf(path, PATH_MAX, "/dev/mic%d", mic->id); + snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id); fd = open(path, O_RDWR); if (fd < 0) { mpsslog("Could not open %s %s\n", path, strerror(errno)); diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index b03bb17cae77..2e4f3ba75c8e 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -53,8 +53,8 @@ comment "Intel MIC Host Driver" config INTEL_MIC_HOST tristate "Intel MIC Host Driver" - depends on 64BIT && PCI && X86 && INTEL_MIC_BUS && SCIF_BUS && MIC_COSM - select VHOST_RING + depends on 64BIT && PCI && X86 + depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS help This enables Host Driver support for the Intel Many Integrated Core (MIC) family of PCIe form factor coprocessor devices that @@ -73,7 +73,8 @@ comment "Intel MIC Card Driver" config INTEL_MIC_CARD tristate "Intel MIC Card Driver" - depends on 64BIT && X86 && INTEL_MIC_BUS && SCIF_BUS && MIC_COSM + depends on 64BIT && X86 + depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS select VIRTIO help This enables card driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index ff03c633541c..e749af48f736 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -249,12 +249,82 @@ static struct scif_hw_ops scif_hw_ops = { .iounmap = ___mic_iounmap, }; +static inline struct mic_driver *vpdev_to_mdrv(struct vop_device *vpdev) +{ + return dev_get_drvdata(vpdev->dev.parent); +} + +static struct mic_irq * +__mic_request_irq(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + return mic_request_card_irq(func, NULL, name, data, intr_src); +} + +static void __mic_free_irq(struct vop_device *vpdev, + struct mic_irq *cookie, void *data) +{ + return mic_free_card_irq(cookie, data); +} + +static void __mic_ack_interrupt(struct vop_device *vpdev, int num) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_ack_interrupt(&mdrv->mdev); +} + +static int __mic_next_db(struct vop_device *vpdev) +{ + return mic_next_card_db(); +} + +static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + return mdrv->dp; +} + +static void __mic_send_intr(struct vop_device *vpdev, int db) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_send_intr(&mdrv->mdev, db); +} + +static void __iomem *__mic_ioremap(struct vop_device *vpdev, + dma_addr_t pa, size_t len) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + return mic_card_map(&mdrv->mdev, pa, len); +} + +static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_card_unmap(&mdrv->mdev, va); +} + +static struct vop_hw_ops vop_hw_ops = { + .request_irq = __mic_request_irq, + .free_irq = __mic_free_irq, + .ack_interrupt = __mic_ack_interrupt, + .next_db = __mic_next_db, + .get_remote_dp = __mic_get_remote_dp, + .send_intr = __mic_send_intr, + .ioremap = __mic_ioremap, + .iounmap = __mic_iounmap, +}; + static int mic_request_dma_chans(struct mic_driver *mdrv) { dma_cap_mask_t mask; struct dma_chan *chan; - request_module("mic_x100_dma"); dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); @@ -308,6 +378,13 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = -ENODEV; goto irq_uninit; } + mdrv->vpdev = vop_register_device(mdrv->dev, VOP_DEV_TRNSP, + NULL, &vop_hw_ops, 0, + NULL, mdrv->dma_ch[0]); + if (IS_ERR(mdrv->vpdev)) { + rc = PTR_ERR(mdrv->vpdev); + goto dma_free; + } bootparam = mdrv->dp; node_id = ioread8(&bootparam->node_id); mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, @@ -317,11 +394,13 @@ int __init mic_driver_init(struct mic_driver *mdrv) mdrv->num_dma_ch, true); if (IS_ERR(mdrv->scdev)) { rc = PTR_ERR(mdrv->scdev); - goto dma_free; + goto vop_remove; } mic_create_card_debug_dir(mdrv); done: return rc; +vop_remove: + vop_unregister_device(mdrv->vpdev); dma_free: mic_free_dma_chans(mdrv); irq_uninit: @@ -342,6 +421,7 @@ void mic_driver_uninit(struct mic_driver *mdrv) { mic_delete_card_debug_dir(mdrv); scif_unregister_device(mdrv->scdev); + vop_unregister_device(mdrv->vpdev); mic_free_dma_chans(mdrv); mic_uninit_irq(); mic_dp_uninit(); diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 1dbf83c41289..333dbed972f6 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h @@ -32,6 +32,7 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" /** * struct mic_intr_info - Contains h/w specific interrupt sources info @@ -76,6 +77,7 @@ struct mic_device { * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available * @scdev: SCIF device on the SCIF virtual bus. + * @vpdev: Virtio over PCIe device on the VOP virtual bus. */ struct mic_driver { char name[20]; @@ -90,6 +92,7 @@ struct mic_driver { struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; struct scif_hw_dev *scdev; + struct vop_device *vpdev; }; /** diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index b2958ce2368c..b9f0710ffa6b 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -326,6 +326,7 @@ static int __init mic_init(void) goto done; } + request_module("mic_x100_dma"); mic_init_card_debugfs(); ret = platform_device_register(&mic_platform_dev); if (ret) { diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 3df305f6282c..8c91c9950b54 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -25,10 +25,118 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" +static inline struct mic_device *vpdev_to_mdev(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static dma_addr_t +_mic_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) +{ + void *va = phys_to_virt(page_to_phys(page)) + offset; + struct mic_device *mdev = vpdev_to_mdev(dev); + + return mic_map_single(mdev, va, size); +} + +static void _mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct mic_device *mdev = vpdev_to_mdev(dev); + + mic_unmap_single(mdev, dma_addr, size); +} + +static const struct dma_map_ops _mic_dma_ops = { + .map_page = _mic_dma_map_page, + .unmap_page = _mic_dma_unmap_page, +}; + +static struct mic_irq * +__mic_request_irq(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_request_threaded_irq(mdev, func, NULL, name, data, + intr_src, MIC_INTR_DB); +} + +static void __mic_free_irq(struct vop_device *vpdev, + struct mic_irq *cookie, void *data) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_free_irq(mdev, cookie, data); +} + +static void __mic_ack_interrupt(struct vop_device *vpdev, int num) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + mdev->ops->intr_workarounds(mdev); +} + +static int __mic_next_db(struct vop_device *vpdev) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_next_db(mdev); +} + +static void *__mic_get_dp(struct vop_device *vpdev) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mdev->dp; +} + +static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) +{ + return NULL; +} + +static void __mic_send_intr(struct vop_device *vpdev, int db) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + mdev->ops->send_intr(mdev, db); +} + +static void __iomem *__mic_ioremap(struct vop_device *vpdev, + dma_addr_t pa, size_t len) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mdev->aper.va + pa; +} + +static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) +{ + /* nothing to do */ +} + +static struct vop_hw_ops vop_hw_ops = { + .request_irq = __mic_request_irq, + .free_irq = __mic_free_irq, + .ack_interrupt = __mic_ack_interrupt, + .next_db = __mic_next_db, + .get_dp = __mic_get_dp, + .get_remote_dp = __mic_get_remote_dp, + .send_intr = __mic_send_intr, + .ioremap = __mic_ioremap, + .iounmap = __mic_iounmap, +}; + static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) { return dev_get_drvdata(scdev->dev.parent); @@ -314,7 +422,6 @@ static int mic_request_dma_chans(struct mic_device *mdev) dma_cap_mask_t mask; struct dma_chan *chan; - request_module("mic_x100_dma"); dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); @@ -386,9 +493,18 @@ static int _mic_start(struct cosm_device *cdev, int id) goto dma_free; } + mdev->vpdev = vop_register_device(&mdev->pdev->dev, + VOP_DEV_TRNSP, &_mic_dma_ops, + &vop_hw_ops, id + 1, &mdev->aper, + mdev->dma_ch[0]); + if (IS_ERR(mdev->vpdev)) { + rc = PTR_ERR(mdev->vpdev); + goto scif_remove; + } + rc = mdev->ops->load_mic_fw(mdev, NULL); if (rc) - goto scif_remove; + goto vop_remove; mic_smpt_restore(mdev); mic_intr_restore(mdev); mdev->intr_ops->enable_interrupts(mdev); @@ -396,6 +512,8 @@ static int _mic_start(struct cosm_device *cdev, int id) mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); mdev->ops->send_firmware_intr(mdev); goto unlock_ret; +vop_remove: + vop_unregister_device(mdev->vpdev); scif_remove: scif_unregister_device(mdev->scdev); dma_free: @@ -422,6 +540,7 @@ static void _mic_stop(struct cosm_device *cdev, bool force) * will be the first to be registered and the last to be * unregistered. */ + vop_unregister_device(mdev->vpdev); scif_unregister_device(mdev->scdev); mic_free_dma_chans(mdev); mbus_unregister_device(mdev->dma_mbdev); diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 8460de122929..52b12b22f4ae 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -29,6 +29,7 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" #include "../bus/cosm_bus.h" #include "mic_intr.h" @@ -68,6 +69,7 @@ extern struct cosm_hw_ops cosm_hw_ops; * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available * @scdev: SCIF device on the SCIF virtual bus. + * @vpdev: Virtio over PCIe device on the VOP virtual bus. * @cosm_dev: COSM device */ struct mic_device { @@ -92,6 +94,7 @@ struct mic_device { struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; struct scif_hw_dev *scdev; + struct vop_device *vpdev; struct cosm_device *cosm_dev; }; diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 400def2106e7..035be3e9ceba 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -317,6 +317,7 @@ static int __init mic_init(void) { int ret; + request_module("mic_x100_dma"); mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); -- cgit v1.2.3 From 990162f038400bd229685316beea1155be095125 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 16:54:25 +0100 Subject: char: nwbutton: avoid unused variable warning When CONFIG_NWBUTTON_REBOOT is disabled, we get a warning about an unused variable: drivers/char/nwbutton.c:37:12: warning: 'reboot_count' defined but not used [-Wunused-variable] static int reboot_count = NUM_PRESSES_REBOOT; /* Number of presses to reboot */ Using if(IS_ENABLED()) instead of #ifdef around the user makes the code nicer to read and avoids the warning. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/nwbutton.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c index 76c490fa0511..0e184426db98 100644 --- a/drivers/char/nwbutton.c +++ b/drivers/char/nwbutton.c @@ -129,10 +129,9 @@ static void button_consume_callbacks (int bpcount) static void button_sequence_finished (unsigned long parameters) { -#ifdef CONFIG_NWBUTTON_REBOOT /* Reboot using button is enabled */ - if (button_press_count == reboot_count) + if (IS_ENABLED(CONFIG_NWBUTTON_REBOOT) && + button_press_count == reboot_count) kill_cad_pid(SIGINT, 1); /* Ask init to reboot us */ -#endif /* CONFIG_NWBUTTON_REBOOT */ button_consume_callbacks (button_press_count); bcount = sprintf (button_output_buffer, "%d\n", button_press_count); button_press_count = 0; /* Reset the button press counter */ -- cgit v1.2.3 From b82fcabe212a11698fd4b3e604d2f81d929d22f6 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 28 Jan 2016 16:14:18 +0800 Subject: phy: core: fix wrong err handle for phy_power_on If phy_pm_runtime_get_sync failed but we already enable regulator, current code return directly without doing regulator_disable. This patch fix this problem and cleanup err handle of phy_power_on to be more readable. Fixes: 3be88125d85d ("phy: core: Support regulator ...") Cc: # v3.18+ Cc: Roger Quadros Cc: Axel Lin Signed-off-by: Shawn Lin Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 8c7f27db6ad3..e7e574dc667a 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -275,20 +275,21 @@ EXPORT_SYMBOL_GPL(phy_exit); int phy_power_on(struct phy *phy) { - int ret; + int ret = 0; if (!phy) - return 0; + goto out; if (phy->pwr) { ret = regulator_enable(phy->pwr); if (ret) - return ret; + goto out; } ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) - return ret; + goto err_pm_sync; + ret = 0; /* Override possible ret == -ENOTSUPP */ mutex_lock(&phy->mutex); @@ -296,19 +297,20 @@ int phy_power_on(struct phy *phy) ret = phy->ops->power_on(phy); if (ret < 0) { dev_err(&phy->dev, "phy poweron failed --> %d\n", ret); - goto out; + goto err_pwr_on; } } ++phy->power_count; mutex_unlock(&phy->mutex); return 0; -out: +err_pwr_on: mutex_unlock(&phy->mutex); phy_pm_runtime_put_sync(phy); +err_pm_sync: if (phy->pwr) regulator_disable(phy->pwr); - +out: return ret; } EXPORT_SYMBOL_GPL(phy_power_on); -- cgit v1.2.3 From b241d31ef2f6a289d33dcaa004714b26e06f476f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 30 Nov 2015 21:39:53 -0800 Subject: phy: twl4030-usb: Relase usb phy on unload Otherwise rmmod omap2430; rmmod phy-twl4030-usb; modprobe omap2430 will try to use a non-existing phy and oops: Unable to handle kernel paging request at virtual address b6f7c1f0 ... [] (devm_usb_get_phy_by_node) from [] (omap2430_musb_init+0x44/0x2b4 [omap2430]) [] (omap2430_musb_init [omap2430]) from [] (musb_init_controller+0x194/0x878 [musb_hdrc]) Cc: stable@vger.kernel.org Cc: Bin Liu Cc: Felipe Balbi Cc: Kishon Vijay Abraham I Cc: NeilBrown Signed-off-by: Tony Lindgren Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-twl4030-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 4a3fc6e59f8e..fe5538ff380f 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -750,6 +750,7 @@ static int twl4030_usb_remove(struct platform_device *pdev) struct twl4030_usb *twl = platform_get_drvdata(pdev); int val; + usb_remove_phy(&twl->phy); pm_runtime_get_sync(twl->dev); cancel_delayed_work(&twl->id_workaround_work); device_remove_file(twl->dev, &dev_attr_vbus); -- cgit v1.2.3 From 58a66dba1beac2121d931cda4682ae4d40816af5 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 30 Nov 2015 21:39:54 -0800 Subject: phy: twl4030-usb: Fix unbalanced pm_runtime_enable on module reload If we reload phy-twl4030-usb, we get a warning about unbalanced pm_runtime_enable. Let's fix the issue and also fix idling of the device on unload before we attempt to shut it down. If we don't properly idle the PHY before shutting it down on removal, the twl4030 ends up consuming about 62mW of extra power compared to running idle with the module loaded. Cc: stable@vger.kernel.org Cc: Bin Liu Cc: Felipe Balbi Cc: Kishon Vijay Abraham I Cc: NeilBrown Signed-off-by: Tony Lindgren Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-twl4030-usb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index fe5538ff380f..840f3eae428b 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -715,6 +715,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. @@ -758,6 +759,13 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* set transceiver mode to power on defaults */ twl4030_usb_set_mode(twl, -1); + /* idle ulpi before powering off */ + if (cable_present(twl->linkstat)) + pm_runtime_put_noidle(twl->dev); + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_sync_suspend(twl->dev); + pm_runtime_disable(twl->dev); + /* autogate 60MHz ULPI clock, * clear dpll clock request for i2c access, * disable 32KHz @@ -772,11 +780,6 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - if (cable_present(twl->linkstat)) - pm_runtime_put_noidle(twl->dev); - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put(twl->dev); - return 0; } -- cgit v1.2.3 From f00ab14c252ac459e86194747a1f580ab503c954 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 9 Feb 2016 09:34:30 -0800 Subject: mmc: block: return error on failed mmc_blk_get() This used to return -EFAULT, but the function above returns -EINVAL on the same condition so let's stick to that. The removal of error return on this path was introduced with b093410c9aef ('mmc: block: copy resp[] data on err for MMC_IOC_MULTI_CMD'). Fixes: b093410c9aef ('mmc: block: copy resp[] data on err for MMC_IOC_MULTI_CMD'). Signed-off-by: Olof Johansson Cc: Grant Grundler Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5914263090fc..951641a96806 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -655,8 +655,10 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, } md = mmc_blk_get(bdev->bd_disk); - if (!md) + if (!md) { + err = -EINVAL; goto cmd_err; + } card = md->queue.card; if (IS_ERR(card)) { -- cgit v1.2.3 From aaa0bf22cb84c7b29c814f3fcf3951c747b904d6 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 6 Feb 2016 22:24:19 +0800 Subject: MIPS: pci-mt7620: Fix return value check in mt7620_pci_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: John Crispin Cc: Matthias Brugger Cc: linux-mips@linux-mips.org Cc: linux-mediatek@lists.infradead.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12451/ Signed-off-by: Ralf Baechle --- arch/mips/pci/pci-mt7620.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/mips/pci/pci-mt7620.c b/arch/mips/pci/pci-mt7620.c index a009ee458934..1ae932c2d78b 100644 --- a/arch/mips/pci/pci-mt7620.c +++ b/arch/mips/pci/pci-mt7620.c @@ -297,12 +297,12 @@ static int mt7620_pci_probe(struct platform_device *pdev) return PTR_ERR(rstpcie0); bridge_base = devm_ioremap_resource(&pdev->dev, bridge_res); - if (!bridge_base) - return -ENOMEM; + if (IS_ERR(bridge_base)) + return PTR_ERR(bridge_base); pcie_base = devm_ioremap_resource(&pdev->dev, pcie_res); - if (!pcie_base) - return -ENOMEM; + if (IS_ERR(pcie_base)) + return PTR_ERR(pcie_base); iomem_resource.start = 0; iomem_resource.end = ~0; -- cgit v1.2.3 From 310a7e60435151ccab6dc4bc64b4b3a93b89c0d1 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 28 Jan 2016 19:08:50 +0530 Subject: gpio: davinci: Add the missing of-node pointer Currently the first parameter of irq_domain_add_legacy is NULL. irq_find_host function returns NULL when we do not populate the of_node and hence irq_of_parse_and_map call fails whenever we want to request a gpio irq. This fixes the request_irq failures for gpio interrupts. Signed-off-by: Keerthy Reviewed-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index ec58f4288649..c889f3166205 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -511,7 +511,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) return irq; } - irq_domain = irq_domain_add_legacy(NULL, ngpio, irq, 0, + irq_domain = irq_domain_add_legacy(dev->of_node, ngpio, irq, 0, &davinci_gpio_irq_ops, chips); if (!irq_domain) { -- cgit v1.2.3 From 6ec9249a83b00a754af435ed57ad02ffed105d93 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 28 Jan 2016 19:08:51 +0530 Subject: gpio: davinci: Fix the number of controllers allocated Driver only needs to allocate for [ngpio / 32] controllers, as each controller handles 32 gpios. But the current driver allocates for ngpio of which the extra allocated are unused. Fix it be registering only the required number of controllers. Signed-off-by: Lokesh Vutla Signed-off-by: Keerthy Reviewed-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index c889f3166205..cd007a67b302 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -195,7 +195,7 @@ static int davinci_gpio_of_xlate(struct gpio_chip *gc, static int davinci_gpio_probe(struct platform_device *pdev) { int i, base; - unsigned ngpio; + unsigned ngpio, nbank; struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; struct davinci_gpio_regs __iomem *regs; @@ -224,8 +224,9 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (WARN_ON(ARCH_NR_GPIOS < ngpio)) ngpio = ARCH_NR_GPIOS; + nbank = DIV_ROUND_UP(ngpio, 32); chips = devm_kzalloc(dev, - ngpio * sizeof(struct davinci_gpio_controller), + nbank * sizeof(struct davinci_gpio_controller), GFP_KERNEL); if (!chips) return -ENOMEM; -- cgit v1.2.3 From 72564b59ffc438ea103b0727a921aaddce766728 Mon Sep 17 00:00:00 2001 From: David Wragg Date: Wed, 10 Feb 2016 00:05:55 +0000 Subject: vxlan: Relax MTU constraints Allow the MTU of vxlan devices without an underlying device to be set to larger values (up to a maximum based on IP packet limits and vxlan overhead). Previously, their MTUs could not be set to higher than the conventional ethernet value of 1500. This is a very arbitrary value in the context of vxlan, and prevented vxlan devices from being able to take advantage of jumbo frames etc. The default MTU remains 1500, for compatibility. Signed-off-by: David Wragg Acked-by: Roopa Prabhu Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 65439188c582..e992c6a05f86 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2367,29 +2367,43 @@ static void vxlan_set_multicast_list(struct net_device *dev) { } -static int vxlan_change_mtu(struct net_device *dev, int new_mtu) +static int __vxlan_change_mtu(struct net_device *dev, + struct net_device *lowerdev, + struct vxlan_rdst *dst, int new_mtu, bool strict) { - struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_rdst *dst = &vxlan->default_dst; - struct net_device *lowerdev; - int max_mtu; + int max_mtu = IP_MAX_MTU; - lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex); - if (lowerdev == NULL) - return eth_change_mtu(dev, new_mtu); + if (lowerdev) + max_mtu = lowerdev->mtu; if (dst->remote_ip.sa.sa_family == AF_INET6) - max_mtu = lowerdev->mtu - VXLAN6_HEADROOM; + max_mtu -= VXLAN6_HEADROOM; else - max_mtu = lowerdev->mtu - VXLAN_HEADROOM; + max_mtu -= VXLAN_HEADROOM; - if (new_mtu < 68 || new_mtu > max_mtu) + if (new_mtu < 68) return -EINVAL; + if (new_mtu > max_mtu) { + if (strict) + return -EINVAL; + + new_mtu = max_mtu; + } + dev->mtu = new_mtu; return 0; } +static int vxlan_change_mtu(struct net_device *dev, int new_mtu) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_rdst *dst = &vxlan->default_dst; + struct net_device *lowerdev = __dev_get_by_index(vxlan->net, + dst->remote_ifindex); + return __vxlan_change_mtu(dev, lowerdev, dst, new_mtu, true); +} + static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb, struct ip_tunnel_info *info, __be16 sport, __be16 dport) -- cgit v1.2.3 From 55e5bfb53cff286c1c1ff49f51325dc15c7fea63 Mon Sep 17 00:00:00 2001 From: David Wragg Date: Wed, 10 Feb 2016 00:05:57 +0000 Subject: geneve: Relax MTU constraints Allow the MTU of geneve devices to be set to large values, in order to exploit underlying networks with larger frame sizes. GENEVE does not have a fixed encapsulation overhead (an openvswitch rule can add variable length options), so there is no relevant maximum MTU to enforce. A maximum of IP_MAX_MTU is used instead. Encapsulated packets that are too big for the underlying network will get dropped on the floor. Signed-off-by: David Wragg Signed-off-by: David S. Miller --- drivers/net/geneve.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 0b14ac3b8d11..d2031ce8baea 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1039,6 +1039,17 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) return geneve_xmit_skb(skb, dev, info); } +static int geneve_change_mtu(struct net_device *dev, int new_mtu) +{ + /* GENEVE overhead is not fixed, so we can't enforce a more + * precise max MTU. + */ + if (new_mtu < 68 || new_mtu > IP_MAX_MTU) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct ip_tunnel_info *info = skb_tunnel_info(skb); @@ -1083,7 +1094,7 @@ static const struct net_device_ops geneve_netdev_ops = { .ndo_stop = geneve_stop, .ndo_start_xmit = geneve_xmit, .ndo_get_stats64 = ip_tunnel_get_stats64, - .ndo_change_mtu = eth_change_mtu, + .ndo_change_mtu = geneve_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_fill_metadata_dst = geneve_fill_metadata_dst, -- cgit v1.2.3 From 7e059158d57b79159eaf1f504825d19866ef2c42 Mon Sep 17 00:00:00 2001 From: David Wragg Date: Wed, 10 Feb 2016 00:05:58 +0000 Subject: vxlan, gre, geneve: Set a large MTU on ovs-created tunnel devices Prior to 4.3, openvswitch tunnel vports (vxlan, gre and geneve) could transmit vxlan packets of any size, constrained only by the ability to send out the resulting packets. 4.3 introduced netdevs corresponding to tunnel vports. These netdevs have an MTU, which limits the size of a packet that can be successfully encapsulated. The default MTU values are low (1500 or less), which is awkwardly small in the context of physical networks supporting jumbo frames, and leads to a conspicuous change in behaviour for userspace. Instead, set the MTU on openvswitch-created netdevs to be the relevant maximum (i.e. the maximum IP packet size minus any relevant overhead), effectively restoring the behaviour prior to 4.3. Signed-off-by: David Wragg Signed-off-by: David S. Miller --- drivers/net/geneve.c | 18 ++++++++++++++---- drivers/net/vxlan.c | 11 ++++++++--- include/net/ip_tunnels.h | 1 + net/ipv4/ip_gre.c | 8 ++++++++ net/ipv4/ip_tunnel.c | 20 +++++++++++++++++--- net/openvswitch/vport-vxlan.c | 2 ++ 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index d2031ce8baea..028e3873c310 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1453,11 +1453,21 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, err = geneve_configure(net, dev, &geneve_remote_unspec, 0, 0, 0, htons(dst_port), true, 0); - if (err) { - free_netdev(dev); - return ERR_PTR(err); - } + if (err) + goto err; + + /* openvswitch users expect packet sizes to be unrestricted, + * so set the largest MTU we can. + */ + err = geneve_change_mtu(dev, IP_MAX_MTU); + if (err) + goto err; + return dev; + + err: + free_netdev(dev); + return ERR_PTR(err); } EXPORT_SYMBOL_GPL(geneve_dev_create_fb); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e992c6a05f86..a31cd954b308 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2779,6 +2779,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, int err; bool use_ipv6 = false; __be16 default_port = vxlan->cfg.dst_port; + struct net_device *lowerdev = NULL; vxlan->net = src_net; @@ -2799,9 +2800,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, } if (conf->remote_ifindex) { - struct net_device *lowerdev - = __dev_get_by_index(src_net, conf->remote_ifindex); - + lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex); dst->remote_ifindex = conf->remote_ifindex; if (!lowerdev) { @@ -2825,6 +2824,12 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, needed_headroom = lowerdev->hard_header_len; } + if (conf->mtu) { + err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false); + if (err) + return err; + } + if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA) needed_headroom += VXLAN6_HEADROOM; else diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 6db96ea0144f..dda9abf6b89c 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -230,6 +230,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd); int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, u8 *protocol, struct flowi4 *fl4); +int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict); int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu); struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev, diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 7c51c4e1661f..56fdf4e0dce4 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1240,6 +1240,14 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name, err = ipgre_newlink(net, dev, tb, NULL); if (err < 0) goto out; + + /* openvswitch users expect packet sizes to be unrestricted, + * so set the largest MTU we can. + */ + err = __ip_tunnel_change_mtu(dev, IP_MAX_MTU, false); + if (err) + goto out; + return dev; out: free_netdev(dev); diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index c7bd72e9b544..89e8861e05fc 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -943,17 +943,31 @@ done: } EXPORT_SYMBOL_GPL(ip_tunnel_ioctl); -int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu) +int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict) { struct ip_tunnel *tunnel = netdev_priv(dev); int t_hlen = tunnel->hlen + sizeof(struct iphdr); + int max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; - if (new_mtu < 68 || - new_mtu > 0xFFF8 - dev->hard_header_len - t_hlen) + if (new_mtu < 68) return -EINVAL; + + if (new_mtu > max_mtu) { + if (strict) + return -EINVAL; + + new_mtu = max_mtu; + } + dev->mtu = new_mtu; return 0; } +EXPORT_SYMBOL_GPL(__ip_tunnel_change_mtu); + +int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu) +{ + return __ip_tunnel_change_mtu(dev, new_mtu, true); +} EXPORT_SYMBOL_GPL(ip_tunnel_change_mtu); static void ip_tunnel_dev_free(struct net_device *dev) diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 1605691d9414..de9cb19efb6a 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -91,6 +91,8 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms) struct vxlan_config conf = { .no_share = true, .flags = VXLAN_F_COLLECT_METADATA, + /* Don't restrict the packets that can be sent by MTU */ + .mtu = IP_MAX_MTU, }; if (!options) { -- cgit v1.2.3 From b96d6a80c95815fd01e99a239cd515fc05e5f867 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 10 Feb 2016 13:56:25 +0000 Subject: MIPS: Octeon: Update OCTEON_FEATURE_PCIE for Octeon III Currently the driver tries to probe the pci driver and oops. Add CN7XXX to case so that driver probes the pcie driver. Signed-off-by: Zubair Lutfullah Kakakhel Cc: david.daney@cavium.com Cc: matt.redfearn@imgtec.com Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12530/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/octeon/octeon-feature.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/octeon/octeon-feature.h b/arch/mips/include/asm/octeon/octeon-feature.h index 8ebd3f579b84..3ed10a8d7865 100644 --- a/arch/mips/include/asm/octeon/octeon-feature.h +++ b/arch/mips/include/asm/octeon/octeon-feature.h @@ -128,7 +128,8 @@ static inline int octeon_has_feature(enum octeon_feature feature) case OCTEON_FEATURE_PCIE: return OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX) - || OCTEON_IS_MODEL(OCTEON_CN6XXX); + || OCTEON_IS_MODEL(OCTEON_CN6XXX) + || OCTEON_IS_MODEL(OCTEON_CN7XXX); case OCTEON_FEATURE_SRIO: return OCTEON_IS_MODEL(OCTEON_CN63XX) -- cgit v1.2.3 From a32ef81c9889c9554a3c4b465c4ee7b2d26c6b10 Mon Sep 17 00:00:00 2001 From: Chris Paterson Date: Wed, 10 Feb 2016 14:07:01 +0000 Subject: mmc: sh_mmcif: Correct TX DMA channel allocation Commit 27cbd7e815a8 ("mmc: sh_mmcif: rework dma channel handling") introduced a typo causing the TX DMA channel allocation to be overwritten by the requested RX DMA channel. Fixes: 27cbd7e815a8 ("mmc: sh_mmcif: rework dma channel handling") Signed-off-by: Chris Paterson Acked-by: Laurent Pinchart Acked-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 1ca8a1359cbc..6234eab38ff3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -445,7 +445,7 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host) pdata->slave_id_rx); } else { host->chan_tx = dma_request_slave_channel(dev, "tx"); - host->chan_tx = dma_request_slave_channel(dev, "rx"); + host->chan_rx = dma_request_slave_channel(dev, "rx"); } dev_dbg(dev, "%s: got channel TX %p RX %p\n", __func__, host->chan_tx, host->chan_rx); -- cgit v1.2.3 From 342decff2b846b46fa61eb5ee40986fab79a9a32 Mon Sep 17 00:00:00 2001 From: Alexandra Yates Date: Fri, 5 Feb 2016 15:27:49 -0800 Subject: ahci: Intel DNV device IDs SATA Adding Intel codename DNV platform device IDs for SATA. Signed-off-by: Alexandra Yates Signed-off-by: Tejun Heo Cc: stable@vger.kernel.org --- drivers/ata/ahci.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 594fcabd22cd..546a3692774f 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -264,6 +264,26 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */ + { PCI_VDEVICE(INTEL, 0x19b0), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b1), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b2), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b3), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b4), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b5), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b6), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b7), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19bE), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19bF), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c0), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c1), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c2), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c3), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c4), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c5), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c6), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c7), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19cE), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19cF), board_ahci }, /* DNV AHCI */ { PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */ { PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */ { PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */ -- cgit v1.2.3 From d6e022f1d207a161cd88e08ef0371554680ffc46 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Feb 2016 13:54:25 -0500 Subject: workqueue: handle NUMA_NO_NODE for unbound pool_workqueue lookup When looking up the pool_workqueue to use for an unbound workqueue, workqueue assumes that the target CPU is always bound to a valid NUMA node. However, currently, when a CPU goes offline, the mapping is destroyed and cpu_to_node() returns NUMA_NO_NODE. This has always been broken but hasn't triggered often enough before 874bbfe600a6 ("workqueue: make sure delayed work run in local cpu"). After the commit, workqueue forcifully assigns the local CPU for delayed work items without explicit target CPU to fix a different issue. This widens the window where CPU can go offline while a delayed work item is pending causing delayed work items dispatched with target CPU set to an already offlined CPU. The resulting NUMA_NO_NODE mapping makes workqueue try to queue the work item on a NULL pool_workqueue and thus crash. While 874bbfe600a6 has been reverted for a different reason making the bug less visible again, it can still happen. Fix it by mapping NUMA_NO_NODE to the default pool_workqueue from unbound_pwq_by_node(). This is a temporary workaround. The long term solution is keeping CPU -> NODE mapping stable across CPU off/online cycles which is being worked on. Signed-off-by: Tejun Heo Reported-by: Mike Galbraith Cc: Tang Chen Cc: Rafael J. Wysocki Cc: Len Brown Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/g/1454424264.11183.46.camel@gmail.com Link: http://lkml.kernel.org/g/1453702100-2597-1-git-send-email-tangchen@cn.fujitsu.com --- kernel/workqueue.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 51d77e7c0989..7ff5dc7d2ac5 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -586,6 +586,16 @@ static struct pool_workqueue *unbound_pwq_by_node(struct workqueue_struct *wq, int node) { assert_rcu_or_wq_mutex_or_pool_mutex(wq); + + /* + * XXX: @node can be NUMA_NO_NODE if CPU goes offline while a + * delayed item is pending. The plan is to keep CPU -> NODE + * mapping valid and stable across CPU on/offlines. Once that + * happens, this workaround can be removed. + */ + if (unlikely(node == NUMA_NO_NODE)) + return wq->dfl_pwq; + return rcu_dereference_raw(wq->numa_pwq_tbl[node]); } -- cgit v1.2.3 From 61c4a1ac4d900e743af0b363fe520405939eab47 Mon Sep 17 00:00:00 2001 From: Pascal Huerst Date: Wed, 10 Feb 2016 15:59:28 +0100 Subject: ASoC: sigmadsp: Fix missleading return value Forwarding the return value of i2c_master_send, leads to errors later on, since i2c_master_send returns the number of bytes transmittet. Check for ret < 0 instead and return 0 otherwise. Signed-off-by: Pascal Huerst Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/sigmadsp-i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c index 21ca3a5e9f66..d374c18d4db7 100644 --- a/sound/soc/codecs/sigmadsp-i2c.c +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -31,7 +31,10 @@ static int sigmadsp_write_i2c(void *control_data, kfree(buf); - return ret; + if (ret < 0) + return ret; + + return 0; } static int sigmadsp_read_i2c(void *control_data, -- cgit v1.2.3 From ca19852884c8937eed89560f924f5a34cfcc22af Mon Sep 17 00:00:00 2001 From: Flora Cui Date: Thu, 4 Feb 2016 15:10:08 +0800 Subject: drm/amdgpu: fix s4 resume No need to re-init asic if it's already been initialized. Skip IB tests since kernel processes are frozen in thaw. Signed-off-by: Flora Cui Reviewed-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 85991cee62e6..51bfc114584e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1795,15 +1795,20 @@ int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon) } /* post card */ - amdgpu_atom_asic_init(adev->mode_info.atom_context); + if (!amdgpu_card_posted(adev)) + amdgpu_atom_asic_init(adev->mode_info.atom_context); r = amdgpu_resume(adev); + if (r) + DRM_ERROR("amdgpu_resume failed (%d).\n", r); amdgpu_fence_driver_resume(adev); - r = amdgpu_ib_ring_tests(adev); - if (r) - DRM_ERROR("ib ring test failed (%d).\n", r); + if (resume) { + r = amdgpu_ib_ring_tests(adev); + if (r) + DRM_ERROR("ib ring test failed (%d).\n", r); + } r = amdgpu_late_init(adev); if (r) -- cgit v1.2.3 From a8d81b36267366603771431747438d18f32ae2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolai=20H=C3=A4hnle?= Date: Fri, 5 Feb 2016 10:59:43 -0500 Subject: drm/amdgpu: hold reference to fences in amdgpu_sa_bo_new (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An arbitrary amount of time can pass between spin_unlock and fence_wait_any_timeout, so we need to ensure that nobody frees the fences from under us. A stress test (rapidly starting and killing hundreds of glxgears instances) ran into a deadlock in fence_wait_any_timeout after about an hour, and this race condition appears to be a plausible cause. v2: agd: rebase on upstream Signed-off-by: Nicolai Hähnle Reviewed-by: Alex Deucher Reviewed-by: Christian König Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index 8b88edb0434b..ca72a2e487b9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -354,12 +354,15 @@ int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, for (i = 0, count = 0; i < AMDGPU_MAX_RINGS; ++i) if (fences[i]) - fences[count++] = fences[i]; + fences[count++] = fence_get(fences[i]); if (count) { spin_unlock(&sa_manager->wq.lock); t = fence_wait_any_timeout(fences, count, false, MAX_SCHEDULE_TIMEOUT); + for (i = 0; i < count; ++i) + fence_put(fences[i]); + r = (t > 0) ? 0 : t; spin_lock(&sa_manager->wq.lock); } else { -- cgit v1.2.3 From b19763d0d867eb863953500a5c87f2fd663863b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolai=20H=C3=A4hnle?= Date: Fri, 5 Feb 2016 10:49:50 -0500 Subject: drm/amdgpu: remove unnecessary forward declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolai Hähnle Reviewed-by: Alex Deucher Reviewed-by: Christian König Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 6808facaf6af..43e7ed1e74e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -567,8 +567,6 @@ struct amdgpu_sa_manager { uint32_t align; }; -struct amdgpu_sa_bo; - /* sub-allocation buffer */ struct amdgpu_sa_bo { struct list_head olist; -- cgit v1.2.3 From f6ff4f67cdf8455d0a4226eeeaf5af17c37d05eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolai=20H=C3=A4hnle?= Date: Fri, 5 Feb 2016 14:35:53 -0500 Subject: drm/radeon: hold reference to fences in radeon_sa_bo_new MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An arbitrary amount of time can pass between spin_unlock and radeon_fence_wait_any, so we need to ensure that nobody frees the fences from under us. Based on the analogous fix for amdgpu. Signed-off-by: Nicolai Hähnle Reviewed-by: Christian König Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_sa.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index c507896aca45..197b157b73d0 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -349,8 +349,13 @@ int radeon_sa_bo_new(struct radeon_device *rdev, /* see if we can skip over some allocations */ } while (radeon_sa_bo_next_hole(sa_manager, fences, tries)); + for (i = 0; i < RADEON_NUM_RINGS; ++i) + radeon_fence_ref(fences[i]); + spin_unlock(&sa_manager->wq.lock); r = radeon_fence_wait_any(rdev, fences, false); + for (i = 0; i < RADEON_NUM_RINGS; ++i) + radeon_fence_unref(&fences[i]); spin_lock(&sa_manager->wq.lock); /* if we have nothing to wait for block */ if (r == -ENOENT) { -- cgit v1.2.3 From cc1de6e800c253172334f8774c419dc64401cd2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Mon, 8 Feb 2016 10:57:22 +0100 Subject: drm/amdgpu: fix issue with overlapping userptrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we could try to evict overlapping userptr BOs in get_user_pages(), leading to a possible circular locking dependency. Signed-off-by: Christian König Acked-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c | 3 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 43e7ed1e74e7..5e7770f9a415 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -2319,6 +2319,8 @@ bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo); int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, uint32_t flags); bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm); +bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start, + unsigned long end); bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm); uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm, struct ttm_mem_reg *mem); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index b1969f2b2038..d4e2780c0796 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -142,7 +142,8 @@ static void amdgpu_mn_invalidate_range_start(struct mmu_notifier *mn, list_for_each_entry(bo, &node->bos, mn_list) { - if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound) + if (!amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, start, + end)) continue; r = amdgpu_bo_reserve(bo, true); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 55cf05e1c81c..6442a06d6fdc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -783,6 +783,25 @@ bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm) return !!gtt->userptr; } +bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start, + unsigned long end) +{ + struct amdgpu_ttm_tt *gtt = (void *)ttm; + unsigned long size; + + if (gtt == NULL) + return false; + + if (gtt->ttm.ttm.state != tt_bound || !gtt->userptr) + return false; + + size = (unsigned long)gtt->ttm.ttm.num_pages * PAGE_SIZE; + if (gtt->userptr > end || gtt->userptr + size <= start) + return false; + + return true; +} + bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm) { struct amdgpu_ttm_tt *gtt = (void *)ttm; -- cgit v1.2.3 From 01582a841493f28caf1688b2af4dafbcbee8135e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Feb 2016 11:56:13 +0000 Subject: ASoC: arizona: fref must be limited in pseudo-fractional mode When the FLL is in pseudo-fractional mode there is an additional limit on fref based on the fratio, to prevent aliasing around the Nyquist frequency. If fref exceeds this limit the refclk divider must be increased and the calculation tried again until a suitable combination of fref and fratio is found or we have to fall back to integer mode. This patch also adds some debug log prints around this code. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 33143fe1de0b..91785318b283 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1929,6 +1929,25 @@ static struct { { 1000000, 13500000, 0, 1 }, }; +static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = { + 13500000, + 6144000, + 6144000, + 3072000, + 3072000, + 2822400, + 2822400, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 768000, +}; + static struct { unsigned int min; unsigned int max; @@ -2042,16 +2061,32 @@ static int arizona_calc_fratio(struct arizona_fll *fll, /* Adjust FRATIO/refdiv to avoid integer mode if possible */ refdiv = cfg->refdiv; + arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n", + init_ratio, Fref, refdiv); + while (div <= ARIZONA_FLL_MAX_REFDIV) { for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { if ((ARIZONA_FLL_VCO_CORNER / 2) / - (fll->vco_mult * ratio) < Fref) + (fll->vco_mult * ratio) < Fref) { + arizona_fll_dbg(fll, "pseudo: hit VCO corner\n"); break; + } + + if (Fref > pseudo_fref_max[ratio - 1]) { + arizona_fll_dbg(fll, + "pseudo: exceeded max fref(%u) for ratio=%u\n", + pseudo_fref_max[ratio - 1], + ratio); + break; + } if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); return ratio; } } @@ -2060,6 +2095,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll, if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); return ratio; } } @@ -2068,6 +2106,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll, Fref /= 2; refdiv++; init_ratio = arizona_find_fratio(Fref, NULL); + arizona_fll_dbg(fll, + "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n", + Fref, refdiv, div, init_ratio); } arizona_fll_warn(fll, "Falling back to integer mode operation\n"); -- cgit v1.2.3 From a1b14d27ed0965838350f1377ff97c93ee383492 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 10 Feb 2016 16:47:11 +0100 Subject: bpf: fix branch offset adjustment on backjumps after patching ctx expansion When ctx access is used, the kernel often needs to expand/rewrite instructions, so after that patching, branch offsets have to be adjusted for both forward and backward jumps in the new eBPF program, but for backward jumps it fails to account the delta. Meaning, for example, if the expansion happens exactly on the insn that sits at the jump target, it doesn't fix up the back jump offset. Analysis on what the check in adjust_branches() is currently doing: /* adjust offset of jmps if necessary */ if (i < pos && i + insn->off + 1 > pos) insn->off += delta; else if (i > pos && i + insn->off + 1 < pos) insn->off -= delta; First condition (forward jumps): Before: After: insns[0] insns[0] insns[1] <--- i/insn insns[1] <--- i/insn insns[2] <--- pos insns[P] <--- pos insns[3] insns[P] `------| delta insns[4] <--- target_X insns[P] `-----| insns[5] insns[3] insns[4] <--- target_X insns[5] First case is if we cross pos-boundary and the jump instruction was before pos. This is handeled correctly. I.e. if i == pos, then this would mean our jump that we currently check was the patchlet itself that we just injected. Since such patchlets are self-contained and have no awareness of any insns before or after the patched one, the delta is correctly not adjusted. Also, for the second condition in case of i + insn->off + 1 == pos, means we jump to that newly patched instruction, so no offset adjustment are needed. That part is correct. Second condition (backward jumps): Before: After: insns[0] insns[0] insns[1] <--- target_X insns[1] <--- target_X insns[2] <--- pos <-- target_Y insns[P] <--- pos <-- target_Y insns[3] insns[P] `------| delta insns[4] <--- i/insn insns[P] `-----| insns[5] insns[3] insns[4] <--- i/insn insns[5] Second interesting case is where we cross pos-boundary and the jump instruction was after pos. Backward jump with i == pos would be impossible and pose a bug somewhere in the patchlet, so the first condition checking i > pos is okay only by itself. However, i + insn->off + 1 < pos does not always work as intended to trigger the adjustment. It works when jump targets would be far off where the delta wouldn't matter. But, for example, where the fixed insn->off before pointed to pos (target_Y), it now points to pos + delta, so that additional room needs to be taken into account for the check. This means that i) both tests here need to be adjusted into pos + delta, and ii) for the second condition, the test needs to be <= as pos itself can be a target in the backjump, too. Fixes: 9bac3d6d548e ("bpf: allow extended BPF programs access skb fields") Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d1d3e8f57de9..2e7f7ab739e4 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2082,7 +2082,7 @@ static void adjust_branches(struct bpf_prog *prog, int pos, int delta) /* adjust offset of jmps if necessary */ if (i < pos && i + insn->off + 1 > pos) insn->off += delta; - else if (i > pos && i + insn->off + 1 < pos) + else if (i > pos + delta && i + insn->off + 1 <= pos + delta) insn->off -= delta; } } -- cgit v1.2.3 From 2e498f25b0daa9dec761e3e880024ff7417f2445 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 10 Feb 2016 20:34:56 -0800 Subject: target: Fix incorrect unmap_zeroes_data_store return This patch fixes an incorrect return of zero from the new unmap_zeroes_data_store() configfs store attribute handler introduced in v4.5-rc1, to use the correct 'count' bytes return value. Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_configfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 3327c49674d3..713c63d9681b 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -898,7 +898,7 @@ static ssize_t unmap_zeroes_data_store(struct config_item *item, da->unmap_zeroes_data = flag; pr_debug("dev[%p]: SE Device Thin Provisioning LBPRZ bit: %d\n", da->da_dev, flag); - return 0; + return count; } /* -- cgit v1.2.3 From 9095adaab8c1d82707e4e9961b6ad79b62f3361b Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Wed, 10 Feb 2016 18:59:13 -0500 Subject: target/transport: add flag to indicate CPU Affinity is observed Signed-off-by: Quinn Tran Reviewed-by: Christoph Hellwig Reviewed-by: Bart Van Assche Fixes: fb3269b ("qla2xxx: Add selective command queuing") Signed-off-by: Himanshu Madhani Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 12 +++++++++--- include/target/target_core_base.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d92cb644d8f9..867bc6d0a68a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -711,10 +711,10 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (cmd->cpuid == -1) - queue_work(target_completion_wq, &cmd->work); - else + if (cmd->se_cmd_flags & SCF_USE_CPUID) queue_work_on(cmd->cpuid, target_completion_wq, &cmd->work); + else + queue_work(target_completion_wq, &cmd->work); } EXPORT_SYMBOL(target_complete_cmd); @@ -1426,6 +1426,12 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, data_length, data_dir, task_attr, sense); + + if (flags & TARGET_SCF_USE_CPUID) + se_cmd->se_cmd_flags |= SCF_USE_CPUID; + else + se_cmd->cpuid = WORK_CPU_UNBOUND; + if (flags & TARGET_SCF_UNKNOWN_SIZE) se_cmd->unknown_data_length = 1; /* diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index d71a3ead9e63..e8c8c08bf575 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -141,6 +141,7 @@ enum se_cmd_flags_table { SCF_COMPARE_AND_WRITE_POST = 0x00100000, SCF_PASSTHROUGH_PROT_SG_TO_MEM_NOALLOC = 0x00200000, SCF_ACK_KREF = 0x00400000, + SCF_USE_CPUID = 0x00800000, }; /* struct se_dev_entry->lun_flags and struct se_lun->lun_access */ @@ -188,6 +189,7 @@ enum target_sc_flags_table { TARGET_SCF_BIDI_OP = 0x01, TARGET_SCF_ACK_KREF = 0x02, TARGET_SCF_UNKNOWN_SIZE = 0x04, + TARGET_SCF_USE_CPUID = 0x08, }; /* fabric independent task management function values */ -- cgit v1.2.3 From 5327c7dbd1a7fd980608f44789076a636e5ee5fc Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Wed, 10 Feb 2016 18:59:14 -0500 Subject: qla2xxx: use TARGET_SCF_USE_CPUID flag to indiate CPU Affinity Signed-off-by: Quinn Tran Reviewed-by: Christoph Hellwig Reviewed-by: Bart Van Assche Fixes: fb3269b ("qla2xxx: Add selective command queuing") Signed-off-by: Himanshu Madhani Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 4 ++-- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 46c667979411..ee967becd257 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4004,7 +4004,8 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, cmd->cmd_in_wq = 1; cmd->cmd_flags |= BIT_0; - cmd->se_cmd.cpuid = -1; + cmd->se_cmd.cpuid = ha->msix_count ? + ha->tgt.rspq_vector_cpuid : WORK_CPU_UNBOUND; spin_lock(&vha->cmd_list_lock); list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list); @@ -4012,7 +4013,6 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, INIT_WORK(&cmd->work, qlt_do_work); if (ha->msix_count) { - cmd->se_cmd.cpuid = ha->tgt.rspq_vector_cpuid; if (cmd->atio.u.isp24.fcp_cmnd.rddata) queue_work_on(smp_processor_id(), qla_tgt_wq, &cmd->work); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 3eecdd1bc6fb..1808a01cfb7e 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -462,6 +462,9 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, if (bidi) flags |= TARGET_SCF_BIDI_OP; + if (se_cmd->cpuid != WORK_CPU_UNBOUND) + flags |= TARGET_SCF_USE_CPUID; + sess = cmd->sess; if (!sess) { pr_err("Unable to locate struct qla_tgt_sess from qla_tgt_cmd\n"); -- cgit v1.2.3 From 8e7199c2c50fff1969302643171eaa33f1ca148f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 8 Feb 2016 21:12:58 +0000 Subject: component: remove device from master match list on failed add Calling component_add() may result in the completion of a set of devices, which will try to bring up a master. In bringing the master up, we populate its match array with the current set of children. If binding any of the devices fails, component_add() itself will fail, free the struct component entry, and return to the caller. The now-freed entry is never removed from the master's match array, and will later be used in a futile attempt to bind to freed memory. Bring component_add's behaviour on failure to bring up a master into line with component_del by removing the (to-be-freed) component from the master's match array. The specific case which broke was: - rockchip_drm_drv adds a component master - dwhdmi_rockchip adds a child component in probe (master incomplete) - rockchip_drm_vop adds two children in probe, which completes the set - inside component_add, we try to bring up the master, having populated the master's match array, and fail with EPROBE_DEFER from dwhdmi_rockchip; we delete the putative component - rockchip_drm_vop's probe fails and returns EPROBE_DEFER - we later re-probe rockchip_drm_vop and add the component; the master is complete, so we attempt to bring it up again - walking the match array, we find the previous child, whose master pointer doesn't match (as it has been freed in the meantime) - rockchip_drm_vop probe fails, and will never be attempted again Fixes: ffc30b74fd6d01588bd3fdebc3b1acc0857e6fc8 Signed-off-by: Daniel Stone Cc: Russell King Cc: Thierry Reding Cc: Laurent Pinchart Signed-off-by: Russell King --- drivers/base/component.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/component.c b/drivers/base/component.c index 2738039aae9e..04a1582e80bb 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -491,6 +491,8 @@ int component_add(struct device *dev, const struct component_ops *ops) ret = try_to_bring_up_masters(component); if (ret < 0) { + if (component->master) + remove_component(component->master, component); list_del(&component->node); kfree(component); -- cgit v1.2.3 From 7bf29d3af6807d2a97a8b7d4627104a8da9bcdcb Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 9 Feb 2016 15:24:56 +0000 Subject: irqchip/gic: Only populate set_affinity for the root controller Setting the affinity of an IRQ, it only applicable for the root interrupt controller and so only populate this operator for the root controller. Acked-by: Marc Zyngier Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 911758c056c1..db7b161b0295 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -384,9 +384,6 @@ static struct irq_chip gic_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, -#ifdef CONFIG_SMP - .irq_set_affinity = gic_set_affinity, -#endif .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED | @@ -400,9 +397,6 @@ static struct irq_chip gic_eoimode1_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoimode1_eoi_irq, .irq_set_type = gic_set_type, -#ifdef CONFIG_SMP - .irq_set_affinity = gic_set_affinity, -#endif .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, @@ -1039,6 +1033,11 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } +#ifdef CONFIG_SMP + if (gic_nr == 0) + gic->chip.irq_set_affinity = gic_set_affinity; +#endif + #ifdef CONFIG_GIC_NON_BANKED if (percpu_offset) { /* Frankein-GIC without banked registers... */ unsigned int cpu; -- cgit v1.2.3 From 389a00d3ad02a06b5d6d692cce76fed6a3dae8b4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 9 Feb 2016 15:24:57 +0000 Subject: irqchip/gic: Only set the EOImodeNS bit for the root controller EOImode1 is only used for the root controller and hence only the root controller uses the eoimode1 functions for handling interrupts. However, if the root controller supports EOImode1, then the EOImodeNS bit will be set for all GICs, enabling EOImode1. This is not what we want and this causes interrupts on non-root GICs to only be dropped in priority but never deactivated. Therefore, only set the EOImodeNS bit for the root controller. Acked-by: Marc Zyngier Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index db7b161b0295..8f9ebf714e2b 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -437,7 +437,7 @@ static void gic_cpu_if_up(struct gic_chip_data *gic) u32 bypass = 0; u32 mode = 0; - if (static_key_true(&supports_deactivate)) + if (gic == &gic_data[0] && static_key_true(&supports_deactivate)) mode = GIC_CPU_CTRL_EOImodeNS; /* -- cgit v1.2.3 From 1a1ebd5fb1e203ee8cc73508cc7a38ac4b804596 Mon Sep 17 00:00:00 2001 From: Tirumalesh Chalamarla Date: Thu, 4 Feb 2016 10:45:25 -0800 Subject: irqchip/gic-v3: Make sure read from ICC_IAR1_EL1 is visible on redestributor The ARM GICv3 specification mentions the need for dsb after a read from the ICC_IAR1_EL1 register: 4.1.1 Physical CPU Interface: The effects of reading ICC_IAR0_EL1 and ICC_IAR1_EL1 on the state of a returned INTID are not guaranteed to be visible until after the execution of a DSB. Not having this could result in missed interrupts, so let's add the required barrier. [Marc: fixed commit message] Acked-by: Marc Zyngier Signed-off-by: Tirumalesh Chalamarla Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/arch_gicv3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index 2731d3b25ed2..8ec88e5b290f 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -103,6 +103,7 @@ static inline u64 gic_read_iar_common(void) u64 irqstat; asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); + dsb(sy); return irqstat; } -- cgit v1.2.3 From f4d3d504198d464e406171cfa554a59bd4773d79 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 10 Feb 2016 10:21:21 +0100 Subject: mips: Differentiate between 32 and 64 bit ELF header Depending on the configuration either the 32 or 64 bit version of elf_check_arch() is defined. parse_crash_elf{32|64}_headers() does some basic verification of the ELF header via vmcore_elf{32|64}_check_arch() which happen to map to elf_check_arch(). Since the implementation 32 and 64 bit version of elf_check_arch() differ, we use the wrong type: In file included from include/linux/elf.h:4:0, from fs/proc/vmcore.c:13: fs/proc/vmcore.c: In function 'parse_crash_elf64_headers': >> arch/mips/include/asm/elf.h:228:23: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] struct elfhdr *__h = (hdr); \ ^ include/linux/crash_dump.h:41:37: note: in expansion of macro 'elf_check_arch' #define vmcore_elf64_check_arch(x) (elf_check_arch(x) || vmcore_elf_check_arch_cross(x)) ^ fs/proc/vmcore.c:1015:4: note: in expansion of macro 'vmcore_elf64_check_arch' !vmcore_elf64_check_arch(&ehdr) || ^ Therefore, we rather define vmcore_elf{32|64}_check_arch() as a basic machine check and use it also in binfm_elf?32.c as well. Signed-off-by: Daniel Wagner Suggested-by: Maciej W. Rozycki Reviewed-by: Maciej W. Rozycki Reported-by: Fengguang Wu Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12529/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/elf.h | 9 +++++++-- arch/mips/kernel/binfmt_elfn32.c | 2 +- arch/mips/kernel/binfmt_elfo32.c | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h index cefb7a596878..e090fc388e02 100644 --- a/arch/mips/include/asm/elf.h +++ b/arch/mips/include/asm/elf.h @@ -227,7 +227,7 @@ struct mips_elf_abiflags_v0 { int __res = 1; \ struct elfhdr *__h = (hdr); \ \ - if (__h->e_machine != EM_MIPS) \ + if (!mips_elf_check_machine(__h)) \ __res = 0; \ if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ __res = 0; \ @@ -258,7 +258,7 @@ struct mips_elf_abiflags_v0 { int __res = 1; \ struct elfhdr *__h = (hdr); \ \ - if (__h->e_machine != EM_MIPS) \ + if (!mips_elf_check_machine(__h)) \ __res = 0; \ if (__h->e_ident[EI_CLASS] != ELFCLASS64) \ __res = 0; \ @@ -285,6 +285,11 @@ struct mips_elf_abiflags_v0 { #endif /* !defined(ELF_ARCH) */ +#define mips_elf_check_machine(x) ((x)->e_machine == EM_MIPS) + +#define vmcore_elf32_check_arch mips_elf_check_machine +#define vmcore_elf64_check_arch mips_elf_check_machine + struct mips_abi; extern struct mips_abi mips_abi; diff --git a/arch/mips/kernel/binfmt_elfn32.c b/arch/mips/kernel/binfmt_elfn32.c index 1188e00bb120..1b992c6e3d8e 100644 --- a/arch/mips/kernel/binfmt_elfn32.c +++ b/arch/mips/kernel/binfmt_elfn32.c @@ -35,7 +35,7 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; int __res = 1; \ struct elfhdr *__h = (hdr); \ \ - if (__h->e_machine != EM_MIPS) \ + if (!mips_elf_check_machine(__h)) \ __res = 0; \ if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ __res = 0; \ diff --git a/arch/mips/kernel/binfmt_elfo32.c b/arch/mips/kernel/binfmt_elfo32.c index 928767858b86..abd3affe5fb3 100644 --- a/arch/mips/kernel/binfmt_elfo32.c +++ b/arch/mips/kernel/binfmt_elfo32.c @@ -47,7 +47,7 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; int __res = 1; \ struct elfhdr *__h = (hdr); \ \ - if (__h->e_machine != EM_MIPS) \ + if (!mips_elf_check_machine(__h)) \ __res = 0; \ if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ __res = 0; \ -- cgit v1.2.3 From bf60e592a1af4d6f65dd54593250183f14360eed Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 9 Feb 2016 16:12:35 +0200 Subject: mmc: sdhci: Allow override of mmc host operations In the past, fixes for specific hardware devices were implemented in sdhci using quirks. That approach is no longer accepted because the growing number of quirks was starting to make the code difficult to understand and maintain. One alternative to quirks, is to allow drivers to override the default mmc host operations. This patch makes it easy to do that, and it is needed for a subsequent bug fix, for which separate patches are provided. Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org # v4.4+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 ++- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d622435d1bcc..9bfa66df963a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2849,6 +2849,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, host = mmc_priv(mmc); host->mmc = mmc; + host->mmc_host_ops = sdhci_ops; + mmc->ops = &host->mmc_host_ops; return host; } @@ -3037,7 +3039,6 @@ int sdhci_add_host(struct sdhci_host *host) /* * Set host parameters. */ - mmc->ops = &sdhci_ops; max_clk = host->max_clk; if (host->ops->get_min_clock) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 7654ae5d2b4e..0115e9907bf8 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -430,6 +430,7 @@ struct sdhci_host { /* Internal data */ struct mmc_host *mmc; /* MMC structure */ + struct mmc_host_ops mmc_host_ops; /* MMC host ops */ u64 dma_mask; /* custom DMA mask */ #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) -- cgit v1.2.3 From 8d28b7a72fe18bcdcdb047243ba8fec36b149955 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 9 Feb 2016 16:12:36 +0200 Subject: mmc: sdhci: Allow override of get_cd() called from sdhci_request() Drivers may need to provide their own get_cd() mmc host op, but currently the internals of the current op (sdhci_get_cd()) are provided by sdhci_do_get_cd() which is also called from sdhci_request(). To allow override of the get_cd functionality, change sdhci_request() to call ->get_cd() instead of sdhci_do_get_cd(). Note, in the future the call to ->get_cd() will likely be removed from sdhci_request() since most drivers don't need actually it. However this change is being done now to facilitate a subsequent bug fix. Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org # v4.4+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9bfa66df963a..add9fdfd1d8f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1360,7 +1360,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) sdhci_runtime_pm_get(host); /* Firstly check card presence */ - present = sdhci_do_get_cd(host); + present = mmc->ops->get_cd(mmc); spin_lock_irqsave(&host->lock, flags); -- cgit v1.2.3 From 163cbe31e5163459908a41a2b4e0d33a28fd557a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 9 Feb 2016 16:12:37 +0200 Subject: mmc: sdhci-pci: Fix card detect race for Intel BXT/APL Intel BXT/APL use a card detect GPIO however the host controller will not enable bus power unless it's card detect also reflects the presence of a card. Unfortunately those 2 things race which can result in commands not starting, after which the controller does nothing and there is a 10 second wait for the driver's 10-second timer to timeout. That is fixed by having the driver look also at the present state register to determine if the card is present. Consequently, provide a 'get_cd' mmc host operation for BXT/APL that does that. Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org # v4.4+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index cc851b065d0a..df3b8eced8c4 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -330,6 +330,33 @@ static void spt_read_drive_strength(struct sdhci_host *host) sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf); } +static int bxt_get_cd(struct mmc_host *mmc) +{ + int gpio_cd = mmc_gpio_get_cd(mmc); + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + int ret = 0; + + if (!gpio_cd) + return 0; + + pm_runtime_get_sync(mmc->parent); + + spin_lock_irqsave(&host->lock, flags); + + if (host->flags & SDHCI_DEVICE_DEAD) + goto out; + + ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); +out: + spin_unlock_irqrestore(&host->lock, flags); + + pm_runtime_mark_last_busy(mmc->parent); + pm_runtime_put_autosuspend(mmc->parent); + + return ret; +} + static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | @@ -362,6 +389,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) slot->cd_con_id = NULL; slot->cd_idx = 0; slot->cd_override_level = true; + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD || + slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD) + slot->host->mmc_host_ops.get_cd = bxt_get_cd; + return 0; } -- cgit v1.2.3 From 6a645dd87a5a506779810d7d9935c25fc2ef4687 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 9 Feb 2016 16:12:38 +0200 Subject: mmc: sdhci-acpi: Fix card detect race for Intel BXT/APL Intel BXT/APL use a card detect GPIO however the host controller will not enable bus power unless it's card detect also reflects the presence of a card. Unfortunately those 2 things race which can result in commands not starting, after which the controller does nothing and there is a 10 second wait for the driver's 10-second timer to timeout. That is fixed by having the driver look also at the present state register to determine if the card is present. Consequently, provide a 'get_cd' mmc host operation for BXT/APL that does that. Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org # v4.4+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index f6047fc94062..a5cda926d38e 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -146,6 +146,33 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { .ops = &sdhci_acpi_ops_int, }; +static int bxt_get_cd(struct mmc_host *mmc) +{ + int gpio_cd = mmc_gpio_get_cd(mmc); + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + int ret = 0; + + if (!gpio_cd) + return 0; + + pm_runtime_get_sync(mmc->parent); + + spin_lock_irqsave(&host->lock, flags); + + if (host->flags & SDHCI_DEVICE_DEAD) + goto out; + + ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); +out: + spin_unlock_irqrestore(&host->lock, flags); + + pm_runtime_mark_last_busy(mmc->parent); + pm_runtime_put_autosuspend(mmc->parent); + + return ret; +} + static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, const char *hid, const char *uid) { @@ -196,6 +223,9 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, /* Platform specific code during sd probe slot goes here */ + if (hid && !strcmp(hid, "80865ACA")) + host->mmc_host_ops.get_cd = bxt_get_cd; + return 0; } -- cgit v1.2.3 From e972c37459c813190461dabfeaac228e00aae259 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 10 Feb 2016 09:25:17 +0100 Subject: ARM: 8519/1: ICST: try other dividends than 1 Since the dawn of time the ICST code has only supported divide by one or hang in an eternal loop. Luckily we were always dividing by one because the reference frequency for the systems using the ICSTs is 24MHz and the [min,max] values for the PLL input if [10,320] MHz for ICST307 and [6,200] for ICST525, so the loop will always terminate immediately without assigning any divisor for the reference frequency. But for the code to make sense, let's insert the missing i++ Reported-by: David Binderman Cc: stable@vger.kernel.org Signed-off-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/common/icst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/common/icst.c b/arch/arm/common/icst.c index d3c0e69df259..d7ed252708c5 100644 --- a/arch/arm/common/icst.c +++ b/arch/arm/common/icst.c @@ -62,6 +62,7 @@ icst_hz_to_vco(const struct icst_params *p, unsigned long freq) if (f > p->vco_min && f <= p->vco_max) break; + i++; } while (i < 8); if (i >= 8) -- cgit v1.2.3 From bc4ef7592f657ae81b017207a1098817126ad4cb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 13 Nov 2015 13:44:28 +0100 Subject: btrfs: properly set the termination value of ctx->pos in readdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value of ctx->pos in the last readdir call is supposed to be set to INT_MAX due to 32bit compatibility, unless 'pos' is intentially set to a larger value, then it's LLONG_MAX. There's a report from PaX SIZE_OVERFLOW plugin that "ctx->pos++" overflows (https://forums.grsecurity.net/viewtopic.php?f=1&t=4284), on a 64bit arch, where the value is 0x7fffffffffffffff ie. LLONG_MAX before the increment. We can get to that situation like that: * emit all regular readdir entries * still in the same call to readdir, bump the last pos to INT_MAX * next call to readdir will not emit any entries, but will reach the bump code again, finds pos to be INT_MAX and sets it to LLONG_MAX Normally this is not a problem, but if we call readdir again, we'll find 'pos' set to LLONG_MAX and the unconditional increment will overflow. The report from Victor at (http://thread.gmane.org/gmane.comp.file-systems.btrfs/49500) with debugging print shows that pattern: Overflow: e Overflow: 7fffffff Overflow: 7fffffffffffffff PAX: size overflow detected in function btrfs_real_readdir fs/btrfs/inode.c:5760 cicus.935_282 max, count: 9, decl: pos; num: 0; context: dir_context; CPU: 0 PID: 2630 Comm: polkitd Not tainted 4.2.3-grsec #1 Hardware name: Gigabyte Technology Co., Ltd. H81ND2H/H81ND2H, BIOS F3 08/11/2015 ffffffff81901608 0000000000000000 ffffffff819015e6 ffffc90004973d48 ffffffff81742f0f 0000000000000007 ffffffff81901608 ffffc90004973d78 ffffffff811cb706 0000000000000000 ffff8800d47359e0 ffffc90004973ed8 Call Trace: [] dump_stack+0x4c/0x7f [] report_size_overflow+0x36/0x40 [] btrfs_real_readdir+0x69c/0x6d0 [] iterate_dir+0xa8/0x150 [] ? __fget_light+0x2d/0x70 [] SyS_getdents+0xba/0x1c0 Overflow: 1a [] ? iterate_dir+0x150/0x150 [] entry_SYSCALL_64_fastpath+0x12/0x83 The jump from 7fffffff to 7fffffffffffffff happens when new dir entries are not yet synced and are processed from the delayed list. Then the code could go to the bump section again even though it might not emit any new dir entries from the delayed list. The fix avoids entering the "bump" section again once we've finished emitting the entries, both for synced and delayed entries. References: https://forums.grsecurity.net/viewtopic.php?f=1&t=4284 Reported-by: Victor CC: stable@vger.kernel.org Signed-off-by: David Sterba Tested-by: Holger Hoffstätte Signed-off-by: Chris Mason --- fs/btrfs/delayed-inode.c | 3 ++- fs/btrfs/delayed-inode.h | 2 +- fs/btrfs/inode.c | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 0be47e4b8136..b57daa895cea 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1689,7 +1689,7 @@ int btrfs_should_delete_dir_index(struct list_head *del_list, * */ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, - struct list_head *ins_list) + struct list_head *ins_list, bool *emitted) { struct btrfs_dir_item *di; struct btrfs_delayed_item *curr, *next; @@ -1733,6 +1733,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, if (over) return 1; + *emitted = true; } return 0; } diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index f70119f25421..0167853c84ae 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -144,7 +144,7 @@ void btrfs_put_delayed_items(struct list_head *ins_list, int btrfs_should_delete_dir_index(struct list_head *del_list, u64 index); int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, - struct list_head *ins_list); + struct list_head *ins_list, bool *emitted); /* for init */ int __init btrfs_delayed_inode_init(void); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e4565456eb01..600bf0d9ceaf 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5716,6 +5716,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) char *name_ptr; int name_len; int is_curr = 0; /* ctx->pos points to the current index? */ + bool emitted; /* FIXME, use a real flag for deciding about the key type */ if (root->fs_info->tree_root == root) @@ -5744,6 +5745,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) if (ret < 0) goto err; + emitted = false; while (1) { leaf = path->nodes[0]; slot = path->slots[0]; @@ -5823,6 +5825,7 @@ skip: if (over) goto nopos; + emitted = true; di_len = btrfs_dir_name_len(leaf, di) + btrfs_dir_data_len(leaf, di) + sizeof(*di); di_cur += di_len; @@ -5835,11 +5838,20 @@ next: if (key_type == BTRFS_DIR_INDEX_KEY) { if (is_curr) ctx->pos++; - ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list); + ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list, &emitted); if (ret) goto nopos; } + /* + * If we haven't emitted any dir entry, we must not touch ctx->pos as + * it was was set to the termination value in previous call. We assume + * that "." and ".." were emitted if we reach this point and set the + * termination value as well for an empty directory. + */ + if (ctx->pos > 2 && !emitted) + goto nopos; + /* Reached end of directory/root. Bump pos past the last item. */ ctx->pos++; -- cgit v1.2.3 From a53b8394ec3c67255928df6ee9cc99dd1cd452e3 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Thu, 11 Feb 2016 12:00:51 +0000 Subject: thermal: cpu_cooling: fix out of bounds access in time_in_idle In __cpufreq_cooling_register() we allocate the arrays for time_in_idle and time_in_idle_timestamp to be as big as the number of cpus in this cpufreq device. However, in get_load() we access this array using the cpu number as index, which can result in an out of bound access. Index time_in_idle{,_timestamp} using the index in the cpufreq_device's allowed_cpus mask, as we do for the load_cpu array in cpufreq_get_requested_power() Reported-by: Nicolas Boichat Cc: Amit Daniel Kachhap Cc: Zhang Rui Cc: Eduardo Valentin Tested-by: Nicolas Boichat Acked-by: Viresh Kumar Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin --- drivers/thermal/cpu_cooling.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index e3fbc5a5d88f..6ceac4f2d4b2 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -377,26 +377,28 @@ static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_device, * get_load() - get load for a cpu since last updated * @cpufreq_device: &struct cpufreq_cooling_device for this cpu * @cpu: cpu number + * @cpu_idx: index of the cpu in cpufreq_device->allowed_cpus * * Return: The average load of cpu @cpu in percentage since this * function was last called. */ -static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu) +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu, + int cpu_idx) { u32 load; u64 now, now_idle, delta_time, delta_idle; now_idle = get_cpu_idle_time(cpu, &now, 0); - delta_idle = now_idle - cpufreq_device->time_in_idle[cpu]; - delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu]; + delta_idle = now_idle - cpufreq_device->time_in_idle[cpu_idx]; + delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu_idx]; if (delta_time <= delta_idle) load = 0; else load = div64_u64(100 * (delta_time - delta_idle), delta_time); - cpufreq_device->time_in_idle[cpu] = now_idle; - cpufreq_device->time_in_idle_timestamp[cpu] = now; + cpufreq_device->time_in_idle[cpu_idx] = now_idle; + cpufreq_device->time_in_idle_timestamp[cpu_idx] = now; return load; } @@ -598,7 +600,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, u32 load; if (cpu_online(cpu)) - load = get_load(cpufreq_device, cpu); + load = get_load(cpufreq_device, cpu, i); else load = 0; -- cgit v1.2.3 From a5ebb87db84392edfd3142c3a6a78431d820a789 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 11 Feb 2016 16:42:58 +0100 Subject: Revert "mmc: block: don't use parameter prefix if built as module" This reverts commit 829b6962f7e3cfc06f7c5c26269fd47ad48cf503. Revert this change as it causes a sysfs path to change and therefore introduces and ABI regression. More precisely Android's vold is not being able to access /sys/module/mmcblk/parameters/perdev_minors any more, since the path becomes changed to: "/sys/module/mmc_block/..." Fixes: 829b6962f7e3 ("mmc: block: don't use parameter prefix if built as module") Reported-by: John Stultz Cc: Andy Shevchenko Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 951641a96806..fe207e542032 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -47,13 +47,10 @@ #include "queue.h" MODULE_ALIAS("mmc:block"); - -#ifdef KERNEL #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX #endif #define MODULE_PARAM_PREFIX "mmcblk." -#endif #define INAND_CMD38_ARG_EXT_CSD 113 #define INAND_CMD38_ARG_ERASE 0x00 -- cgit v1.2.3 From fa51b247d67b521e1a1103cad75eb7a666fc9ff9 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 9 Feb 2016 21:11:14 +0100 Subject: net/mlx4: fix some error handling in mlx4_multi_func_init() The while loop after err_slaves should use post-decrement; otherwise we'll fail to do the kfrees for i==0, and will run into out-of-bounds accesses if the setup above failed already at i==0. [I'm not sure why one even bothers populating the ->vlan_filter array: mlx4.h isn't #included by anything outside drivers/net/ethernet/mellanox/mlx4/, and "git grep -C2 -w vlan_filter drivers/net/ethernet/mellanox/mlx4/" seems to suggest that the vlan_filter elements aren't used at all.] Signed-off-by: Rasmus Villemoes Signed-off-by: Doug Ledford --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index d48d5793407d..e94ca1c3fc7c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2429,7 +2429,7 @@ err_thread: flush_workqueue(priv->mfunc.master.comm_wq); destroy_workqueue(priv->mfunc.master.comm_wq); err_slaves: - while (--i) { + while (i--) { for (port = 1; port <= MLX4_MAX_PORTS; port++) kfree(priv->mfunc.master.slave_state[i].vlan_filter[port]); } -- cgit v1.2.3 From ee50aeac60ba5c4c7e072fbc0c9aa2043c8896e6 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Thu, 11 Feb 2016 10:24:42 +0200 Subject: IB/core: Fix reading capability mask of the port info class When checking specific attribute from a bit mask, need to use bitwise AND and not logical AND, fixed that. Fixes: 145d9c541032 ('IB/core: Display extended counter set if available') Signed-off-by: Eran Ben Elisha Signed-off-by: Matan Barak Reviewed-by: Or Gerlitz Reviewed-by: Christoph Lameter Signed-off-by: Doug Ledford --- drivers/infiniband/core/sysfs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index ec46386e3c7f..14606afbfaa8 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -720,12 +720,11 @@ static struct attribute_group *get_counter_table(struct ib_device *dev, if (get_perf_mad(dev, port_num, IB_PMA_CLASS_PORT_INFO, &cpi, 40, sizeof(cpi)) >= 0) { - - if (cpi.capability_mask && IB_PMA_CLASS_CAP_EXT_WIDTH) + if (cpi.capability_mask & IB_PMA_CLASS_CAP_EXT_WIDTH) /* We have extended counters */ return &pma_group_ext; - if (cpi.capability_mask && IB_PMA_CLASS_CAP_EXT_WIDTH_NOIETF) + if (cpi.capability_mask & IB_PMA_CLASS_CAP_EXT_WIDTH_NOIETF) /* But not the IETF ones */ return &pma_group_noietf; } -- cgit v1.2.3 From 7707535ab95e2231b6d7f2bfb4f27558e83c4dc2 Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Thu, 11 Feb 2016 16:12:55 -0800 Subject: ubsan: cosmetic fix to Kconfig text When enabling UBSAN_SANITIZE_ALL, the kernel image size gets increased significantly (~3x). So, it sounds better to have some note in Kconfig. And, fixed a typo. Signed-off-by: Yang Shi Acked-by: Andrey Ryabinin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/Kconfig.ubsan | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan index 49518fb48cab..e07c1ba9ba13 100644 --- a/lib/Kconfig.ubsan +++ b/lib/Kconfig.ubsan @@ -18,6 +18,8 @@ config UBSAN_SANITIZE_ALL This option activates instrumentation for the entire kernel. If you don't enable this option, you have to explicitly specify UBSAN_SANITIZE := y for the files/directories you want to check for UB. + Enabling this option will get kernel image size increased + significantly. config UBSAN_ALIGNMENT bool "Enable checking of pointers alignment" @@ -25,5 +27,5 @@ config UBSAN_ALIGNMENT default y if !HAVE_EFFICIENT_UNALIGNED_ACCESS help This option enables detection of unaligned memory accesses. - Enabling this option on architectures that support unalligned + Enabling this option on architectures that support unaligned accesses may produce a lot of false positives. -- cgit v1.2.3 From 62eb320ab077890dbbcc28343fa6432a82a10c35 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 11 Feb 2016 16:12:58 -0800 Subject: mm: fix filemap.c kernel doc warning Add missing kernel-doc notation for function parameter 'gfp_mask' to fix kernel-doc warning. mm/filemap.c:1898: warning: No description found for parameter 'gfp_mask' Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/filemap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/filemap.c b/mm/filemap.c index bc943867d68c..23edccecadb0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1890,6 +1890,7 @@ EXPORT_SYMBOL(generic_file_read_iter); * page_cache_read - adds requested page to the page cache if not already there * @file: file to read * @offset: page index + * @gfp_mask: memory allocation flags * * This adds the requested page to the page cache if it isn't already there, * and schedules an I/O to read in its contents from disk. -- cgit v1.2.3 From 7eb391299419a03cbe0fa5ab0e6b0932e42c7a36 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 11 Feb 2016 16:13:00 -0800 Subject: vsprintf: kptr_restrict is okay in IRQ when 2 The kptr_restrict flag, when set to 1, only prints the kernel address when the user has CAP_SYSLOG. When it is set to 2, the kernel address is always printed as zero. When set to 1, this needs to check whether or not we're in IRQ. However, when set to 2, this check is unneccessary, and produces confusing results in dmesg. Thus, only make sure we're not in IRQ when mode 1 is used, but not mode 2. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Jason A. Donenfeld Cc: Rasmus Villemoes Cc: Kees Cook Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/vsprintf.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 48ff9c36644d..f44e178e6ede 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1590,22 +1590,23 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return buf; } case 'K': - /* - * %pK cannot be used in IRQ context because its test - * for CAP_SYSLOG would be meaningless. - */ - if (kptr_restrict && (in_irq() || in_serving_softirq() || - in_nmi())) { - if (spec.field_width == -1) - spec.field_width = default_width; - return string(buf, end, "pK-error", spec); - } - switch (kptr_restrict) { case 0: /* Always print %pK values */ break; case 1: { + const struct cred *cred; + + /* + * kptr_restrict==1 cannot be used in IRQ context + * because its test for CAP_SYSLOG would be meaningless. + */ + if (in_irq() || in_serving_softirq() || in_nmi()) { + if (spec.field_width == -1) + spec.field_width = default_width; + return string(buf, end, "pK-error", spec); + } + /* * Only print the real pointer value if the current * process has CAP_SYSLOG and is running with the @@ -1615,8 +1616,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, * leak pointer values if a binary opens a file using * %pK and then elevates privileges before reading it. */ - const struct cred *cred = current_cred(); - + cred = current_cred(); if (!has_capability_noaudit(current, CAP_SYSLOG) || !uid_eq(cred->euid, cred->uid) || !gid_eq(cred->egid, cred->gid)) -- cgit v1.2.3 From 6b9116a652bd9e0e2994505cfaaa5f66deaa2a05 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Thu, 11 Feb 2016 16:13:03 -0800 Subject: mm, dax: check for pmd_none() after split_huge_pmd() DAX implements split_huge_pmd() by clearing pmd. This simple approach reduces memory overhead, as we don't need to deposit page table on huge page mapping to make split_huge_pmd() never-fail. PTE table can be allocated and populated later on page fault from backing store. But one side effect is that have to check if pmd is pmd_none() after split_huge_pmd(). In most places we do this already to deal with parallel MADV_DONTNEED. But I found two call sites which is not affected by MADV_DONTNEED (due down_write(mmap_sem)), but need to have the check to work with DAX properly. Signed-off-by: Kirill A. Shutemov Cc: Dan Williams Cc: Matthew Wilcox Cc: Andrea Arcangeli Cc: Ross Zwisler Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/mprotect.c | 6 ++++-- mm/mremap.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mm/mprotect.c b/mm/mprotect.c index 8eb7bb40dc40..f7cb3d4d9c2e 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -160,9 +160,11 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, } if (pmd_trans_huge(*pmd) || pmd_devmap(*pmd)) { - if (next - addr != HPAGE_PMD_SIZE) + if (next - addr != HPAGE_PMD_SIZE) { split_huge_pmd(vma, pmd, addr); - else { + if (pmd_none(*pmd)) + continue; + } else { int nr_ptes = change_huge_pmd(vma, pmd, addr, newprot, prot_numa); diff --git a/mm/mremap.c b/mm/mremap.c index d77946a997f7..8eeba02fc991 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -210,6 +210,8 @@ unsigned long move_page_tables(struct vm_area_struct *vma, } } split_huge_pmd(vma, old_pmd, old_addr); + if (pmd_none(*old_pmd)) + continue; VM_BUG_ON(pmd_trans_huge(*old_pmd)); } if (pmd_none(*new_pmd) && __pte_alloc(new_vma->vm_mm, new_vma, -- cgit v1.2.3 From 078c6c3a5e7dc53a9a23408cc32c83954abb5d0d Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 11 Feb 2016 16:13:06 -0800 Subject: mm/backing-dev.c: fix error path in wb_init() We need to use post-decrement to get percpu_counter_destroy() called on &wb->stat[0]. Moreover, the pre-decremebt would cause infinite out-of-bounds accesses if the setup code failed at i==0. Signed-off-by: Rasmus Villemoes Cc: Johannes Weiner Cc: Michal Hocko Cc: Vladimir Davydov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/backing-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 926c76d56388..c554d173a65f 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -328,7 +328,7 @@ static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi, return 0; out_destroy_stat: - while (--i) + while (i--) percpu_counter_destroy(&wb->stat[i]); fprop_local_destroy_percpu(&wb->completions); out_put_cong: -- cgit v1.2.3 From 6a6ac72fd6ea32594b316513e1826c3f6db4cc93 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Thu, 11 Feb 2016 16:13:09 -0800 Subject: mm,thp: khugepaged: call pte flush at the time of collapse This showed up on ARC when running LMBench bw_mem tests as Overlapping TLB Machine Check Exception triggered due to STLB entry (2M pages) overlapping some NTLB entry (regular 8K page). bw_mem 2m touches a large chunk of vaddr creating NTLB entries. In the interim khugepaged kicks in, collapsing the contiguous ptes into a single pmd. pmdp_collapse_flush()->flush_pmd_tlb_range() is called to flush out NTLB entries for the ptes. This for ARC (by design) can only shootdown STLB entries (for pmd). The stray NTLB entries cause the overlap with the subsequent STLB entry for collapsed page. So make pmdp_collapse_flush() call pte flush interface not pmd flush. Note that originally all thp flush call sites in generic code called flush_tlb_range() leaving it to architecture to implement the flush for pte and/or pmd. Commit 12ebc1581ad11454 changed this by calling a new opt-in API flush_pmd_tlb_range() which made the semantics more explicit but failed to distinguish the pte vs pmd flush in generic code, which is what this patch fixes. Note that ARC can fixed w/o touching the generic pmdp_collapse_flush() by defining a ARC version, but that defeats the purpose of generic version, plus sementically this is the right thing to do. Fixes STAR 9000961194: LMBench on AXS103 triggering duplicate TLB exceptions with super pages Fixes: 12ebc1581ad11454 ("mm,thp: introduce flush_pmd_tlb_range") Signed-off-by: Vineet Gupta Reviewed-by: Aneesh Kumar K.V Acked-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: [4.4] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/pgtable-generic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c index 9d4767698a1c..9f131c6034c5 100644 --- a/mm/pgtable-generic.c +++ b/mm/pgtable-generic.c @@ -195,7 +195,9 @@ pmd_t pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address, VM_BUG_ON(address & ~HPAGE_PMD_MASK); VM_BUG_ON(pmd_trans_huge(*pmdp)); pmd = pmdp_huge_get_and_clear(vma->vm_mm, address, pmdp); - flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE); + + /* collapse entails shooting down ptes not pmd */ + flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); return pmd; } #endif -- cgit v1.2.3 From 6b75d14912f2d89a3539c0b3a100519e1eec9a63 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Thu, 11 Feb 2016 16:13:11 -0800 Subject: mm,thp: fix spellos in describing __HAVE_ARCH_FLUSH_PMD_TLB_RANGE [akpm@linux-foundation.org: s/threshhold/threshold/] Signed-off-by: Vineet Gupta Cc: Kirill A. Shutemov Cc: Aneesh Kumar K.V Cc: Andrea Arcangeli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/pgtable-generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c index 9f131c6034c5..06a005b979a7 100644 --- a/mm/pgtable-generic.c +++ b/mm/pgtable-generic.c @@ -90,9 +90,9 @@ pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address, * ARCHes with special requirements for evicting THP backing TLB entries can * implement this. Otherwise also, it can help optimize normal TLB flush in * THP regime. stock flush_tlb_range() typically has optimization to nuke the - * entire TLB TLB if flush span is greater than a threshhold, which will + * entire TLB if flush span is greater than a threshold, which will * likely be true for a single huge page. Thus a single thp flush will - * invalidate the entire TLB which is not desitable. + * invalidate the entire TLB which is not desirable. * e.g. see arch/arc: flush_pmd_tlb_range */ #define flush_pmd_tlb_range(vma, addr, end) flush_tlb_range(vma, addr, end) -- cgit v1.2.3 From 4a389810bc3cb0e73443104f0827e81e23cb1e12 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 11 Feb 2016 16:13:14 -0800 Subject: kernel/locking/lockdep.c: convert hash tables to hlists Mike said: : CONFIG_UBSAN_ALIGNMENT breaks x86-64 kernel with lockdep enabled, i. e : kernel with CONFIG_UBSAN_ALIGNMENT fails to load without even any error : message. : : The problem is that ubsan callbacks use spinlocks and might be called : before lockdep is initialized. Particularly this line in the : reserve_ebda_region function causes problem: : : lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES); : : If i put lockdep_init() before reserve_ebda_region call in : x86_64_start_reservations kernel loads well. Fix this ordering issue permanently: change lockdep so that it uses hlists for the hash tables. Unlike a list_head, an hlist_head is in its initialized state when it is all-zeroes, so lockdep is ready for operation immediately upon boot - lockdep_init() need not have run. The patch will also save some memory. lockdep_init() and lockdep_initialized can be done away with now - a 4.6 patch has been prepared to do this. Reported-by: Mike Krinkin Suggested-by: Mike Krinkin Cc: Andrey Ryabinin Cc: Ingo Molnar Cc: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/lockdep.h | 4 ++-- kernel/locking/lockdep.c | 42 +++++++++++++++++++----------------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index c57e424d914b..4dca42fd32f5 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -66,7 +66,7 @@ struct lock_class { /* * class-hash: */ - struct list_head hash_entry; + struct hlist_node hash_entry; /* * global list of all lock-classes: @@ -199,7 +199,7 @@ struct lock_chain { u8 irq_context; u8 depth; u16 base; - struct list_head entry; + struct hlist_node entry; u64 chain_key; }; diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 60ace56618f6..7537e568dabe 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -292,7 +292,7 @@ LIST_HEAD(all_lock_classes); #define __classhashfn(key) hash_long((unsigned long)key, CLASSHASH_BITS) #define classhashentry(key) (classhash_table + __classhashfn((key))) -static struct list_head classhash_table[CLASSHASH_SIZE]; +static struct hlist_head classhash_table[CLASSHASH_SIZE]; /* * We put the lock dependency chains into a hash-table as well, to cache @@ -303,7 +303,7 @@ static struct list_head classhash_table[CLASSHASH_SIZE]; #define __chainhashfn(chain) hash_long(chain, CHAINHASH_BITS) #define chainhashentry(chain) (chainhash_table + __chainhashfn((chain))) -static struct list_head chainhash_table[CHAINHASH_SIZE]; +static struct hlist_head chainhash_table[CHAINHASH_SIZE]; /* * The hash key of the lock dependency chains is a hash itself too: @@ -666,7 +666,7 @@ static inline struct lock_class * look_up_lock_class(struct lockdep_map *lock, unsigned int subclass) { struct lockdep_subclass_key *key; - struct list_head *hash_head; + struct hlist_head *hash_head; struct lock_class *class; #ifdef CONFIG_DEBUG_LOCKDEP @@ -719,7 +719,7 @@ look_up_lock_class(struct lockdep_map *lock, unsigned int subclass) if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) return NULL; - list_for_each_entry_rcu(class, hash_head, hash_entry) { + hlist_for_each_entry_rcu(class, hash_head, hash_entry) { if (class->key == key) { /* * Huh! same key, different name? Did someone trample @@ -742,7 +742,7 @@ static inline struct lock_class * register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) { struct lockdep_subclass_key *key; - struct list_head *hash_head; + struct hlist_head *hash_head; struct lock_class *class; DEBUG_LOCKS_WARN_ON(!irqs_disabled()); @@ -774,7 +774,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) * We have to do the hash-walk again, to avoid races * with another CPU: */ - list_for_each_entry_rcu(class, hash_head, hash_entry) { + hlist_for_each_entry_rcu(class, hash_head, hash_entry) { if (class->key == key) goto out_unlock_set; } @@ -805,7 +805,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) * We use RCU's safe list-add method to make * parallel walking of the hash-list safe: */ - list_add_tail_rcu(&class->hash_entry, hash_head); + hlist_add_head_rcu(&class->hash_entry, hash_head); /* * Add it to the global list of classes: */ @@ -2017,7 +2017,7 @@ static inline int lookup_chain_cache(struct task_struct *curr, u64 chain_key) { struct lock_class *class = hlock_class(hlock); - struct list_head *hash_head = chainhashentry(chain_key); + struct hlist_head *hash_head = chainhashentry(chain_key); struct lock_chain *chain; struct held_lock *hlock_curr; int i, j; @@ -2033,7 +2033,7 @@ static inline int lookup_chain_cache(struct task_struct *curr, * We can walk it lock-free, because entries only get added * to the hash: */ - list_for_each_entry_rcu(chain, hash_head, entry) { + hlist_for_each_entry_rcu(chain, hash_head, entry) { if (chain->chain_key == chain_key) { cache_hit: debug_atomic_inc(chain_lookup_hits); @@ -2057,7 +2057,7 @@ cache_hit: /* * We have to walk the chain again locked - to avoid duplicates: */ - list_for_each_entry(chain, hash_head, entry) { + hlist_for_each_entry(chain, hash_head, entry) { if (chain->chain_key == chain_key) { graph_unlock(); goto cache_hit; @@ -2091,7 +2091,7 @@ cache_hit: } chain_hlocks[chain->base + j] = class - lock_classes; } - list_add_tail_rcu(&chain->entry, hash_head); + hlist_add_head_rcu(&chain->entry, hash_head); debug_atomic_inc(chain_lookup_misses); inc_chains(); @@ -3875,7 +3875,7 @@ void lockdep_reset(void) nr_process_chains = 0; debug_locks = 1; for (i = 0; i < CHAINHASH_SIZE; i++) - INIT_LIST_HEAD(chainhash_table + i); + INIT_HLIST_HEAD(chainhash_table + i); raw_local_irq_restore(flags); } @@ -3894,7 +3894,7 @@ static void zap_class(struct lock_class *class) /* * Unhash the class and remove it from the all_lock_classes list: */ - list_del_rcu(&class->hash_entry); + hlist_del_rcu(&class->hash_entry); list_del_rcu(&class->lock_entry); RCU_INIT_POINTER(class->key, NULL); @@ -3917,7 +3917,7 @@ static inline int within(const void *addr, void *start, unsigned long size) void lockdep_free_key_range(void *start, unsigned long size) { struct lock_class *class; - struct list_head *head; + struct hlist_head *head; unsigned long flags; int i; int locked; @@ -3930,9 +3930,7 @@ void lockdep_free_key_range(void *start, unsigned long size) */ for (i = 0; i < CLASSHASH_SIZE; i++) { head = classhash_table + i; - if (list_empty(head)) - continue; - list_for_each_entry_rcu(class, head, hash_entry) { + hlist_for_each_entry_rcu(class, head, hash_entry) { if (within(class->key, start, size)) zap_class(class); else if (within(class->name, start, size)) @@ -3962,7 +3960,7 @@ void lockdep_free_key_range(void *start, unsigned long size) void lockdep_reset_lock(struct lockdep_map *lock) { struct lock_class *class; - struct list_head *head; + struct hlist_head *head; unsigned long flags; int i, j; int locked; @@ -3987,9 +3985,7 @@ void lockdep_reset_lock(struct lockdep_map *lock) locked = graph_lock(); for (i = 0; i < CLASSHASH_SIZE; i++) { head = classhash_table + i; - if (list_empty(head)) - continue; - list_for_each_entry_rcu(class, head, hash_entry) { + hlist_for_each_entry_rcu(class, head, hash_entry) { int match = 0; for (j = 0; j < NR_LOCKDEP_CACHING_CLASSES; j++) @@ -4027,10 +4023,10 @@ void lockdep_init(void) return; for (i = 0; i < CLASSHASH_SIZE; i++) - INIT_LIST_HEAD(classhash_table + i); + INIT_HLIST_HEAD(classhash_table + i); for (i = 0; i < CHAINHASH_SIZE; i++) - INIT_LIST_HEAD(chainhash_table + i); + INIT_HLIST_HEAD(chainhash_table + i); lockdep_initialized = 1; } -- cgit v1.2.3 From db78c22230d0bcc8b27b81f05b39f104f08232c5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 11 Feb 2016 16:13:17 -0800 Subject: mm: fix pfn_t vs highmem The pfn_t type uses an unsigned long to store a pfn + flags value. On a 64-bit platform the upper 12 bits of an unsigned long are never used for storing the value of a pfn. However, this is not true on highmem platforms, all 32-bits of a pfn value are used to address a 44-bit physical address space. A pfn_t needs to store a 64-bit value. Link: https://bugzilla.kernel.org/show_bug.cgi?id=112211 Fixes: 01c8f1c44b83 ("mm, dax, gpu: convert vm_insert_mixed to pfn_t") Signed-off-by: Dan Williams Reported-by: Stuart Foster Reported-by: Julian Margetson Tested-by: Julian Margetson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pfn.h | 2 +- include/linux/pfn_t.h | 19 +++++++++---------- kernel/memremap.c | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/include/linux/pfn.h b/include/linux/pfn.h index 2d8e49711b63..1132953235c0 100644 --- a/include/linux/pfn.h +++ b/include/linux/pfn.h @@ -10,7 +10,7 @@ * backing is indicated by flags in the high bits of the value. */ typedef struct { - unsigned long val; + u64 val; } pfn_t; #endif diff --git a/include/linux/pfn_t.h b/include/linux/pfn_t.h index 37448ab5fb5c..94994810c7c0 100644 --- a/include/linux/pfn_t.h +++ b/include/linux/pfn_t.h @@ -9,14 +9,13 @@ * PFN_DEV - pfn is not covered by system memmap by default * PFN_MAP - pfn has a dynamic page mapping established by a device driver */ -#define PFN_FLAGS_MASK (((unsigned long) ~PAGE_MASK) \ - << (BITS_PER_LONG - PAGE_SHIFT)) -#define PFN_SG_CHAIN (1UL << (BITS_PER_LONG - 1)) -#define PFN_SG_LAST (1UL << (BITS_PER_LONG - 2)) -#define PFN_DEV (1UL << (BITS_PER_LONG - 3)) -#define PFN_MAP (1UL << (BITS_PER_LONG - 4)) - -static inline pfn_t __pfn_to_pfn_t(unsigned long pfn, unsigned long flags) +#define PFN_FLAGS_MASK (((u64) ~PAGE_MASK) << (BITS_PER_LONG_LONG - PAGE_SHIFT)) +#define PFN_SG_CHAIN (1ULL << (BITS_PER_LONG_LONG - 1)) +#define PFN_SG_LAST (1ULL << (BITS_PER_LONG_LONG - 2)) +#define PFN_DEV (1ULL << (BITS_PER_LONG_LONG - 3)) +#define PFN_MAP (1ULL << (BITS_PER_LONG_LONG - 4)) + +static inline pfn_t __pfn_to_pfn_t(unsigned long pfn, u64 flags) { pfn_t pfn_t = { .val = pfn | (flags & PFN_FLAGS_MASK), }; @@ -29,7 +28,7 @@ static inline pfn_t pfn_to_pfn_t(unsigned long pfn) return __pfn_to_pfn_t(pfn, 0); } -extern pfn_t phys_to_pfn_t(phys_addr_t addr, unsigned long flags); +extern pfn_t phys_to_pfn_t(phys_addr_t addr, u64 flags); static inline bool pfn_t_has_page(pfn_t pfn) { @@ -87,7 +86,7 @@ static inline pmd_t pfn_t_pmd(pfn_t pfn, pgprot_t pgprot) #ifdef __HAVE_ARCH_PTE_DEVMAP static inline bool pfn_t_devmap(pfn_t pfn) { - const unsigned long flags = PFN_DEV|PFN_MAP; + const u64 flags = PFN_DEV|PFN_MAP; return (pfn.val & flags) == flags; } diff --git a/kernel/memremap.c b/kernel/memremap.c index 70ee3775de24..2c468dea60bc 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c @@ -150,7 +150,7 @@ void devm_memunmap(struct device *dev, void *addr) } EXPORT_SYMBOL(devm_memunmap); -pfn_t phys_to_pfn_t(phys_addr_t addr, unsigned long flags) +pfn_t phys_to_pfn_t(phys_addr_t addr, u64 flags) { return __pfn_to_pfn_t(addr >> PAGE_SHIFT, flags); } -- cgit v1.2.3 From 1ecb4ae5f0aaf48b538b9e90b7b360215a2cf9ed Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 11 Feb 2016 16:13:20 -0800 Subject: arch/x86/Kconfig: CONFIG_X86_UV should depend on CONFIG_EFI arch/x86/built-in.o: In function `uv_bios_call': (.text+0xeba00): undefined reference to `efi_call' Reported-by: kbuild test robot Suggested-by: "H. Peter Anvin" Cc: Ingo Molnar Reviewed-by: Matt Fleming Acked-by: Alex Thorlton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 9af2e6338400..ab2ed5328f0a 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -475,6 +475,7 @@ config X86_UV depends on X86_64 depends on X86_EXTENDED_PLATFORM depends on NUMA + depends on EFI depends on X86_X2APIC depends on PCI ---help--- -- cgit v1.2.3 From dfdf141429f0895b63c882facc42c86f225033cb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 8 Feb 2016 22:04:29 +0100 Subject: nvmem: core: fix error path in nvmem_add_cells() The current code fails to nvmem_cell_drop(cells[0]) - even worse, if the loop above fails already at i==0, we'll enter an essentially infinite loop doing nvmem_cell_drop on cells[-1], cells[-2], ... which is unlikely to end well. Also, we're not freeing the temporary backing array cells on the error path. Signed-off-by: Rasmus Villemoes Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9d11d9837312..de14fae6f7f6 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -294,9 +294,11 @@ static int nvmem_add_cells(struct nvmem_device *nvmem, return 0; err: - while (--i) + while (i--) nvmem_cell_drop(cells[i]); + kfree(cells); + return rval; } -- cgit v1.2.3 From cf09d6428de3aa6bb6a8164d86bc1cc0cd678d8e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 10 Feb 2016 11:41:08 +0100 Subject: w1: w1_process() is not freezable kthread w1_process() calls try_to_freeze(), but the thread doesn't mark itself freezable through set_freezable(), so the try_to_freeze() call is useless. Acked-by: Evgeniy Polyakov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index c9a7ff67d395..89a784751738 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -1147,7 +1147,6 @@ int w1_process(void *data) jremain = 1; } - try_to_freeze(); __set_current_state(TASK_INTERRUPTIBLE); /* hold list_mutex until after interruptible to prevent loosing -- cgit v1.2.3 From e1379b56e9e88653fcb58cbaa71cd6b1cc304918 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 10 Feb 2016 14:32:07 -0500 Subject: misc: eeprom_93xx46: Add quirks to support Atmel AT93C46D device. Atmel devices in this family have some quirks not found in other similar chips - they do not support a sequential read of the entire EEPROM contents, and the control word sent at the start of each operation varies in bit length. This commit adds quirk support to the driver and modifies the read implementation to support non-sequential reads for consistency with other misc/eeprom drivers. Tested on a custom Freescale VF610-based platform, with an AT93C46D device attached via dspi2. The spi-gpio driver was used to allow the necessary non-byte-sized transfers. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 126 ++++++++++++++++++++++++++---------- include/linux/eeprom_93xx46.h | 6 ++ 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index f0c30b366218..a3c3136abf3e 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -27,6 +27,15 @@ #define ADDR_ERAL 0x20 #define ADDR_EWEN 0x30 +struct eeprom_93xx46_devtype_data { + unsigned int quirks; +}; + +static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { + .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | + EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, +}; + struct eeprom_93xx46_dev { struct spi_device *spi; struct eeprom_93xx46_platform_data *pdata; @@ -35,6 +44,16 @@ struct eeprom_93xx46_dev { int addrlen; }; +static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev) +{ + return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ; +} + +static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev) +{ + return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH; +} + static ssize_t eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -42,58 +61,73 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, { struct eeprom_93xx46_dev *edev; struct device *dev; - struct spi_message m; - struct spi_transfer t[2]; - int bits, ret; - u16 cmd_addr; + ssize_t ret = 0; dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); - cmd_addr = OP_READ << edev->addrlen; + mutex_lock(&edev->lock); - if (edev->addrlen == 7) { - cmd_addr |= off & 0x7f; - bits = 10; - } else { - cmd_addr |= (off >> 1) & 0x3f; - bits = 9; - } + if (edev->pdata->prepare) + edev->pdata->prepare(edev); - dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", - cmd_addr, edev->spi->max_speed_hz); + while (count) { + struct spi_message m; + struct spi_transfer t[2] = { { 0 } }; + u16 cmd_addr = OP_READ << edev->addrlen; + size_t nbytes = count; + int bits; + int err; + + if (edev->addrlen == 7) { + cmd_addr |= off & 0x7f; + bits = 10; + if (has_quirk_single_word_read(edev)) + nbytes = 1; + } else { + cmd_addr |= (off >> 1) & 0x3f; + bits = 9; + if (has_quirk_single_word_read(edev)) + nbytes = 2; + } - spi_message_init(&m); - memset(t, 0, sizeof(t)); + dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", + cmd_addr, edev->spi->max_speed_hz); - t[0].tx_buf = (char *)&cmd_addr; - t[0].len = 2; - t[0].bits_per_word = bits; - spi_message_add_tail(&t[0], &m); + spi_message_init(&m); - t[1].rx_buf = buf; - t[1].len = count; - t[1].bits_per_word = 8; - spi_message_add_tail(&t[1], &m); + t[0].tx_buf = (char *)&cmd_addr; + t[0].len = 2; + t[0].bits_per_word = bits; + spi_message_add_tail(&t[0], &m); - mutex_lock(&edev->lock); + t[1].rx_buf = buf; + t[1].len = count; + t[1].bits_per_word = 8; + spi_message_add_tail(&t[1], &m); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + err = spi_sync(edev->spi, &m); + /* have to wait at least Tcsl ns */ + ndelay(250); - ret = spi_sync(edev->spi, &m); - /* have to wait at least Tcsl ns */ - ndelay(250); - if (ret) { - dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", - count, (int)off, ret); + if (err) { + dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", + nbytes, (int)off, err); + ret = err; + break; + } + + buf += nbytes; + off += nbytes; + count -= nbytes; + ret += nbytes; } if (edev->pdata->finish) edev->pdata->finish(edev); mutex_unlock(&edev->lock); - return ret ? : count; + return ret; } static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) @@ -112,7 +146,13 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) bits = 9; } - dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr); + if (has_quirk_instruction_length(edev)) { + cmd_addr <<= 2; + bits += 2; + } + + dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n", + is_on ? "en" : "ds", cmd_addr, bits); spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -247,6 +287,13 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) bits = 9; } + if (has_quirk_instruction_length(edev)) { + cmd_addr <<= 2; + bits += 2; + } + + dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); + spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -298,12 +345,15 @@ static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); static const struct of_device_id eeprom_93xx46_of_table[] = { { .compatible = "eeprom-93xx46", }, + { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, {} }; MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); static int eeprom_93xx46_probe_dt(struct spi_device *spi) { + const struct of_device_id *of_id = + of_match_device(eeprom_93xx46_of_table, &spi->dev); struct device_node *np = spi->dev.of_node; struct eeprom_93xx46_platform_data *pd; u32 tmp; @@ -331,6 +381,12 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) if (of_property_read_bool(np, "read-only")) pd->flags |= EE_READONLY; + if (of_id->data) { + const struct eeprom_93xx46_devtype_data *data = of_id->data; + + pd->quirks = data->quirks; + } + spi->dev.platform_data = pd; return 0; diff --git a/include/linux/eeprom_93xx46.h b/include/linux/eeprom_93xx46.h index 06791811e49d..92fa4c37ac1f 100644 --- a/include/linux/eeprom_93xx46.h +++ b/include/linux/eeprom_93xx46.h @@ -9,6 +9,12 @@ struct eeprom_93xx46_platform_data { #define EE_ADDR16 0x02 /* 16 bit addr. cfg */ #define EE_READONLY 0x08 /* forbid writing */ + unsigned int quirks; +/* Single word read transfers only; no sequential read. */ +#define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0) +/* Instructions such as EWEN are (addrlen + 2) in length. */ +#define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1) + /* * optional hooks to control additional logic * before and after spi transfer. -- cgit v1.2.3 From 3ca9b1ac28398c6fe0bed335d2d71a35e1c5f7c9 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 10 Feb 2016 14:32:08 -0500 Subject: misc: eeprom_93xx46: Add support for a GPIO 'select' line. This commit adds support to the eeprom_93x46 driver allowing a GPIO line to function as a 'select' or 'enable' signal prior to accessing the EEPROM. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/eeprom_93xx46.h | 3 +++ 2 files changed, 38 insertions(+) diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index a3c3136abf3e..f62ab29e293c 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -10,11 +10,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -343,6 +345,20 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev, } static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); +static void select_assert(void *context) +{ + struct eeprom_93xx46_dev *edev = context; + + gpiod_set_value_cansleep(edev->pdata->select, 1); +} + +static void select_deassert(void *context) +{ + struct eeprom_93xx46_dev *edev = context; + + gpiod_set_value_cansleep(edev->pdata->select, 0); +} + static const struct of_device_id eeprom_93xx46_of_table[] = { { .compatible = "eeprom-93xx46", }, { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, @@ -357,6 +373,8 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) struct device_node *np = spi->dev.of_node; struct eeprom_93xx46_platform_data *pd; u32 tmp; + int gpio; + enum of_gpio_flags of_flags; int ret; pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); @@ -381,6 +399,23 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) if (of_property_read_bool(np, "read-only")) pd->flags |= EE_READONLY; + gpio = of_get_named_gpio_flags(np, "select-gpios", 0, &of_flags); + if (gpio_is_valid(gpio)) { + unsigned long flags = + of_flags == OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0; + + ret = devm_gpio_request_one(&spi->dev, gpio, flags, + "eeprom_93xx46_select"); + if (ret) + return ret; + + pd->select = gpio_to_desc(gpio); + pd->prepare = select_assert; + pd->finish = select_deassert; + + gpiod_direction_output(pd->select, 0); + } + if (of_id->data) { const struct eeprom_93xx46_devtype_data *data = of_id->data; diff --git a/include/linux/eeprom_93xx46.h b/include/linux/eeprom_93xx46.h index 92fa4c37ac1f..885f587a3555 100644 --- a/include/linux/eeprom_93xx46.h +++ b/include/linux/eeprom_93xx46.h @@ -3,6 +3,8 @@ * platform description for 93xx46 EEPROMs. */ +struct gpio_desc; + struct eeprom_93xx46_platform_data { unsigned char flags; #define EE_ADDR8 0x01 /* 8 bit addr. cfg */ @@ -21,4 +23,5 @@ struct eeprom_93xx46_platform_data { */ void (*prepare)(void *); void (*finish)(void *); + struct gpio_desc *select; }; -- cgit v1.2.3 From 6cbb097fd3903fd6b419303ee8dc7f72b47d06f0 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 10 Feb 2016 23:57:26 +0200 Subject: mei: fix double freeing of a cb during link reset Fix double freeing of the cb that can happen if link reset kicks in the middle of blocked write from a device on the cl bus. Free cb inside mei_cl_write function on failure and drop cb free operation from callers, during a link reset the mei_cl_write function returns with an error, but the caller doesn't know if the cb was already queued or not so it doesn't know if the cb will be freed upon queue reclaim or it has to free it itself. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 4 +--- drivers/misc/mei/client.c | 4 +++- drivers/misc/mei/main.c | 26 ++++++++++---------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index f4cf43b47c7a..5d5996e39a67 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -44,7 +44,7 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *bus; - struct mei_cl_cb *cb = NULL; + struct mei_cl_cb *cb; ssize_t rets; if (WARN_ON(!cl || !cl->dev)) @@ -86,8 +86,6 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, out: mutex_unlock(&bus->device_lock); - if (rets < 0) - mei_io_cb_free(cb); return rets; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index af6816bc268f..a9cdb92b52d1 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1645,7 +1645,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); - return rets; + goto free; } cb->buf_idx = 0; @@ -1724,6 +1724,8 @@ err: cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); +free: + mei_io_cb_free(cb); return rets; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 17970163eacc..05775a6f7c88 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -267,7 +267,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; - struct mei_cl_cb *write_cb = NULL; + struct mei_cl_cb *cb; struct mei_device *dev; int rets; @@ -305,36 +305,30 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, } *offset = 0; - write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); - if (!write_cb) { + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); + if (!cb) { rets = -ENOMEM; goto out; } - rets = copy_from_user(write_cb->buf.data, ubuf, length); + rets = copy_from_user(cb->buf.data, ubuf, length); if (rets) { dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; + mei_io_cb_free(cb); goto out; } if (cl == &dev->iamthif_cl) { - rets = mei_amthif_write(cl, write_cb); - - if (rets) { - dev_err(dev->dev, - "amthif write failed with status = %d\n", rets); - goto out; - } - mutex_unlock(&dev->device_lock); - return length; + rets = mei_amthif_write(cl, cb); + if (!rets) + rets = length; + goto out; } - rets = mei_cl_write(cl, write_cb, false); + rets = mei_cl_write(cl, cb, false); out: mutex_unlock(&dev->device_lock); - if (rets < 0) - mei_io_cb_free(write_cb); return rets; } -- cgit v1.2.3 From 46d83a8782e73468c9f744d859fbb6af4d53c520 Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Wed, 10 Feb 2016 10:02:02 +0530 Subject: misc: ibmasm: Replace timeval with timespec64 This patch replaces timeval with timespec64 as 32 bit 'struct timeval' will not give current time beyond year 2038. The patch changes the code to use ktime_get_real_ts64() which returns a 'struct timespec64' instead of do_gettimeofday() which returns a 'struct timeval' This patch also alters the format strings in sprintf() for now.tv_sec and now.tv_nsec to incorporate 'long long' on 32 bit architectures and leading zeroes respectively. Signed-off-by: Amitoj Kaur Chawla Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/ibmasm.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h index 5bd127727d8e..9fea49d2e15b 100644 --- a/drivers/misc/ibmasm/ibmasm.h +++ b/drivers/misc/ibmasm/ibmasm.h @@ -34,6 +34,7 @@ #include #include #include +#include /* Driver identification */ #define DRIVER_NAME "ibmasm" @@ -53,9 +54,11 @@ extern int ibmasm_debug; static inline char *get_timestamp(char *buf) { - struct timeval now; - do_gettimeofday(&now); - sprintf(buf, "%lu.%lu", now.tv_sec, now.tv_usec); + struct timespec64 now; + + ktime_get_real_ts64(&now); + sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, + now.tv_nsec / NSEC_PER_USEC); return buf; } -- cgit v1.2.3 From 212265e5ad726ed7fd2ec7d61d36d9e0b0d3e655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 9 Feb 2016 21:05:32 -0800 Subject: android: binder: More offset validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure offsets don't point to overlapping flat_binder_object structs. Cc: Colin Cross Cc: Arve HjønnevÃ¥g Cc: Dmitry Shmidt Cc: Rom Lemarchand Cc: Serban Constantinescu Cc: Greg Kroah-Hartman Cc: Android Kernel Team Signed-off-by: Dmitry Shmidt Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a39e85f9efa9..1db376184955 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1321,6 +1321,7 @@ static void binder_transaction(struct binder_proc *proc, struct binder_transaction *t; struct binder_work *tcomplete; binder_size_t *offp, *off_end; + binder_size_t off_min; struct binder_proc *target_proc; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; @@ -1522,18 +1523,24 @@ static void binder_transaction(struct binder_proc *proc, goto err_bad_offset; } off_end = (void *)offp + tr->offsets_size; + off_min = 0; for (; offp < off_end; offp++) { struct flat_binder_object *fp; if (*offp > t->buffer->data_size - sizeof(*fp) || + *offp < off_min || t->buffer->data_size < sizeof(*fp) || !IS_ALIGNED(*offp, sizeof(u32))) { - binder_user_error("%d:%d got transaction with invalid offset, %lld\n", - proc->pid, thread->pid, (u64)*offp); + binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n", + proc->pid, thread->pid, (u64)*offp, + (u64)off_min, + (u64)(t->buffer->data_size - + sizeof(*fp))); return_error = BR_FAILED_REPLY; goto err_bad_offset; } fp = (struct flat_binder_object *)(t->buffer->data + *offp); + off_min = *offp + sizeof(struct flat_binder_object); switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { -- cgit v1.2.3 From 83050a4e21979fe1821916fce2fca36255569ed3 Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Tue, 9 Feb 2016 21:05:33 -0800 Subject: android: drivers: Avoid debugfs race in binder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a /d/binder/proc/[pid] entry is kept open after linux has torn down the associated process, binder_proc_show can deference an invalid binder_proc that has been stashed in the debugfs inode. Validate that the binder_proc ptr passed into binder_proc_show has not been freed by looking for it within the global process list whilst the global lock is held. If the ptr is not valid, print nothing. Cc: Colin Cross Cc: Arve HjønnevÃ¥g Cc: Dmitry Shmidt Cc: Rom Lemarchand Cc: Serban Constantinescu Cc: Greg Kroah-Hartman Cc: Android Kernel Team Signed-off-by: Dmitry Shmidt [jstultz: Minor commit message tweaks] Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 1db376184955..f0ce9959d14d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3600,13 +3600,24 @@ static int binder_transactions_show(struct seq_file *m, void *unused) static int binder_proc_show(struct seq_file *m, void *unused) { + struct binder_proc *itr; struct binder_proc *proc = m->private; int do_lock = !binder_debug_no_lock; + bool valid_proc = false; if (do_lock) binder_lock(__func__); - seq_puts(m, "binder proc state:\n"); - print_binder_proc(m, proc, 1); + + hlist_for_each_entry(itr, &binder_procs, proc_node) { + if (itr == proc) { + valid_proc = true; + break; + } + } + if (valid_proc) { + seq_puts(m, "binder proc state:\n"); + print_binder_proc(m, proc, 1); + } if (do_lock) binder_unlock(__func__); return 0; -- cgit v1.2.3 From 37eda9df5bd8444263418495632ea6ec750f03f9 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 10 Feb 2016 06:52:07 +0530 Subject: ARC: mm: Introduce explicit super page size support MMUv4 supports 2 concurrent page sizes: Normal and Super [4K to 16M] So far Linux supported a single super page size for a given Normal page, depending on the software page walking address split. e.g. we had 11:8:13 address split for 8K page, which meant super page was 2 ^(8+13) = 2M (given that THP size has to be PMD_SHIFT) Now we turn this around, by allowing multiple Super Pages in Kconfig (currently 2M and 16M only) and forcing page walker address split to PGDIR_SHIFT and PAGE_SHIFT For configs without Super page, things are same as before and PGDIR_SHIFT can be hacked to get non default address split The motivation for this change is a customer who needs 16M super page and a 8K Normal page combo. Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 19 ++++++++++++++++++ arch/arc/include/asm/pgtable.h | 45 ++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index bb15e8062b1f..63979c7eede5 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -341,6 +341,19 @@ config ARC_PAGE_SIZE_4K endchoice +choice + prompt "MMU Super Page Size" + depends on ISA_ARCV2 && TRANSPARENT_HUGEPAGE + default ARC_HUGEPAGE_2M + +config ARC_HUGEPAGE_2M + bool "2MB" + +config ARC_HUGEPAGE_16M + bool "16MB" + +endchoice + if ISA_ARCOMPACT config ARC_COMPACT_IRQ_LEVELS @@ -569,6 +582,12 @@ endmenu endmenu # "ARC Architecture Configuration" source "mm/Kconfig" + +config FORCE_MAX_ZONEORDER + int "Maximum zone order" + default "12" if ARC_HUGEPAGE_16M + default "11" + source "net/Kconfig" source "drivers/Kconfig" source "fs/Kconfig" diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h index 57af2f05ae84..d426d4215513 100644 --- a/arch/arc/include/asm/pgtable.h +++ b/arch/arc/include/asm/pgtable.h @@ -179,37 +179,44 @@ #define __S111 PAGE_U_X_W_R /**************************************************************** - * Page Table Lookup split + * 2 tier (PGD:PTE) software page walker * - * We implement 2 tier paging and since this is all software, we are free - * to customize the span of a PGD / PTE entry to suit us - * - * 32 bit virtual address + * [31] 32 bit virtual address [0] * ------------------------------------------------------- - * | BITS_FOR_PGD | BITS_FOR_PTE | BITS_IN_PAGE | + * | | <------------ PGDIR_SHIFT ----------> | + * | | | + * | BITS_FOR_PGD | BITS_FOR_PTE | <-- PAGE_SHIFT --> | * ------------------------------------------------------- * | | | * | | --> off in page frame - * | | * | ---> index into Page Table - * | * ----> index into Page Directory + * + * In a single page size configuration, only PAGE_SHIFT is fixed + * So both PGD and PTE sizing can be tweaked + * e.g. 8K page (PAGE_SHIFT 13) can have + * - PGDIR_SHIFT 21 -> 11:8:13 address split + * - PGDIR_SHIFT 24 -> 8:11:13 address split + * + * If Super Page is configured, PGDIR_SHIFT becomes fixed too, + * so the sizing flexibility is gone. */ -#define BITS_IN_PAGE PAGE_SHIFT - -/* Optimal Sizing of Pg Tbl - based on MMU page size */ -#if defined(CONFIG_ARC_PAGE_SIZE_8K) -#define BITS_FOR_PTE 8 /* 11:8:13 */ -#elif defined(CONFIG_ARC_PAGE_SIZE_16K) -#define BITS_FOR_PTE 8 /* 10:8:14 */ -#elif defined(CONFIG_ARC_PAGE_SIZE_4K) -#define BITS_FOR_PTE 9 /* 11:9:12 */ +#if defined(CONFIG_ARC_HUGEPAGE_16M) +#define PGDIR_SHIFT 24 +#elif defined(CONFIG_ARC_HUGEPAGE_2M) +#define PGDIR_SHIFT 21 +#else +/* + * Only Normal page support so "hackable" (see comment above) + * Default value provides 11:8:13 (8K), 11:9:12 (4K) + */ +#define PGDIR_SHIFT 21 #endif -#define BITS_FOR_PGD (32 - BITS_FOR_PTE - BITS_IN_PAGE) +#define BITS_FOR_PTE (PGDIR_SHIFT - PAGE_SHIFT) +#define BITS_FOR_PGD (32 - PGDIR_SHIFT) -#define PGDIR_SHIFT (32 - BITS_FOR_PGD) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) /* vaddr span, not PDG sz */ #define PGDIR_MASK (~(PGDIR_SIZE-1)) -- cgit v1.2.3 From 613317bd212c585c20796c10afe5daaa95d4b0a1 Mon Sep 17 00:00:00 2001 From: Ryan Ware Date: Thu, 11 Feb 2016 15:58:44 -0800 Subject: EVM: Use crypto_memneq() for digest comparisons This patch fixes vulnerability CVE-2016-2085. The problem exists because the vm_verify_hmac() function includes a use of memcmp(). Unfortunately, this allows timing side channel attacks; specifically a MAC forgery complexity drop from 2^128 to 2^12. This patch changes the memcmp() to the cryptographically safe crypto_memneq(). Reported-by: Xiaofei Rex Guo Signed-off-by: Ryan Ware Cc: stable@vger.kernel.org Signed-off-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/evm/evm_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index f7160253f17f..e6ea9d4b1de9 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "evm.h" int evm_initialized; @@ -148,7 +149,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, xattr_value_len, calc.digest); if (rc) break; - rc = memcmp(xattr_data->digest, calc.digest, + rc = crypto_memneq(xattr_data->digest, calc.digest, sizeof(calc.digest)); if (rc) rc = -EINVAL; -- cgit v1.2.3 From 08bc327629cbd63bb2f66677e4b33b643695097c Mon Sep 17 00:00:00 2001 From: Alex Estrin Date: Thu, 11 Feb 2016 16:30:51 -0500 Subject: IB/ipoib: fix for rare multicast join race condition A narrow window for race condition still exist between multicast join thread and *dev_flush workers. A kernel crash caused by prolong erratic link state changes was observed (most likely a faulty cabling): [167275.656270] BUG: unable to handle kernel NULL pointer dereference at 0000000000000020 [167275.665973] IP: [] ipoib_mcast_join+0xae/0x1d0 [ib_ipoib] [167275.674443] PGD 0 [167275.677373] Oops: 0000 [#1] SMP ... [167275.977530] Call Trace: [167275.982225] [] ? ipoib_mcast_free+0x200/0x200 [ib_ipoib] [167275.992024] [] ipoib_mcast_join_task+0x2a7/0x490 [ib_ipoib] [167276.002149] [] process_one_work+0x17b/0x470 [167276.010754] [] worker_thread+0x11b/0x400 [167276.019088] [] ? rescuer_thread+0x400/0x400 [167276.027737] [] kthread+0xcf/0xe0 Here was a hit spot: ipoib_mcast_join() { .............. rec.qkey = priv->broadcast->mcmember.qkey; ^^^^^^^ ..... } Proposed patch should prevent multicast join task to continue if link state change is detected. Signed-off-by: Alex Estrin Changes from v4: - as suggested by Doug Ledford, optimized spinlock usage, i.e. ipoib_mcast_join() is called with lock held. Changes from v3: - sync with priv->lock before flag check. Chages from v2: - Move check for OPER_UP flag state to mcast_join() to ensure no event worker is in progress. - minor style fixes. Changes from v1: - No need to lock again if error detected. Signed-off-by: Doug Ledford --- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 050dfa175d16..25889311b1e9 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -456,7 +456,10 @@ out_locked: return status; } -static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) +/* + * Caller must hold 'priv->lock' + */ +static int ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ib_sa_multicast *multicast; @@ -466,6 +469,10 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) ib_sa_comp_mask comp_mask; int ret = 0; + if (!priv->broadcast || + !test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + return -EINVAL; + ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw); rec.mgid = mcast->mcmember.mgid; @@ -525,20 +532,23 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) rec.join_state = 4; #endif } + spin_unlock_irq(&priv->lock); multicast = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port, &rec, comp_mask, GFP_KERNEL, ipoib_mcast_join_complete, mcast); + spin_lock_irq(&priv->lock); if (IS_ERR(multicast)) { ret = PTR_ERR(multicast); ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret); - spin_lock_irq(&priv->lock); /* Requeue this join task with a backoff delay */ __ipoib_mcast_schedule_join_thread(priv, mcast, 1); clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); spin_unlock_irq(&priv->lock); complete(&mcast->done); + spin_lock_irq(&priv->lock); } + return 0; } void ipoib_mcast_join_task(struct work_struct *work) @@ -620,9 +630,10 @@ void ipoib_mcast_join_task(struct work_struct *work) /* Found the next unjoined group */ init_completion(&mcast->done); set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - spin_unlock_irq(&priv->lock); - ipoib_mcast_join(dev, mcast); - spin_lock_irq(&priv->lock); + if (ipoib_mcast_join(dev, mcast)) { + spin_unlock_irq(&priv->lock); + return; + } } else if (!delay_until || time_before(mcast->delay_until, delay_until)) delay_until = mcast->delay_until; @@ -641,10 +652,9 @@ out: if (mcast) { init_completion(&mcast->done); set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + ipoib_mcast_join(dev, mcast); } spin_unlock_irq(&priv->lock); - if (mcast) - ipoib_mcast_join(dev, mcast); } int ipoib_mcast_start_thread(struct net_device *dev) -- cgit v1.2.3 From 75c1657e1d50730dc0130a67977f7831a4e241f4 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 11 Feb 2016 21:09:57 +0200 Subject: IB/mlx5: Fix RC transport send queue overhead computation Fix the RC QPs send queue overhead computation to take into account two additional segments in the WQE which are needed for registration operations. The ATOMIC and UMR segments can't coexist together, so chose maximum out of them. The commit 9e65dc371b5c ("IB/mlx5: Fix RC transport send queue overhead computation") was intended to update RC transport as commit messages states, but added the code to UC transport. Fixes: 9e65dc371b5c ("IB/mlx5: Fix RC transport send queue overhead computation") Signed-off-by: Kamal Heib Signed-off-by: Leon Romanovsky Reviewed-by: Sagi Grimberg Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/qp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 9116bc3988a6..34cb8e87c7b8 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -270,8 +270,10 @@ static int sq_overhead(enum ib_qp_type qp_type) /* fall through */ case IB_QPT_RC: size += sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_atomic_seg) + - sizeof(struct mlx5_wqe_raddr_seg); + max(sizeof(struct mlx5_wqe_atomic_seg) + + sizeof(struct mlx5_wqe_raddr_seg), + sizeof(struct mlx5_wqe_umr_ctrl_seg) + + sizeof(struct mlx5_mkey_seg)); break; case IB_QPT_XRC_TGT: @@ -279,9 +281,9 @@ static int sq_overhead(enum ib_qp_type qp_type) case IB_QPT_UC: size += sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_raddr_seg) + - sizeof(struct mlx5_wqe_umr_ctrl_seg) + - sizeof(struct mlx5_mkey_seg); + max(sizeof(struct mlx5_wqe_raddr_seg), + sizeof(struct mlx5_wqe_umr_ctrl_seg) + + sizeof(struct mlx5_mkey_seg)); break; case IB_QPT_UD: -- cgit v1.2.3 From 07d86ca93db7e5cdf4743564d98292042ec21af7 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Sat, 13 Feb 2016 11:08:06 +0300 Subject: ALSA: usb-audio: avoid freeing umidi object twice The 'umidi' object will be free'd on the error path by snd_usbmidi_free() when tearing down the rawmidi interface. So we shouldn't try to free it in snd_usbmidi_create() after having registered the rawmidi interface. Found by KASAN. Signed-off-by: Andrey Konovalov Acked-by: Clemens Ladisch Cc: Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index cc39f63299ef..007cf5831121 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2455,7 +2455,6 @@ int snd_usbmidi_create(struct snd_card *card, else err = snd_usbmidi_create_endpoints(umidi, endpoints); if (err < 0) { - snd_usbmidi_free(umidi); return err; } -- cgit v1.2.3 From 18558cae0272f8fd9647e69d3fec1565a7949865 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 14 Feb 2016 13:05:20 -0800 Subject: Linux 4.5-rc4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 682840850f58..701c36056ca8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 4 PATCHLEVEL = 5 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Blurry Fish Butt # *DOCUMENTATION* -- cgit v1.2.3 From 6ddf2f0cb1e1931d16e08e30130ed04e5a441f2c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Feb 2016 09:40:26 +0300 Subject: misc: mic: use after free printing error message Swap the printk and kfree() to avoid a use after free bug. Fixes: 61e9c905df78 ('misc: mic: Enable VOP host side functionality') Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index c1dd000f042b..c3613f39eb35 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -290,9 +290,9 @@ static int vop_virtio_add_device(struct vop_vdev *vdev, ret = vop_copy_dp_entry(vdev, argp, &type, &dd); if (ret) { - kfree(vdev); dev_err(vop_dev(vdev), "%s %d err %d\n", __func__, __LINE__, ret); + kfree(vdev); return ret; } -- cgit v1.2.3 From 59ea25904406a606d1329650e45e13e682e89320 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Feb 2016 09:44:34 +0300 Subject: misc: mic: silence an overflow warning Static checkers complain that the this is a potential array overflow. We verify that it's not on the next line so this code is OK, but static checker warnings are annoying. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index c3613f39eb35..e94c7fb6712a 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -848,12 +848,13 @@ static int vop_virtio_copy_desc(struct vop_vdev *vdev, struct mic_copy_desc *copy) { int err; - struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + struct vop_vringh *vvr; err = vop_verify_copy_args(vdev, copy); if (err) return err; + vvr = &vdev->vvr[copy->vr_idx]; mutex_lock(&vvr->vr_mutex); if (!vop_vdevup(vdev)) { err = -ENODEV; -- cgit v1.2.3 From 783ea44db7dbe60d0def22b8812b89940b7555b4 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:39 +0530 Subject: ppdev: space prohibited between function name and parenthesis checkpatch was complaining about space between function name and open parenthesis. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 192 +++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 9e98d0153148..80ab757de8d3 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -106,13 +106,13 @@ static DEFINE_MUTEX(pp_do_mutex); #define PPGETTIME64 _IOR(PP_IOCTL, 0x95, s64[2]) #define PPSETTIME64 _IOW(PP_IOCTL, 0x96, s64[2]) -static inline void pp_enable_irq (struct pp_struct *pp) +static inline void pp_enable_irq(struct pp_struct *pp) { struct parport *port = pp->pdev->port; - port->ops->enable_irq (port); + port->ops->enable_irq(port); } -static ssize_t pp_read (struct file * file, char __user * buf, size_t count, +static ssize_t pp_read(struct file * file, char __user * buf, size_t count, loff_t * ppos) { unsigned int minor = iminor(file_inode(file)); @@ -139,7 +139,7 @@ static ssize_t pp_read (struct file * file, char __user * buf, size_t count, pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); - parport_set_timeout (pp->pdev, + parport_set_timeout(pp->pdev, (file->f_flags & O_NONBLOCK) ? PARPORT_INACTIVITY_O_NONBLOCK : pp->default_inactivity); @@ -165,7 +165,7 @@ static ssize_t pp_read (struct file * file, char __user * buf, size_t count, } bytes_read = (*fn)(pport, kbuffer, need, flags); } else { - bytes_read = parport_read (pport, kbuffer, need); + bytes_read = parport_read(pport, kbuffer, need); } if (bytes_read != 0) @@ -176,7 +176,7 @@ static ssize_t pp_read (struct file * file, char __user * buf, size_t count, break; } - if (signal_pending (current)) { + if (signal_pending(current)) { bytes_read = -ERESTARTSYS; break; } @@ -184,17 +184,17 @@ static ssize_t pp_read (struct file * file, char __user * buf, size_t count, cond_resched(); } - parport_set_timeout (pp->pdev, pp->default_inactivity); + parport_set_timeout(pp->pdev, pp->default_inactivity); - if (bytes_read > 0 && copy_to_user (buf, kbuffer, bytes_read)) + if (bytes_read > 0 && copy_to_user(buf, kbuffer, bytes_read)) bytes_read = -EFAULT; - kfree (kbuffer); - pp_enable_irq (pp); + kfree(kbuffer); + pp_enable_irq(pp); return bytes_read; } -static ssize_t pp_write (struct file * file, const char __user * buf, +static ssize_t pp_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) { unsigned int minor = iminor(file_inode(file)); @@ -218,7 +218,7 @@ static ssize_t pp_write (struct file * file, const char __user * buf, pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); - parport_set_timeout (pp->pdev, + parport_set_timeout(pp->pdev, (file->f_flags & O_NONBLOCK) ? PARPORT_INACTIVITY_O_NONBLOCK : pp->default_inactivity); @@ -226,7 +226,7 @@ static ssize_t pp_write (struct file * file, const char __user * buf, while (bytes_written < count) { ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE); - if (copy_from_user (kbuffer, buf + bytes_written, n)) { + if (copy_from_user(kbuffer, buf + bytes_written, n)) { bytes_written = -EFAULT; break; } @@ -234,14 +234,14 @@ static ssize_t pp_write (struct file * file, const char __user * buf, if ((pp->flags & PP_FASTWRITE) && (mode == IEEE1284_MODE_EPP)) { /* do a fast EPP write */ if (pport->ieee1284.mode & IEEE1284_ADDR) { - wrote = pport->ops->epp_write_addr (pport, + wrote = pport->ops->epp_write_addr(pport, kbuffer, n, PARPORT_EPP_FAST); } else { - wrote = pport->ops->epp_write_data (pport, + wrote = pport->ops->epp_write_data(pport, kbuffer, n, PARPORT_EPP_FAST); } } else { - wrote = parport_write (pp->pdev->port, kbuffer, n); + wrote = parport_write(pp->pdev->port, kbuffer, n); } if (wrote <= 0) { @@ -259,33 +259,33 @@ static ssize_t pp_write (struct file * file, const char __user * buf, break; } - if (signal_pending (current)) + if (signal_pending(current)) break; cond_resched(); } - parport_set_timeout (pp->pdev, pp->default_inactivity); + parport_set_timeout(pp->pdev, pp->default_inactivity); - kfree (kbuffer); - pp_enable_irq (pp); + kfree(kbuffer); + pp_enable_irq(pp); return bytes_written; } -static void pp_irq (void *private) +static void pp_irq(void *private) { struct pp_struct *pp = private; if (pp->irqresponse) { - parport_write_control (pp->pdev->port, pp->irqctl); + parport_write_control(pp->pdev->port, pp->irqctl); pp->irqresponse = 0; } - atomic_inc (&pp->irqc); - wake_up_interruptible (&pp->irq_wait); + atomic_inc(&pp->irqc); + wake_up_interruptible(&pp->irq_wait); } -static int register_device (int minor, struct pp_struct *pp) +static int register_device(int minor, struct pp_struct *pp) { struct parport *port; struct pardevice * pdev = NULL; @@ -296,21 +296,21 @@ static int register_device (int minor, struct pp_struct *pp) if (name == NULL) return -ENOMEM; - port = parport_find_number (minor); + port = parport_find_number(minor); if (!port) { - printk (KERN_WARNING "%s: no associated port!\n", name); - kfree (name); + printk(KERN_WARNING "%s: no associated port!\n", name); + kfree(name); return -ENXIO; } fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; - pdev = parport_register_device (port, name, NULL, + pdev = parport_register_device(port, name, NULL, NULL, pp_irq, fl, pp); - parport_put_port (port); + parport_put_port(port); if (!pdev) { - printk (KERN_WARNING "%s: failed to register device!\n", name); - kfree (name); + printk(KERN_WARNING "%s: failed to register device!\n", name); + kfree(name); return -ENXIO; } @@ -319,7 +319,7 @@ static int register_device (int minor, struct pp_struct *pp) return 0; } -static enum ieee1284_phase init_phase (int mode) +static enum ieee1284_phase init_phase(int mode) { switch (mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR)) { @@ -367,13 +367,13 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* Deferred device registration. */ if (!pp->pdev) { - int err = register_device (minor, pp); + int err = register_device(minor, pp); if (err) { return err; } } - ret = parport_claim_or_block (pp->pdev); + ret = parport_claim_or_block(pp->pdev); if (ret < 0) return ret; @@ -381,7 +381,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* For interrupt-reporting to work, we need to be * informed of each interrupt. */ - pp_enable_irq (pp); + pp_enable_irq(pp); /* We may need to fix up the state machine. */ info = &pp->pdev->port->ieee1284; @@ -389,8 +389,8 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) pp->saved_state.phase = info->phase; info->mode = pp->state.mode; info->phase = pp->state.phase; - pp->default_inactivity = parport_set_timeout (pp->pdev, 0); - parport_set_timeout (pp->pdev, pp->default_inactivity); + pp->default_inactivity = parport_set_timeout(pp->pdev, 0); + parport_set_timeout(pp->pdev, pp->default_inactivity); return 0; } @@ -412,11 +412,11 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPSETMODE: { int mode; - if (copy_from_user (&mode, argp, sizeof (mode))) + if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; /* FIXME: validate mode */ pp->state.mode = mode; - pp->state.phase = init_phase (mode); + pp->state.phase = init_phase(mode); if (pp->flags & PP_CLAIMED) { pp->pdev->port->ieee1284.mode = mode; @@ -434,7 +434,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } else { mode = pp->state.mode; } - if (copy_to_user (argp, &mode, sizeof (mode))) { + if (copy_to_user(argp, &mode, sizeof(mode))) { return -EFAULT; } return 0; @@ -442,7 +442,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPSETPHASE: { int phase; - if (copy_from_user (&phase, argp, sizeof (phase))) { + if (copy_from_user(&phase, argp, sizeof(phase))) { return -EFAULT; } /* FIXME: validate phase */ @@ -463,7 +463,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } else { phase = pp->state.phase; } - if (copy_to_user (argp, &phase, sizeof (phase))) { + if (copy_to_user(argp, &phase, sizeof(phase))) { return -EFAULT; } return 0; @@ -472,13 +472,13 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int modes; - port = parport_find_number (minor); + port = parport_find_number(minor); if (!port) return -ENODEV; modes = port->modes; parport_put_port(port); - if (copy_to_user (argp, &modes, sizeof (modes))) { + if (copy_to_user(argp, &modes, sizeof(modes))) { return -EFAULT; } return 0; @@ -487,7 +487,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int uflags; - if (copy_from_user (&uflags, argp, sizeof (uflags))) { + if (copy_from_user(&uflags, argp, sizeof(uflags))) { return -EFAULT; } pp->flags &= ~PP_FLAGMASK; @@ -499,7 +499,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int uflags; uflags = pp->flags & PP_FLAGMASK; - if (copy_to_user (argp, &uflags, sizeof (uflags))) { + if (copy_to_user(argp, &uflags, sizeof(uflags))) { return -EFAULT; } return 0; @@ -525,22 +525,22 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int ret; case PPRSTATUS: - reg = parport_read_status (port); - if (copy_to_user (argp, ®, sizeof (reg))) + reg = parport_read_status(port); + if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPRDATA: - reg = parport_read_data (port); - if (copy_to_user (argp, ®, sizeof (reg))) + reg = parport_read_data(port); + if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPRCONTROL: - reg = parport_read_control (port); - if (copy_to_user (argp, ®, sizeof (reg))) + reg = parport_read_control(port); + if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPYIELD: - parport_yield_blocking (pp->pdev); + parport_yield_blocking(pp->pdev); return 0; case PPRELEASE: @@ -550,45 +550,45 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) pp->state.phase = info->phase; info->mode = pp->saved_state.mode; info->phase = pp->saved_state.phase; - parport_release (pp->pdev); + parport_release(pp->pdev); pp->flags &= ~PP_CLAIMED; return 0; case PPWCONTROL: - if (copy_from_user (®, argp, sizeof (reg))) + if (copy_from_user(®, argp, sizeof(reg))) return -EFAULT; - parport_write_control (port, reg); + parport_write_control(port, reg); return 0; case PPWDATA: - if (copy_from_user (®, argp, sizeof (reg))) + if (copy_from_user(®, argp, sizeof(reg))) return -EFAULT; - parport_write_data (port, reg); + parport_write_data(port, reg); return 0; case PPFCONTROL: - if (copy_from_user (&mask, argp, - sizeof (mask))) + if (copy_from_user(&mask, argp, + sizeof(mask))) return -EFAULT; - if (copy_from_user (®, 1 + (unsigned char __user *) arg, - sizeof (reg))) + if (copy_from_user(®, 1 + (unsigned char __user *) arg, + sizeof(reg))) return -EFAULT; - parport_frob_control (port, mask, reg); + parport_frob_control(port, mask, reg); return 0; case PPDATADIR: - if (copy_from_user (&mode, argp, sizeof (mode))) + if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; if (mode) - port->ops->data_reverse (port); + port->ops->data_reverse(port); else - port->ops->data_forward (port); + port->ops->data_forward(port); return 0; case PPNEGOT: - if (copy_from_user (&mode, argp, sizeof (mode))) + if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; - switch ((ret = parport_negotiate (port, mode))) { + switch ((ret = parport_negotiate(port, mode))) { case 0: break; case -1: /* handshake failed, peripheral not IEEE 1284 */ ret = -EIO; @@ -597,11 +597,11 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -ENXIO; break; } - pp_enable_irq (pp); + pp_enable_irq(pp); return ret; case PPWCTLONIRQ: - if (copy_from_user (®, argp, sizeof (reg))) + if (copy_from_user(®, argp, sizeof(reg))) return -EFAULT; /* Remember what to set the control lines to, for next @@ -611,10 +611,10 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; case PPCLRIRQ: - ret = atomic_read (&pp->irqc); - if (copy_to_user (argp, &ret, sizeof (ret))) + ret = atomic_read(&pp->irqc); + if (copy_to_user(argp, &ret, sizeof(ret))) return -EFAULT; - atomic_sub (ret, &pp->irqc); + atomic_sub(ret, &pp->irqc); return 0; case PPSETTIME32: @@ -679,7 +679,7 @@ static long pp_compat_ioctl(struct file *file, unsigned int cmd, } #endif -static int pp_open (struct inode * inode, struct file * file) +static int pp_open(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); struct pp_struct *pp; @@ -687,16 +687,16 @@ static int pp_open (struct inode * inode, struct file * file) if (minor >= PARPORT_MAX) return -ENXIO; - pp = kmalloc (sizeof (struct pp_struct), GFP_KERNEL); + pp = kmalloc(sizeof(struct pp_struct), GFP_KERNEL); if (!pp) return -ENOMEM; pp->state.mode = IEEE1284_MODE_COMPAT; - pp->state.phase = init_phase (pp->state.mode); + pp->state.phase = init_phase(pp->state.mode); pp->flags = 0; pp->irqresponse = 0; - atomic_set (&pp->irqc, 0); - init_waitqueue_head (&pp->irq_wait); + atomic_set(&pp->irqc, 0); + init_waitqueue_head(&pp->irq_wait); /* Defer the actual device registration until the first claim. * That way, we know whether or not the driver wants to have @@ -708,7 +708,7 @@ static int pp_open (struct inode * inode, struct file * file) return 0; } -static int pp_release (struct inode * inode, struct file * file) +static int pp_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); struct pp_struct *pp = file->private_data; @@ -720,7 +720,7 @@ static int pp_release (struct inode * inode, struct file * file) struct ieee1284_info *info; /* parport released, but not in compatibility mode */ - parport_claim_or_block (pp->pdev); + parport_claim_or_block(pp->pdev); pp->flags |= PP_CLAIMED; info = &pp->pdev->port->ieee1284; pp->saved_state.mode = info->mode; @@ -733,7 +733,7 @@ static int pp_release (struct inode * inode, struct file * file) compat_negot = 2; } if (compat_negot) { - parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT); + parport_negotiate(pp->pdev->port, IEEE1284_MODE_COMPAT); pr_debug(CHRDEV "%x: negotiated back to compatibility " "mode because user-space forgot\n", minor); } @@ -746,7 +746,7 @@ static int pp_release (struct inode * inode, struct file * file) pp->state.phase = info->phase; info->mode = pp->saved_state.mode; info->phase = pp->saved_state.phase; - parport_release (pp->pdev); + parport_release(pp->pdev); if (compat_negot != 1) { pr_debug(CHRDEV "%x: released pardevice " "because user-space forgot\n", minor); @@ -755,25 +755,25 @@ static int pp_release (struct inode * inode, struct file * file) if (pp->pdev) { const char *name = pp->pdev->name; - parport_unregister_device (pp->pdev); - kfree (name); + parport_unregister_device(pp->pdev); + kfree(name); pp->pdev = NULL; pr_debug(CHRDEV "%x: unregistered pardevice\n", minor); } - kfree (pp); + kfree(pp); return 0; } /* No kernel lock held - fine */ -static unsigned int pp_poll (struct file * file, poll_table * wait) +static unsigned int pp_poll(struct file * file, poll_table * wait) { struct pp_struct *pp = file->private_data; unsigned int mask = 0; - poll_wait (file, &pp->irq_wait, wait); - if (atomic_read (&pp->irqc)) + poll_wait(file, &pp->irq_wait, wait); + if (atomic_read(&pp->irqc)) mask |= POLLIN | POLLRDNORM; return mask; @@ -812,12 +812,12 @@ static struct parport_driver pp_driver = { .detach = pp_detach, }; -static int __init ppdev_init (void) +static int __init ppdev_init(void) { int err = 0; - if (register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) { - printk (KERN_WARNING CHRDEV ": unable to get major %d\n", + if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) { + printk(KERN_WARNING CHRDEV ": unable to get major %d\n", PP_MAJOR); return -EIO; } @@ -828,11 +828,11 @@ static int __init ppdev_init (void) } err = parport_register_driver(&pp_driver); if (err < 0) { - printk (KERN_WARNING CHRDEV ": unable to register with parport\n"); + printk(KERN_WARNING CHRDEV ": unable to register with parport\n"); goto out_class; } - printk (KERN_INFO PP_VERSION "\n"); + printk(KERN_INFO PP_VERSION "\n"); goto out; out_class: @@ -843,12 +843,12 @@ out: return err; } -static void __exit ppdev_cleanup (void) +static void __exit ppdev_cleanup(void) { /* Clean up all parport stuff */ parport_unregister_driver(&pp_driver); class_destroy(ppdev_class); - unregister_chrdev (PP_MAJOR, CHRDEV); + unregister_chrdev(PP_MAJOR, CHRDEV); } module_init(ppdev_init); -- cgit v1.2.3 From d85c1a2d1591e0eb04d0c77b61e1062af61a9156 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:40 +0530 Subject: ppdev: remove whitespace around pointers checkpatch was complaining about space around the pointer. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 80ab757de8d3..8a168c4d094c 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -75,7 +75,7 @@ #define CHRDEV "ppdev" struct pp_struct { - struct pardevice * pdev; + struct pardevice *pdev; wait_queue_head_t irq_wait; atomic_t irqc; unsigned int flags; @@ -112,12 +112,12 @@ static inline void pp_enable_irq(struct pp_struct *pp) port->ops->enable_irq(port); } -static ssize_t pp_read(struct file * file, char __user * buf, size_t count, - loff_t * ppos) +static ssize_t pp_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) { unsigned int minor = iminor(file_inode(file)); struct pp_struct *pp = file->private_data; - char * kbuffer; + char *kbuffer; ssize_t bytes_read = 0; struct parport *pport; int mode; @@ -194,12 +194,12 @@ static ssize_t pp_read(struct file * file, char __user * buf, size_t count, return bytes_read; } -static ssize_t pp_write(struct file * file, const char __user * buf, - size_t count, loff_t * ppos) +static ssize_t pp_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { unsigned int minor = iminor(file_inode(file)); struct pp_struct *pp = file->private_data; - char * kbuffer; + char *kbuffer; ssize_t bytes_written = 0; ssize_t wrote; int mode; @@ -288,7 +288,7 @@ static void pp_irq(void *private) static int register_device(int minor, struct pp_struct *pp) { struct parport *port; - struct pardevice * pdev = NULL; + struct pardevice *pdev = NULL; char *name; int fl; @@ -350,7 +350,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = iminor(file_inode(file)); struct pp_struct *pp = file->private_data; - struct parport * port; + struct parport *port; void __user *argp = (void __user *)arg; /* First handle the cases that don't take arguments. */ @@ -679,7 +679,7 @@ static long pp_compat_ioctl(struct file *file, unsigned int cmd, } #endif -static int pp_open(struct inode * inode, struct file * file) +static int pp_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct pp_struct *pp; @@ -708,7 +708,7 @@ static int pp_open(struct inode * inode, struct file * file) return 0; } -static int pp_release(struct inode * inode, struct file * file) +static int pp_release(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct pp_struct *pp = file->private_data; @@ -767,7 +767,7 @@ static int pp_release(struct inode * inode, struct file * file) } /* No kernel lock held - fine */ -static unsigned int pp_poll(struct file * file, poll_table * wait) +static unsigned int pp_poll(struct file *file, poll_table *wait) { struct pp_struct *pp = file->private_data; unsigned int mask = 0; -- cgit v1.2.3 From 27f3b8a3bf1442f8b571dffcb1cdf20d72c364ad Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:41 +0530 Subject: ppdev: add missing blank line kernel coding style recommends a blank line after varaiable declaration. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 8a168c4d094c..c551c0c88d8e 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -109,6 +109,7 @@ static DEFINE_MUTEX(pp_do_mutex); static inline void pp_enable_irq(struct pp_struct *pp) { struct parport *port = pp->pdev->port; + port->ops->enable_irq(port); } @@ -368,6 +369,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* Deferred device registration. */ if (!pp->pdev) { int err = register_device(minor, pp); + if (err) { return err; } @@ -412,6 +414,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPSETMODE: { int mode; + if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; /* FIXME: validate mode */ @@ -442,6 +445,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPSETPHASE: { int phase; + if (copy_from_user(&phase, argp, sizeof(phase))) { return -EFAULT; } @@ -665,6 +669,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; + mutex_lock(&pp_do_mutex); ret = pp_do_ioctl(file, cmd, arg); mutex_unlock(&pp_do_mutex); @@ -755,6 +760,7 @@ static int pp_release(struct inode *inode, struct file *file) if (pp->pdev) { const char *name = pp->pdev->name; + parport_unregister_device(pp->pdev); kfree(name); pp->pdev = NULL; -- cgit v1.2.3 From 83e80605cc3ce6a2ac966151cd515f691e4835e0 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:42 +0530 Subject: ppdev: remove braces For single statement if and else blocks we do not need braces. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 57 ++++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index c551c0c88d8e..dfb419772038 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -134,9 +134,8 @@ static ssize_t pp_read(struct file *file, char __user *buf, size_t count, return 0; kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL); - if (!kbuffer) { + if (!kbuffer) return -ENOMEM; - } pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); @@ -153,17 +152,14 @@ static ssize_t pp_read(struct file *file, char __user *buf, size_t count, int flags = 0; size_t (*fn)(struct parport *, void *, size_t, int); - if (pp->flags & PP_W91284PIC) { + if (pp->flags & PP_W91284PIC) flags |= PARPORT_W91284PIC; - } - if (pp->flags & PP_FASTREAD) { + if (pp->flags & PP_FASTREAD) flags |= PARPORT_EPP_FAST; - } - if (pport->ieee1284.mode & IEEE1284_ADDR) { + if (pport->ieee1284.mode & IEEE1284_ADDR) fn = pport->ops->epp_read_addr; - } else { + else fn = pport->ops->epp_read_data; - } bytes_read = (*fn)(pport, kbuffer, need, flags); } else { bytes_read = parport_read(pport, kbuffer, need); @@ -213,9 +209,9 @@ static ssize_t pp_write(struct file *file, const char __user *buf, } kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL); - if (!kbuffer) { + if (!kbuffer) return -ENOMEM; - } + pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); @@ -246,9 +242,8 @@ static ssize_t pp_write(struct file *file, const char __user *buf, } if (wrote <= 0) { - if (!bytes_written) { + if (!bytes_written) bytes_written = wrote; - } break; } @@ -370,9 +365,8 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (!pp->pdev) { int err = register_device(minor, pp); - if (err) { + if (err) return err; - } } ret = parport_claim_or_block(pp->pdev); @@ -432,29 +426,27 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int mode; - if (pp->flags & PP_CLAIMED) { + if (pp->flags & PP_CLAIMED) mode = pp->pdev->port->ieee1284.mode; - } else { + else mode = pp->state.mode; - } - if (copy_to_user(argp, &mode, sizeof(mode))) { + + if (copy_to_user(argp, &mode, sizeof(mode))) return -EFAULT; - } return 0; } case PPSETPHASE: { int phase; - if (copy_from_user(&phase, argp, sizeof(phase))) { + if (copy_from_user(&phase, argp, sizeof(phase))) return -EFAULT; - } + /* FIXME: validate phase */ pp->state.phase = phase; - if (pp->flags & PP_CLAIMED) { + if (pp->flags & PP_CLAIMED) pp->pdev->port->ieee1284.phase = phase; - } return 0; } @@ -462,14 +454,12 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int phase; - if (pp->flags & PP_CLAIMED) { + if (pp->flags & PP_CLAIMED) phase = pp->pdev->port->ieee1284.phase; - } else { + else phase = pp->state.phase; - } - if (copy_to_user(argp, &phase, sizeof(phase))) { + if (copy_to_user(argp, &phase, sizeof(phase))) return -EFAULT; - } return 0; } case PPGETMODES: @@ -482,18 +472,16 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) modes = port->modes; parport_put_port(port); - if (copy_to_user(argp, &modes, sizeof(modes))) { + if (copy_to_user(argp, &modes, sizeof(modes))) return -EFAULT; - } return 0; } case PPSETFLAGS: { int uflags; - if (copy_from_user(&uflags, argp, sizeof(uflags))) { + if (copy_from_user(&uflags, argp, sizeof(uflags))) return -EFAULT; - } pp->flags &= ~PP_FLAGMASK; pp->flags |= (uflags & PP_FLAGMASK); return 0; @@ -503,9 +491,8 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int uflags; uflags = pp->flags & PP_FLAGMASK; - if (copy_to_user(argp, &uflags, sizeof(uflags))) { + if (copy_to_user(argp, &uflags, sizeof(uflags))) return -EFAULT; - } return 0; } } /* end switch() */ -- cgit v1.2.3 From a7c71c07a31fde429627f0c7eed5fb57cb986fa6 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:43 +0530 Subject: ppdev: fix parenthesis alignment checkpatch was complaining that the alignment was not matching with the open parenthesis. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index dfb419772038..39dedb775d5f 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -114,7 +114,7 @@ static inline void pp_enable_irq(struct pp_struct *pp) } static ssize_t pp_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) + loff_t *ppos) { unsigned int minor = iminor(file_inode(file)); struct pp_struct *pp = file->private_data; @@ -140,9 +140,9 @@ static ssize_t pp_read(struct file *file, char __user *buf, size_t count, mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); parport_set_timeout(pp->pdev, - (file->f_flags & O_NONBLOCK) ? - PARPORT_INACTIVITY_O_NONBLOCK : - pp->default_inactivity); + (file->f_flags & O_NONBLOCK) ? + PARPORT_INACTIVITY_O_NONBLOCK : + pp->default_inactivity); while (bytes_read == 0) { ssize_t need = min_t(unsigned long, count, PP_BUFFER_SIZE); @@ -192,7 +192,7 @@ static ssize_t pp_read(struct file *file, char __user *buf, size_t count, } static ssize_t pp_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { unsigned int minor = iminor(file_inode(file)); struct pp_struct *pp = file->private_data; @@ -216,9 +216,9 @@ static ssize_t pp_write(struct file *file, const char __user *buf, mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); parport_set_timeout(pp->pdev, - (file->f_flags & O_NONBLOCK) ? - PARPORT_INACTIVITY_O_NONBLOCK : - pp->default_inactivity); + (file->f_flags & O_NONBLOCK) ? + PARPORT_INACTIVITY_O_NONBLOCK : + pp->default_inactivity); while (bytes_written < count) { ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE); @@ -301,7 +301,7 @@ static int register_device(int minor, struct pp_struct *pp) fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; pdev = parport_register_device(port, name, NULL, - NULL, pp_irq, fl, pp); + NULL, pp_irq, fl, pp); parport_put_port(port); if (!pdev) { @@ -559,10 +559,10 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPFCONTROL: if (copy_from_user(&mask, argp, - sizeof(mask))) + sizeof(mask))) return -EFAULT; if (copy_from_user(®, 1 + (unsigned char __user *) arg, - sizeof(reg))) + sizeof(reg))) return -EFAULT; parport_frob_control(port, mask, reg); return 0; @@ -665,7 +665,7 @@ static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #ifdef CONFIG_COMPAT static long pp_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg) { return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } @@ -811,7 +811,7 @@ static int __init ppdev_init(void) if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) { printk(KERN_WARNING CHRDEV ": unable to get major %d\n", - PP_MAJOR); + PP_MAJOR); return -EIO; } ppdev_class = class_create(THIS_MODULE, CHRDEV); -- cgit v1.2.3 From 3c8db584323875a50696718c89d94cef0ed54f30 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:44 +0530 Subject: ppdev: remove space before tab For alignment we should use tab in all possible places. checkpatch was complaining for using space before tab. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 39dedb775d5f..bccc15a4b25d 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -709,7 +709,7 @@ static int pp_release(struct inode *inode, struct file *file) compat_negot = 0; if (!(pp->flags & PP_CLAIMED) && pp->pdev && (pp->state.mode != IEEE1284_MODE_COMPAT)) { - struct ieee1284_info *info; + struct ieee1284_info *info; /* parport released, but not in compatibility mode */ parport_claim_or_block(pp->pdev); -- cgit v1.2.3 From e7223f18603374d235d8bb0398532323e5f318b9 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:45 +0530 Subject: ppdev: use new parport device model Modify ppdev driver to use the new parallel port device model. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index bccc15a4b25d..6152c09e89d9 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -286,7 +286,7 @@ static int register_device(int minor, struct pp_struct *pp) struct parport *port; struct pardevice *pdev = NULL; char *name; - int fl; + struct pardev_cb ppdev_cb; name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor); if (name == NULL) @@ -299,9 +299,11 @@ static int register_device(int minor, struct pp_struct *pp) return -ENXIO; } - fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; - pdev = parport_register_device(port, name, NULL, - NULL, pp_irq, fl, pp); + memset(&ppdev_cb, 0, sizeof(ppdev_cb)); + ppdev_cb.irq_func = pp_irq; + ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; + ppdev_cb.private = pp; + pdev = parport_register_dev_model(port, name, &ppdev_cb, minor); parport_put_port(port); if (!pdev) { @@ -799,10 +801,23 @@ static void pp_detach(struct parport *port) device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number)); } +static int pp_probe(struct pardevice *par_dev) +{ + struct device_driver *drv = par_dev->dev.driver; + int len = strlen(drv->name); + + if (strncmp(par_dev->name, drv->name, len)) + return -ENODEV; + + return 0; +} + static struct parport_driver pp_driver = { .name = CHRDEV, - .attach = pp_attach, + .probe = pp_probe, + .match_port = pp_attach, .detach = pp_detach, + .devmodel = true, }; static int __init ppdev_init(void) -- cgit v1.2.3 From 396ec3dea3d82d0a32906efe9803a8ef2647df51 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 12 Feb 2016 18:33:46 +0530 Subject: ppdev: use dev_* macros It is more preffered to use the dev_* family of macros instead of using the generic pr_*. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 6152c09e89d9..d23368874710 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -313,7 +313,7 @@ static int register_device(int minor, struct pp_struct *pp) } pp->pdev = pdev; - pr_debug("%s: registered pardevice\n", name); + dev_dbg(&pdev->dev, "registered pardevice\n"); return 0; } @@ -359,7 +359,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int ret; if (pp->flags & PP_CLAIMED) { - pr_debug(CHRDEV "%x: you've already got it!\n", minor); + dev_dbg(&pp->pdev->dev, "you've already got it!\n"); return -EINVAL; } @@ -394,8 +394,8 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } case PPEXCL: if (pp->pdev) { - pr_debug(CHRDEV "%x: too late for PPEXCL; " - "already registered\n", minor); + dev_dbg(&pp->pdev->dev, + "too late for PPEXCL; already registered\n"); if (pp->flags & PP_EXCL) /* But it's not really an error. */ return 0; @@ -647,7 +647,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; default: - pr_debug(CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd); + dev_dbg(&pp->pdev->dev, "What? (cmd=0x%x)\n", cmd); return -EINVAL; } @@ -728,8 +728,8 @@ static int pp_release(struct inode *inode, struct file *file) } if (compat_negot) { parport_negotiate(pp->pdev->port, IEEE1284_MODE_COMPAT); - pr_debug(CHRDEV "%x: negotiated back to compatibility " - "mode because user-space forgot\n", minor); + dev_dbg(&pp->pdev->dev, + "negotiated back to compatibility mode because user-space forgot\n"); } if (pp->flags & PP_CLAIMED) { -- cgit v1.2.3 From f45f40ad65336699757af948b81414c5999f1d48 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Mon, 15 Feb 2016 19:11:51 +0200 Subject: stm class: Use a signed return type for stm_find_master_chan The return type "unsigned int" was used by the stm_find_master_chan function despite of the aspect that it will eventually return a negative error code. Done with the help of Coccinelle. Signed-off-by: Lucas Tanure Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index aef8ddb24451..cdec240bd6b6 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -233,7 +233,7 @@ static int find_free_channels(unsigned long *bitmap, unsigned int start, return -1; } -static unsigned int +static int stm_find_master_chan(struct stm_device *stm, unsigned int width, unsigned int *mstart, unsigned int mend, unsigned int *cstart, unsigned int cend) @@ -293,7 +293,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, goto unlock; ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend); - if (ret) + if (ret < 0) goto unlock; output->master = midx; -- cgit v1.2.3 From 73a3ed1903f7611f62fb5e1cd3c37afd2908e669 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Mon, 15 Feb 2016 19:11:52 +0200 Subject: stm class: Fix master deallocation in device unregistering The device unregister path uses wrong master index range when it tries to free the allocated masters, it should, as does the rest of the stm class code, use real master IDs. This patch fixes the device unregister path to use real master IDs to avoid memory leaks after unloading the stm driver. Signed-off-by: Chunyan Zhang [alexander.shishkin@intel.com: re-wrote the commit message] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index cdec240bd6b6..79cca94bfb58 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -690,7 +690,7 @@ void stm_unregister_device(struct stm_data *stm_data) stp_policy_unbind(stm->policy); mutex_unlock(&stm->policy_mutex); - for (i = 0; i < stm->sw_nmasters; i++) + for (i = stm->data->sw_start; i <= stm->data->sw_end; i++) stp_master_free(stm, i); device_unregister(&stm->dev); -- cgit v1.2.3 From 993c7f11192ffcfb435cee51c3414509a557e916 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:11:53 +0200 Subject: intel_th: Depend on HAS_IOMEM This driver requires io memory to operate, so don't even consider it for NO_IOMEM architectures. Reported-by: Richard Weinberger Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index 90b08440938b..1b412f8a56b5 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -1,6 +1,6 @@ config INTEL_TH tristate "Intel(R) Trace Hub controller" - depends on HAS_DMA + depends on HAS_DMA && HAS_IOMEM help Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that produce, switch and output trace data from multiple hardware and -- cgit v1.2.3 From c1a327c4d3d7d2ce2c75e77fcfd34a729ab45f6f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:11:54 +0200 Subject: intel_th: gth: Remove commented-out code There's a commented-out function in the GTH driver that's a leftover from previous versions of the driver, where we tried to inherit the pre-existing configuration, which didn't prove to be a sound idea. This patch removes the function. No functional changes. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/gth.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 2dc5378ccd3a..e4c9811c1f40 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -146,24 +146,6 @@ gth_master_set(struct gth_device *gth, unsigned int master, int port) iowrite32(val, gth->base + reg); } -/*static int gth_master_get(struct gth_device *gth, unsigned int master) -{ - unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u); - unsigned int shift = (master & 0x7) * 4; - u32 val; - - if (master >= 256) { - reg = REG_GTH_GSWTDEST; - shift = 0; - } - - val = ioread32(gth->base + reg); - val &= (0xf << shift); - val >>= shift; - - return val ? val & 0x7 : -1; - }*/ - static ssize_t master_attr_show(struct device *dev, struct device_attribute *attr, char *buf) -- cgit v1.2.3 From 4d02ceff32f35f3ea745be6114503c2d2505da99 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:11:55 +0200 Subject: intel_th: Update scratchpad bits according to enabled output activity Intel TH implements a scratchpad register to indicate to the firmware and external debuggers what trace configuration is enabled so that everybody plays nicely together. The register is a bit field and the bit assignment convention is described in the developer's manual. This patch enables the driver to automatically set scratchpad register bits according to the output configuration that's enabled. Based on work by Yann Fouassier. Signed-off-by: Yann Fouassier Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 5 +++++ drivers/hwtracing/intel_th/gth.c | 14 +++++++++++- drivers/hwtracing/intel_th/gth.h | 3 --- drivers/hwtracing/intel_th/intel_th.h | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 165d3001c301..b8b9895da5d1 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -319,6 +319,7 @@ static struct intel_th_subdevice { unsigned nres; unsigned type; unsigned otype; + unsigned scrpd; int id; } intel_th_subdevices[TH_SUBDEVICE_MAX] = { { @@ -352,6 +353,7 @@ static struct intel_th_subdevice { .id = 0, .type = INTEL_TH_OUTPUT, .otype = GTH_MSU, + .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC0_IS_ENABLED, }, { .nres = 2, @@ -371,6 +373,7 @@ static struct intel_th_subdevice { .id = 1, .type = INTEL_TH_OUTPUT, .otype = GTH_MSU, + .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC1_IS_ENABLED, }, { .nres = 2, @@ -403,6 +406,7 @@ static struct intel_th_subdevice { .name = "pti", .type = INTEL_TH_OUTPUT, .otype = GTH_PTI, + .scrpd = SCRPD_PTI_IS_PRIM_DEST, }, { .nres = 1, @@ -477,6 +481,7 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, thdev->dev.devt = MKDEV(th->major, i); thdev->output.type = subdev->otype; thdev->output.port = -1; + thdev->output.scratchpad = subdev->scrpd; } err = device_add(&thdev->dev); diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index e4c9811c1f40..9beea0b54231 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -286,6 +286,10 @@ static int intel_th_gth_reset(struct gth_device *gth) if (scratchpad & SCRPD_DEBUGGER_IN_USE) return -EBUSY; + /* Always save/restore STH and TU registers in S0ix entry/exit */ + scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); + /* output ports */ for (port = 0; port < 8; port++) { if (gth_output_parm_get(gth, port, TH_OUTPUT_PARM(port)) == @@ -488,6 +492,10 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, if (!count) dev_dbg(&thdev->dev, "timeout waiting for GTH[%d] PLE\n", output->port); + + reg = ioread32(gth->base + REG_GTH_SCRPD0); + reg &= ~output->scratchpad; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); } /** @@ -502,7 +510,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - u32 scr = 0xfc0000; + u32 scr = 0xfc0000, scrpd; int master; spin_lock(>h->gth_lock); @@ -517,6 +525,10 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, output->active = true; spin_unlock(>h->gth_lock); + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); + scrpd |= output->scratchpad; + iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); + iowrite32(scr, gth->base + REG_GTH_SCR); iowrite32(0, gth->base + REG_GTH_SCR2); } diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h index 3b714b7a61db..56f0d2620577 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -57,9 +57,6 @@ enum { REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ }; -/* Externall debugger is using Intel TH */ -#define SCRPD_DEBUGGER_IN_USE BIT(24) - /* waiting for Pipeline Empty bit(s) to assert for GTH */ #define GTH_PLE_WAITLOOP_DEPTH 10000 diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 57fd72b20fae..eedd09332db6 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -30,6 +30,7 @@ enum { * struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices * @port: output port number, assigned by the switch * @type: GTH_{MSU,CTP,PTI} + * @scratchpad: scratchpad bits to flag when this output is enabled * @multiblock: true for multiblock output configuration * @active: true when this output is enabled * @@ -41,6 +42,7 @@ enum { struct intel_th_output { int port; unsigned int type; + unsigned int scratchpad; bool multiblock; bool active; }; @@ -241,4 +243,43 @@ enum { GTH_PTI = 4, /* MIPI-PTI */ }; +/* + * Scratchpad bits: tell firmware and external debuggers + * what we are up to. + */ +enum { + /* Memory is the primary destination */ + SCRPD_MEM_IS_PRIM_DEST = BIT(0), + /* XHCI DbC is the primary destination */ + SCRPD_DBC_IS_PRIM_DEST = BIT(1), + /* PTI is the primary destination */ + SCRPD_PTI_IS_PRIM_DEST = BIT(2), + /* BSSB is the primary destination */ + SCRPD_BSSB_IS_PRIM_DEST = BIT(3), + /* PTI is the alternate destination */ + SCRPD_PTI_IS_ALT_DEST = BIT(4), + /* BSSB is the alternate destination */ + SCRPD_BSSB_IS_ALT_DEST = BIT(5), + /* DeepSx exit occurred */ + SCRPD_DEEPSX_EXIT = BIT(6), + /* S4 exit occurred */ + SCRPD_S4_EXIT = BIT(7), + /* S5 exit occurred */ + SCRPD_S5_EXIT = BIT(8), + /* MSU controller 0/1 is enabled */ + SCRPD_MSC0_IS_ENABLED = BIT(9), + SCRPD_MSC1_IS_ENABLED = BIT(10), + /* Sx exit occurred */ + SCRPD_SX_EXIT = BIT(11), + /* Trigger Unit is enabled */ + SCRPD_TRIGGER_IS_ENABLED = BIT(12), + SCRPD_ODLA_IS_ENABLED = BIT(13), + SCRPD_SOCHAP_IS_ENABLED = BIT(14), + SCRPD_STH_IS_ENABLED = BIT(15), + SCRPD_DCIH_IS_ENABLED = BIT(16), + SCRPD_VER_IS_ENABLED = BIT(17), + /* External debugger is using Intel TH */ + SCRPD_DEBUGGER_IN_USE = BIT(24), +}; + #endif -- cgit v1.2.3 From e4eca2a1c71fda33cd8b5aacc7744176e440f80d Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 15 Feb 2016 19:11:56 +0200 Subject: intel_th: msu: Fix offset for wrapped block Fix offset for the second pass on the wrapped block when iterating over memory in multi-block mode, otherwise wrong part of the block will get copied. Signed-off-by: Laurent FERT Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 70ca27e45602..3c793bbf6ab2 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -408,7 +408,7 @@ msc_buffer_iterate(struct msc_iter *iter, size_t size, void *data, * Second time (wrap_count==1), it's just like any other block, * containing data in the range of [MSC_BDESC..data_bytes]. */ - if (iter->block == iter->start_block && iter->wrap_count) { + if (iter->block == iter->start_block && iter->wrap_count == 2) { tocopy = DATA_IN_PAGE - data_bytes; src += data_bytes; } -- cgit v1.2.3 From 2bed074aade2caa9c350121837c55189a8fd197e Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 15 Feb 2016 19:11:57 +0200 Subject: intel_th: msu: Release resources on read error Right now, reading from msc character device will leak its's user count on read error. This patch makes sure resources are released when there is no data left to read from the buffer. Signed-off-by: Laurent FERT Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 3c793bbf6ab2..d9d6022c5aca 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1112,12 +1112,11 @@ static ssize_t intel_th_msc_read(struct file *file, char __user *buf, size = msc->nr_pages << PAGE_SHIFT; if (!size) - return 0; + goto put_count; - if (off >= size) { - len = 0; + if (off >= size) goto put_count; - } + if (off + len >= size) len = size - off; -- cgit v1.2.3 From 97007500ec3f9b08913ebec986068b9d250a6eed Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:11:58 +0200 Subject: intel_th: sth: Sanitize packet callback's return values According to the stm class interface, the packet callback should return an error if it is asked to generate packets that it doesn't support. When it succeeds, it should return number of bytes consumed from its payload. Currently, for FLAG packet it mistakenly returns 1. This patch addresses these issues. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/sth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/intel_th/sth.c b/drivers/hwtracing/intel_th/sth.c index 56101c33e10f..e1aee61dd7b3 100644 --- a/drivers/hwtracing/intel_th/sth.c +++ b/drivers/hwtracing/intel_th/sth.c @@ -94,10 +94,13 @@ static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master, case STP_PACKET_TRIG: if (flags & STP_PACKET_TIMESTAMPED) reg += 4; - iowrite8(*payload, sth->base + reg); + writeb_relaxed(*payload, sth->base + reg); break; case STP_PACKET_MERR: + if (size > 4) + size = 4; + sth_iowrite(&out->MERR, payload, size); break; @@ -107,8 +110,8 @@ static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master, else outp = (u64 __iomem *)&out->FLAG; - size = 1; - sth_iowrite(outp, payload, size); + size = 0; + writeb_relaxed(0, outp); break; case STP_PACKET_USER: @@ -129,6 +132,8 @@ static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master, sth_iowrite(outp, payload, size); break; + default: + return -ENOTSUPP; } return size; -- cgit v1.2.3 From d7b1787161b78a5125cbb91d0f7512612bb642ad Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:11:59 +0200 Subject: intel_th: Set root device's drvdata early Already during the subdevice initialization time, devices will need to reference Intel TH controller descriptor structure. This patch moves setting the drvdata from the pci glue to intel_th core, before subdevices are populated. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 2 ++ drivers/hwtracing/intel_th/pci.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index b8b9895da5d1..6df3cd9774bc 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -584,6 +584,8 @@ intel_th_alloc(struct device *dev, struct resource *devres, } th->dev = dev; + dev_set_drvdata(dev, th); + err = intel_th_populate(th, devres, ndevres, irq); if (err) goto err_chrdev; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 09017073d7a4..bca7a2ac00d6 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -46,8 +46,6 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (IS_ERR(th)) return PTR_ERR(th); - pci_set_drvdata(pdev, th); - return 0; } -- cgit v1.2.3 From 14136e368f909ced74f97cf04199d7288933ad41 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:00 +0200 Subject: intel_th: Use real device index in the node names Most of the intel_th core supports multiple co-existing TH devices, except for output device nodes, where intel_th device id is hardcoded to be zero. Fix this by fetching the actual intel_th device id from the parent device's drvdata. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 6df3cd9774bc..4272f2ce5f6e 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -124,17 +124,34 @@ static struct device_type intel_th_source_device_type = { .release = intel_th_device_release, }; +static struct intel_th *to_intel_th(struct intel_th_device *thdev) +{ + /* + * subdevice tree is flat: if this one is not a switch, its + * parent must be + */ + if (thdev->type != INTEL_TH_SWITCH) + thdev = to_intel_th_hub(thdev); + + if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) + return NULL; + + return dev_get_drvdata(thdev->dev.parent); +} + static char *intel_th_output_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) { struct intel_th_device *thdev = to_intel_th_device(dev); + struct intel_th *th = to_intel_th(thdev); char *node; if (thdev->id >= 0) - node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", 0, thdev->name, - thdev->id); + node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", th->id, + thdev->name, thdev->id); else - node = kasprintf(GFP_KERNEL, "intel_th%d/%s", 0, thdev->name); + node = kasprintf(GFP_KERNEL, "intel_th%d/%s", th->id, + thdev->name); return node; } -- cgit v1.2.3 From f8560a9bc76b2cd5c06fa412cb7b5481d70fcf34 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:01 +0200 Subject: stm class: Use driver's packet callback return value STM drivers provide a callback to generate/send individual STP packets; it also tells the stm core how many bytes of payload it has consumed. However, we would also need to use the negative space of this return value to communicate errors that occur during the packet generation, in which case the stm core will have to take appropriate action. For now, we need to account for the possibility that the stm driver may not support certain combinations of packet type/flags, in which case it is expected to signal an error. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 19 ++++++++++++------- include/linux/stm.h | 7 +++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 79cca94bfb58..0db303b50e51 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -380,8 +380,8 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) return ret; } -static void stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +static ssize_t stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) { unsigned int flags = STP_PACKET_TIMESTAMPED; const unsigned char *p = buf, nil = 0; @@ -393,9 +393,14 @@ static void stm_write(struct stm_data *data, unsigned int master, sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, sz, p); flags = 0; + + if (sz < 0) + break; } data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); + + return pos; } static ssize_t stm_char_write(struct file *file, const char __user *buf, @@ -433,8 +438,8 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, return -EFAULT; } - stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf, - count); + count = stm_write(stm->data, stmf->output.master, stmf->output.channel, + kbuf, count); kfree(kbuf); @@ -996,9 +1001,9 @@ int stm_source_write(struct stm_source_data *data, unsigned int chan, stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) - stm_write(stm->data, src->output.master, - src->output.channel + chan, - buf, count); + count = stm_write(stm->data, src->output.master, + src->output.channel + chan, + buf, count); else count = -ENODEV; diff --git a/include/linux/stm.h b/include/linux/stm.h index 9d0083d364e6..ab8ceca4f570 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -67,6 +67,13 @@ struct stm_device; * description. That is, the lowest master that can be allocated to software * writers is @sw_start and data from this writer will appear is @sw_start * master in the STP stream. + * + * The @packet callback should adhere to the following rules: + * 1) it must return the number of bytes it consumed from the payload; + * 2) therefore, if it sent a packet that does not have payload (like FLAG), + * it must return zero; + * 3) if it does not support the requested packet type/flag combination, + * it must return -ENOTSUPP. */ struct stm_data { const char *name; -- cgit v1.2.3 From 59be422e4ce10e3d49d4c9407a80fab8a9b7bc84 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:02 +0200 Subject: stm class: Support devices with multiple instances By convention, the name of the stm policy directory in configfs consists of the device name to which it applies and the actual policy name, separated by a dot. Now, some devices already have dots in their names that separate name of the actual device from its instance identifier. Such devices will result in two (or more, who can tell) dots in the policy directory name. Existing policy code, however, will treat the first dot as the one that separates device name from policy name, therefore failing the above case. This patch makes the last dot in the directory name be the separator, thus prohibiting dots from being used in policy names. Suggested-by: Chunyan Zhang Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 94d3abfb737a..1db189657b2b 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -332,10 +332,11 @@ stp_policies_make(struct config_group *group, const char *name) /* * node must look like ., where - * is the name of an existing stm device and - * is an arbitrary string + * is the name of an existing stm device; may + * contain dots; + * is an arbitrary string; may not contain dots */ - p = strchr(devname, '.'); + p = strrchr(devname, '.'); if (!p) { kfree(devname); return ERR_PTR(-EINVAL); -- cgit v1.2.3 From bcfdf8afdebe63a2217fa632ae94f8aeecf9126f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:03 +0200 Subject: stm class: dummy_stm: Create multiple devices STM framework should be able to handle multiple STM devices at a time, each one with its own master allocation policy. This patch changes dummy_stm driver to create multiple STM sinks to help testing the framework. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/dummy_stm.c | 57 ++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 3709bef0b21f..4ff5961cd36f 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -40,22 +40,61 @@ dummy_stm_packet(struct stm_data *stm_data, unsigned int master, return size; } -static struct stm_data dummy_stm = { - .name = "dummy_stm", - .sw_start = 0x0000, - .sw_end = 0xffff, - .sw_nchannels = 0xffff, - .packet = dummy_stm_packet, -}; +#define DUMMY_STM_MAX 32 + +static struct stm_data dummy_stm[DUMMY_STM_MAX]; + +static int nr_dummies = 4; + +module_param(nr_dummies, int, 0600); + +static unsigned int dummy_stm_nr; static int dummy_stm_init(void) { - return stm_register_device(NULL, &dummy_stm, THIS_MODULE); + int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); + + if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX) + return -EINVAL; + + for (i = 0; i < __nr_dummies; i++) { + dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); + if (!dummy_stm[i].name) + goto fail_unregister; + + dummy_stm[i].sw_start = 0x0000; + dummy_stm[i].sw_end = 0xffff; + dummy_stm[i].sw_nchannels = 0xffff; + dummy_stm[i].packet = dummy_stm_packet; + + ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); + if (ret) + goto fail_free; + } + + dummy_stm_nr = __nr_dummies; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_unregister_device(&dummy_stm[i]); +fail_free: + kfree(dummy_stm[i].name); + } + + return ret; + } static void dummy_stm_exit(void) { - stm_unregister_device(&dummy_stm); + int i; + + for (i = 0; i < dummy_stm_nr; i++) { + stm_unregister_device(&dummy_stm[i]); + kfree(dummy_stm[i].name); + } } module_init(dummy_stm_init); -- cgit v1.2.3 From 1192918530381b5cfc0e5da51233fa94f783b221 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:04 +0200 Subject: stm class: Add heartbeat stm source device Heartbeat stm source may have multiple instances (for connecting to different stm devices). Each instance will send a periodic test message over its stm device when it is linked. This can be used for testing stm class framework, stm device drivers or as a heartbeat over the stm link. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 11 ++++ drivers/hwtracing/stm/Makefile | 2 + drivers/hwtracing/stm/heartbeat.c | 130 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 drivers/hwtracing/stm/heartbeat.c diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index e0ac75395526..847a39b35307 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -28,4 +28,15 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. +config STM_SOURCE_HEARTBEAT + tristate "Heartbeat over STM devices" + help + This is a kernel space trace source that sends periodic + heartbeat messages to trace hosts over STM devices. It is + also useful for testing stm class drivers and the stm class + framework itself. + + If you want to send heartbeat messages over STM devices, + say Y. + endif diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index f9312c38dd7a..a9ce3d487e57 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -5,5 +5,7 @@ stm_core-y := core.o policy.o obj-$(CONFIG_STM_DUMMY) += dummy_stm.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o +obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o stm_console-y := console.o +stm_heartbeat-y := heartbeat.o diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c new file mode 100644 index 000000000000..0133571b506f --- /dev/null +++ b/drivers/hwtracing/stm/heartbeat.c @@ -0,0 +1,130 @@ +/* + * Simple heartbeat STM source driver + * 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. + * + * Heartbeat STM source will send repetitive messages over STM devices to a + * trace host. + */ + +#include +#include +#include +#include +#include + +#define STM_HEARTBEAT_MAX 32 + +static int nr_devs = 4; +static int interval_ms = 10; + +module_param(nr_devs, int, 0600); +module_param(interval_ms, int, 0600); + +static struct stm_heartbeat { + struct stm_source_data data; + struct hrtimer hrtimer; + unsigned int active; +} stm_heartbeat[STM_HEARTBEAT_MAX]; + +static unsigned int nr_instances; + +static const char str[] = "heartbeat stm source driver is here to serve you"; + +static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) +{ + struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat, + hrtimer); + + stm_source_write(&heartbeat->data, 0, str, sizeof str); + if (heartbeat->active) + hrtimer_forward_now(hr, ms_to_ktime(interval_ms)); + + return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int stm_heartbeat_link(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 1; + hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms), + HRTIMER_MODE_ABS); + + return 0; +} + +static void stm_heartbeat_unlink(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 0; + hrtimer_cancel(&heartbeat->hrtimer); +} + +static int stm_heartbeat_init(void) +{ + int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs); + + if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX) + return -EINVAL; + + for (i = 0; i < __nr_instances; i++) { + stm_heartbeat[i].data.name = + kasprintf(GFP_KERNEL, "heartbeat.%d", i); + if (!stm_heartbeat[i].data.name) + goto fail_unregister; + + stm_heartbeat[i].data.nr_chans = 1; + stm_heartbeat[i].data.link = stm_heartbeat_link; + stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; + hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + stm_heartbeat[i].hrtimer.function = + stm_heartbeat_hrtimer_handler; + + ret = stm_source_register_device(NULL, &stm_heartbeat[i].data); + if (ret) + goto fail_free; + } + + nr_instances = __nr_instances; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_source_unregister_device(&stm_heartbeat[i].data); +fail_free: + kfree(stm_heartbeat[i].data.name); + } + + return ret; +} + +static void stm_heartbeat_exit(void) +{ + int i; + + for (i = 0; i < nr_instances; i++) { + stm_source_unregister_device(&stm_heartbeat[i].data); + kfree(stm_heartbeat[i].data.name); + } +} + +module_init(stm_heartbeat_init); +module_exit(stm_heartbeat_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("stm_heartbeat driver"); +MODULE_AUTHOR("Alexander Shishkin "); -- cgit v1.2.3 From 1810f2c44817c74ca3d05d1e3981e3a2e2ceb6f5 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:05 +0200 Subject: stm class: Fix unlocking braino in the error path If an illegal attempt is made to unlink stm source device from an stm device, the stm device's link spinlock mistakenly remains locked. While this really shouldn't happen (there's a warning in place), the locking should remain in order so that we can still recover from this situation if it indeed does happen. This patch unifies the unlocking in the exit path of __stm_source_link_drop() to fix this. Reported-by: Laurent Fert Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0db303b50e51..4a626d8990b2 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -816,10 +816,8 @@ static void __stm_source_link_drop(struct stm_source_device *src, spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) { - spin_unlock(&src->link_lock); - return; - } + if (WARN_ON_ONCE(link != stm)) + goto unlock; stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -827,6 +825,7 @@ static void __stm_source_link_drop(struct stm_source_device *src, stm_put_device(link); rcu_assign_pointer(src->link, NULL); +unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); } -- cgit v1.2.3 From cde4ad8368840e414ecf67db258fe1dabaa5fd2e Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:06 +0200 Subject: stm class: Guard output assignment against concurrency It is possible to concurrently assign the same output (a character device writer or an stm_source device) to different stm devices, which sets off a strategically placed warning in stm_output_assign(). To avoid this, use a spinlock to serialize (un)assignments between outputs and stm devices. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 17 +++++++++++++++++ drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 4a626d8990b2..f6ade21729fa 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -185,6 +185,9 @@ static void stm_output_claim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) return; @@ -199,6 +202,9 @@ stm_output_disclaim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + bitmap_release_region(&master->chan_map[0], output->channel, ilog2(output->nr_chans)); @@ -288,6 +294,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, } spin_lock(&stm->mc_lock); + spin_lock(&output->lock); /* output is already assigned -- shouldn't happen */ if (WARN_ON_ONCE(output->nr_chans)) goto unlock; @@ -304,6 +311,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, ret = 0; unlock: + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); return ret; @@ -312,11 +320,18 @@ unlock: static void stm_output_free(struct stm_device *stm, struct stm_output *output) { spin_lock(&stm->mc_lock); + spin_lock(&output->lock); if (output->nr_chans) stm_output_disclaim(stm, output); + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); } +static void stm_output_init(struct stm_output *output) +{ + spin_lock_init(&output->lock); +} + static int major_match(struct device *dev, const void *data) { unsigned int major = *(unsigned int *)data; @@ -339,6 +354,7 @@ static int stm_char_open(struct inode *inode, struct file *file) if (!stmf) return -ENOMEM; + stm_output_init(&stmf->output); stmf->stm = to_stm_device(dev); if (!try_module_get(stmf->stm->owner)) @@ -952,6 +968,7 @@ int stm_source_register_device(struct device *parent, if (err) goto err; + stm_output_init(&src->output); spin_lock_init(&src->link_lock); INIT_LIST_HEAD(&src->link_entry); src->data = data; diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 97ee02241440..4e8c6926260f 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -57,6 +57,7 @@ struct stm_device { container_of((_d), struct stm_device, dev) struct stm_output { + spinlock_t lock; unsigned int master; unsigned int channel; unsigned int nr_chans; -- cgit v1.2.3 From f7c81c7176c72c7899390754b4b038a64b296e4d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:07 +0200 Subject: stm class: Fix unbalanced module/device refcounting STM code takes references to the stm device and its module for the duration of the character device's existence or the stm_source link. Dropping these references is not well balanced everywhere, which may lead to leaks. This patch balances the acquisition and releasing of these two references and annotates each site so that it's easier to verify correctness by reading the code. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index f6ade21729fa..f86f56d8535f 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -113,6 +113,7 @@ struct stm_device *stm_find_device(const char *buf) stm = to_stm_device(dev); if (!try_module_get(stm->owner)) { + /* matches class_find_device() above */ put_device(dev); return NULL; } @@ -125,7 +126,7 @@ struct stm_device *stm_find_device(const char *buf) * @stm: stm device, previously acquired by stm_find_device() * * This drops the module reference and device reference taken by - * stm_find_device(). + * stm_find_device() or stm_char_open(). */ void stm_put_device(struct stm_device *stm) { @@ -365,6 +366,8 @@ static int stm_char_open(struct inode *inode, struct file *file) return nonseekable_open(inode, file); err_free: + /* matches class_find_device() above */ + put_device(dev); kfree(stmf); return err; @@ -375,6 +378,11 @@ static int stm_char_release(struct inode *inode, struct file *file) struct stm_file *stmf = file->private_data; stm_output_free(stmf->stm, &stmf->output); + + /* + * matches the stm_char_open()'s + * class_find_device() + try_module_get() + */ stm_put_device(stmf->stm); kfree(stmf); @@ -539,10 +547,8 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) ret = stm->data->link(stm->data, stmf->output.master, stmf->output.channel); - if (ret) { + if (ret) stm_output_free(stmf->stm, &stmf->output); - stm_put_device(stmf->stm); - } err_free: kfree(id); @@ -679,6 +685,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return 0; err_device: + /* matches device_initialize() above */ put_device(&stm->dev); err_free: kfree(stm); @@ -791,7 +798,6 @@ static int stm_source_link_add(struct stm_source_device *src, fail_free_output: stm_output_free(stm, &src->output); - stm_put_device(stm); fail_detach: mutex_lock(&stm->link_mutex); @@ -905,8 +911,10 @@ static ssize_t stm_source_link_store(struct device *dev, return -EINVAL; err = stm_source_link_add(src, link); - if (err) + if (err) { + /* matches the stm_find_device() above */ stm_put_device(link); + } return err ? : count; } -- cgit v1.2.3 From b4ca34aaf78ed0cdfc15956d377064104257a437 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:08 +0200 Subject: stm class: Fix a race in unlinking There is a window in stm_source_link_drop(), during which the source's link may change before locks are acquired. When this happens, it throws a warning, since this is not an expected scenario. This patch handles the race in such a way that if the link appears to have changed by the time we took the locks, it will release them and repeat the whole unlinking procedure from the beginning, unless the other contender beat us to it. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 54 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index f86f56d8535f..30181821d909 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -694,18 +694,26 @@ err_free: } EXPORT_SYMBOL_GPL(stm_register_device); -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm); +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm); void stm_unregister_device(struct stm_data *stm_data) { struct stm_device *stm = stm_data->stm; struct stm_source_device *src, *iter; - int i; + int i, ret; mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); + /* + * src <-> stm link must not change under the same + * stm::link_mutex, so complain loudly if it has; + * also in this situation ret!=0 means this src is + * not connected to this stm and it should be otherwise + * safe to proceed with the tear-down of stm. + */ + WARN_ON_ONCE(ret); } mutex_unlock(&stm->link_mutex); @@ -824,22 +832,28 @@ fail_detach: * * Caller must hold stm::link_mutex. */ -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm) +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm) { struct stm_device *link; + int ret = 0; lockdep_assert_held(&stm->link_mutex); - if (src->data->unlink) - src->data->unlink(src->data); - /* for stm::link_list modification, we hold both mutex and spinlock */ spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) + + /* + * The linked device may have changed since we last looked, because + * we weren't holding the src::link_lock back then; if this is the + * case, tell the caller to retry. + */ + if (link != stm) { + ret = -EAGAIN; goto unlock; + } stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -850,6 +864,11 @@ static void __stm_source_link_drop(struct stm_source_device *src, unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + + if (!ret && src->data->unlink) + src->data->unlink(src->data); + + return ret; } /** @@ -865,18 +884,29 @@ unlock: static void stm_source_link_drop(struct stm_source_device *src) { struct stm_device *stm; - int idx; + int idx, ret; +retry: idx = srcu_read_lock(&stm_source_srcu); + /* + * The stm device will be valid for the duration of this + * read section, but the link may change before we grab + * the src::link_lock in __stm_source_link_drop(). + */ stm = srcu_dereference(src->link, &stm_source_srcu); + ret = 0; if (stm) { mutex_lock(&stm->link_mutex); - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); + + /* if it did change, retry */ + if (ret == -EAGAIN) + goto retry; } static ssize_t stm_source_link_show(struct device *dev, -- cgit v1.2.3 From cc8424074e51355e0c6ba717d8edc50d408f2802 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:09 +0200 Subject: stm class: Plug stm device's unlink callback STM device's unlink callback is never actually called from anywhere in the stm class code. This patch adds calls to stm driver's unlink method after the unlinking has succeeded. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 23 +++++++++++++++++++---- include/linux/stm.h | 3 +++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 30181821d909..de80d45d8df9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -376,14 +376,19 @@ err_free: static int stm_char_release(struct inode *inode, struct file *file) { struct stm_file *stmf = file->private_data; + struct stm_device *stm = stmf->stm; + + if (stm->data->unlink) + stm->data->unlink(stm->data, stmf->output.master, + stmf->output.channel); - stm_output_free(stmf->stm, &stmf->output); + stm_output_free(stm, &stmf->output); /* * matches the stm_char_open()'s * class_find_device() + try_module_get() */ - stm_put_device(stmf->stm); + stm_put_device(stm); kfree(stmf); return 0; @@ -865,8 +870,18 @@ unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); - if (!ret && src->data->unlink) - src->data->unlink(src->data); + /* + * Call the unlink callbacks for both source and stm, when we know + * that we have actually performed the unlinking. + */ + if (!ret) { + if (src->data->unlink) + src->data->unlink(src->data); + + if (stm->data->unlink) + stm->data->unlink(stm->data, src->output.master, + src->output.channel); + } return ret; } diff --git a/include/linux/stm.h b/include/linux/stm.h index ab8ceca4f570..1a79ed8e43da 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -74,6 +74,9 @@ struct stm_device; * it must return zero; * 3) if it does not support the requested packet type/flag combination, * it must return -ENOTSUPP. + * + * The @unlink callback is called when there are no more active writers so + * that the master/channel can be quiesced. */ struct stm_data { const char *name; -- cgit v1.2.3 From adcde635f5d6b77e9f11087af09c6f62da2bf108 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:10 +0200 Subject: stm class: dummy_stm: Add link callback for fault injection STM device's link callback has the power to abort master/channel assignment by returning a negative error code. Use this in dummy stm device to optionally abort assigning certain channel IDs. This is useful as fault injection into the stm class core, for testing purposes. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/dummy_stm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 4ff5961cd36f..310adf57e7a1 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -50,6 +50,19 @@ module_param(nr_dummies, int, 0600); static unsigned int dummy_stm_nr; +static unsigned int fail_mode; + +module_param(fail_mode, int, 0600); + +static int dummy_stm_link(struct stm_data *data, unsigned int master, + unsigned int channel) +{ + if (fail_mode && (channel & fail_mode)) + return -EINVAL; + + return 0; +} + static int dummy_stm_init(void) { int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); @@ -66,6 +79,7 @@ static int dummy_stm_init(void) dummy_stm[i].sw_end = 0xffff; dummy_stm[i].sw_nchannels = 0xffff; dummy_stm[i].packet = dummy_stm_packet; + dummy_stm[i].link = dummy_stm_link; ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); if (ret) -- cgit v1.2.3 From bf16e5b8cdeabc1fe6565af0be475bb2084dc388 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:43 -0700 Subject: coresight: "DEVICE_ATTR_RO" should defined as static. "DEVICE_ATTR_RO(name)" should be defined as static. And there is an unnecessary space at the front of the code. The sparse tool output logs as the following: coresight-etm4x.c:2224:1: warning: symbol 'dev_attr_trcoslsr' was not declared. Should it be static? coresight-etm4x.c:2225:1: warning: symbol 'dev_attr_trcpdcr' was not declared. Should it be static? coresight-etm4x.c:2226:1: warning: symbol 'dev_attr_trcpdsr' was not declared. Should it be static? And the smatch tool output logs as the following: of_coresight.c:89 of_coresight_alloc_memory() warn: inconsistent indenting Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/of_coresight.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b6ae9cb6ff57..d6a92c6d3a66 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2227,7 +2227,7 @@ static ssize_t name##_show(struct device *_dev, \ return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ readl_relaxed(drvdata->base + offset)); \ } \ -DEVICE_ATTR_RO(name) +static DEVICE_ATTR_RO(name) coresight_simple_func(trcoslsr, TRCOSLSR); coresight_simple_func(trcpdcr, TRCPDCR); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index b0973617826f..3cc57c1e3b5d 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -86,7 +86,7 @@ static int of_coresight_alloc_memory(struct device *dev, return -ENOMEM; /* Children connected to this component via @outports */ - pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * + pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * sizeof(*pdata->child_names), GFP_KERNEL); if (!pdata->child_names) -- cgit v1.2.3 From 61390593f72377c3a8f41ef998462e2d3985adac Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:44 -0700 Subject: coresight: etm4x: Check every parameter used by dma_xx_coherent. The dma_alloc_coherent return an "void *" not an "void __iomen *". It uses the wrong parameters when calls dma_free_coherent function. The sparse tool output logs as the following: coresight-tmc.c:199:23: expected void * coresight-tmc.c:199:23: got void [noderef] *vaddr coresight-tmc.c:336:30: warning: incorrect type in assignment (different address spaces) coresight-tmc.c:336:30: expected char *buf coresight-tmc.c:336:30: got void [noderef] * coresight-tmc.c:769:50: warning: incorrect type in argument 4 (different base types) coresight-tmc.c:769:50: expected unsigned long long [unsigned] [usertype] dma_handle coresight-tmc.c:769:50: got restricted gfp_t Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 570d96815f78..c4fa70ed14ce 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -124,7 +124,7 @@ struct tmc_drvdata { bool reading; char *buf; dma_addr_t paddr; - void __iomem *vaddr; + void *vaddr; u32 size; bool enable; enum tmc_config_type config_type; @@ -766,7 +766,7 @@ err_misc_register: err_devm_kzalloc: if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) dma_free_coherent(dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); + drvdata->vaddr, drvdata->paddr); return ret; } -- cgit v1.2.3 From b3e94405941e6916d5e365454d74560c2bea47ca Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:45 -0700 Subject: coresight: associating path with session rather than tracer When using the Coresight framework from the sysFS interface a tracer is always handling a single session and as such, a path can be associated with a tracer. But when supporting multiple session per tracer there is no guarantee that sessions will always have the same path from source to sink. This patch is removing the automatic association between path and tracers. The building of a path and enablement of the components in the path are decoupled, allowing for the association of a path with a session rather than a tracer. To keep backward functionality with the current sysFS access methods a per-cpu place holder is used to keep a handle on the path built when tracers are enabled. Lastly APIs to build paths and enable tracers are made public so that other subsystem can interact with the Coresight framework. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-priv.h | 5 + drivers/hwtracing/coresight/coresight.c | 296 ++++++++++++++++++--------- include/linux/coresight.h | 2 - 3 files changed, 206 insertions(+), 97 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 62fcd98cc7cf..7b193a34d709 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -52,6 +52,11 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } +void coresight_disable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path); +struct list_head *coresight_build_path(struct coresight_device *csdev); +void coresight_release_path(struct list_head *path); + #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X extern int etm_readl_cp14(u32 off, unsigned int *val); extern int etm_writel_cp14(u32 off, u32 val); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 7e6e9ff27dd1..f26589effb70 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -29,6 +29,22 @@ static DEFINE_MUTEX(coresight_mutex); +/** + * struct coresight_node - elements of a path, from source to sink + * @csdev: Address of an element. + * @link: hook to the list. + */ +struct coresight_node { + struct coresight_device *csdev; + struct list_head link; +}; + +/* + * When operating Coresight drivers from the sysFS interface, only a single + * path can exist from a tracer (associated to a CPU) to a sink. + */ +static DEFINE_PER_CPU(struct list_head *, sysfs_path); + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; @@ -68,15 +84,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev) csdev, coresight_id_match); } -static int coresight_find_link_inport(struct coresight_device *csdev) +static int coresight_find_link_inport(struct coresight_device *csdev, + struct coresight_device *parent) { int i; - struct coresight_device *parent; struct coresight_connection *conn; - parent = container_of(csdev->path_link.next, - struct coresight_device, path_link); - for (i = 0; i < parent->nr_outport; i++) { conn = &parent->conns[i]; if (conn->child_dev == csdev) @@ -89,15 +102,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev) return 0; } -static int coresight_find_link_outport(struct coresight_device *csdev) +static int coresight_find_link_outport(struct coresight_device *csdev, + struct coresight_device *child) { int i; - struct coresight_device *child; struct coresight_connection *conn; - child = container_of(csdev->path_link.prev, - struct coresight_device, path_link); - for (i = 0; i < csdev->nr_outport; i++) { conn = &csdev->conns[i]; if (conn->child_dev == child) @@ -138,14 +148,19 @@ static void coresight_disable_sink(struct coresight_device *csdev) } } -static int coresight_enable_link(struct coresight_device *csdev) +static int coresight_enable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int ret; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return -EINVAL; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) @@ -168,14 +183,19 @@ static int coresight_enable_link(struct coresight_device *csdev) return 0; } -static void coresight_disable_link(struct coresight_device *csdev) +static void coresight_disable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int i, nr_conns; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { @@ -235,109 +255,167 @@ static void coresight_disable_source(struct coresight_device *csdev) } } -static int coresight_enable_path(struct list_head *path) +void coresight_disable_path(struct list_head *path) { - int ret = 0; - struct coresight_device *cd; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; - /* - * At this point we have a full @path, from source to sink. The - * sink is the first entry and the source the last one. Go through - * all the components and enable them one by one. - */ - list_for_each_entry(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - ret = coresight_enable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * Don't enable the source just yet - this needs to - * happen at the very end when all links and sink - * along the path have been configured properly. - */ - ; - } else { - ret = coresight_enable_link(cd); + list_for_each_entry(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + coresight_disable_sink(csdev); + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are disabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + coresight_disable_link(csdev, parent, child); + break; + default: + break; } - if (ret) - goto err; } +} - return 0; -err: - list_for_each_entry_continue_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - ; - } else { - coresight_disable_link(cd); +int coresight_enable_path(struct list_head *path) +{ + + int ret = 0; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; + + list_for_each_entry_reverse(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + ret = coresight_enable_sink(csdev); + if (ret) + goto err; + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are enabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + ret = coresight_enable_link(csdev, parent, child); + if (ret) + goto err; + break; + default: + goto err; } } +out: return ret; +err: + coresight_disable_path(path); + goto out; } -static int coresight_disable_path(struct list_head *path) +/** + * _coresight_build_path - recursively build a path from a @csdev to a sink. + * @csdev: The device to start from. + * @path: The list to add devices to. + * + * The tree of Coresight device is traversed until an activated sink is + * found. From there the sink is added to the list along with all the + * devices that led to that point - the end result is a list from source + * to sink. In that list the source is the first device and the sink the + * last one. + */ +static int _coresight_build_path(struct coresight_device *csdev, + struct list_head *path) { - struct coresight_device *cd; + int i; + bool found = false; + struct coresight_node *node; + struct coresight_connection *conn; - list_for_each_entry_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * The source has already been stopped, no need - * to do it again here. - */ - ; - } else { - coresight_disable_link(cd); + /* An activated sink has been found. Enqueue the element */ + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated) + goto out; + + /* Not a sink - recursively explore each port found on this element */ + for (i = 0; i < csdev->nr_outport; i++) { + conn = &csdev->conns[i]; + if (_coresight_build_path(conn->child_dev, path) == 0) { + found = true; + break; } } + if (!found) + return -ENODEV; + +out: + /* + * A path from this element to a sink has been found. The elements + * leading to the sink are already enqueued, all that is left to do + * is add a node for this element. + */ + node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->csdev = csdev; + list_add(&node->link, path); + return 0; } -static int coresight_build_paths(struct coresight_device *csdev, - struct list_head *path, - bool enable) +struct list_head *coresight_build_path(struct coresight_device *csdev) { - int i, ret = -EINVAL; - struct coresight_connection *conn; + struct list_head *path; - list_add(&csdev->path_link, path); + path = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!path) + return NULL; - if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - csdev->activated) { - if (enable) - ret = coresight_enable_path(path); - else - ret = coresight_disable_path(path); - } else { - for (i = 0; i < csdev->nr_outport; i++) { - conn = &csdev->conns[i]; - if (coresight_build_paths(conn->child_dev, - path, enable) == 0) - ret = 0; - } + INIT_LIST_HEAD(path); + + if (_coresight_build_path(csdev, path)) { + kfree(path); + path = NULL; } - if (list_first_entry(path, struct coresight_device, path_link) != csdev) - dev_err(&csdev->dev, "wrong device in %s\n", __func__); + return path; +} - list_del(&csdev->path_link); +/** + * coresight_release_path - release a previously built path. + * @path: the path to release. + * + * Go through all the elements of a path and 1) removed it from the list and + * 2) free the memory allocated for each node. + */ +void coresight_release_path(struct list_head *path) +{ + struct coresight_node *nd, *next; - return ret; + list_for_each_entry_safe(nd, next, path, link) { + list_del(&nd->link); + kfree(nd); + } + + kfree(path); + path = NULL; } int coresight_enable(struct coresight_device *csdev) { int ret = 0; - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -348,22 +426,47 @@ int coresight_enable(struct coresight_device *csdev) if (csdev->enable) goto out; - if (coresight_build_paths(csdev, &path, true)) { - dev_err(&csdev->dev, "building path(s) failed\n"); + path = coresight_build_path(csdev); + if (!path) { + pr_err("building path(s) failed\n"); goto out; } - if (coresight_enable_source(csdev)) - dev_err(&csdev->dev, "source enable failed\n"); + ret = coresight_enable_path(path); + if (ret) + goto err_path; + + ret = coresight_enable_source(csdev); + if (ret) + goto err_source; + + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(sysfs_path, cpu) = path; + out: mutex_unlock(&coresight_mutex); return ret; + +err_source: + coresight_disable_path(path); + +err_path: + coresight_release_path(path); + goto out; } EXPORT_SYMBOL_GPL(coresight_enable); void coresight_disable(struct coresight_device *csdev) { - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -373,9 +476,12 @@ void coresight_disable(struct coresight_device *csdev) if (!csdev->enable) goto out; + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(sysfs_path, cpu); coresight_disable_source(csdev); - if (coresight_build_paths(csdev, &path, false)) - dev_err(&csdev->dev, "releasing path(s) failed\n"); + coresight_disable_path(path); + coresight_release_path(path); + per_cpu(sysfs_path, cpu) = NULL; out: mutex_unlock(&coresight_mutex); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index bf62b265bf52..851ecb22397e 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -152,7 +152,6 @@ struct coresight_connection { by @coresight_ops. * @dev: The device entity associated to this component. * @refcnt: keep track of what is in use. - * @path_link: link of current component into the path being enabled. * @orphan: true if the component has connections that haven't been linked. * @enable: 'true' if component is currently part of an active path. * @activated: 'true' only if a _sink_ has been activated. A sink can be @@ -168,7 +167,6 @@ struct coresight_device { const struct coresight_ops *ops; struct device dev; atomic_t *refcnt; - struct list_head path_link; bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */ -- cgit v1.2.3 From b6404e21f023e4aa208a0ba03d55a9c8a57cb940 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:46 -0700 Subject: coresight: add API to get sink from path Add an API allowing external code to quickly get a handle on the sink within a path. The sink is always last, but adding an API allows to keep the path's node structure private and remove redundant checks. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-priv.h | 1 + drivers/hwtracing/coresight/coresight.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 7b193a34d709..14f245a2018d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -54,6 +54,7 @@ static inline void CS_UNLOCK(void __iomem *addr) void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path); +struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f26589effb70..2ed48545df26 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -321,6 +321,21 @@ err: goto out; } +struct coresight_device *coresight_get_sink(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_last_entry(path, struct coresight_node, link)->csdev; + if (csdev->type != CORESIGHT_DEV_TYPE_SINK && + csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) + return NULL; + + return csdev; +} + /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. -- cgit v1.2.3 From 5da5325fa85658ee793792b5285dd5fdb76ccfb7 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:47 -0700 Subject: coresight: moving PM runtime operations to core framework Moving PM runtime operations in Coresight devices enable() and disable() API to the framework core when a path is setup. That way the runtime core doesn't have to be involved everytime a path is enabled. It also avoids calling runtime PM operations in IRQ context. Signed-off-by: Mathieu Poirier Reviewed-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 4 ---- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ------ drivers/hwtracing/coresight/coresight-funnel.c | 2 -- drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 ---- drivers/hwtracing/coresight/coresight-replicator.c | 2 -- drivers/hwtracing/coresight/coresight-tmc.c | 5 ----- drivers/hwtracing/coresight/coresight-tpiu.c | 2 -- drivers/hwtracing/coresight/coresight.c | 9 ++++++++- 9 files changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 92969dae739d..917562ecf82a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -137,8 +137,6 @@ static int etb_enable(struct coresight_device *csdev) struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); drvdata->enable = true; @@ -247,8 +245,6 @@ static void etb_disable(struct coresight_device *csdev) drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETB disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index aae80e14508d..fe6791e0c66c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -349,7 +349,6 @@ static int etm_enable(struct coresight_device *csdev) struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(csdev->dev.parent); spin_lock(&drvdata->spinlock); /* @@ -373,7 +372,6 @@ static int etm_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(csdev->dev.parent); return ret; } @@ -422,7 +420,6 @@ static void etm_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d6a92c6d3a66..c2f518fbc9a8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -79,7 +79,6 @@ static int etm4_trace_id(struct coresight_device *csdev) if (!drvdata->enable) return drvdata->trcid; - pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); CS_UNLOCK(drvdata->base); @@ -88,7 +87,6 @@ static int etm4_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return trace_id; } @@ -194,7 +192,6 @@ static int etm4_enable(struct coresight_device *csdev) struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(drvdata->dev); spin_lock(&drvdata->spinlock); /* @@ -214,7 +211,6 @@ static int etm4_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(drvdata->dev); return ret; } @@ -263,8 +259,6 @@ static void etm4_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 25e8ea140a09..fb2c679fbc44 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -69,7 +69,6 @@ static int funnel_enable(struct coresight_device *csdev, int inport, { struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); funnel_enable_hw(drvdata, inport); dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); @@ -95,7 +94,6 @@ static void funnel_disable(struct coresight_device *csdev, int inport, struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); funnel_disable_hw(drvdata, inport); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); } diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 444815179460..286f90b50989 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -48,8 +48,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); - CS_UNLOCK(drvdata->base); /* @@ -86,8 +84,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, CS_LOCK(drvdata->base); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b77d700a3f0e..0ce98903992c 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -41,7 +41,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR enabled\n"); return 0; } @@ -51,7 +50,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index c4fa70ed14ce..b526396d80b6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -242,12 +242,9 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return -EBUSY; } @@ -381,8 +378,6 @@ out: drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "TMC disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index ca50e73d9df7..0d8fce651ff7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -74,7 +74,6 @@ static int tpiu_enable(struct coresight_device *csdev) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(csdev->dev.parent); tpiu_enable_hw(drvdata); dev_info(drvdata->dev, "TPIU enabled\n"); @@ -98,7 +97,6 @@ static void tpiu_disable(struct coresight_device *csdev) struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); tpiu_disable_hw(drvdata); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "TPIU disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2ed48545df26..6b44928c1076 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "coresight-priv.h" @@ -376,7 +377,8 @@ out: /* * A path from this element to a sink has been found. The elements * leading to the sink are already enqueued, all that is left to do - * is add a node for this element. + * is tell the PM runtime core we need this element and add a node + * for it. */ node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); if (!node) @@ -384,6 +386,7 @@ out: node->csdev = csdev; list_add(&node->link, path); + pm_runtime_get_sync(csdev->dev.parent); return 0; } @@ -415,9 +418,13 @@ struct list_head *coresight_build_path(struct coresight_device *csdev) */ void coresight_release_path(struct list_head *path) { + struct coresight_device *csdev; struct coresight_node *nd, *next; list_for_each_entry_safe(nd, next, path, link) { + csdev = nd->csdev; + + pm_runtime_put_sync(csdev->dev.parent); list_del(&nd->link); kfree(nd); } -- cgit v1.2.3 From c1f8e57c9e6692f6e8c6c1f9eab7a46264ac4245 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:48 -0700 Subject: coresight: etm3x: moving etm_readl/writel to header file Moving functions etm_readl/writel to file "coresight-etm.h" so that the main ETM3x driver can be split in more than one file. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 29 +++++++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 --------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index b4481eb29304..34f7db881fa7 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -251,4 +251,33 @@ enum etm_addr_type { ETM_ADDR_TYPE_START, ETM_ADDR_TYPE_STOP, }; + +static inline void etm_writel(struct etm_drvdata *drvdata, + u32 val, u32 off) +{ + if (drvdata->use_cp14) { + if (etm_writel_cp14(off, val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + writel_relaxed(val, drvdata->base + off); + } +} + +static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) +{ + u32 val; + + if (drvdata->use_cp14) { + if (etm_readl_cp14(off, &val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + val = readl_relaxed(drvdata->base + off); + } + + return val; +} #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index fe6791e0c66c..d53440e9af6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static inline void etm_writel(struct etm_drvdata *drvdata, - u32 val, u32 off) -{ - if (drvdata->use_cp14) { - if (etm_writel_cp14(off, val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - writel_relaxed(val, drvdata->base + off); - } -} - -static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) -{ - u32 val; - - if (drvdata->use_cp14) { - if (etm_readl_cp14(off, &val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - val = readl_relaxed(drvdata->base + off); - } - - return val; -} - /* * Memory mapped writes to clear os lock are not supported on some processors * and OS lock must be unlocked before any memory mapped access on such -- cgit v1.2.3 From c04148e708c0d8d7eabc447946b712a66b468b47 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:49 -0700 Subject: coresight: etm3x: moving sysFS entries to dedicated file SysFS entries are big enough to justify their own file. As such moving all sysFS related declarations to a dedicated location. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/Makefile | 3 +- drivers/hwtracing/coresight/coresight-etm.h | 4 + .../hwtracing/coresight/coresight-etm3x-sysfs.c | 1218 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 1234 +------------------- 4 files changed, 1241 insertions(+), 1218 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 99f8e5f6256e..233d66cf22d3 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o -obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o +obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ + coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 34f7db881fa7..9a30aa392ed9 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) return val; } + +extern const struct attribute_group *coresight_etm_groups[]; +int etm_get_trace_id(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c new file mode 100644 index 000000000000..f409f5a88e95 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -0,0 +1,1218 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include "coresight-etm.h" + +static ssize_t nr_addr_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_addr_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_addr_cmp); + +static ssize_t nr_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_cntr; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_cntr); + +static ssize_t nr_ctxid_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ctxid_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ctxid_cmp); + +static ssize_t etmsr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags, val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + CS_UNLOCK(drvdata->base); + + val = etm_readl(drvdata, ETMSR); + + CS_LOCK(drvdata->base); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); + + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(etmsr); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val) { + spin_lock(&drvdata->spinlock); + drvdata->mode = ETM_MODE_EXCLUDE; + drvdata->ctrl = 0x0; + drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; + drvdata->startstop_ctrl = 0x0; + drvdata->addr_idx = 0x0; + for (i = 0; i < drvdata->nr_addr_cmp; i++) { + drvdata->addr_val[i] = 0x0; + drvdata->addr_acctype[i] = 0x0; + drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + } + drvdata->cntr_idx = 0x0; + + etm_set_default(drvdata); + spin_unlock(&drvdata->spinlock); + } + + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->mode; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->mode = val & ETM_MODE_ALL; + + if (drvdata->mode & ETM_MODE_EXCLUDE) + drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + else + drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + + if (drvdata->mode & ETM_MODE_CYCACC) + drvdata->ctrl |= ETMCR_CYC_ACC; + else + drvdata->ctrl &= ~ETMCR_CYC_ACC; + + if (drvdata->mode & ETM_MODE_STALL) { + if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { + dev_warn(drvdata->dev, "stall mode not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_STALL_MODE; + } else + drvdata->ctrl &= ~ETMCR_STALL_MODE; + + if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { + dev_warn(drvdata->dev, "timestamp not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + } else + drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + + if (drvdata->mode & ETM_MODE_CTXID) + drvdata->ctrl |= ETMCR_CTXID_SIZE; + else + drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + spin_unlock(&drvdata->spinlock); + + return size; + +err_unlock: + spin_unlock(&drvdata->spinlock); + return ret; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t trigger_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->trigger_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t trigger_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->trigger_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(trigger_event); + +static ssize_t enable_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->enable_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t enable_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->enable_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(enable_event); + +static ssize_t fifofull_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->fifofull_level; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t fifofull_level_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->fifofull_level = val; + + return size; +} +static DEVICE_ATTR_RW(fifofull_level); + +static ssize_t addr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->addr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_addr_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->addr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_idx); + +static ssize_t addr_single_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_single_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_single); + +static ssize_t addr_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val1 = drvdata->addr_val[idx]; + val2 = drvdata->addr_val[idx + 1]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx %#lx\n", val1, val2); +} + +static ssize_t addr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* Lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val1; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + drvdata->addr_val[idx + 1] = val2; + drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + drvdata->enable_ctrl1 |= (1 << (idx/2)); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_range); + +static ssize_t addr_start_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; + drvdata->startstop_ctrl |= (1 << idx); + drvdata->enable_ctrl1 |= BIT(25); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_start); + +static ssize_t addr_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; + drvdata->startstop_ctrl |= (1 << (idx + 16)); + drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_stop); + +static ssize_t addr_acctype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->addr_acctype[drvdata->addr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_acctype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->addr_acctype[drvdata->addr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_acctype); + +static ssize_t cntr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cntr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_cntr) + return -EINVAL; + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->cntr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_idx); + +static ssize_t cntr_rld_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_val); + +static ssize_t cntr_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_event); + +static ssize_t cntr_rld_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_event); + +static ssize_t cntr_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, ret = 0; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + spin_lock(&drvdata->spinlock); + for (i = 0; i < drvdata->nr_cntr; i++) + ret += sprintf(buf, "counter %d: %x\n", + i, drvdata->cntr_val[i]); + spin_unlock(&drvdata->spinlock); + return ret; + } + + for (i = 0; i < drvdata->nr_cntr; i++) { + val = etm_readl(drvdata, ETMCNTVRn(i)); + ret += sprintf(buf, "counter %d: %x\n", i, val); + } + + return ret; +} + +static ssize_t cntr_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_val); + +static ssize_t seq_12_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_12_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_12_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_12_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_12_event); + +static ssize_t seq_21_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_21_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_21_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_21_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_21_event); + +static ssize_t seq_23_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_23_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_23_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_23_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_23_event); + +static ssize_t seq_31_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_31_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_31_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_31_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_31_event); + +static ssize_t seq_32_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_32_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_32_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_32_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_32_event); + +static ssize_t seq_13_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_13_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_13_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_13_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_13_event); + +static ssize_t seq_curr_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val, flags; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + val = drvdata->seq_curr_state; + goto out; + } + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + + CS_UNLOCK(drvdata->base); + val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + CS_LOCK(drvdata->base); + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); +out: + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_curr_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val > ETM_SEQ_STATE_MAX_VAL) + return -EINVAL; + + drvdata->seq_curr_state = val; + + return size; +} +static DEVICE_ATTR_RW(seq_curr_state); + +static ssize_t ctxid_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_ctxid_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->ctxid_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_idx); + +static ssize_t ctxid_pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_pid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long vpid, pid; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &vpid); + if (ret) + return ret; + + pid = coresight_vpid_to_pid(vpid); + + spin_lock(&drvdata->spinlock); + drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; + drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_pid); + +static ssize_t ctxid_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_mask; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->ctxid_mask = val; + return size; +} +static DEVICE_ATTR_RW(ctxid_mask); + +static ssize_t sync_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->sync_freq; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t sync_freq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->sync_freq = val & ETM_SYNC_MASK; + return size; +} +static DEVICE_ATTR_RW(sync_freq); + +static ssize_t timestamp_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->timestamp_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t timestamp_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->timestamp_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(timestamp_event); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cpu; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); + +} +static DEVICE_ATTR_RO(cpu); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = etm_get_trace_id(drvdata); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t traceid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->traceid = val & ETM_TRACEID_MASK; + return size; +} +static DEVICE_ATTR_RW(traceid); + +static struct attribute *coresight_etm_attrs[] = { + &dev_attr_nr_addr_cmp.attr, + &dev_attr_nr_cntr.attr, + &dev_attr_nr_ctxid_cmp.attr, + &dev_attr_etmsr.attr, + &dev_attr_reset.attr, + &dev_attr_mode.attr, + &dev_attr_trigger_event.attr, + &dev_attr_enable_event.attr, + &dev_attr_fifofull_level.attr, + &dev_attr_addr_idx.attr, + &dev_attr_addr_single.attr, + &dev_attr_addr_range.attr, + &dev_attr_addr_start.attr, + &dev_attr_addr_stop.attr, + &dev_attr_addr_acctype.attr, + &dev_attr_cntr_idx.attr, + &dev_attr_cntr_rld_val.attr, + &dev_attr_cntr_event.attr, + &dev_attr_cntr_rld_event.attr, + &dev_attr_cntr_val.attr, + &dev_attr_seq_12_event.attr, + &dev_attr_seq_21_event.attr, + &dev_attr_seq_23_event.attr, + &dev_attr_seq_31_event.attr, + &dev_attr_seq_32_event.attr, + &dev_attr_seq_13_event.attr, + &dev_attr_seq_curr_state.attr, + &dev_attr_ctxid_idx.attr, + &dev_attr_ctxid_pid.attr, + &dev_attr_ctxid_mask.attr, + &dev_attr_sync_freq.attr, + &dev_attr_timestamp_event.attr, + &dev_attr_traceid.attr, + &dev_attr_cpu.attr, + NULL, +}; + +#define coresight_simple_func(name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +DEVICE_ATTR_RO(name) + +coresight_simple_func(etmccr, ETMCCR); +coresight_simple_func(etmccer, ETMCCER); +coresight_simple_func(etmscr, ETMSCR); +coresight_simple_func(etmidr, ETMIDR); +coresight_simple_func(etmcr, ETMCR); +coresight_simple_func(etmtraceidr, ETMTRACEIDR); +coresight_simple_func(etmteevr, ETMTEEVR); +coresight_simple_func(etmtssvr, ETMTSSCR); +coresight_simple_func(etmtecr1, ETMTECR1); +coresight_simple_func(etmtecr2, ETMTECR2); + +static struct attribute *coresight_etm_mgmt_attrs[] = { + &dev_attr_etmccr.attr, + &dev_attr_etmccer.attr, + &dev_attr_etmscr.attr, + &dev_attr_etmidr.attr, + &dev_attr_etmcr.attr, + &dev_attr_etmtraceidr.attr, + &dev_attr_etmteevr.attr, + &dev_attr_etmtssvr.attr, + &dev_attr_etmtecr1.attr, + &dev_attr_etmtecr2.attr, + NULL, +}; + +static const struct attribute_group coresight_etm_group = { + .attrs = coresight_etm_attrs, +}; + +static const struct attribute_group coresight_etm_mgmt_group = { + .attrs = coresight_etm_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_etm_groups[] = { + &coresight_etm_group, + &coresight_etm_mgmt_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d53440e9af6f..6ea35b350958 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -static void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_drvdata *drvdata) { int i; @@ -293,15 +293,18 @@ static int etm_cpu_id(struct coresight_device *csdev) return drvdata->cpu; } -static int etm_trace_id(struct coresight_device *csdev) +int etm_get_trace_id(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; int trace_id = -1; + if (!drvdata) + goto out; + if (!drvdata->enable) return drvdata->traceid; - pm_runtime_get_sync(csdev->dev.parent); + + pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); @@ -310,9 +313,18 @@ static int etm_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(csdev->dev.parent); + pm_runtime_put(drvdata->dev); +out: return trace_id; + +} + +static int etm_trace_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return etm_get_trace_id(drvdata); } static int etm_enable(struct coresight_device *csdev) @@ -406,1218 +418,6 @@ static const struct coresight_ops etm_cs_ops = { .source_ops = &etm_source_ops, }; -static ssize_t nr_addr_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_addr_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_addr_cmp); - -static ssize_t nr_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_cntr; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_cntr); - -static ssize_t nr_ctxid_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ctxid_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ctxid_cmp); - -static ssize_t etmsr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags, val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = etm_readl(drvdata, ETMSR); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(etmsr); - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int i, ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val) { - spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; - for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; - } - drvdata->cntr_idx = 0x0; - - etm_set_default(drvdata); - spin_unlock(&drvdata->spinlock); - } - - return size; -} -static DEVICE_ATTR_WO(reset); - -static ssize_t mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->mode; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; - - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; - else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; - else - drvdata->ctrl &= ~ETMCR_CYC_ACC; - - if (drvdata->mode & ETM_MODE_STALL) { - if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { - dev_warn(drvdata->dev, "stall mode not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_STALL_MODE; - } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; - - if (drvdata->mode & ETM_MODE_TIMESTAMP) { - if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { - dev_warn(drvdata->dev, "timestamp not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; - } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; - - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; - else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; - spin_unlock(&drvdata->spinlock); - - return size; - -err_unlock: - spin_unlock(&drvdata->spinlock); - return ret; -} -static DEVICE_ATTR_RW(mode); - -static ssize_t trigger_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->trigger_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t trigger_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->trigger_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(trigger_event); - -static ssize_t enable_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->enable_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t enable_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->enable_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(enable_event); - -static ssize_t fifofull_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->fifofull_level; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t fifofull_level_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->fifofull_level = val; - - return size; -} -static DEVICE_ATTR_RW(fifofull_level); - -static ssize_t addr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->addr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_addr_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_idx); - -static ssize_t addr_single_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_single_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_single); - -static ssize_t addr_range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx %#lx\n", val1, val2); -} - -static ssize_t addr_range_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - /* Lower address comparator cannot have a higher address value */ - if (val1 > val2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_range); - -static ssize_t addr_start_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_start_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_start); - -static ssize_t addr_stop_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_stop_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_stop); - -static ssize_t addr_acctype_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_acctype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_acctype); - -static ssize_t cntr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cntr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_cntr) - return -EINVAL; - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_idx); - -static ssize_t cntr_rld_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_val); - -static ssize_t cntr_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_event); - -static ssize_t cntr_rld_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_event); - -static ssize_t cntr_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i, ret = 0; - u32 val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); - spin_unlock(&drvdata->spinlock); - return ret; - } - - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); - } - - return ret; -} - -static ssize_t cntr_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_val); - -static ssize_t seq_12_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_12_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_12_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_12_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_12_event); - -static ssize_t seq_21_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_21_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_21_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_21_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_21_event); - -static ssize_t seq_23_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_23_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_23_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_23_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_23_event); - -static ssize_t seq_31_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_31_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_31_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_31_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_31_event); - -static ssize_t seq_32_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_32_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_32_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_32_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_32_event); - -static ssize_t seq_13_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_13_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_13_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_13_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_13_event); - -static ssize_t seq_curr_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->seq_curr_state; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_curr_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val > ETM_SEQ_STATE_MAX_VAL) - return -EINVAL; - - drvdata->seq_curr_state = val; - - return size; -} -static DEVICE_ATTR_RW(seq_curr_state); - -static ssize_t ctxid_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_ctxid_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_idx); - -static ssize_t ctxid_pid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_pid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long vpid, pid; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &vpid); - if (ret) - return ret; - - pid = coresight_vpid_to_pid(vpid); - - spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_pid); - -static ssize_t ctxid_mask_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_mask; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_mask_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->ctxid_mask = val; - return size; -} -static DEVICE_ATTR_RW(ctxid_mask); - -static ssize_t sync_freq_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->sync_freq; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t sync_freq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->sync_freq = val & ETM_SYNC_MASK; - return size; -} -static DEVICE_ATTR_RW(sync_freq); - -static ssize_t timestamp_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->timestamp_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t timestamp_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->timestamp_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(timestamp_event); - -static ssize_t cpu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cpu; - return scnprintf(buf, PAGE_SIZE, "%d\n", val); - -} -static DEVICE_ATTR_RO(cpu); - -static ssize_t traceid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->traceid; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t traceid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->traceid = val & ETM_TRACEID_MASK; - return size; -} -static DEVICE_ATTR_RW(traceid); - -static struct attribute *coresight_etm_attrs[] = { - &dev_attr_nr_addr_cmp.attr, - &dev_attr_nr_cntr.attr, - &dev_attr_nr_ctxid_cmp.attr, - &dev_attr_etmsr.attr, - &dev_attr_reset.attr, - &dev_attr_mode.attr, - &dev_attr_trigger_event.attr, - &dev_attr_enable_event.attr, - &dev_attr_fifofull_level.attr, - &dev_attr_addr_idx.attr, - &dev_attr_addr_single.attr, - &dev_attr_addr_range.attr, - &dev_attr_addr_start.attr, - &dev_attr_addr_stop.attr, - &dev_attr_addr_acctype.attr, - &dev_attr_cntr_idx.attr, - &dev_attr_cntr_rld_val.attr, - &dev_attr_cntr_event.attr, - &dev_attr_cntr_rld_event.attr, - &dev_attr_cntr_val.attr, - &dev_attr_seq_12_event.attr, - &dev_attr_seq_21_event.attr, - &dev_attr_seq_23_event.attr, - &dev_attr_seq_31_event.attr, - &dev_attr_seq_32_event.attr, - &dev_attr_seq_13_event.attr, - &dev_attr_seq_curr_state.attr, - &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_pid.attr, - &dev_attr_ctxid_mask.attr, - &dev_attr_sync_freq.attr, - &dev_attr_timestamp_event.attr, - &dev_attr_traceid.attr, - &dev_attr_cpu.attr, - NULL, -}; - -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) - -coresight_simple_func(etmccr, ETMCCR); -coresight_simple_func(etmccer, ETMCCER); -coresight_simple_func(etmscr, ETMSCR); -coresight_simple_func(etmidr, ETMIDR); -coresight_simple_func(etmcr, ETMCR); -coresight_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_simple_func(etmteevr, ETMTEEVR); -coresight_simple_func(etmtssvr, ETMTSSCR); -coresight_simple_func(etmtecr1, ETMTECR1); -coresight_simple_func(etmtecr2, ETMTECR2); - -static struct attribute *coresight_etm_mgmt_attrs[] = { - &dev_attr_etmccr.attr, - &dev_attr_etmccer.attr, - &dev_attr_etmscr.attr, - &dev_attr_etmidr.attr, - &dev_attr_etmcr.attr, - &dev_attr_etmtraceidr.attr, - &dev_attr_etmteevr.attr, - &dev_attr_etmtssvr.attr, - &dev_attr_etmtecr1.attr, - &dev_attr_etmtecr2.attr, - NULL, -}; - -static const struct attribute_group coresight_etm_group = { - .attrs = coresight_etm_attrs, -}; - - -static const struct attribute_group coresight_etm_mgmt_group = { - .attrs = coresight_etm_mgmt_attrs, - .name = "mgmt", -}; - -static const struct attribute_group *coresight_etm_groups[] = { - &coresight_etm_group, - &coresight_etm_mgmt_group, - NULL, -}; - static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { -- cgit v1.2.3 From ae69a1da399fccaed406932f5cbee55a6f9d4425 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:50 -0700 Subject: coresight: etm3x: unlocking tracers in default arch init Calling function 'smp_call_function_single()' to unlock a tracer and calling it again right after to perform the default initialisation doesn't make sense. Moving 'etm_os_unlock()' just before making the default initialisation results in the same outcome while saving one call to 'smp_call_function_single()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 6ea35b350958..f2c74bb56993 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS]; * and OS lock must be unlocked before any memory mapped access on such * processors, otherwise memory mapped reads/writes will be invalid. */ -static void etm_os_unlock(void *info) +static void etm_os_unlock(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = (struct etm_drvdata *)info; /* Writing any value to ETMOSLAR unlocks the trace registers */ etm_writel(drvdata, 0x0, ETMOSLAR); + drvdata->os_unlock = true; isb(); } @@ -483,6 +483,9 @@ static void etm_init_arch_data(void *info) u32 etmccr; struct etm_drvdata *drvdata = info; + /* Make sure all registers are accessible */ + etm_os_unlock(drvdata); + CS_UNLOCK(drvdata->base); /* First dummy read */ @@ -607,9 +610,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) get_online_cpus(); etmdrvdata[drvdata->cpu] = drvdata; - if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1)) - drvdata->os_unlock = true; - if (smp_call_function_single(drvdata->cpu, etm_init_arch_data, drvdata, 1)) dev_err(dev, "ETM arch init failed\n"); -- cgit v1.2.3 From 1925a470ce69cdfa2b82ac1565d58dfd39cd877d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:51 -0700 Subject: coresight: etm3x: splitting struct etm_drvdata Splitting "etm_drvdata" in two sections, one for the HW specific data and another for user configuration. That way it is easier to manipulate and zero out the configuration data when more than one concurrent tracing session configuration is active. Also taking care of up-lifting all the code affected by this new arrangement. No loss or gain of functionality (other than what is mentioned above) is introduced by this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 102 ++++---- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 290 ++++++++++++--------- drivers/hwtracing/coresight/coresight-etm3x.c | 153 +++++------ 3 files changed, 305 insertions(+), 240 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 9a30aa392ed9..371fb7d2e829 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -136,29 +136,9 @@ #define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \ ETM_ADD_COMP_0 | \ ETM_EVENT_NOT_A) + /** - * struct etm_drvdata - specifics associated to an ETM component - * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. - * @atclk: optional clock for the core parts of the ETM. - * @csdev: component vitals needed by the framework. - * @spinlock: only one at a time pls. - * @cpu: the cpu this component is affined to. - * @port_size: port size as reported by ETMCR bit 4-6 and 21. - * @arch: ETM/PTM version number. - * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. - * @sticky_enable: true if ETM base configuration has been done. - * @boot_enable:true if we should start tracing at boot time. - * @os_unlock: true if access to management registers is allowed. - * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. - * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. - * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. - * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. - * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. - * @etmccr: value of register ETMCCR. - * @etmccer: value of register ETMCCER. - * @traceid: value of the current ID for this component. + * struct etm_config - configuration information related to an ETM * @mode: controls various modes supported by this ETM/PTM. * @ctrl: used in conjunction with @mode. * @trigger_event: setting for register ETMTRIGGER. @@ -189,30 +169,9 @@ * @ctxid_mask: mask applicable to all the context IDs. * @sync_freq: Synchronisation frequency. * @timestamp_event: Defines an event that requests the insertion - of a timestamp into the trace stream. + * of a timestamp into the trace stream. */ -struct etm_drvdata { - void __iomem *base; - struct device *dev; - struct clk *atclk; - struct coresight_device *csdev; - spinlock_t spinlock; - int cpu; - int port_size; - u8 arch; - bool use_cp14; - bool enable; - bool sticky_enable; - bool boot_enable; - bool os_unlock; - u8 nr_addr_cmp; - u8 nr_cntr; - u8 nr_ext_inp; - u8 nr_ext_out; - u8 nr_ctxid_cmp; - u32 etmccr; - u32 etmccer; - u32 traceid; +struct etm_config { u32 mode; u32 ctrl; u32 trigger_event; @@ -244,6 +203,56 @@ struct etm_drvdata { u32 timestamp_event; }; +/** + * struct etm_drvdata - specifics associated to an ETM component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @atclk: optional clock for the core parts of the ETM. + * @csdev: component vitals needed by the framework. + * @spinlock: only one at a time pls. + * @cpu: the cpu this component is affined to. + * @port_size: port size as reported by ETMCR bit 4-6 and 21. + * @arch: ETM/PTM version number. + * @use_cpu14: true if management registers need to be accessed via CP14. + * @enable: is this ETM/PTM currently tracing. + * @sticky_enable: true if ETM base configuration has been done. + * @boot_enable:true if we should start tracing at boot time. + * @os_unlock: true if access to management registers is allowed. + * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. + * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. + * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. + * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. + * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. + * @etmccr: value of register ETMCCR. + * @etmccer: value of register ETMCCER. + * @traceid: value of the current ID for this component. + * @config: structure holding configuration parameters. + */ +struct etm_drvdata { + void __iomem *base; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; + spinlock_t spinlock; + int cpu; + int port_size; + u8 arch; + bool use_cp14; + bool enable; + bool sticky_enable; + bool boot_enable; + bool os_unlock; + u8 nr_addr_cmp; + u8 nr_cntr; + u8 nr_ext_inp; + u8 nr_ext_out; + u8 nr_ctxid_cmp; + u32 etmccr; + u32 etmccer; + u32 traceid; + struct etm_config config; +}; + enum etm_addr_type { ETM_ADDR_TYPE_NONE, ETM_ADDR_TYPE_SINGLE, @@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); -void etm_set_default(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_config *config); +struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index f409f5a88e95..456df2378a6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -78,6 +78,7 @@ static ssize_t reset_store(struct device *dev, int i, ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -85,19 +86,14 @@ static ssize_t reset_store(struct device *dev, if (val) { spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; + memset(config, 0, sizeof(struct etm_config)); + config->mode = ETM_MODE_EXCLUDE; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + config->addr_type[i] = ETM_ADDR_TYPE_NONE; } - drvdata->cntr_idx = 0x0; - etm_set_default(drvdata); + etm_set_default(config); spin_unlock(&drvdata->spinlock); } @@ -110,8 +106,9 @@ static ssize_t mode_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->mode; + val = config->mode; return sprintf(buf, "%#lx\n", val); } @@ -122,48 +119,49 @@ static ssize_t mode_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; + config->mode = val & ETM_MODE_ALL; - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + if (config->mode & ETM_MODE_EXCLUDE) + config->enable_ctrl1 |= ETMTECR1_INC_EXC; else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + config->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; + if (config->mode & ETM_MODE_CYCACC) + config->ctrl |= ETMCR_CYC_ACC; else - drvdata->ctrl &= ~ETMCR_CYC_ACC; + config->ctrl &= ~ETMCR_CYC_ACC; - if (drvdata->mode & ETM_MODE_STALL) { + if (config->mode & ETM_MODE_STALL) { if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { dev_warn(drvdata->dev, "stall mode not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_STALL_MODE; + config->ctrl |= ETMCR_STALL_MODE; } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; + config->ctrl &= ~ETMCR_STALL_MODE; - if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (config->mode & ETM_MODE_TIMESTAMP) { if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { dev_warn(drvdata->dev, "timestamp not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + config->ctrl |= ETMCR_TIMESTAMP_EN; } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + config->ctrl &= ~ETMCR_TIMESTAMP_EN; - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; + if (config->mode & ETM_MODE_CTXID) + config->ctrl |= ETMCR_CTXID_SIZE; else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + config->ctrl &= ~ETMCR_CTXID_SIZE; spin_unlock(&drvdata->spinlock); return size; @@ -179,8 +177,9 @@ static ssize_t trigger_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->trigger_event; + val = config->trigger_event; return sprintf(buf, "%#lx\n", val); } @@ -191,12 +190,13 @@ static ssize_t trigger_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->trigger_event = val & ETM_EVENT_MASK; + config->trigger_event = val & ETM_EVENT_MASK; return size; } @@ -207,8 +207,9 @@ static ssize_t enable_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->enable_event; + val = config->enable_event; return sprintf(buf, "%#lx\n", val); } @@ -219,12 +220,13 @@ static ssize_t enable_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->enable_event = val & ETM_EVENT_MASK; + config->enable_event = val & ETM_EVENT_MASK; return size; } @@ -235,8 +237,9 @@ static ssize_t fifofull_level_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->fifofull_level; + val = config->fifofull_level; return sprintf(buf, "%#lx\n", val); } @@ -247,12 +250,13 @@ static ssize_t fifofull_level_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->fifofull_level = val; + config->fifofull_level = val; return size; } @@ -263,8 +267,9 @@ static ssize_t addr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->addr_idx; + val = config->addr_idx; return sprintf(buf, "%#lx\n", val); } @@ -275,6 +280,7 @@ static ssize_t addr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -288,7 +294,7 @@ static ssize_t addr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; + config->addr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -301,16 +307,17 @@ static ssize_t addr_single_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -324,21 +331,22 @@ static ssize_t addr_single_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; spin_unlock(&drvdata->spinlock); return size; @@ -351,23 +359,24 @@ static ssize_t addr_range_show(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; + val1 = config->addr_val[idx]; + val2 = config->addr_val[idx + 1]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx %#lx\n", val1, val2); @@ -380,6 +389,7 @@ static ssize_t addr_range_store(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) return -EINVAL; @@ -388,24 +398,24 @@ static ssize_t addr_range_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); + config->addr_val[idx] = val1; + config->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx + 1] = val2; + config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + config->enable_ctrl1 |= (1 << (idx/2)); spin_unlock(&drvdata->spinlock); return size; @@ -418,16 +428,17 @@ static ssize_t addr_start_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -441,23 +452,24 @@ static ssize_t addr_start_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_START; + config->startstop_ctrl |= (1 << idx); + config->enable_ctrl1 |= BIT(25); spin_unlock(&drvdata->spinlock); return size; @@ -470,16 +482,17 @@ static ssize_t addr_stop_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -493,23 +506,24 @@ static ssize_t addr_stop_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_STOP; + config->startstop_ctrl |= (1 << (idx + 16)); + config->enable_ctrl1 |= ETMTECR1_START_STOP; spin_unlock(&drvdata->spinlock); return size; @@ -521,9 +535,10 @@ static ssize_t addr_acctype_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; + val = config->addr_acctype[config->addr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -536,13 +551,14 @@ static ssize_t addr_acctype_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; + config->addr_acctype[config->addr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -554,8 +570,9 @@ static ssize_t cntr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->cntr_idx; + val = config->cntr_idx; return sprintf(buf, "%#lx\n", val); } @@ -566,6 +583,7 @@ static ssize_t cntr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -578,7 +596,7 @@ static ssize_t cntr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; + config->cntr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -590,9 +608,10 @@ static ssize_t cntr_rld_val_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + val = config->cntr_rld_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -605,13 +624,14 @@ static ssize_t cntr_rld_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + config->cntr_rld_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -623,9 +643,10 @@ static ssize_t cntr_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; + val = config->cntr_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -638,13 +659,14 @@ static ssize_t cntr_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -656,9 +678,10 @@ static ssize_t cntr_rld_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + val = config->cntr_rld_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -671,13 +694,14 @@ static ssize_t cntr_rld_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -690,12 +714,13 @@ static ssize_t cntr_val_show(struct device *dev, int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); + i, config->cntr_val[i]); spin_unlock(&drvdata->spinlock); return ret; } @@ -715,13 +740,14 @@ static ssize_t cntr_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; + config->cntr_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -733,8 +759,9 @@ static ssize_t seq_12_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_12_event; + val = config->seq_12_event; return sprintf(buf, "%#lx\n", val); } @@ -745,12 +772,13 @@ static ssize_t seq_12_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_12_event = val & ETM_EVENT_MASK; + config->seq_12_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_12_event); @@ -760,8 +788,9 @@ static ssize_t seq_21_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_21_event; + val = config->seq_21_event; return sprintf(buf, "%#lx\n", val); } @@ -772,12 +801,13 @@ static ssize_t seq_21_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_21_event = val & ETM_EVENT_MASK; + config->seq_21_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_21_event); @@ -787,8 +817,9 @@ static ssize_t seq_23_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_23_event; + val = config->seq_23_event; return sprintf(buf, "%#lx\n", val); } @@ -799,12 +830,13 @@ static ssize_t seq_23_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_23_event = val & ETM_EVENT_MASK; + config->seq_23_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_23_event); @@ -814,8 +846,9 @@ static ssize_t seq_31_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_31_event; + val = config->seq_31_event; return sprintf(buf, "%#lx\n", val); } @@ -826,12 +859,13 @@ static ssize_t seq_31_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_31_event = val & ETM_EVENT_MASK; + config->seq_31_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_31_event); @@ -841,8 +875,9 @@ static ssize_t seq_32_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_32_event; + val = config->seq_32_event; return sprintf(buf, "%#lx\n", val); } @@ -853,12 +888,13 @@ static ssize_t seq_32_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_32_event = val & ETM_EVENT_MASK; + config->seq_32_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_32_event); @@ -868,8 +904,9 @@ static ssize_t seq_13_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_13_event; + val = config->seq_13_event; return sprintf(buf, "%#lx\n", val); } @@ -880,12 +917,13 @@ static ssize_t seq_13_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_13_event = val & ETM_EVENT_MASK; + config->seq_13_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_13_event); @@ -895,9 +933,10 @@ static ssize_t seq_curr_state_show(struct device *dev, { unsigned long val, flags; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { - val = drvdata->seq_curr_state; + val = config->seq_curr_state; goto out; } @@ -921,6 +960,7 @@ static ssize_t seq_curr_state_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -929,7 +969,7 @@ static ssize_t seq_curr_state_store(struct device *dev, if (val > ETM_SEQ_STATE_MAX_VAL) return -EINVAL; - drvdata->seq_curr_state = val; + config->seq_curr_state = val; return size; } @@ -940,8 +980,9 @@ static ssize_t ctxid_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_idx; + val = config->ctxid_idx; return sprintf(buf, "%#lx\n", val); } @@ -952,6 +993,7 @@ static ssize_t ctxid_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -965,7 +1007,7 @@ static ssize_t ctxid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; + config->ctxid_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -977,9 +1019,10 @@ static ssize_t ctxid_pid_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + val = config->ctxid_vpid[config->ctxid_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -992,6 +1035,7 @@ static ssize_t ctxid_pid_store(struct device *dev, int ret; unsigned long vpid, pid; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &vpid); if (ret) @@ -1000,8 +1044,8 @@ static ssize_t ctxid_pid_store(struct device *dev, pid = coresight_vpid_to_pid(vpid); spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + config->ctxid_pid[config->ctxid_idx] = pid; + config->ctxid_vpid[config->ctxid_idx] = vpid; spin_unlock(&drvdata->spinlock); return size; @@ -1013,8 +1057,9 @@ static ssize_t ctxid_mask_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_mask; + val = config->ctxid_mask; return sprintf(buf, "%#lx\n", val); } @@ -1025,12 +1070,13 @@ static ssize_t ctxid_mask_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->ctxid_mask = val; + config->ctxid_mask = val; return size; } static DEVICE_ATTR_RW(ctxid_mask); @@ -1040,8 +1086,9 @@ static ssize_t sync_freq_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->sync_freq; + val = config->sync_freq; return sprintf(buf, "%#lx\n", val); } @@ -1052,12 +1099,13 @@ static ssize_t sync_freq_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->sync_freq = val & ETM_SYNC_MASK; + config->sync_freq = val & ETM_SYNC_MASK; return size; } static DEVICE_ATTR_RW(sync_freq); @@ -1067,8 +1115,9 @@ static ssize_t timestamp_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->timestamp_event; + val = config->timestamp_event; return sprintf(buf, "%#lx\n", val); } @@ -1079,12 +1128,13 @@ static ssize_t timestamp_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->timestamp_event = val & ETM_EVENT_MASK; + config->timestamp_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(timestamp_event); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index f2c74bb56993..1952dc31fee4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; +static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -186,36 +187,39 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_config *config) { int i; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->enable_event = ETM_HARD_WIRE_RES_A; - - drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL; - drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL; - - for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntr_rld_val[i] = 0x0; - drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_val[i] = 0x0; + if (WARN_ON_ONCE(!config)) + return; + + config->trigger_event = ETM_DEFAULT_EVENT_VAL; + config->enable_event = ETM_HARD_WIRE_RES_A; + + config->seq_12_event = ETM_DEFAULT_EVENT_VAL; + config->seq_21_event = ETM_DEFAULT_EVENT_VAL; + config->seq_23_event = ETM_DEFAULT_EVENT_VAL; + config->seq_31_event = ETM_DEFAULT_EVENT_VAL; + config->seq_32_event = ETM_DEFAULT_EVENT_VAL; + config->seq_13_event = ETM_DEFAULT_EVENT_VAL; + config->timestamp_event = ETM_DEFAULT_EVENT_VAL; + + for (i = 0; i < ETM_MAX_CNTR; i++) { + config->cntr_rld_val[i] = 0x0; + config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_val[i] = 0x0; } - drvdata->seq_curr_state = 0x0; - drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->nr_ctxid_cmp; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->seq_curr_state = 0x0; + config->ctxid_idx = 0x0; + for (i = 0; i < ETM_MAX_CTXID_CMP; i++) { + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask = 0x0; + config->ctxid_mask = 0x0; } static void etm_enable_hw(void *info) @@ -223,6 +227,7 @@ static void etm_enable_hw(void *info) int i; u32 etmcr; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -238,39 +243,39 @@ static void etm_enable_hw(void *info) etmcr = etm_readl(drvdata, ETMCR); etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); etmcr |= drvdata->port_size; - etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR); - etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER); - etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR); - etm_writel(drvdata, drvdata->enable_event, ETMTEEVR); - etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1); - etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR); + etm_writel(drvdata, config->ctrl | etmcr, ETMCR); + etm_writel(drvdata, config->trigger_event, ETMTRIGGER); + etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); + etm_writel(drvdata, config->enable_event, ETMTEEVR); + etm_writel(drvdata, config->enable_ctrl1, ETMTECR1); + etm_writel(drvdata, config->fifofull_level, ETMFFLR); for (i = 0; i < drvdata->nr_addr_cmp; i++) { - etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i)); - etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i)); + etm_writel(drvdata, config->addr_val[i], ETMACVRn(i)); + etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i)); } for (i = 0; i < drvdata->nr_cntr; i++) { - etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i)); - etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i)); - etm_writel(drvdata, drvdata->cntr_rld_event[i], + etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i)); + etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i)); + etm_writel(drvdata, config->cntr_rld_event[i], ETMCNTRLDEVRn(i)); - etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i)); + etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i)); } - etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR); - etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR); - etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR); - etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR); - etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR); - etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR); - etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR); + etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR); + etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR); + etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR); + etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR); + etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR); + etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR); + etm_writel(drvdata, config->seq_curr_state, ETMSQR); for (i = 0; i < drvdata->nr_ext_out; i++) etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); for (i = 0; i < drvdata->nr_ctxid_cmp; i++) - etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i)); - etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR); - etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR); + etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i)); + etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR); + etm_writel(drvdata, config->sync_freq, ETMSYNCFR); /* No external input selected */ etm_writel(drvdata, 0x0, ETMEXTINSELR); - etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR); + etm_writel(drvdata, config->timestamp_event, ETMTSEVR); /* No auxiliary control selected */ etm_writel(drvdata, 0x0, ETMAUXCR); etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR); @@ -278,7 +283,7 @@ static void etm_enable_hw(void *info) etm_writel(drvdata, 0x0, ETMVMIDCVR); /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); + etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -362,6 +367,7 @@ static void etm_disable_hw(void *info) { int i; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); @@ -370,10 +376,10 @@ static void etm_disable_hw(void *info) etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); /* Read back sequencer and counters for post trace analysis */ - drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); for (i = 0; i < drvdata->nr_cntr; i++) - drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); + config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); etm_set_pwrdwn(drvdata); CS_LOCK(drvdata->base); @@ -522,14 +528,8 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_drvdata *drvdata) +static void etm_init_default_data(struct etm_config *config) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and will be just as good. - */ - static int etm3x_traceid = 0x10; - u32 flags = (1 << 0 | /* instruction execute*/ 3 << 3 | /* ARM instruction */ 0 << 5 | /* No data value comparison */ @@ -537,25 +537,28 @@ static void etm_init_default_data(struct etm_drvdata *drvdata) 0 << 8 | /* Ignore context ID */ 0 << 10); /* Security ignored */ + if (WARN_ON_ONCE(!config)) + return; + + config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + config->addr_val[0] = (u32) _stext; + config->addr_val[1] = (u32) _etext; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; + + etm_set_default(config); +} + +static void etm_init_trace_id(struct etm_drvdata *drvdata) +{ /* - * Initial configuration only - guarantees sources handled by - * this driver have a unique ID at startup time but not between - * all other types of sources. For that we lean on the core - * framework. + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. */ - drvdata->traceid = etm3x_traceid++; - drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - if (drvdata->nr_addr_cmp >= 2) { - drvdata->addr_val[0] = (u32) _stext; - drvdata->addr_val[1] = (u32) _etext; - drvdata->addr_acctype[0] = flags; - drvdata->addr_acctype[1] = flags; - drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; - } - - etm_set_default(drvdata); + drvdata->traceid = 0x10 + drvdata->cpu; } static int etm_probe(struct amba_device *adev, const struct amba_id *id) @@ -623,7 +626,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) ret = -EINVAL; goto err_arch_supported; } - etm_init_default_data(drvdata); + + etm_init_trace_id(drvdata); + etm_init_default_data(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; -- cgit v1.2.3 From 22fd532eaa0c24d86e23d8e9e3b7feac4a8cac80 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:52 -0700 Subject: coresight: etm3x: adding operation mode for etm_enable() Adding a new mode to source API enable() in order to distinguish where the request comes from. That way it is possible to perform different operations based on where the request was issued from. The ETM4x driver is also modified to keep in sync with the new interface. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 5 +- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 4 +- drivers/hwtracing/coresight/coresight-etm3x.c | 68 +++++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 6 ++ drivers/hwtracing/coresight/coresight.c | 6 +- include/linux/coresight.h | 2 +- 7 files changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 371fb7d2e829..5b29d5540fe5 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -13,6 +13,7 @@ #ifndef _CORESIGHT_CORESIGHT_ETM_H #define _CORESIGHT_CORESIGHT_ETM_H +#include #include #include "coresight-priv.h" @@ -214,7 +215,7 @@ struct etm_config { * @port_size: port size as reported by ETMCR bit 4-6 and 21. * @arch: ETM/PTM version number. * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. + * @mode: this tracer's mode, i.e sysFS, Perf or disabled. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:true if we should start tracing at boot time. * @os_unlock: true if access to management registers is allowed. @@ -238,7 +239,7 @@ struct etm_drvdata { int port_size; u8 arch; bool use_cp14; - bool enable; + local_t mode; bool sticky_enable; bool boot_enable; bool os_unlock; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 456df2378a6f..387c79fd9d5e 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -716,7 +716,7 @@ static ssize_t cntr_val_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", @@ -935,7 +935,7 @@ static ssize_t seq_curr_state_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { val = config->seq_curr_state; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 1952dc31fee4..e16501b41ed8 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata) if (!drvdata) goto out; - if (!drvdata->enable) + if (!local_read(&drvdata->mode)) return drvdata->traceid; pm_runtime_get_sync(drvdata->dev); @@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } -static int etm_enable(struct coresight_device *csdev) +static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; @@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev) goto err; } - drvdata->enable = true; drvdata->sticky_enable = true; - spin_unlock(&drvdata->spinlock); dev_info(drvdata->dev, "ETM tracing enabled\n"); return 0; + err: spin_unlock(&drvdata->spinlock); return ret; } +static int etm_enable(struct coresight_device *csdev, u32 mode) +{ + int ret; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + switch (mode) { + case CS_MODE_SYSFS: + ret = etm_enable_sysfs(csdev); + break; + default: + ret = -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + local_set(&drvdata->mode, CS_MODE_DISABLED); + + return ret; +} + static void etm_disable_hw(void *info) { int i; @@ -387,7 +413,7 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static void etm_disable(struct coresight_device *csdev) +static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev) * ensures that register writes occur when cpu is powered. */ smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1); - drvdata->enable = false; spin_unlock(&drvdata->spinlock); put_online_cpus(); @@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } +static void etm_disable(struct coresight_device *csdev) +{ + u32 mode; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode = local_read(&drvdata->mode); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + etm_disable_sysfs(csdev); + break; + default: + WARN_ON_ONCE(mode); + return; + } + + if (mode) + local_set(&drvdata->mode, CS_MODE_DISABLED); +} + static const struct coresight_ops_source etm_source_ops = { .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, @@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, etmdrvdata[cpu]->os_unlock = true; } - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; @@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, case CPU_DYING: spin_lock(&etmdrvdata[cpu]->spinlock); - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c2f518fbc9a8..0026092fec7f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -187,7 +187,7 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev) +static int etm4_enable(struct coresight_device *csdev, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 14f245a2018d..ed116b303e87 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,12 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +enum cs_mode { + CS_MODE_DISABLED, + CS_MODE_SYSFS, + CS_MODE_PERF, +}; + static inline void CS_LOCK(void __iomem *addr) { do { diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6b44928c1076..b20afb709141 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -222,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev, csdev->enable = false; } -static int coresight_enable_source(struct coresight_device *csdev) +static int coresight_enable_source(struct coresight_device *csdev, u32 mode) { int ret; @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev); + ret = source_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -458,7 +458,7 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto err_path; - ret = coresight_enable_source(csdev); + ret = coresight_enable_source(csdev, CS_MODE_SYSFS); if (ret) goto err_source; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 851ecb22397e..61dfb8d511ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -213,7 +213,7 @@ struct coresight_ops_link { struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; -- cgit v1.2.3 From 47cd066cd00a65902ee3bd57da5bd395cb83aff9 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:53 -0700 Subject: coresight: etm3x: set progbit to stop trace collection There is no need to use the event enable's "always false" event to stop trace collection. For that purpose setting the programming bit (ETMCR:10) is enough. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index e16501b41ed8..447459969cb5 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -398,9 +398,6 @@ static void etm_disable_hw(void *info) CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); - /* Program trace enable to low by using always false event */ - etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); - /* Read back sequencer and counters for post trace analysis */ config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); -- cgit v1.2.3 From e19217299caf1a54c55081ab6339b3baccec63b0 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:54 -0700 Subject: coresight: etm3x: changing default trace configuration Changing default configuration to include the entire address range rather than just the kernel. That way traces are more inclusive and it is easier to narrow down if needed. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 2 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 ++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 5b29d5540fe5..44585d4adb26 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -146,6 +146,7 @@ * @startstop_ctrl: setting for register ETMTSSCR. * @enable_event: setting for register ETMTEEVR. * @enable_ctrl1: setting for register ETMTECR1. + * @enable_ctrl2: setting for register ETMTECR2. * @fifofull_level: setting for register ETMFFLR. * @addr_idx: index for the address comparator selection. * @addr_val: value for address comparator register. @@ -179,6 +180,7 @@ struct etm_config { u32 startstop_ctrl; u32 enable_event; u32 enable_ctrl1; + u32 enable_ctrl2; u32 fifofull_level; u8 addr_idx; u32 addr_val[ETM_MAX_ADDR_CMP]; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 447459969cb5..92139674bea4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -579,26 +579,23 @@ static void etm_init_arch_data(void *info) static void etm_init_default_data(struct etm_config *config) { - u32 flags = (1 << 0 | /* instruction execute*/ - 3 << 3 | /* ARM instruction */ - 0 << 5 | /* No data value comparison */ - 0 << 7 | /* No exact mach */ - 0 << 8 | /* Ignore context ID */ - 0 << 10); /* Security ignored */ - if (WARN_ON_ONCE(!config)) return; - config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - config->addr_val[0] = (u32) _stext; - config->addr_val[1] = (u32) _etext; - config->addr_acctype[0] = flags; - config->addr_acctype[1] = flags; - config->addr_type[0] = ETM_ADDR_TYPE_RANGE; - config->addr_type[1] = ETM_ADDR_TYPE_RANGE; - etm_set_default(config); + + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; } static void etm_init_trace_id(struct etm_drvdata *drvdata) -- cgit v1.2.3 From c528a25ac7c4dacba9e4d98d5f06846939c5966f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:55 -0700 Subject: coresight: etm3x: consolidating initial config There is really no point in having two functions to take care of doing the initial tracer configuration. As such moving everything to 'etm_set_default()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 37 ++++++++++----------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 92139674bea4..34a69583ccbc 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -194,6 +193,19 @@ void etm_set_default(struct etm_config *config) if (WARN_ON_ONCE(!config)) return; + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; config->enable_event = ETM_HARD_WIRE_RES_A; @@ -577,27 +589,6 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_config *config) -{ - if (WARN_ON_ONCE(!config)) - return; - - etm_set_default(config); - - /* - * Taken verbatim from the TRM: - * - * To trace all memory: - * set bit [24] in register 0x009, the ETMTECR1, to 1 - * set all other bits in register 0x009, the ETMTECR1, to 0 - * set all bits in register 0x007, the ETMTECR2, to 0 - * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). - */ - config->enable_ctrl1 = BIT(24); - config->enable_ctrl2 = 0x0; - config->enable_event = ETM_HARD_WIRE_RES_A; -} - static void etm_init_trace_id(struct etm_drvdata *drvdata) { /* @@ -674,7 +665,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) } etm_init_trace_id(drvdata); - etm_init_default_data(&drvdata->config); + etm_set_default(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; -- cgit v1.2.3 From 2127154d115d4fe8f18300e5ef6f566581359d56 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:56 -0700 Subject: coresight: etm3x: implementing user/kernel mode tracing Adding new mode to limit tracing to kernel or user space. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm.h | 6 ++- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 4 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 63 ++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 ++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 44585d4adb26..51597cb2c08a 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -110,7 +110,10 @@ #define ETM_MODE_STALL BIT(2) #define ETM_MODE_TIMESTAMP BIT(3) #define ETM_MODE_CTXID BIT(4) -#define ETM_MODE_ALL 0x1f +#define ETM_MODE_ALL (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \ + ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \ + ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \ + ETM_MODE_EXCL_USER) #define ETM_SQR_MASK 0x3 #define ETM_TRACEID_MASK 0x3f @@ -296,5 +299,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); void etm_set_default(struct etm_config *config); +void etm_config_trace_mode(struct etm_config *config); struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 387c79fd9d5e..cbb4046c1070 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -162,6 +162,10 @@ static ssize_t mode_store(struct device *dev, config->ctrl |= ETMCR_CTXID_SIZE; else config->ctrl &= ~ETMCR_CTXID_SIZE; + + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + etm_config_trace_mode(config); + spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 34a69583ccbc..c82d545e68ef 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -234,6 +234,69 @@ void etm_set_default(struct etm_config *config) config->ctxid_mask = 0x0; } +void etm_config_trace_mode(struct etm_config *config) +{ + u32 flags, mode; + + mode = config->mode; + + mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); + + /* excluding kernel AND user space doesn't make sense */ + if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + return; + + /* nothing to do if neither flags are set */ + if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) + return; + + flags = (1 << 0 | /* instruction execute */ + 3 << 3 | /* ARM instruction */ + 0 << 5 | /* No data value comparison */ + 0 << 7 | /* No exact mach */ + 0 << 8); /* Ignore context ID */ + + /* No need to worry about single address comparators. */ + config->enable_ctrl2 = 0x0; + + /* Bit 0 is address range comparator 1 */ + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + + /* + * On ETMv3.5: + * ETMACTRn[13,11] == Non-secure state comparison control + * ETMACTRn[12,10] == Secure state comparison control + * + * b00 == Match in all modes in this state + * b01 == Do not match in any more in this state + * b10 == Match in all modes excepts user mode in this state + * b11 == Match only in user mode in this state + */ + + /* Tracing in secure mode is not supported at this time */ + flags |= (0 << 12 | 1 << 10); + + if (mode & ETM_MODE_EXCL_USER) { + /* exclude user, match all modes except user mode */ + flags |= (1 << 13 | 0 << 11); + } else { + /* exclude kernel, match only in user mode */ + flags |= (1 << 13 | 1 << 11); + } + + /* + * The ETMEEVR register is already set to "hard wire A". As such + * all there is to do is setup an address comparator that spans + * the entire address range and configure the state and mode bits. + */ + config->addr_val[0] = (u32) 0x0; + config->addr_val[1] = (u32) ~0x0; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; +} + static void etm_enable_hw(void *info) { int i; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ed116b303e87..932f34a84d96 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,9 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +#define ETM_MODE_EXCL_KERN BIT(30) +#define ETM_MODE_EXCL_USER BIT(31) + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, -- cgit v1.2.3 From 882d5e112491c875ab7c8c336b8beaeec54d0509 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:57 -0700 Subject: coresight: etm3x: implementing perf_enable/disable() API That way traces can be enabled and disabled automatically from the Perf subystem using the PMU abstraction. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/Kconfig | 1 + drivers/hwtracing/coresight/coresight-etm3x.c | 95 +++++++++++++++++++++++++-- drivers/hwtracing/coresight/coresight-etm4x.c | 4 +- drivers/hwtracing/coresight/coresight.c | 2 +- include/linux/coresight.h | 6 +- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index c85935f3525a..db0541031c72 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,6 +4,7 @@ menuconfig CORESIGHT bool "CoreSight Tracing Support" select ARM_AMBA + select PERF_EVENTS help This framework provides a kernel interface for the CoreSight debug and trace drivers to register themselves with. It's intended to build diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index c82d545e68ef..a9b820ec16aa 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "coresight-etm.h" @@ -297,6 +298,47 @@ void etm_config_trace_mode(struct etm_config *config) config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) + +static int etm_parse_event_config(struct etm_drvdata *drvdata, + struct perf_event_attr *attr) +{ + struct etm_config *config = &drvdata->config; + + if (!attr) + return -EINVAL; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etm_config)); + + if (attr->exclude_kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (attr->exclude_user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm_set_default(config); + + /* + * By default the tracers are configured to trace the whole address + * range. Narrow the field only if requested by user space. + */ + if (config->mode) + etm_config_trace_mode(config); + + /* + * At this time only cycle accurate and timestamp options are + * available. + */ + if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) + return -EINVAL; + + config->ctrl = attr->config; + + return 0; +} + static void etm_enable_hw(void *info) { int i; @@ -316,8 +358,10 @@ static void etm_enable_hw(void *info) etm_set_prog(drvdata); etmcr = etm_readl(drvdata, ETMCR); - etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); + /* Clear setting from a previous run if need be */ + etmcr &= ~ETM3X_SUPPORTED_OPTIONS; etmcr |= drvdata->port_size; + etmcr |= ETMCR_ETM_EN; etm_writel(drvdata, config->ctrl | etmcr, ETMCR); etm_writel(drvdata, config->trigger_event, ETMTRIGGER); etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); @@ -357,9 +401,6 @@ static void etm_enable_hw(void *info) /* No VMID comparator value selected */ etm_writel(drvdata, 0x0, ETMVMIDCVR); - /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); - etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -407,6 +448,22 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } +static int etm_enable_perf(struct coresight_device *csdev, + struct perf_event_attr *attr) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Configure the tracer based on the session's specifics */ + etm_parse_event_config(drvdata, attr); + /* And enable it */ + etm_enable_hw(drvdata); + + return 0; +} + static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -437,7 +494,8 @@ err: return ret; } -static int etm_enable(struct coresight_device *csdev, u32 mode) +static int etm_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { int ret; u32 val; @@ -453,6 +511,9 @@ static int etm_enable(struct coresight_device *csdev, u32 mode) case CS_MODE_SYSFS: ret = etm_enable_sysfs(csdev); break; + case CS_MODE_PERF: + ret = etm_enable_perf(csdev, attr); + break; default: ret = -EINVAL; } @@ -485,6 +546,27 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } +static void etm_disable_perf(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return; + + CS_UNLOCK(drvdata->base); + + /* Setting the prog bit disables tracing immediately */ + etm_set_prog(drvdata); + + /* + * There is no way to know when the tracer will be used again so + * power down the tracer. + */ + etm_set_pwrdwn(drvdata); + + CS_LOCK(drvdata->base); +} + static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -528,6 +610,9 @@ static void etm_disable(struct coresight_device *csdev) case CS_MODE_SYSFS: etm_disable_sysfs(csdev); break; + case CS_MODE_PERF: + etm_disable_perf(csdev); + break; default: WARN_ON_ONCE(mode); return; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 0026092fec7f..d0169ba7fbf2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "coresight-etm4x.h" @@ -187,7 +188,8 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev, u32 mode) +static int etm4_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index b20afb709141..95cccb179763 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev, mode); + ret = source_ops(csdev)->enable(csdev, NULL, mode); if (ret) return ret; } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 61dfb8d511ea..6801dd64ee5d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -14,6 +14,7 @@ #define _LINUX_CORESIGHT_H #include +#include #include /* Peripheral id registers (0xFD0-0xFEC) */ @@ -206,14 +207,15 @@ struct coresight_ops_link { * @cpu_id: returns the value of the CPU number this component * is associated to. * @trace_id: returns the value of the component's trace ID as known - to the HW. + * to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev, u32 mode); + int (*enable)(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode); void (*disable)(struct coresight_device *csdev); }; -- cgit v1.2.3 From 27b10da8fff27d74b755707e61637f6ab488c617 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:58 -0700 Subject: coresight: etb10: moving to local atomic operations Moving to use local atomic operations to take advantage of the lockless implementation, something that will come handy when the ETB is accessed from the Perf subsystem. Also changing the name of the variable to something more meaningful. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 917562ecf82a..162c9ccc8c33 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -71,7 +72,7 @@ * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. - * @in_use: synchronise user space access to etb buffer. + * @reading: synchronise user space access to etb buffer. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. * @enable: this ETB is being used. @@ -84,7 +85,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; - atomic_t in_use; + local_t reading; u8 *buf; u32 buffer_depth; bool enable; @@ -277,7 +278,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (atomic_cmpxchg(&drvdata->in_use, 0, 1)) + if (local_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); @@ -313,7 +314,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - atomic_set(&drvdata->in_use, 0); + local_set(&drvdata->reading, 0); dev_dbg(drvdata->dev, "%s: released\n", __func__); return 0; -- cgit v1.2.3 From e827d4550aa3225b8965ce4c266208cfe0297509 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:59 -0700 Subject: coresight: etb10: adding operation mode for sink->enable() Adding an operation mode to the sink->enable() API in order to prevent simultaneous access from different callers. TPIU and TMC won't be supplemented with the AUX area API immediately and as such ignore the new mode. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 30 ++++++++++++++++++++------- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- drivers/hwtracing/coresight/coresight-tpiu.c | 2 +- drivers/hwtracing/coresight/coresight.c | 10 ++++----- include/linux/coresight.h | 2 +- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 162c9ccc8c33..79099f95ba3f 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -73,9 +73,9 @@ * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. * @reading: synchronise user space access to etb buffer. + * @mode: this ETB is being used. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. - * @enable: this ETB is being used. * @trigger_cntr: amount of words to store after a trigger. */ struct etb_drvdata { @@ -86,9 +86,9 @@ struct etb_drvdata { struct miscdevice miscdev; spinlock_t spinlock; local_t reading; + local_t mode; u8 *buf; u32 buffer_depth; - bool enable; u32 trigger_cntr; }; @@ -133,16 +133,31 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int etb_enable(struct coresight_device *csdev) +static int etb_enable(struct coresight_device *csdev, u32 mode) { - struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + u32 val; unsigned long flags; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, + CS_MODE_DISABLED, mode); + /* + * When accessing from Perf, a HW buffer can be handled + * by a single trace entity. In sysFS mode many tracers + * can be logging to the same HW buffer. + */ + if (val == CS_MODE_PERF) + return -EBUSY; + + /* Nothing to do, the tracer is already enabled. */ + if (val == CS_MODE_SYSFS) + goto out; spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); - drvdata->enable = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); +out: dev_info(drvdata->dev, "ETB enabled\n"); return 0; } @@ -243,9 +258,10 @@ static void etb_disable(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); etb_disable_hw(drvdata); etb_dump_hw(drvdata); - drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + local_set(&drvdata->mode, CS_MODE_DISABLED); + dev_info(drvdata->dev, "ETB disabled\n"); } @@ -263,7 +279,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { etb_disable_hw(drvdata); etb_dump_hw(drvdata); etb_enable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 932f34a84d96..333eddaed339 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -62,7 +62,7 @@ static inline void CS_UNLOCK(void __iomem *addr) } void coresight_disable_path(struct list_head *path); -int coresight_enable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path, u32 mode); struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index b526396d80b6..ac91b0b40ec2 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -265,7 +265,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) return 0; } -static int tmc_enable_sink(struct coresight_device *csdev) +static int tmc_enable_sink(struct coresight_device *csdev, u32 mode) { struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 0d8fce651ff7..71582f6dfd4c 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -70,7 +70,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tpiu_enable(struct coresight_device *csdev) +static int tpiu_enable(struct coresight_device *csdev, u32 mode) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 95cccb179763..6ec2b66af9ee 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -121,13 +121,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return 0; } -static int coresight_enable_sink(struct coresight_device *csdev) +static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) { int ret; if (!csdev->enable) { if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev); + ret = sink_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -283,7 +283,7 @@ void coresight_disable_path(struct list_head *path) } } -int coresight_enable_path(struct list_head *path) +int coresight_enable_path(struct list_head *path, u32 mode) { int ret = 0; @@ -296,7 +296,7 @@ int coresight_enable_path(struct list_head *path) switch (csdev->type) { case CORESIGHT_DEV_TYPE_SINK: case CORESIGHT_DEV_TYPE_LINKSINK: - ret = coresight_enable_sink(csdev); + ret = coresight_enable_sink(csdev, mode); if (ret) goto err; break; @@ -454,7 +454,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - ret = coresight_enable_path(path); + ret = coresight_enable_path(path, CS_MODE_SYSFS); if (ret) goto err_path; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 6801dd64ee5d..9fa92dcdd2ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -186,7 +186,7 @@ struct coresight_device { * @disable: disables the sink. */ struct coresight_ops_sink { - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; -- cgit v1.2.3 From 2997aa4063d97fdb39450c6078bd81a7b0504f22 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:00 -0700 Subject: coresight: etb10: implementing AUX API Adding an ETB10 specific AUX area operations to be used by the perf framework when events are initialised. Part of this operation involves modeling the mmap'ed area based on the specific ways a sink buffer gathers information. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 234 ++++++++++++++++++++++++++ include/linux/coresight.h | 21 ++- 2 files changed, 253 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 79099f95ba3f..a2eb6bdeaafa 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include + +#include #include "coresight-priv.h" @@ -64,6 +69,26 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 +/** + * struct cs_buffer - keep track of a recording session' specifics + * @cur: index of the current buffer + * @nr_pages: max number of pages granted to us + * @offset: offset within the current buffer + * @data_size: how much we collected in this run + * @lost: other than zero if we had a HW buffer wrap around + * @snapshot: is this run in snapshot mode + * @data_pages: a handle the ring buffer + */ +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + local_t lost; + bool snapshot; + void **data_pages; +}; + /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. @@ -265,9 +290,218 @@ static void etb_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETB disabled\n"); } +static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite) +{ + int node; + struct cs_buffers *buf; + + if (cpu == -1) + cpu = smp_processor_id(); + node = cpu_to_node(cpu); + + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void etb_free_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int etb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int ret = 0; + unsigned long head; + struct cs_buffers *buf = sink_config; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to */ + buf->cur = head / PAGE_SIZE; + + /* and offset within that page */ + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return ret; +} + +static unsigned long etb_reset_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost) +{ + unsigned long size = 0; + struct cs_buffers *buf = sink_config; + + if (buf) { + /* + * In snapshot mode ->data_size holds the new address of the + * ring buffer's head. The size itself is the whole address + * range since we want the latest information. + */ + if (buf->snapshot) + handle->head = local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + + /* + * Tell the tracer PMU how much we got in this run and if + * something went wrong along the way. Nobody else can use + * this cs_buffers instance until we are done. As such + * resetting parameters here and squaring off with the ring + * buffer API in the tracer PMU is fine. + */ + *lost = !!local_xchg(&buf->lost, 0); + size = local_xchg(&buf->data_size, 0); + } + + return size; +} + +static void etb_update_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int i, cur; + u8 *buf_ptr; + u32 read_ptr, write_ptr, capacity; + u32 status, read_data, to_read; + unsigned long offset; + struct cs_buffers *buf = sink_config; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!buf) + return; + + capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; + + CS_UNLOCK(drvdata->base); + etb_disable_hw(drvdata); + + /* unit is in words, not bytes */ + read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); + write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * Entries should be aligned to the frame size. If they are not + * go back to the last alignement point to give decoding tools a + * chance to fix things. + */ + if (write_ptr % ETB_FRAME_SIZE_WORDS) { + dev_err(drvdata->dev, + "write_ptr: %lu not aligned to formatter frame size\n", + (unsigned long)write_ptr); + + write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); + local_inc(&buf->lost); + } + + /* + * Get a hold of the status register and see if a wrap around + * has occurred. If so adjust things accordingly. Otherwise + * start at the beginning and go until the write pointer has + * been reached. + */ + status = readl_relaxed(drvdata->base + ETB_STATUS_REG); + if (status & ETB_STATUS_RAM_FULL) { + local_inc(&buf->lost); + to_read = capacity; + read_ptr = write_ptr; + } else { + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth); + to_read *= ETB_FRAME_SIZE_WORDS; + } + + /* + * Make sure we don't overwrite data that hasn't been consumed yet. + * It is entirely possible that the HW buffer has more data than the + * ring buffer can currently handle. If so adjust the start address + * to take only the last traces. + * + * In snapshot mode we are looking to get the latest traces only and as + * such, we don't care about not overwriting data that hasn't been + * processed by user space. + */ + if (!buf->snapshot && to_read > handle->size) { + u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1); + + /* The new read pointer must be frame size aligned */ + to_read -= handle->size & mask; + /* + * Move the RAM read pointer up, keeping in mind that + * everything is in frame size units. + */ + read_ptr = (write_ptr + drvdata->buffer_depth) - + to_read / ETB_FRAME_SIZE_WORDS; + /* Wrap around if need be*/ + read_ptr &= ~(drvdata->buffer_depth - 1); + /* let the decoder know we've skipped ahead */ + local_inc(&buf->lost); + } + + /* finally tell HW where we want to start reading from */ + writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); + + cur = buf->cur; + offset = buf->offset; + for (i = 0; i < to_read; i += 4) { + buf_ptr = buf->data_pages[cur] + offset; + read_data = readl_relaxed(drvdata->base + + ETB_RAM_READ_DATA_REG); + *buf_ptr++ = read_data >> 0; + *buf_ptr++ = read_data >> 8; + *buf_ptr++ = read_data >> 16; + *buf_ptr++ = read_data >> 24; + + offset += 4; + if (offset >= PAGE_SIZE) { + offset = 0; + cur++; + /* wrap around at the end of the buffer */ + cur &= buf->nr_pages - 1; + } + } + + /* reset ETB buffer for next run */ + writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); + writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * In snapshot mode all we have to do is communicate to + * perf_aux_output_end() the address of the current head. In full + * trace mode the same function expects a size to move rb->aux_head + * forward. + */ + if (buf->snapshot) + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); + else + local_add(to_read, &buf->data_size); + + etb_enable_hw(drvdata); + CS_LOCK(drvdata->base); +} + static const struct coresight_ops_sink etb_sink_ops = { .enable = etb_enable, .disable = etb_disable, + .alloc_buffer = etb_alloc_buffer, + .free_buffer = etb_free_buffer, + .set_buffer = etb_set_buffer, + .reset_buffer = etb_reset_buffer, + .update_buffer = etb_update_buffer, }; static const struct coresight_ops etb_cs_ops = { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 9fa92dcdd2ea..385d62e64abb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -182,12 +182,29 @@ struct coresight_device { /** * struct coresight_ops_sink - basic operations for a sink * Operations available for sinks - * @enable: enables the sink. - * @disable: disables the sink. + * @enable: enables the sink. + * @disable: disables the sink. + * @alloc_buffer: initialises perf's ring buffer for trace collection. + * @free_buffer: release memory allocated in @get_config. + * @set_buffer: initialises buffer mechanic before a trace session. + * @reset_buffer: finalises buffer mechanic after a trace session. + * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); + void *(*alloc_buffer)(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite); + void (*free_buffer)(void *config); + int (*set_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); + unsigned long (*reset_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost); + void (*update_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); }; /** -- cgit v1.2.3 From 0bcbf2e30ff2271b54f54c8697a185f7d86ec6e4 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:01 -0700 Subject: coresight: etm-perf: new PMU driver for ETM tracers Perf is a well known and used tool for performance monitoring and much more. A such it is an ideal candidate for integration with coresight based HW tracing. This patch introduces a PMU that represent a coresight tracer to the Perf core. Cc: Alexander Shishkin Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/Makefile | 3 +- drivers/hwtracing/coresight/coresight-etm-perf.c | 393 +++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm-perf.h | 32 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 7 + include/linux/coresight-pmu.h | 27 ++ 5 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h create mode 100644 include/linux/coresight-pmu.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 233d66cf22d3..cf8c6d689747 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ - coresight-etm3x-sysfs.o + coresight-etm3x-sysfs.o \ + coresight-etm-perf.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c new file mode 100644 index 000000000000..36153a77e982 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -0,0 +1,393 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" + +static struct pmu etm_pmu; +static bool etm_perf_up; + +/** + * struct etm_event_data - Coresight specifics associated to an event + * @work: Handle to free allocated memory outside IRQ context. + * @mask: Hold the CPU(s) this event was set for. + * @snk_config: The sink configuration. + * @path: An array of path, each slot for one CPU. + */ +struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head **path; +}; + +static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle); +static DEFINE_PER_CPU(struct coresight_device *, csdev_src); + +/* ETMv3.5/PTM's ETMCR is 'config' */ +PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); +PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); + +static struct attribute *etm_config_formats_attr[] = { + &format_attr_cycacc.attr, + &format_attr_timestamp.attr, + NULL, +}; + +static struct attribute_group etm_pmu_format_group = { + .name = "format", + .attrs = etm_config_formats_attr, +}; + +static const struct attribute_group *etm_pmu_attr_groups[] = { + &etm_pmu_format_group, + NULL, +}; + +static void etm_event_read(struct perf_event *event) {} + +static int etm_event_init(struct perf_event *event) +{ + if (event->attr.type != etm_pmu.type) + return -ENOENT; + + return 0; +} + +static void free_event_data(struct work_struct *work) +{ + int cpu; + cpumask_t *mask; + struct etm_event_data *event_data; + struct coresight_device *sink; + + event_data = container_of(work, struct etm_event_data, work); + mask = &event_data->mask; + /* + * First deal with the sink configuration. See comment in + * etm_setup_aux() about why we take the first available path. + */ + if (event_data->snk_config) { + cpu = cpumask_first(mask); + sink = coresight_get_sink(event_data->path[cpu]); + if (sink_ops(sink)->free_buffer) + sink_ops(sink)->free_buffer(event_data->snk_config); + } + + for_each_cpu(cpu, mask) { + if (event_data->path[cpu]) + coresight_release_path(event_data->path[cpu]); + } + + kfree(event_data->path); + kfree(event_data); +} + +static void *alloc_event_data(int cpu) +{ + int size; + cpumask_t *mask; + struct etm_event_data *event_data; + + /* First get memory for the session's data */ + event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL); + if (!event_data) + return NULL; + + /* Make sure nothing disappears under us */ + get_online_cpus(); + size = num_online_cpus(); + + mask = &event_data->mask; + if (cpu != -1) + cpumask_set_cpu(cpu, mask); + else + cpumask_copy(mask, cpu_online_mask); + put_online_cpus(); + + /* + * Each CPU has a single path between source and destination. As such + * allocate an array using CPU numbers as indexes. That way a path + * for any CPU can easily be accessed at any given time. We proceed + * the same way for sessions involving a single CPU. The cost of + * unused memory when dealing with single CPU trace scenarios is small + * compared to the cost of searching through an optimized array. + */ + event_data->path = kcalloc(size, + sizeof(struct list_head *), GFP_KERNEL); + if (!event_data->path) { + kfree(event_data); + return NULL; + } + + return event_data; +} + +static void etm_free_aux(void *data) +{ + struct etm_event_data *event_data = data; + + schedule_work(&event_data->work); +} + +static void *etm_setup_aux(int event_cpu, void **pages, + int nr_pages, bool overwrite) +{ + int cpu; + cpumask_t *mask; + struct coresight_device *sink; + struct etm_event_data *event_data = NULL; + + event_data = alloc_event_data(event_cpu); + if (!event_data) + return NULL; + + INIT_WORK(&event_data->work, free_event_data); + + mask = &event_data->mask; + + /* Setup the path for each CPU in a trace session */ + for_each_cpu(cpu, mask) { + struct coresight_device *csdev; + + csdev = per_cpu(csdev_src, cpu); + if (!csdev) + goto err; + + /* + * Building a path doesn't enable it, it simply builds a + * list of devices from source to sink that can be + * referenced later when the path is actually needed. + */ + event_data->path[cpu] = coresight_build_path(csdev); + if (!event_data->path[cpu]) + goto err; + } + + /* + * In theory nothing prevent tracers in a trace session from being + * associated with different sinks, nor having a sink per tracer. But + * until we have HW with this kind of topology and a way to convey + * sink assignement from the perf cmd line we need to assume tracers + * in a trace session are using the same sink. Therefore pick the sink + * found at the end of the first available path. + */ + cpu = cpumask_first(mask); + /* Grab the sink at the end of the path */ + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + goto err; + + if (!sink_ops(sink)->alloc_buffer) + goto err; + + /* Get the AUX specific data from the sink buffer */ + event_data->snk_config = + sink_ops(sink)->alloc_buffer(sink, cpu, pages, + nr_pages, overwrite); + if (!event_data->snk_config) + goto err; + +out: + return event_data; + +err: + etm_free_aux(event_data); + event_data = NULL; + goto out; +} + +static void etm_event_start(struct perf_event *event, int flags) +{ + int cpu = smp_processor_id(); + struct etm_event_data *event_data; + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + + if (!csdev) + goto fail; + + /* + * Deal with the ring buffer API and get a handle on the + * session's information. + */ + event_data = perf_aux_output_begin(handle, event); + if (!event_data) + goto fail; + + /* We need a sink, no need to continue without one */ + sink = coresight_get_sink(event_data->path[cpu]); + if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer)) + goto fail_end_stop; + + /* Configure the sink */ + if (sink_ops(sink)->set_buffer(sink, handle, + event_data->snk_config)) + goto fail_end_stop; + + /* Nothing will happen without a path */ + if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF)) + goto fail_end_stop; + + /* Tell the perf core the event is alive */ + event->hw.state = 0; + + /* Finally enable the tracer */ + if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF)) + goto fail_end_stop; + +out: + return; + +fail_end_stop: + perf_aux_output_end(handle, 0, true); +fail: + event->hw.state = PERF_HES_STOPPED; + goto out; +} + +static void etm_event_stop(struct perf_event *event, int mode) +{ + bool lost; + int cpu = smp_processor_id(); + unsigned long size; + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct etm_event_data *event_data = perf_get_aux(handle); + + if (event->hw.state == PERF_HES_STOPPED) + return; + + if (!csdev) + return; + + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + return; + + /* stop tracer */ + source_ops(csdev)->disable(csdev); + + /* tell the core */ + event->hw.state = PERF_HES_STOPPED; + + if (mode & PERF_EF_UPDATE) { + if (WARN_ON_ONCE(handle->event != event)) + return; + + /* update trace information */ + if (!sink_ops(sink)->update_buffer) + return; + + sink_ops(sink)->update_buffer(sink, handle, + event_data->snk_config); + + if (!sink_ops(sink)->reset_buffer) + return; + + size = sink_ops(sink)->reset_buffer(sink, handle, + event_data->snk_config, + &lost); + + perf_aux_output_end(handle, size, lost); + } + + /* Disabling the path make its elements available to other sessions */ + coresight_disable_path(event_data->path[cpu]); +} + +static int etm_event_add(struct perf_event *event, int mode) +{ + int ret = 0; + struct hw_perf_event *hwc = &event->hw; + + if (mode & PERF_EF_START) { + etm_event_start(event, 0); + if (hwc->state & PERF_HES_STOPPED) + ret = -EINVAL; + } else { + hwc->state = PERF_HES_STOPPED; + } + + return ret; +} + +static void etm_event_del(struct perf_event *event, int mode) +{ + etm_event_stop(event, PERF_EF_UPDATE); +} + +int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ + char entry[sizeof("cpu9999999")]; + int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev); + struct device *pmu_dev = etm_pmu.dev; + struct device *cs_dev = &csdev->dev; + + sprintf(entry, "cpu%d", cpu); + + if (!etm_perf_up) + return -EPROBE_DEFER; + + if (link) { + ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry); + if (ret) + return ret; + per_cpu(csdev_src, cpu) = csdev; + } else { + sysfs_remove_link(&pmu_dev->kobj, entry); + per_cpu(csdev_src, cpu) = NULL; + } + + return 0; +} + +static int __init etm_perf_init(void) +{ + int ret; + + etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; + + etm_pmu.attr_groups = etm_pmu_attr_groups; + etm_pmu.task_ctx_nr = perf_sw_context; + etm_pmu.read = etm_event_read; + etm_pmu.event_init = etm_event_init; + etm_pmu.setup_aux = etm_setup_aux; + etm_pmu.free_aux = etm_free_aux; + etm_pmu.start = etm_event_start; + etm_pmu.stop = etm_event_stop; + etm_pmu.add = etm_event_add; + etm_pmu.del = etm_event_del; + + ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); + if (ret == 0) + etm_perf_up = true; + + return ret; +} +module_init(etm_perf_init); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h new file mode 100644 index 000000000000..87f5a134eb6f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -0,0 +1,32 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * 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 _CORESIGHT_ETM_PERF_H +#define _CORESIGHT_ETM_PERF_H + +struct coresight_device; + +#ifdef CONFIG_CORESIGHT +int etm_perf_symlink(struct coresight_device *csdev, bool link); + +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } + +#endif /* CONFIG_CORESIGHT */ + +#endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index a9b820ec16aa..77b37413803f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -35,6 +35,7 @@ #include #include "coresight-etm.h" +#include "coresight-etm-perf.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -827,6 +828,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; } + ret = etm_perf_symlink(drvdata->csdev, true); + if (ret) { + coresight_unregister(drvdata->csdev); + goto err_arch_supported; + } + pm_runtime_put(&adev->dev); dev_info(dev, "%s initialized\n", (char *)id->data); diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h new file mode 100644 index 000000000000..6c5386b23b10 --- /dev/null +++ b/include/linux/coresight-pmu.h @@ -0,0 +1,27 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * 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 _LINUX_CORESIGHT_PMU_H +#define _LINUX_CORESIGHT_PMU_H + +#define CORESIGHT_ETM_PMU_NAME "cs_etm" + +/* ETMv3.5/PTM's ETMCR config bit */ +#define ETM_OPT_CYCACC 12 +#define ETM_OPT_TS 28 + +#endif -- cgit v1.2.3 From 17534ceb835a1a96eb921a2a80df168723d6570a Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:02 -0700 Subject: coresight: introducing a global trace ID function TraceID values have to be unique for all tracers and consistent between drivers and user space. As such introducing a central function to be used whenever a traceID value is required. The patch also account for data traceIDs, which are usually I(N) + 1. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 7 ++----- include/linux/coresight-pmu.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 77b37413803f..0ba1a3981373 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -740,11 +741,7 @@ static void etm_init_arch_data(void *info) static void etm_init_trace_id(struct etm_drvdata *drvdata) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and go from there. - */ - drvdata->traceid = 0x10 + drvdata->cpu; + drvdata->traceid = coresight_get_trace_id(drvdata->cpu); } static int etm_probe(struct amba_device *adev, const struct amba_id *id) diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 6c5386b23b10..7d410260661b 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -19,9 +19,21 @@ #define _LINUX_CORESIGHT_PMU_H #define CORESIGHT_ETM_PMU_NAME "cs_etm" +#define CORESIGHT_ETM_PMU_SEED 0x10 /* ETMv3.5/PTM's ETMCR config bit */ #define ETM_OPT_CYCACC 12 #define ETM_OPT_TS 28 +static inline int coresight_get_trace_id(int cpu) +{ + /* + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. Since + * the common convention is to have data trace IDs be I(N) + 1, + * set instruction trace IDs as a function of the CPU number. + */ + return (CORESIGHT_ETM_PMU_SEED + (cpu * 2)); +} + #endif -- cgit v1.2.3 From 941943cf519f7cacbbcecee5c4ef4b77b466bd5c Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 17 Feb 2016 17:52:03 -0700 Subject: drivers/hwtracing: make coresight-* explicitly non-modular None of the Kconfig currently controlling compilation of any of the files here are tristate, meaning that none of it currently is being built as a module by anyone. We need not be concerned about .remove functions and blocking the unbind sysfs operations, since that was already done in a recent commit. Lets remove any remaining modular references, so that when reading the drivers there is no doubt they are builtin-only. All drivers get mostly the same changes, so they are handled in batch. Changes are (1) convert to builtin_amba_driver, (2) delete module.h include where unused, and (3) relocate the description into the comments so we don't need MODULE_DESCRIPTION and associated tags. The etm3x and etm4x use module_param_named, and have been adjusted to just include moduleparam.h for that purpose. In commit f309d4443130bf814e991f836e919dca22df37ae ("platform_device: better support builtin boilerplate avoidance") we introduced the builtin_driver macro. Here we use that support and extend it to amba driver registration, so where a driver is clearly non-modular and builtin-only, we can update with the simple mapping of module_amba_driver(...) ---> builtin_amba_driver(...) Since module_amba_driver() uses the same init level priority as builtin_amba_driver() the init ordering remains unchanged with this commit. Cc: Mathieu Poirier Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Signed-off-by: Mathieu Poirier Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++------ drivers/hwtracing/coresight/coresight-etm3x.c | 14 ++++++++------ drivers/hwtracing/coresight/coresight-etm4x.c | 4 +--- drivers/hwtracing/coresight/coresight-funnel.c | 9 +++------ drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 +--- drivers/hwtracing/coresight/coresight-replicator.c | 7 ++----- drivers/hwtracing/coresight/coresight-tmc.c | 9 +++------ drivers/hwtracing/coresight/coresight-tpiu.c | 9 +++------ drivers/hwtracing/coresight/coresight.c | 3 --- drivers/hwtracing/coresight/of_coresight.c | 1 - include/linux/amba/bus.h | 9 +++++++++ 11 files changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index a2eb6bdeaafa..acbce79934d6 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Embedded Trace Buffer driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,7 +14,6 @@ #include #include -#include #include #include #include @@ -781,8 +782,4 @@ static struct amba_driver etb_driver = { .probe = etb_probe, .id_table = etb_ids, }; - -module_amba_driver(etb_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver"); +builtin_amba_driver(etb_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 0ba1a3981373..d83ab82672e4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Program Flow Trace driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,7 @@ */ #include -#include +#include #include #include #include @@ -38,6 +40,10 @@ #include "coresight-etm.h" #include "coresight-etm-perf.h" +/* + * Not really modular but using module_param is the easiest way to + * remain consistent with existing use cases for now. + */ static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -912,8 +918,4 @@ static struct amba_driver etm_driver = { .probe = etm_probe, .id_table = etm_ids, }; - -module_amba_driver(etm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Program Flow Trace driver"); +builtin_amba_driver(etm_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d0169ba7fbf2..1c59bd36834c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -2710,5 +2709,4 @@ static struct amba_driver etm4x_driver = { .probe = etm4_probe, .id_table = etm4_ids, }; - -module_amba_driver(etm4x_driver); +builtin_amba_driver(etm4x_driver); diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fb2c679fbc44..0600ca30649d 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Funnel driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -268,8 +269,4 @@ static struct amba_driver funnel_driver = { .probe = funnel_probe, .id_table = funnel_ids, }; - -module_amba_driver(funnel_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Funnel driver"); +builtin_amba_driver(funnel_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 286f90b50989..700f710e4bfa 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -198,5 +197,4 @@ static struct amba_driver replicator_driver = { .probe = replicator_probe, .id_table = replicator_ids, }; - -module_amba_driver(replicator_driver); +builtin_amba_driver(replicator_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 0ce98903992c..4299c0569340 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Replicator driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -166,8 +167,4 @@ static struct platform_driver replicator_driver = { .suppress_bind_attrs = true, }, }; - builtin_platform_driver(replicator_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Replicator driver"); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ac91b0b40ec2..1be191f5d39c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1,4 +1,6 @@ /* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Memory Controller driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -782,8 +783,4 @@ static struct amba_driver tmc_driver = { .probe = tmc_probe, .id_table = tmc_ids, }; - -module_amba_driver(tmc_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver"); +builtin_amba_driver(tmc_driver); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 71582f6dfd4c..8fb09d9237ab 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Port Interface Unit driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -218,8 +219,4 @@ static struct amba_driver tpiu_driver = { .probe = tpiu_probe, .id_table = tpiu_ids, }; - -module_amba_driver(tpiu_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); +builtin_amba_driver(tpiu_driver); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6ec2b66af9ee..2ea5961092c1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -894,5 +893,3 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 3cc57c1e3b5d..b68da1888fd5 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -10,7 +10,6 @@ * GNU General Public License for more details. */ -#include #include #include #include diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9006c4e75cf7..3d8dcdd1aeae 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -163,4 +163,13 @@ struct amba_device name##_device = { \ #define module_amba_driver(__amba_drv) \ module_driver(__amba_drv, amba_driver_register, amba_driver_unregister) +/* + * builtin_amba_driver() - Helper macro for drivers that don't do anything + * special in driver initcall. This eliminates a lot of boilerplate. Each + * driver may only use this macro once, and calling it replaces the instance + * device_initcall(). + */ +#define builtin_amba_driver(__amba_drv) \ + builtin_driver(__amba_drv, amba_driver_register) + #endif -- cgit v1.2.3 From 35bf7692e765c2275bf93fe573f7ca868ab73453 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 17 Feb 2016 18:27:34 +0200 Subject: mei: fix format string in debug prints buf_idx type was changed to size_t, and few places missed out to change the print format from %ld to %zu. Use also uz for buf.size which is also of size_t Fixes: commit 56988f22e097 ("mei: fix possible integer overflow issue")' Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/interrupt.c | 6 +++--- drivers/misc/mei/main.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 04525ada9eda..194360a5f782 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -160,7 +160,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(dev->dev, "amthif cb->buf.size - %zd cb->buf_idx - %zd\n", + dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n", cb->buf.size, cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a9cdb92b52d1..bab17e4197b6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1583,7 +1583,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - cl_dbg(dev, cl, "buf: size = %zd idx = %zd\n", + cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n", cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 06b744a503a3..1e5cb1f704f8 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -128,7 +128,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, buf_sz = mei_hdr->length + cb->buf_idx; /* catch for integer overflow */ if (buf_sz < cb->buf_idx) { - cl_err(dev, cl, "message is too big len %d idx %ld\n", + cl_err(dev, cl, "message is too big len %d idx %zu\n", mei_hdr->length, cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); @@ -137,7 +137,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, } if (cb->buf.size < buf_sz) { - cl_dbg(dev, cl, "message overflow. size %zd len %d idx %zd\n", + cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cb->buf.size, mei_hdr->length, cb->buf_idx); buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL); @@ -156,7 +156,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cb->buf_idx += mei_hdr->length; if (mei_hdr->msg_complete) { - cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); + cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } else { pm_runtime_mark_last_busy(dev->dev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7e637eb46cb9..52635b063873 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -221,7 +221,7 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %zd buf.idx = %zd offset = %lld\n", + cl_dbg(dev, cl, "buf.size = %zu buf.idx = %zu offset = %lld\n", cb->buf.size, cb->buf_idx, *offset); if (*offset >= cb->buf_idx) { rets = 0; -- cgit v1.2.3