* Re: Why is 64bit option always on by default now?
From: Theodore Ts'o @ 2016-12-23 2:45 UTC (permalink / raw)
To: Dan Arena; +Cc: linux-ext4
In-Reply-To: <CAKnMEmEd0hdS_ajNhsr9t9GvCZXG4MD6-K-tsx5YKRX9gsQDmw@mail.gmail.com>
64-bit support has been around for 7 years (since e2fsprogs 1.41).
And yes, e2fsprogs 1.43 now has the ability to convert a file system
from 32-bit to 64-bit, but this is an inherently dangerous thing to
do, since it requires rewriting the inode table. If you ever crash or
power fail during the conversion, *boom*, you can lose all or most of
your data. So the conversion can be used as a short cut where you
back up the whole file system, and then try to convert to 64-bit, and
if it succeeds, then you don't have to do the restore step. If it
crashes and you lose everything, then you can reformat the file system
and restore from backups. :-)
In general, I assume that embedded developers are more sophisticated
than users (who will use the mke2fs in the installer to install thier
root file system, which will be a matched set with the bootloader). I
also can't be responsible for crappy, obsolete bootloader on embedded
devices, some of which have device drivers only available in ancient
BSP kernels using 3.10, etc.
Also, people who care can always edit /etc/mke2fs.conf to adjust the
defaults.
> While doing some research I read that it also broke compatibility with
> a few other things. I am not sure what the best solution is, I was
> just surprised how this compatibility-breaking change was made with as
> far as I know not much warning to the community or when running the
> command or something.
It's documented in the Release Notes. You're the first person who has
complained. Part of it is that community distributions have a much
lower threshold of compatibility requirements compared with, say,
enterprise distributions. Enterprise distribution vendors are free to
edit /etc/mke2fs.conf in their packages if they want to be more
conservative. Similarly, with Debian, we enabled the meta checksum
feature in the testing distribution precisely so we could get more
users testing the code and submitting bug report.
Community distributions have always been more aggressive --- so in
general, users aren't whining when Fedora 25 ships Wayland which might
have some incompatibilities with hardware and software. Instead they
are (in general) happy that Fedora is taking an aggressive stance with
Wayland. Someone has to be first to release software....
- Ted
^ permalink raw reply
* [PATCH 2/2] drm/amd/powerplay: update pp_dbg_log print to pr_debug level
From: Huang Rui @ 2016-12-23 2:45 UTC (permalink / raw)
To: Alex Deucher, Christian König,
amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
Cc: Huang Rui
In-Reply-To: <1482461108-8463-1-git-send-email-ray.huang-5C7GfCeVMHo@public.gmane.org>
Signed-off-by: Huang Rui <ray.huang@amd.com>
---
drivers/gpu/drm/amd/powerplay/inc/pp_debug.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
index 8301b82..5bb1b44 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
@@ -45,7 +45,7 @@
#define PP_DBG_LOG(fmt, ...) \
do { \
- if(0)pr_info(fmt, ##__VA_ARGS__); \
+ pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
--
2.7.4
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx
^ permalink raw reply related
* [PATCH 1/2] drm/amd/powerplay: add prefix for all powerplay pr_* prints
From: Huang Rui @ 2016-12-23 2:45 UTC (permalink / raw)
To: Alex Deucher, Christian König,
amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
Cc: Arindam Nath, Huang Rui
Powerplay will be used them instead of raw printk, and we can dynamic
change the debug level with it.
The prefix is like below:
[ 197.755167] [powerplay] amdgpu: powerplay initialized
Suggested-by: Grazvydas Ignotas <notasas@gmail.com>
Signed-off-by: Huang Rui <ray.huang@amd.com>
Cc: Arindam Nath <Arindam.Nath@amd.com>
---
drivers/gpu/drm/amd/powerplay/amd_powerplay.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c | 3 +--
drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c | 3 ++-
drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c | 2 +-
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c | 2 +-
drivers/gpu/drm/amd/powerplay/inc/pp_debug.h | 10 ++++++++--
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c | 2 +-
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c | 2 +-
19 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
index cc72190..8b85153 100644
--- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
@@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
@@ -29,7 +30,6 @@
#include "pp_instance.h"
#include "power_state.h"
#include "eventmanager.h"
-#include "pp_debug.h"
#define PP_CHECK(handle) \
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c
index 74dbbd1..d043337 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c
@@ -20,13 +20,13 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "atom-types.h"
#include "atombios.h"
#include "processpptables.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "smu/smu_8_0_d.h"
#include "smu8_fusion.h"
@@ -38,7 +38,6 @@
#include "cz_hwmgr.h"
#include "power_state.h"
#include "cz_clockpowergating.h"
-#include "pp_debug.h"
#define ixSMUSVI_NB_CURRENTVID 0xD8230044
#define CURRENT_NB_VID_MASK 0xff000000
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
index c355a0f..0eb8e886 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
@@ -20,11 +20,11 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/errno.h>
#include "hwmgr.h"
#include "hardwaremanager.h"
#include "power_state.h"
-#include "pp_debug.h"
#define PHM_FUNC_CHECK(hw) \
do { \
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
index b036064..fcfd648 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
@@ -20,6 +20,8 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+
+#include "pp_debug.h"
#include "linux/delay.h"
#include <linux/types.h>
#include <linux/kernel.h>
@@ -29,7 +31,6 @@
#include "power_state.h"
#include "hwmgr.h"
#include "pppcielanes.h"
-#include "pp_debug.h"
#include "ppatomctrl.h"
#include "ppsmc.h"
#include "pp_acpi.h"
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c
index ddaea1d..2c60f7b 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c
@@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fb.h>
@@ -27,7 +28,6 @@
#include "ppatomctrl.h"
#include "atombios.h"
#include "cgs_common.h"
-#include "pp_debug.h"
#include "ppevvmath.h"
#define MEM_ID_MASK 0xff000000
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c
index 7925185..f2f0fcc 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c
@@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fb.h>
@@ -27,7 +28,6 @@
#include "process_pptables_v1_0.h"
#include "ppatomctrl.h"
#include "atombios.h"
-#include "pp_debug.h"
#include "hwmgr.h"
#include "cgs_common.h"
#include "pptable_v1_0.h"
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c
index a4e9cf4..ed6c934 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c
@@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -27,7 +28,6 @@
#include "processpptables.h"
#include <atom-types.h>
#include <atombios.h>
-#include "pp_debug.h"
#include "pptable.h"
#include "power_state.h"
#include "hwmgr.h"
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index ae5517a..9dc0e52 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -20,13 +20,13 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <asm/div64.h>
#include "linux/delay.h"
#include "pp_acpi.h"
-#include "pp_debug.h"
#include "ppatomctrl.h"
#include "atombios.h"
#include "pptable_v1_0.h"
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c
index 6cd1287..0f2325e 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c
@@ -20,11 +20,11 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include "hwmgr.h"
#include "smumgr.h"
#include "smu7_hwmgr.h"
#include "smu7_powertune.h"
-#include "pp_debug.h"
#include "smu7_common.h"
#define VOLTAGE_SCALE 4
diff --git a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
index bfdbec1..8301b82 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
@@ -24,6 +24,12 @@
#ifndef PP_DEBUG_H
#define PP_DEBUG_H
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) "[powerplay] " fmt
+
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -31,7 +37,7 @@
#define PP_ASSERT_WITH_CODE(cond, msg, code) \
do { \
if (!(cond)) { \
- printk("%s\n", msg); \
+ pr_warning("%s\n", msg); \
code; \
} \
} while (0)
@@ -39,7 +45,7 @@
#define PP_DBG_LOG(fmt, ...) \
do { \
- if(0)printk(KERN_INFO "[ pp_dbg ] " fmt, ##__VA_ARGS__); \
+ if(0)pr_info(fmt, ##__VA_ARGS__); \
} while (0)
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
index 6aeb1d2..5d5b8a0 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
@@ -21,13 +21,13 @@
*
*/
+#include "pp_debug.h"
#include "fiji_smc.h"
#include "smu7_dyn_defaults.h"
#include "smu7_hwmgr.h"
#include "hardwaremanager.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "atombios.h"
#include "fiji_smumgr.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c
index 26eff56..7a87e5a 100755
--- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c
@@ -21,6 +21,7 @@
*
*/
+#include "pp_debug.h"
#include "smumgr.h"
#include "smu73.h"
#include "smu_ucode_xfer_vi.h"
@@ -36,7 +37,6 @@
#include "gca/gfx_8_0_d.h"
#include "bif/bif_5_0_d.h"
#include "bif/bif_5_0_sh_mask.h"
-#include "pp_debug.h"
#include "fiji_pwrvirus.h"
#include "fiji_smc.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
index a24971a..ef435f1 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
@@ -21,13 +21,13 @@
*
*/
+#include "pp_debug.h"
#include "iceland_smc.h"
#include "smu7_dyn_defaults.h"
#include "smu7_hwmgr.h"
#include "hardwaremanager.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "atombios.h"
#include "pppcielanes.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c
index eeafefc..1fde30d 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c
@@ -22,6 +22,7 @@
* Author: Huang Rui <ray.huang@amd.com>
*
*/
+#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -29,7 +30,6 @@
#include "smumgr.h"
#include "iceland_smumgr.h"
-#include "pp_debug.h"
#include "smu_ucode_xfer_vi.h"
#include "ppsmc.h"
#include "smu/smu_7_1_1_d.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
index 5190e82..c2889b5 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
@@ -21,13 +21,13 @@
*
*/
+#include "pp_debug.h"
#include "polaris10_smc.h"
#include "smu7_dyn_defaults.h"
#include "smu7_hwmgr.h"
#include "hardwaremanager.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "atombios.h"
#include "polaris10_smumgr.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
index f38a687..7e1e330 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
@@ -21,6 +21,7 @@
*
*/
+#include "pp_debug.h"
#include "smumgr.h"
#include "smu74.h"
#include "smu_ucode_xfer_vi.h"
@@ -36,7 +37,6 @@
#include "bif/bif_5_0_sh_mask.h"
#include "polaris10_pwrvirus.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "polaris10_smc.h"
#include "smu7_ppsmc.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c
index f49b548..d0a77be 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c
@@ -22,12 +22,12 @@
*/
+#include "pp_debug.h"
#include "smumgr.h"
#include "smu_ucode_xfer_vi.h"
#include "smu/smu_7_1_3_d.h"
#include "smu/smu_7_1_3_sh_mask.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "smu7_ppsmc.h"
#include "smu7_smumgr.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
index 2e1493c..93bfb19 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
@@ -21,13 +21,13 @@
*
*/
+#include "pp_debug.h"
#include "tonga_smc.h"
#include "smu7_dyn_defaults.h"
#include "smu7_hwmgr.h"
#include "hardwaremanager.h"
#include "ppatomctrl.h"
-#include "pp_debug.h"
#include "cgs_common.h"
#include "atombios.h"
#include "tonga_smumgr.h"
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
index eff9a232..d0aef72 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
@@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
+#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -27,7 +28,6 @@
#include "smumgr.h"
#include "tonga_smumgr.h"
-#include "pp_debug.h"
#include "smu_ucode_xfer_vi.h"
#include "tonga_ppsmc.h"
#include "smu/smu_7_1_2_d.h"
--
2.7.4
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx
^ permalink raw reply related
* Re: [PATCH v2 5/5] drivers/misc: Add aspeed ast2400/ast2500 lpc controlling driver
From: Andrew Jeffery @ 2016-12-23 2:43 UTC (permalink / raw)
To: Cyril Bur, openbmc; +Cc: millerjo
In-Reply-To: <1482450861.2578.1.camel@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 384 bytes --]
On Fri, 2016-12-23 at 10:54 +1100, Cyril Bur wrote:
> > > + mtd = get_mtd_device_nm("1e630000.spi:pnor@0");
>
> I rebased right before sending and admitedly pressed send before it
> finished booting, turns out we've changed it back to simply "pnor". Was
> this intentional?
Can we just derive it from the devicetree? That way we wouldn't care
what it was.
Andrew
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH v2 4/5] drivers/mailbox: Add aspeed ast2400/ast2500 mbox driver
From: Andrew Jeffery @ 2016-12-23 2:42 UTC (permalink / raw)
To: Cyril Bur, openbmc; +Cc: millerjo
In-Reply-To: <20161222060610.29695-5-cyrilbur@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 13507 bytes --]
Hi Cyril,
This looks good to me. A couple of minor comments below.
On Thu, 2016-12-22 at 17:06 +1100, Cyril Bur wrote:
> This provides access to the mbox registers on the ast2400 and ast2500 boards.
>
> An ioctl() is used to allow users to write to a specific register with a specific
> value. This is useful as the other end have configured the mbox registers to
> provide an interrupt when a specific regster gets written to.
>
> > Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
> ---
> drivers/mailbox/Kconfig | 9 +
> drivers/mailbox/Makefile | 3 +
> drivers/mailbox/aspeed-mbox.c | 354 +++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/aspeed-mbox.h | 23 +++
> 4 files changed, 389 insertions(+)
> create mode 100644 drivers/mailbox/aspeed-mbox.c
> create mode 100644 include/uapi/linux/aspeed-mbox.h
>
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index 5305923752d2..815cfe852dea 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -123,4 +123,13 @@ config XGENE_SLIMPRO_MBOX
> > It is used to send short messages between ARM64-bit cores and
> > the SLIMpro Management Engine, primarily for PM. Say Y here if you
> > want to use the APM X-Gene SLIMpro IPCM support.
> +
> +config ASPEED_MBOX
> > + depends on ARCH_ASPEED || COMPILE_TEST
> > + bool "Aspeed ast2400/2500 Mailbox Controller"
> > + default "y"
> > + ---help---
> > + Provides a driver for the MBOX regsiters found on Aspeed SOCs
> > + (AST2400 and AST2500). This driver provides a device for aspeed
> > + mbox registers
> endif
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 0be3e742bb7d..99d17f3b9552 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -25,3 +25,6 @@ obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o
> obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
>
> > obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
> +
> > +obj-$(CONFIG_ASPEED_MBOX) += aspeed-mbox.o
> +
> diff --git a/drivers/mailbox/aspeed-mbox.c b/drivers/mailbox/aspeed-mbox.c
> new file mode 100644
> index 000000000000..a5da7fc0fd23
> --- /dev/null
> +++ b/drivers/mailbox/aspeed-mbox.c
> @@ -0,0 +1,354 @@
> +/*
> + * Copyright 2016 IBM Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/errno.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/miscdevice.h>
> +#include <linux/timer.h>
> +#include <linux/jiffies.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/aspeed-mbox.h>
> +
> > +#define DEVICE_NAME "aspeed-mbox"
> +
> +#define MBOX_NUM_REGS 16
> +#define MBOX_NUM_DATA_REGS 14
> +
> +#define MBOX_DATA_0 0x00
> +#define MBOX_STATUS_0 0x40
> +#define MBOX_STATUS_1 0x44
> +#define MBOX_BMC_CTRL 0x48
> +#define MBOX_CTRL_RECV BIT(7)
> +#define MBOX_CTRL_MASK BIT(1)
> +#define MBOX_CTRL_SEND BIT(0)
> +#define MBOX_HOST_CTRL 0x4c
> +#define MBOX_INTERRUPT_0 0x50
> +#define MBOX_INTERRUPT_1 0x54
> +
> +struct mbox {
> > > + struct miscdevice miscdev;
> > > + struct regmap *regmap;
> > > + unsigned int base;
> > > + int irq;
> > > + wait_queue_head_t queue;
> > > + struct timer_list poll_timer;
> +};
> +
> +static atomic_t mbox_open_count = ATOMIC_INIT(0);
> +
> +static u8 mbox_inb(struct mbox *mbox, int reg)
> +{
> > + /*
> > + * The mbox registers are actually only 1 byte but are addressed 4
> > + * bytes appart. The other three bytes are marked 'reserved', they
> > + * *should* be zero but lets not rely on it.
> > + * I am going to rely on the fact we can casually read/write to them...
> > + */
> > + unsigned int val = 0xff; /* If regmap throws an error return 0xff */
> > + regmap_read(mbox->regmap, mbox->base + reg, &val);
> + return val & 0xff;
> +}
> +
> +static void mbox_outb(struct mbox *mbox, u8 data, int reg)
> +{
> + regmap_write(mbox->regmap, mbox->base + reg, data);
We could use regmap_update_bits() here, but ultimately it's not going
to matter. Should we say something if regmap_write() fails (it
shouldn't, but if it does that's extra concerning)?
> +}
> +
> +static struct mbox *file_mbox(struct file *file)
> +{
> > + return container_of(file->private_data, struct mbox, miscdev);
> +}
> +
> +static int mbox_open(struct inode *inode, struct file *file)
> +{
> > + struct mbox *mbox = file_mbox(file);
> +
> > + if (atomic_inc_return(&mbox_open_count) == 1) {
> > + /*
> > + * Clear the interrupt status bit if it was left on and unmask
> > + * interrupts.
> > + * MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step
> > + */
> > + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL);
> > + return 0;
> > + }
> +
> > + atomic_dec(&mbox_open_count);
> > + return -EBUSY;
> +}
> +
> +static ssize_t mbox_read(struct file *file, char __user *buf,
> > + size_t count, loff_t *ppos)
> +{
> > + struct mbox *mbox = file_mbox(file);
> > + char __user *p = buf;
> > + int i;
> +
> > + if (!access_ok(VERIFY_WRITE, buf, count))
> > + return -EFAULT;
> +
> > + WARN_ON(*ppos);
> +
> > + if (wait_event_interruptible(mbox->queue,
> > + mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV))
> > + return -ERESTARTSYS;
> +
> > + for (i = 0; i < MBOX_NUM_DATA_REGS; i++)
> > + if (__put_user(mbox_inb(mbox, MBOX_DATA_0 + (i * 4)), p++))
> > + return -EFAULT;
> +
> > + /* MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step */
> > + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL);
> > + return p - buf;
> +}
> +
> +static ssize_t mbox_write(struct file *file, const char __user *buf,
> > + size_t count, loff_t *ppos)
> +{
> > + struct mbox *mbox = file_mbox(file);
> > + const char __user *p = buf;
> > + char c;
> > + int i;
> +
> > + if (!access_ok(VERIFY_READ, buf, count))
> > + return -EFAULT;
> +
> > + WARN_ON(*ppos);
> +
> > + for (i = 0; i < MBOX_NUM_DATA_REGS; i++) {
> > + if (__get_user(c, p))
> > + return -EFAULT;
> +
> > + mbox_outb(mbox, c, MBOX_DATA_0 + (i * 4));
> > + p++;
> > + }
> +
> > + mbox_outb(mbox, MBOX_CTRL_SEND, MBOX_BMC_CTRL);
> +
> > + return p - buf;
> +}
> +
> +static long mbox_ioctl(struct file *file, unsigned int cmd,
> > + unsigned long param)
> +{
> > + struct aspeed_mbox_atn atn;
> > + struct mbox *mbox = file_mbox(file);
> > + void __user *p = (void __user *)param;
> +
> > + switch (cmd) {
> + case ASPEED_MBOX_IOCTL_ATN:
I think we should rename the IOCTL as what we do below doesn't
necessarily raise an interrupt.
> + if (copy_from_user(&atn, p, sizeof(atn)))
> > + return -EFAULT;
> > + if (atn.reg > 15)
> > + return -EINVAL;
> +
> > + mbox_outb(mbox, atn.val, atn.reg);
> > + return 0;
> > + }
> +
> > + return -EINVAL;
> +}
> +
> +static unsigned int mbox_poll(struct file *file, poll_table *wait)
> +{
> > + struct mbox *mbox = file_mbox(file);
> > + unsigned int mask = 0;
> +
> > + poll_wait(file, &mbox->queue, wait);
> +
> > + if (mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV)
> > + mask |= POLLIN;
> +
> > + return mask;
> +}
> +
> +static int mbox_release(struct inode *inode, struct file *file)
> +{
> > + atomic_dec(&mbox_open_count);
> > + return 0;
> +}
> +
> +static const struct file_operations mbox_fops = {
> > > + .owner = THIS_MODULE,
> > > + .open = mbox_open,
> > > + .read = mbox_read,
> > > + .write = mbox_write,
> > > + .release = mbox_release,
> > > + .poll = mbox_poll,
> > > + .unlocked_ioctl = mbox_ioctl,
> +};
> +
> +static void poll_timer(unsigned long data)
> +{
> > + struct mbox *mbox = (void *)data;
> +
> > + mbox->poll_timer.expires += msecs_to_jiffies(500);
> > + wake_up(&mbox->queue);
> > + add_timer(&mbox->poll_timer);
> +}
> +
> +static irqreturn_t mbox_irq(int irq, void *arg)
> +{
> > + struct mbox *mbox = arg;
> +
> > + if (!(mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV))
> > + return IRQ_NONE;
> +
> > + /*
> > + * Leave the status bit set so that we know the data is for us,
> > + * clear it once it has been read.
> > + */
> +
> > + /* Mask it off, we'll clear it when we the data gets read */
> > + mbox_outb(mbox, MBOX_CTRL_MASK, MBOX_BMC_CTRL);
> +
> > + wake_up(&mbox->queue);
> > + return IRQ_HANDLED;
> +}
> +
> +static int mbox_config_irq(struct mbox *mbox,
> > + struct platform_device *pdev)
> +{
> > + struct device *dev = &pdev->dev;
> > + int rc;
> +
> > + mbox->irq = irq_of_parse_and_map(dev->of_node, 0);
> > + if (!mbox->irq)
> > + return -ENODEV;
> +
> > + rc = devm_request_irq(dev, mbox->irq, mbox_irq, IRQF_SHARED,
> > + DEVICE_NAME, mbox);
> > + if (rc < 0) {
> > + dev_warn(dev, "Unable to request IRQ %d\n", mbox->irq);
> > + mbox->irq = 0;
> > + return rc;
> > + }
> +
> > + /*
> > + * Disable all register based interrupts, we'll have to change
> > + * this, protocol seemingly will require regs 0 and 15
> > + */
> > + mbox_outb(mbox, 0x00, MBOX_INTERRUPT_0); /* regs 0 - 7 */
> > + mbox_outb(mbox, 0x00, MBOX_INTERRUPT_1); /* regs 8 - 15 */
> +
> > + /* W1C */
> > + mbox_outb(mbox, 0xff, MBOX_STATUS_0);
> > + mbox_outb(mbox, 0xff, MBOX_STATUS_1);
> +
> > + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL);
> > + return 0;
> +}
> +
> +static int mbox_probe(struct platform_device *pdev)
> +{
> > + struct mbox *mbox;
> > + struct device *dev;
> > + int rc;
> +
> > + if (!pdev || !pdev->dev.of_node)
> > + return -ENODEV;
> +
> > + dev = &pdev->dev;
> > + dev_info(dev, "Found mbox host device\n");
> +
> > + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
> > + if (!mbox)
> > + return -ENOMEM;
> +
> > + dev_set_drvdata(&pdev->dev, mbox);
> +
> > + rc = of_property_read_u32(dev->of_node, "reg", &mbox->base);
> > + if (rc) {
> > + dev_err(dev, "Couldn't read reg device-tree property\n");
> > + goto out;
> > + }
> +
> > + mbox->regmap = syscon_node_to_regmap(
> > + pdev->dev.parent->of_node);
> > + if (IS_ERR(mbox->regmap)) {
> > + dev_err(dev, "Couldn't get regmap\n");
> > + rc = -ENODEV;
> > + goto out;
> > + }
> +
> > + init_waitqueue_head(&mbox->queue);
> +
> > + mbox->miscdev.minor = MISC_DYNAMIC_MINOR;
> > + mbox->miscdev.name = DEVICE_NAME;
> > + mbox->miscdev.fops = &mbox_fops;
> > + mbox->miscdev.parent = dev;
> > + rc = misc_register(&mbox->miscdev);
> > + if (rc) {
> > + dev_err(dev, "Unable to register device\n");
> > + goto out;
> > + }
> +
> > + mbox_config_irq(mbox, pdev);
> +
> > + if (mbox->irq) {
> > + dev_info(dev, "Using IRQ %d\n", mbox->irq);
> > + } else {
> > + dev_info(dev, "No IRQ; using timer\n");
> > + setup_timer(&mbox->poll_timer, poll_timer,
> > + (unsigned long)mbox);
> > + mbox->poll_timer.expires = jiffies + msecs_to_jiffies(10);
> > + add_timer(&mbox->poll_timer);
> > + }
> +
> +out:
> > + return rc;
> +
> +}
> +
> +static int mbox_remove(struct platform_device *pdev)
> +{
> > + struct mbox *mbox = dev_get_drvdata(&pdev->dev);
> +
> > + misc_deregister(&mbox->miscdev);
> > + if (!mbox->irq)
> > + del_timer_sync(&mbox->poll_timer);
> > + mbox = NULL;
> +
> > + return 0;
> +}
> +
> +static const struct of_device_id mbox_match[] = {
> > + { .compatible = "aspeed,ast2400-mbox" },
> > + { },
> +};
> +
> +static struct platform_driver mbox_driver = {
> > + .driver = {
> > > + .name = DEVICE_NAME,
> > + .of_match_table = mbox_match,
> > + },
> > + .probe = mbox_probe,
> > + .remove = mbox_remove,
> +};
> +
> +module_platform_driver(mbox_driver);
> +
> +MODULE_DEVICE_TABLE(of, mbox_match);
> +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
> +MODULE_DESCRIPTION("Linux device interface to the Aspeed MBOX registers");
> diff --git a/include/uapi/linux/aspeed-mbox.h b/include/uapi/linux/aspeed-mbox.h
> new file mode 100644
> index 000000000000..f4e6a9242fd0
> --- /dev/null
> +++ b/include/uapi/linux/aspeed-mbox.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright 2016 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#ifndef _UAPI_LINUX_MBOX_HOST_H
> +#define _UAPI_LINUX_MBOX_HOST_H
> +
> +#include <linux/ioctl.h>
> +
> +struct aspeed_mbox_atn {
> > + uint8_t reg;
> > + uint8_t val;
> +};
> +
> > +#define __ASPEED_MBOX_IOCTL_MAGIC 0xb1
> > +#define ASPEED_MBOX_IOCTL_ATN _IOW(__ASPEED_MBOX_IOCTL_MAGIC, 0x00, struct aspeed_mbox_atn)
> +
> +#endif /* _UAPI_LINUX_MBOX_HOST_H */
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* [PATCH v3 12/12] scsi: ufs: Improve fatal error logs
From: Subhash Jadavani @ 2016-12-23 2:42 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Dolev Raviv, Subhash Jadavani, open list
From: Dolev Raviv <draviv@codeaurora.org>
Errors such as UIC error, illegal OCS values, and others may require
more information for debugging. Such information could be hibern8 events,
events sequences, recoverable errors, error history, and more.
This patch improves tracking of important errors and events in debug level
to be enabled when debugging a such issues. It includes:
* UIC error history
* Successful hibern8 events
* Successful command after hibern8 exit
* Clk-freq info
* Failed device command
* Infrastructure for dumping host controller debug information
Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v1 -> v2:
- Added explicit new line character at the end of the printk messages.
---
drivers/scsi/ufs/ufshcd.c | 203 ++++++++++++++++++++++++++++++++++++----------
drivers/scsi/ufs/ufshcd.h | 47 +++++++++++
2 files changed, 208 insertions(+), 42 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 918c597..c2b15a2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -328,6 +328,37 @@ static void ufshcd_add_command_trace(struct ufs_hba *hba,
doorbell, transfer_len, intr, lba, opcode);
}
+static void ufshcd_print_clk_freqs(struct ufs_hba *hba)
+{
+ struct ufs_clk_info *clki;
+ struct list_head *head = &hba->clk_list_head;
+
+ if (!head || list_empty(head))
+ return;
+
+ list_for_each_entry(clki, head, list) {
+ if (!IS_ERR_OR_NULL(clki->clk) && clki->min_freq &&
+ clki->max_freq)
+ dev_err(hba->dev, "clk: %s, rate: %u\n",
+ clki->name, clki->curr_freq);
+ }
+}
+
+static void ufshcd_print_uic_err_hist(struct ufs_hba *hba,
+ struct ufs_uic_err_reg_hist *err_hist, char *err_name)
+{
+ int i;
+
+ for (i = 0; i < UIC_ERR_REG_HIST_LENGTH; i++) {
+ int p = (i + err_hist->pos - 1) % UIC_ERR_REG_HIST_LENGTH;
+
+ if (err_hist->reg[p] == 0)
+ continue;
+ dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, i,
+ err_hist->reg[p], ktime_to_us(err_hist->tstamp[p]));
+ }
+}
+
static void ufshcd_print_host_regs(struct ufs_hba *hba)
{
/*
@@ -344,6 +375,21 @@ static void ufshcd_print_host_regs(struct ufs_hba *hba)
dev_err(hba->dev,
"hba->outstanding_reqs = 0x%x, hba->outstanding_tasks = 0x%x\n",
(u32)hba->outstanding_reqs, (u32)hba->outstanding_tasks);
+ dev_err(hba->dev,
+ "last_hibern8_exit_tstamp at %lld us, hibern8_exit_cnt = %d\n",
+ ktime_to_us(hba->ufs_stats.last_hibern8_exit_tstamp),
+ hba->ufs_stats.hibern8_exit_cnt);
+
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.pa_err, "pa_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.dl_err, "dl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.nl_err, "nl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.tl_err, "tl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.dme_err, "dme_err");
+
+ ufshcd_print_clk_freqs(hba);
+
+ if (hba->vops && hba->vops->dbg_register_dump)
+ hba->vops->dbg_register_dump(hba);
}
static
@@ -355,22 +401,30 @@ void ufshcd_print_trs(struct ufs_hba *hba, unsigned long bitmap, bool pr_prdt)
for_each_set_bit(tag, &bitmap, hba->nutrs) {
lrbp = &hba->lrb[tag];
- dev_err(hba->dev, "UPIU[%d] - Transfer Request Descriptor\n",
- tag);
+ dev_err(hba->dev, "UPIU[%d] - issue time %lld us\n",
+ tag, ktime_to_us(lrbp->issue_time_stamp));
+ dev_err(hba->dev,
+ "UPIU[%d] - Transfer Request Descriptor phys@0x%llx\n",
+ tag, (u64)lrbp->utrd_dma_addr);
+
ufshcd_hex_dump("UPIU TRD: ", lrbp->utr_descriptor_ptr,
sizeof(struct utp_transfer_req_desc));
- dev_err(hba->dev, "UPIU[%d] - Request UPIU\n", tag);
+ dev_err(hba->dev, "UPIU[%d] - Request UPIU phys@0x%llx\n", tag,
+ (u64)lrbp->ucd_req_dma_addr);
ufshcd_hex_dump("UPIU REQ: ", lrbp->ucd_req_ptr,
sizeof(struct utp_upiu_req));
- dev_err(hba->dev, "UPIU[%d] - Response UPIU\n", tag);
+ dev_err(hba->dev, "UPIU[%d] - Response UPIU phys@0x%llx\n", tag,
+ (u64)lrbp->ucd_rsp_dma_addr);
ufshcd_hex_dump("UPIU RSP: ", lrbp->ucd_rsp_ptr,
sizeof(struct utp_upiu_rsp));
if (pr_prdt) {
int prdt_length = le16_to_cpu(
lrbp->utr_descriptor_ptr->prd_table_length);
- dev_err(hba->dev, "UPIU[%d] - PRDT - %d entries\n", tag,
- prdt_length);
+ dev_err(hba->dev,
+ "UPIU[%d] - PRDT - %d entries phys@0x%llx\n",
+ tag, prdt_length,
+ (u64)lrbp->ucd_prdt_dma_addr);
ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
sizeof(struct ufshcd_sg_entry) *
prdt_length);
@@ -399,6 +453,32 @@ static void ufshcd_print_tmrs(struct ufs_hba *hba, unsigned long bitmap)
}
}
+/**
+ * ufshcd_print_pwr_info - print power params as saved in hba
+ * power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_print_pwr_info(struct ufs_hba *hba)
+{
+ static const char * const names[] = {
+ "INVALID MODE",
+ "FAST MODE",
+ "SLOW_MODE",
+ "INVALID MODE",
+ "FASTAUTO_MODE",
+ "SLOWAUTO_MODE",
+ "INVALID MODE",
+ };
+
+ dev_err(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
+ __func__,
+ hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
+ hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
+ names[hba->pwr_info.pwr_rx],
+ names[hba->pwr_info.pwr_tx],
+ hba->pwr_info.hs_rate);
+}
+
/*
* ufshcd_wait_for_register - wait for register value to change
* @hba - per-adapter interface
@@ -1196,6 +1276,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
static inline
void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
{
+ hba->lrb[task_tag].issue_time_stamp = ktime_get();
ufshcd_clk_scaling_start_busy(hba);
__set_bit(task_tag, &hba->outstanding_reqs);
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
@@ -1877,6 +1958,7 @@ static int ufshcd_compose_dev_cmd(struct ufs_hba *hba,
int resp;
int err = 0;
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
resp = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
switch (resp) {
@@ -2647,32 +2729,6 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
}
/**
- * ufshcd_print_pwr_info - print power params as saved in hba
- * power info
- * @hba: per-adapter instance
- */
-static void ufshcd_print_pwr_info(struct ufs_hba *hba)
-{
- static const char * const names[] = {
- "INVALID MODE",
- "FAST MODE",
- "SLOW_MODE",
- "INVALID MODE",
- "FASTAUTO_MODE",
- "SLOWAUTO_MODE",
- "INVALID MODE",
- };
-
- dev_info(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
- __func__,
- hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
- hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
- names[hba->pwr_info.pwr_rx],
- names[hba->pwr_info.pwr_tx],
- hba->pwr_info.hs_rate);
-}
-
-/**
* ufshcd_host_memory_configure - configure local reference block with
* memory offsets
* @hba: per adapter instance
@@ -2734,12 +2790,19 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
}
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
+ hba->lrb[i].utrd_dma_addr = hba->utrdl_dma_addr +
+ (i * sizeof(struct utp_transfer_req_desc));
hba->lrb[i].ucd_req_ptr =
(struct utp_upiu_req *)(cmd_descp + i);
+ hba->lrb[i].ucd_req_dma_addr = cmd_desc_element_addr;
hba->lrb[i].ucd_rsp_ptr =
(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
+ hba->lrb[i].ucd_rsp_dma_addr = cmd_desc_element_addr +
+ response_offset;
hba->lrb[i].ucd_prdt_ptr =
(struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
+ hba->lrb[i].ucd_prdt_dma_addr = cmd_desc_element_addr +
+ prdt_offset;
}
}
@@ -2763,7 +2826,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
if (ret)
- dev_err(hba->dev,
+ dev_dbg(hba->dev,
"dme-link-startup: error code %d\n", ret);
return ret;
}
@@ -3113,9 +3176,12 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d\n",
__func__, ret);
ret = ufshcd_link_recovery(hba);
- } else
+ } else {
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT,
POST_CHANGE);
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_get();
+ hba->ufs_stats.hibern8_exit_cnt++;
+ }
return ret;
}
@@ -3885,7 +3951,7 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
switch (ocs) {
case OCS_SUCCESS:
result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
-
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
switch (result) {
case UPIU_TRANSACTION_RESPONSE:
/*
@@ -3946,7 +4012,9 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
default:
result |= DID_ERROR << 16;
dev_err(hba->dev,
- "OCS error from controller = %x\n", ocs);
+ "OCS error from controller = %x for tag %d\n",
+ ocs, lrbp->task_tag);
+ ufshcd_print_host_regs(hba);
break;
} /* end of switch */
@@ -4555,6 +4623,14 @@ static void ufshcd_err_handler(struct work_struct *work)
pm_runtime_put_sync(hba->dev);
}
+static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist,
+ u32 reg)
+{
+ reg_hist->reg[reg_hist->pos] = reg;
+ reg_hist->tstamp[reg_hist->pos] = ktime_get();
+ reg_hist->pos = (reg_hist->pos + 1) % UIC_ERR_REG_HIST_LENGTH;
+}
+
/**
* ufshcd_update_uic_error - check and set fatal UIC error flags.
* @hba: per-adapter instance
@@ -4567,15 +4643,20 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
/* Ignore LINERESET indication, as this is not an error */
if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) &&
- (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK))
+ (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) {
/*
* To know whether this error is fatal or not, DB timeout
* must be checked but this error is handled separately.
*/
dev_dbg(hba->dev, "%s: UIC Lane error reported\n", __func__);
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.pa_err, reg);
+ }
/* PA_INIT_ERROR is fatal and needs UIC reset */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER);
+ if (reg)
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.dl_err, reg);
+
if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
else if (hba->dev_quirks &
@@ -4589,16 +4670,22 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
/* UIC NL/TL/DME errors needs software retry */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.nl_err, reg);
hba->uic_error |= UFSHCD_UIC_NL_ERROR;
+ }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.tl_err, reg);
hba->uic_error |= UFSHCD_UIC_TL_ERROR;
+ }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.dme_err, reg);
hba->uic_error |= UFSHCD_UIC_DME_ERROR;
+ }
dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n",
__func__, hba->uic_error);
@@ -4965,12 +5052,16 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
UFS_QUERY_TASK, &resp);
if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
/* cmd pending in the device */
+ dev_err(hba->dev, "%s: cmd pending in the device. tag = %d\n",
+ __func__, tag);
break;
} else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
/*
* cmd not pending in the device, check if it is
* in transition.
*/
+ dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n",
+ __func__, tag);
reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
if (reg & (1 << tag)) {
/* sleep for max. 200us to stabilize */
@@ -4978,8 +5069,13 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
continue;
}
/* command completed already */
+ dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n",
+ __func__, tag);
goto out;
} else {
+ dev_err(hba->dev,
+ "%s: no response from device. tag = %d, err %d\n",
+ __func__, tag, err);
if (!err)
err = resp; /* service response error */
goto out;
@@ -4994,14 +5090,20 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
UFS_ABORT_TASK, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
- if (!err)
+ if (!err) {
err = resp; /* service response error */
+ dev_err(hba->dev, "%s: issued. tag = %d, err %d\n",
+ __func__, tag, err);
+ }
goto out;
}
err = ufshcd_clear_cmd(hba, tag);
- if (err)
+ if (err) {
+ dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
+ __func__, tag, err);
goto out;
+ }
scsi_dma_unmap(cmd);
@@ -5583,6 +5685,20 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
ufshcd_vops_apply_dev_quirks(hba);
}
+static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba)
+{
+ int err_reg_hist_size = sizeof(struct ufs_uic_err_reg_hist);
+
+ hba->ufs_stats.hibern8_exit_cnt = 0;
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
+
+ memset(&hba->ufs_stats.pa_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.dl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.nl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.tl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.dme_err, 0, err_reg_hist_size);
+}
+
/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
@@ -5602,6 +5718,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
hba->is_urgent_bkops_lvl_checked = false;
+ /* Debug counters initialization */
+ ufshcd_clear_dbg_ufs_stats(hba);
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index f25d468..0ea49f9 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -152,6 +152,10 @@ struct ufs_pm_lvl_states {
* @ucd_req_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
+ * @utrd_dma_addr: UTRD dma address for debug
+ * @ucd_prdt_dma_addr: PRDT dma address for debug
+ * @ucd_rsp_dma_addr: UPIU response dma address for debug
+ * @ucd_req_dma_addr: UPIU request dma address for debug
* @cmd: pointer to SCSI command
* @sense_buffer: pointer to sense buffer address of the SCSI command
* @sense_bufflen: Length of the sense buffer
@@ -160,6 +164,7 @@ struct ufs_pm_lvl_states {
* @task_tag: Task tag of the command
* @lun: LUN of the command
* @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
+ * @issue_time_stamp: time stamp for debug purposes
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
@@ -167,6 +172,11 @@ struct ufshcd_lrb {
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
+ dma_addr_t utrd_dma_addr;
+ dma_addr_t ucd_req_dma_addr;
+ dma_addr_t ucd_rsp_dma_addr;
+ dma_addr_t ucd_prdt_dma_addr;
+
struct scsi_cmnd *cmd;
u8 *sense_buffer;
unsigned int sense_bufflen;
@@ -176,6 +186,7 @@ struct ufshcd_lrb {
int task_tag;
u8 lun; /* UPIU LUN id field is only 8-bit wide */
bool intr_cmd;
+ ktime_t issue_time_stamp;
};
/**
@@ -355,6 +366,41 @@ struct ufs_init_prefetch {
u32 icc_level;
};
+#define UIC_ERR_REG_HIST_LENGTH 8
+/**
+ * struct ufs_uic_err_reg_hist - keeps history of uic errors
+ * @pos: index to indicate cyclic buffer position
+ * @reg: cyclic buffer for registers value
+ * @tstamp: cyclic buffer for time stamp
+ */
+struct ufs_uic_err_reg_hist {
+ int pos;
+ u32 reg[UIC_ERR_REG_HIST_LENGTH];
+ ktime_t tstamp[UIC_ERR_REG_HIST_LENGTH];
+};
+
+/**
+ * struct ufs_stats - keeps usage/err statistics
+ * @hibern8_exit_cnt: Counter to keep track of number of exits,
+ * reset this after link-startup.
+ * @last_hibern8_exit_tstamp: Set time after the hibern8 exit.
+ * Clear after the first successful command completion.
+ * @pa_err: tracks pa-uic errors
+ * @dl_err: tracks dl-uic errors
+ * @nl_err: tracks nl-uic errors
+ * @tl_err: tracks tl-uic errors
+ * @dme_err: tracks dme errors
+ */
+struct ufs_stats {
+ u32 hibern8_exit_cnt;
+ ktime_t last_hibern8_exit_tstamp;
+ struct ufs_uic_err_reg_hist pa_err;
+ struct ufs_uic_err_reg_hist dl_err;
+ struct ufs_uic_err_reg_hist nl_err;
+ struct ufs_uic_err_reg_hist tl_err;
+ struct ufs_uic_err_reg_hist dme_err;
+};
+
/**
* struct ufs_hba - per adapter private structure
* @mmio_base: UFSHCI base register address
@@ -531,6 +577,7 @@ struct ufs_hba {
u32 uic_error;
u32 saved_err;
u32 saved_uic_err;
+ struct ufs_stats ufs_stats;
/* Device management request data */
struct ufs_dev_cmd dev_cmd;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 12/12] scsi: ufs: Improve fatal error logs
From: Subhash Jadavani @ 2016-12-23 2:42 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Dolev Raviv, Subhash Jadavani, open list
From: Dolev Raviv <draviv@codeaurora.org>
Errors such as UIC error, illegal OCS values, and others may require
more information for debugging. Such information could be hibern8 events,
events sequences, recoverable errors, error history, and more.
This patch improves tracking of important errors and events in debug level
to be enabled when debugging a such issues. It includes:
* UIC error history
* Successful hibern8 events
* Successful command after hibern8 exit
* Clk-freq info
* Failed device command
* Infrastructure for dumping host controller debug information
Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v1 -> v2:
- Added explicit new line character at the end of the printk messages.
---
drivers/scsi/ufs/ufshcd.c | 203 ++++++++++++++++++++++++++++++++++++----------
drivers/scsi/ufs/ufshcd.h | 47 +++++++++++
2 files changed, 208 insertions(+), 42 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 918c597..c2b15a2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -328,6 +328,37 @@ static void ufshcd_add_command_trace(struct ufs_hba *hba,
doorbell, transfer_len, intr, lba, opcode);
}
+static void ufshcd_print_clk_freqs(struct ufs_hba *hba)
+{
+ struct ufs_clk_info *clki;
+ struct list_head *head = &hba->clk_list_head;
+
+ if (!head || list_empty(head))
+ return;
+
+ list_for_each_entry(clki, head, list) {
+ if (!IS_ERR_OR_NULL(clki->clk) && clki->min_freq &&
+ clki->max_freq)
+ dev_err(hba->dev, "clk: %s, rate: %u\n",
+ clki->name, clki->curr_freq);
+ }
+}
+
+static void ufshcd_print_uic_err_hist(struct ufs_hba *hba,
+ struct ufs_uic_err_reg_hist *err_hist, char *err_name)
+{
+ int i;
+
+ for (i = 0; i < UIC_ERR_REG_HIST_LENGTH; i++) {
+ int p = (i + err_hist->pos - 1) % UIC_ERR_REG_HIST_LENGTH;
+
+ if (err_hist->reg[p] == 0)
+ continue;
+ dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, i,
+ err_hist->reg[p], ktime_to_us(err_hist->tstamp[p]));
+ }
+}
+
static void ufshcd_print_host_regs(struct ufs_hba *hba)
{
/*
@@ -344,6 +375,21 @@ static void ufshcd_print_host_regs(struct ufs_hba *hba)
dev_err(hba->dev,
"hba->outstanding_reqs = 0x%x, hba->outstanding_tasks = 0x%x\n",
(u32)hba->outstanding_reqs, (u32)hba->outstanding_tasks);
+ dev_err(hba->dev,
+ "last_hibern8_exit_tstamp at %lld us, hibern8_exit_cnt = %d\n",
+ ktime_to_us(hba->ufs_stats.last_hibern8_exit_tstamp),
+ hba->ufs_stats.hibern8_exit_cnt);
+
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.pa_err, "pa_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.dl_err, "dl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.nl_err, "nl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.tl_err, "tl_err");
+ ufshcd_print_uic_err_hist(hba, &hba->ufs_stats.dme_err, "dme_err");
+
+ ufshcd_print_clk_freqs(hba);
+
+ if (hba->vops && hba->vops->dbg_register_dump)
+ hba->vops->dbg_register_dump(hba);
}
static
@@ -355,22 +401,30 @@ void ufshcd_print_trs(struct ufs_hba *hba, unsigned long bitmap, bool pr_prdt)
for_each_set_bit(tag, &bitmap, hba->nutrs) {
lrbp = &hba->lrb[tag];
- dev_err(hba->dev, "UPIU[%d] - Transfer Request Descriptor\n",
- tag);
+ dev_err(hba->dev, "UPIU[%d] - issue time %lld us\n",
+ tag, ktime_to_us(lrbp->issue_time_stamp));
+ dev_err(hba->dev,
+ "UPIU[%d] - Transfer Request Descriptor phys@0x%llx\n",
+ tag, (u64)lrbp->utrd_dma_addr);
+
ufshcd_hex_dump("UPIU TRD: ", lrbp->utr_descriptor_ptr,
sizeof(struct utp_transfer_req_desc));
- dev_err(hba->dev, "UPIU[%d] - Request UPIU\n", tag);
+ dev_err(hba->dev, "UPIU[%d] - Request UPIU phys@0x%llx\n", tag,
+ (u64)lrbp->ucd_req_dma_addr);
ufshcd_hex_dump("UPIU REQ: ", lrbp->ucd_req_ptr,
sizeof(struct utp_upiu_req));
- dev_err(hba->dev, "UPIU[%d] - Response UPIU\n", tag);
+ dev_err(hba->dev, "UPIU[%d] - Response UPIU phys@0x%llx\n", tag,
+ (u64)lrbp->ucd_rsp_dma_addr);
ufshcd_hex_dump("UPIU RSP: ", lrbp->ucd_rsp_ptr,
sizeof(struct utp_upiu_rsp));
if (pr_prdt) {
int prdt_length = le16_to_cpu(
lrbp->utr_descriptor_ptr->prd_table_length);
- dev_err(hba->dev, "UPIU[%d] - PRDT - %d entries\n", tag,
- prdt_length);
+ dev_err(hba->dev,
+ "UPIU[%d] - PRDT - %d entries phys@0x%llx\n",
+ tag, prdt_length,
+ (u64)lrbp->ucd_prdt_dma_addr);
ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
sizeof(struct ufshcd_sg_entry) *
prdt_length);
@@ -399,6 +453,32 @@ static void ufshcd_print_tmrs(struct ufs_hba *hba, unsigned long bitmap)
}
}
+/**
+ * ufshcd_print_pwr_info - print power params as saved in hba
+ * power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_print_pwr_info(struct ufs_hba *hba)
+{
+ static const char * const names[] = {
+ "INVALID MODE",
+ "FAST MODE",
+ "SLOW_MODE",
+ "INVALID MODE",
+ "FASTAUTO_MODE",
+ "SLOWAUTO_MODE",
+ "INVALID MODE",
+ };
+
+ dev_err(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
+ __func__,
+ hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
+ hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
+ names[hba->pwr_info.pwr_rx],
+ names[hba->pwr_info.pwr_tx],
+ hba->pwr_info.hs_rate);
+}
+
/*
* ufshcd_wait_for_register - wait for register value to change
* @hba - per-adapter interface
@@ -1196,6 +1276,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
static inline
void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
{
+ hba->lrb[task_tag].issue_time_stamp = ktime_get();
ufshcd_clk_scaling_start_busy(hba);
__set_bit(task_tag, &hba->outstanding_reqs);
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
@@ -1877,6 +1958,7 @@ static int ufshcd_compose_dev_cmd(struct ufs_hba *hba,
int resp;
int err = 0;
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
resp = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
switch (resp) {
@@ -2647,32 +2729,6 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
}
/**
- * ufshcd_print_pwr_info - print power params as saved in hba
- * power info
- * @hba: per-adapter instance
- */
-static void ufshcd_print_pwr_info(struct ufs_hba *hba)
-{
- static const char * const names[] = {
- "INVALID MODE",
- "FAST MODE",
- "SLOW_MODE",
- "INVALID MODE",
- "FASTAUTO_MODE",
- "SLOWAUTO_MODE",
- "INVALID MODE",
- };
-
- dev_info(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
- __func__,
- hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
- hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
- names[hba->pwr_info.pwr_rx],
- names[hba->pwr_info.pwr_tx],
- hba->pwr_info.hs_rate);
-}
-
-/**
* ufshcd_host_memory_configure - configure local reference block with
* memory offsets
* @hba: per adapter instance
@@ -2734,12 +2790,19 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
}
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
+ hba->lrb[i].utrd_dma_addr = hba->utrdl_dma_addr +
+ (i * sizeof(struct utp_transfer_req_desc));
hba->lrb[i].ucd_req_ptr =
(struct utp_upiu_req *)(cmd_descp + i);
+ hba->lrb[i].ucd_req_dma_addr = cmd_desc_element_addr;
hba->lrb[i].ucd_rsp_ptr =
(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
+ hba->lrb[i].ucd_rsp_dma_addr = cmd_desc_element_addr +
+ response_offset;
hba->lrb[i].ucd_prdt_ptr =
(struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
+ hba->lrb[i].ucd_prdt_dma_addr = cmd_desc_element_addr +
+ prdt_offset;
}
}
@@ -2763,7 +2826,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
if (ret)
- dev_err(hba->dev,
+ dev_dbg(hba->dev,
"dme-link-startup: error code %d\n", ret);
return ret;
}
@@ -3113,9 +3176,12 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d\n",
__func__, ret);
ret = ufshcd_link_recovery(hba);
- } else
+ } else {
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT,
POST_CHANGE);
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_get();
+ hba->ufs_stats.hibern8_exit_cnt++;
+ }
return ret;
}
@@ -3885,7 +3951,7 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
switch (ocs) {
case OCS_SUCCESS:
result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
-
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
switch (result) {
case UPIU_TRANSACTION_RESPONSE:
/*
@@ -3946,7 +4012,9 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
default:
result |= DID_ERROR << 16;
dev_err(hba->dev,
- "OCS error from controller = %x\n", ocs);
+ "OCS error from controller = %x for tag %d\n",
+ ocs, lrbp->task_tag);
+ ufshcd_print_host_regs(hba);
break;
} /* end of switch */
@@ -4555,6 +4623,14 @@ static void ufshcd_err_handler(struct work_struct *work)
pm_runtime_put_sync(hba->dev);
}
+static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist,
+ u32 reg)
+{
+ reg_hist->reg[reg_hist->pos] = reg;
+ reg_hist->tstamp[reg_hist->pos] = ktime_get();
+ reg_hist->pos = (reg_hist->pos + 1) % UIC_ERR_REG_HIST_LENGTH;
+}
+
/**
* ufshcd_update_uic_error - check and set fatal UIC error flags.
* @hba: per-adapter instance
@@ -4567,15 +4643,20 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
/* Ignore LINERESET indication, as this is not an error */
if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) &&
- (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK))
+ (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) {
/*
* To know whether this error is fatal or not, DB timeout
* must be checked but this error is handled separately.
*/
dev_dbg(hba->dev, "%s: UIC Lane error reported\n", __func__);
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.pa_err, reg);
+ }
/* PA_INIT_ERROR is fatal and needs UIC reset */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER);
+ if (reg)
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.dl_err, reg);
+
if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
else if (hba->dev_quirks &
@@ -4589,16 +4670,22 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
/* UIC NL/TL/DME errors needs software retry */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.nl_err, reg);
hba->uic_error |= UFSHCD_UIC_NL_ERROR;
+ }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.tl_err, reg);
hba->uic_error |= UFSHCD_UIC_TL_ERROR;
+ }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
- if (reg)
+ if (reg) {
+ ufshcd_update_uic_reg_hist(&hba->ufs_stats.dme_err, reg);
hba->uic_error |= UFSHCD_UIC_DME_ERROR;
+ }
dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n",
__func__, hba->uic_error);
@@ -4965,12 +5052,16 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
UFS_QUERY_TASK, &resp);
if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
/* cmd pending in the device */
+ dev_err(hba->dev, "%s: cmd pending in the device. tag = %d\n",
+ __func__, tag);
break;
} else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
/*
* cmd not pending in the device, check if it is
* in transition.
*/
+ dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n",
+ __func__, tag);
reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
if (reg & (1 << tag)) {
/* sleep for max. 200us to stabilize */
@@ -4978,8 +5069,13 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
continue;
}
/* command completed already */
+ dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n",
+ __func__, tag);
goto out;
} else {
+ dev_err(hba->dev,
+ "%s: no response from device. tag = %d, err %d\n",
+ __func__, tag, err);
if (!err)
err = resp; /* service response error */
goto out;
@@ -4994,14 +5090,20 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
UFS_ABORT_TASK, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
- if (!err)
+ if (!err) {
err = resp; /* service response error */
+ dev_err(hba->dev, "%s: issued. tag = %d, err %d\n",
+ __func__, tag, err);
+ }
goto out;
}
err = ufshcd_clear_cmd(hba, tag);
- if (err)
+ if (err) {
+ dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
+ __func__, tag, err);
goto out;
+ }
scsi_dma_unmap(cmd);
@@ -5583,6 +5685,20 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
ufshcd_vops_apply_dev_quirks(hba);
}
+static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba)
+{
+ int err_reg_hist_size = sizeof(struct ufs_uic_err_reg_hist);
+
+ hba->ufs_stats.hibern8_exit_cnt = 0;
+ hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
+
+ memset(&hba->ufs_stats.pa_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.dl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.nl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.tl_err, 0, err_reg_hist_size);
+ memset(&hba->ufs_stats.dme_err, 0, err_reg_hist_size);
+}
+
/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
@@ -5602,6 +5718,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
hba->is_urgent_bkops_lvl_checked = false;
+ /* Debug counters initialization */
+ ufshcd_clear_dbg_ufs_stats(hba);
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index f25d468..0ea49f9 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -152,6 +152,10 @@ struct ufs_pm_lvl_states {
* @ucd_req_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
+ * @utrd_dma_addr: UTRD dma address for debug
+ * @ucd_prdt_dma_addr: PRDT dma address for debug
+ * @ucd_rsp_dma_addr: UPIU response dma address for debug
+ * @ucd_req_dma_addr: UPIU request dma address for debug
* @cmd: pointer to SCSI command
* @sense_buffer: pointer to sense buffer address of the SCSI command
* @sense_bufflen: Length of the sense buffer
@@ -160,6 +164,7 @@ struct ufs_pm_lvl_states {
* @task_tag: Task tag of the command
* @lun: LUN of the command
* @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
+ * @issue_time_stamp: time stamp for debug purposes
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
@@ -167,6 +172,11 @@ struct ufshcd_lrb {
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
+ dma_addr_t utrd_dma_addr;
+ dma_addr_t ucd_req_dma_addr;
+ dma_addr_t ucd_rsp_dma_addr;
+ dma_addr_t ucd_prdt_dma_addr;
+
struct scsi_cmnd *cmd;
u8 *sense_buffer;
unsigned int sense_bufflen;
@@ -176,6 +186,7 @@ struct ufshcd_lrb {
int task_tag;
u8 lun; /* UPIU LUN id field is only 8-bit wide */
bool intr_cmd;
+ ktime_t issue_time_stamp;
};
/**
@@ -355,6 +366,41 @@ struct ufs_init_prefetch {
u32 icc_level;
};
+#define UIC_ERR_REG_HIST_LENGTH 8
+/**
+ * struct ufs_uic_err_reg_hist - keeps history of uic errors
+ * @pos: index to indicate cyclic buffer position
+ * @reg: cyclic buffer for registers value
+ * @tstamp: cyclic buffer for time stamp
+ */
+struct ufs_uic_err_reg_hist {
+ int pos;
+ u32 reg[UIC_ERR_REG_HIST_LENGTH];
+ ktime_t tstamp[UIC_ERR_REG_HIST_LENGTH];
+};
+
+/**
+ * struct ufs_stats - keeps usage/err statistics
+ * @hibern8_exit_cnt: Counter to keep track of number of exits,
+ * reset this after link-startup.
+ * @last_hibern8_exit_tstamp: Set time after the hibern8 exit.
+ * Clear after the first successful command completion.
+ * @pa_err: tracks pa-uic errors
+ * @dl_err: tracks dl-uic errors
+ * @nl_err: tracks nl-uic errors
+ * @tl_err: tracks tl-uic errors
+ * @dme_err: tracks dme errors
+ */
+struct ufs_stats {
+ u32 hibern8_exit_cnt;
+ ktime_t last_hibern8_exit_tstamp;
+ struct ufs_uic_err_reg_hist pa_err;
+ struct ufs_uic_err_reg_hist dl_err;
+ struct ufs_uic_err_reg_hist nl_err;
+ struct ufs_uic_err_reg_hist tl_err;
+ struct ufs_uic_err_reg_hist dme_err;
+};
+
/**
* struct ufs_hba - per adapter private structure
* @mmio_base: UFSHCI base register address
@@ -531,6 +577,7 @@ struct ufs_hba {
u32 uic_error;
u32 saved_err;
u32 saved_uic_err;
+ struct ufs_stats ufs_stats;
/* Device management request data */
struct ufs_dev_cmd dev_cmd;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 11/12] scsi: ufs: add trace event for ufs commands
From: Subhash Jadavani @ 2016-12-23 2:42 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Lee Susman, Subhash Jadavani, Steven Rostedt,
Ingo Molnar, Sahitya Tummala, Venkat Gopalakrishnan, open list
From: Lee Susman <lsusman@codeaurora.org>
Use the ftrace infrastructure to conditionally trace ufs command events.
New trace event is created, which samples the following ufs command data:
- device name
- optional identification string
- task tag
- doorbell register
- number of transfer bytes
- interrupt status register
- request start LBA
- command opcode
Currently we only fully trace read(10) and write(10) commands.
All other commands which pass through ufshcd_send_command() will be
printed with "-1" in the lba and transfer_len fields.
Usage:
echo 1 > /sys/kernel/debug/tracing/events/ufs/enable
cat /sys/kernel/debug/tracing/trace_pipe
Signed-off-by: Lee Susman <lsusman@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v2 -> v3:
- Removed conditional compilation based on CONFIG_TRACEPOINTS and
used trace_*_enabled() to skip unnecessary code path execution.
---
drivers/scsi/ufs/ufshcd.c | 42 +++++++++++++++++++++++++++++++++++++++++-
include/trace/events/ufs.h | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 36d239d..918c597 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -293,6 +293,41 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
+static void ufshcd_add_command_trace(struct ufs_hba *hba,
+ unsigned int tag, const char *str)
+{
+ sector_t lba = -1;
+ u8 opcode = 0;
+ u32 intr, doorbell;
+ struct ufshcd_lrb *lrbp;
+ int transfer_len = -1;
+
+ if (!trace_ufshcd_command_enabled())
+ return;
+
+ lrbp = &hba->lrb[tag];
+
+ if (lrbp->cmd) { /* data phase exists */
+ opcode = (u8)(*lrbp->cmd->cmnd);
+ if ((opcode == READ_10) || (opcode == WRITE_10)) {
+ /*
+ * Currently we only fully trace read(10) and write(10)
+ * commands
+ */
+ if (lrbp->cmd->request && lrbp->cmd->request->bio)
+ lba =
+ lrbp->cmd->request->bio->bi_iter.bi_sector;
+ transfer_len = be32_to_cpu(
+ lrbp->ucd_req_ptr->sc.exp_data_transfer_len);
+ }
+ }
+
+ intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
+ doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ trace_ufshcd_command(dev_name(hba->dev), str, tag,
+ doorbell, transfer_len, intr, lba, opcode);
+}
+
static void ufshcd_print_host_regs(struct ufs_hba *hba)
{
/*
@@ -1166,6 +1201,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* Make sure that doorbell is committed immediately */
wmb();
+ ufshcd_add_command_trace(hba, task_tag, "send");
}
/**
@@ -3955,6 +3991,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
lrbp = &hba->lrb[index];
cmd = lrbp->cmd;
if (cmd) {
+ ufshcd_add_command_trace(hba, index, "complete");
result = ufshcd_transfer_rsp_status(hba, lrbp);
scsi_dma_unmap(cmd);
cmd->result = result;
@@ -3966,8 +4003,11 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
__ufshcd_release(hba);
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE ||
lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) {
- if (hba->dev_cmd.complete)
+ if (hba->dev_cmd.complete) {
+ ufshcd_add_command_trace(hba, index,
+ "dev_complete");
complete(hba->dev_cmd.complete);
+ }
}
}
diff --git a/include/trace/events/ufs.h b/include/trace/events/ufs.h
index e9634df..bf6f826 100644
--- a/include/trace/events/ufs.h
+++ b/include/trace/events/ufs.h
@@ -219,6 +219,44 @@
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
+
+TRACE_EVENT(ufshcd_command,
+ TP_PROTO(const char *dev_name, const char *str, unsigned int tag,
+ u32 doorbell, int transfer_len, u32 intr, u64 lba,
+ u8 opcode),
+
+ TP_ARGS(dev_name, str, tag, doorbell, transfer_len, intr, lba, opcode),
+
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name)
+ __string(str, str)
+ __field(unsigned int, tag)
+ __field(u32, doorbell)
+ __field(int, transfer_len)
+ __field(u32, intr)
+ __field(u64, lba)
+ __field(u8, opcode)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev_name, dev_name);
+ __assign_str(str, str);
+ __entry->tag = tag;
+ __entry->doorbell = doorbell;
+ __entry->transfer_len = transfer_len;
+ __entry->intr = intr;
+ __entry->lba = lba;
+ __entry->opcode = opcode;
+ ),
+
+ TP_printk(
+ "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x",
+ __get_str(str), __get_str(dev_name), __entry->tag,
+ __entry->doorbell, __entry->transfer_len,
+ __entry->intr, __entry->lba, (u32)__entry->opcode
+ )
+);
+
#endif /* if !defined(_TRACE_UFS_H) || defined(TRACE_HEADER_MULTI_READ) */
/* This part must be outside protection */
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 11/12] scsi: ufs: add trace event for ufs commands
From: Subhash Jadavani @ 2016-12-23 2:42 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Lee Susman, Subhash Jadavani, Steven Rostedt,
Ingo Molnar, Sahitya Tummala, Venkat Gopalakrishnan, open list
From: Lee Susman <lsusman@codeaurora.org>
Use the ftrace infrastructure to conditionally trace ufs command events.
New trace event is created, which samples the following ufs command data:
- device name
- optional identification string
- task tag
- doorbell register
- number of transfer bytes
- interrupt status register
- request start LBA
- command opcode
Currently we only fully trace read(10) and write(10) commands.
All other commands which pass through ufshcd_send_command() will be
printed with "-1" in the lba and transfer_len fields.
Usage:
echo 1 > /sys/kernel/debug/tracing/events/ufs/enable
cat /sys/kernel/debug/tracing/trace_pipe
Signed-off-by: Lee Susman <lsusman@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v2 -> v3:
- Removed conditional compilation based on CONFIG_TRACEPOINTS and
used trace_*_enabled() to skip unnecessary code path execution.
---
drivers/scsi/ufs/ufshcd.c | 42 +++++++++++++++++++++++++++++++++++++++++-
include/trace/events/ufs.h | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 36d239d..918c597 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -293,6 +293,41 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
+static void ufshcd_add_command_trace(struct ufs_hba *hba,
+ unsigned int tag, const char *str)
+{
+ sector_t lba = -1;
+ u8 opcode = 0;
+ u32 intr, doorbell;
+ struct ufshcd_lrb *lrbp;
+ int transfer_len = -1;
+
+ if (!trace_ufshcd_command_enabled())
+ return;
+
+ lrbp = &hba->lrb[tag];
+
+ if (lrbp->cmd) { /* data phase exists */
+ opcode = (u8)(*lrbp->cmd->cmnd);
+ if ((opcode == READ_10) || (opcode == WRITE_10)) {
+ /*
+ * Currently we only fully trace read(10) and write(10)
+ * commands
+ */
+ if (lrbp->cmd->request && lrbp->cmd->request->bio)
+ lba =
+ lrbp->cmd->request->bio->bi_iter.bi_sector;
+ transfer_len = be32_to_cpu(
+ lrbp->ucd_req_ptr->sc.exp_data_transfer_len);
+ }
+ }
+
+ intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
+ doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ trace_ufshcd_command(dev_name(hba->dev), str, tag,
+ doorbell, transfer_len, intr, lba, opcode);
+}
+
static void ufshcd_print_host_regs(struct ufs_hba *hba)
{
/*
@@ -1166,6 +1201,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* Make sure that doorbell is committed immediately */
wmb();
+ ufshcd_add_command_trace(hba, task_tag, "send");
}
/**
@@ -3955,6 +3991,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
lrbp = &hba->lrb[index];
cmd = lrbp->cmd;
if (cmd) {
+ ufshcd_add_command_trace(hba, index, "complete");
result = ufshcd_transfer_rsp_status(hba, lrbp);
scsi_dma_unmap(cmd);
cmd->result = result;
@@ -3966,8 +4003,11 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
__ufshcd_release(hba);
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE ||
lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) {
- if (hba->dev_cmd.complete)
+ if (hba->dev_cmd.complete) {
+ ufshcd_add_command_trace(hba, index,
+ "dev_complete");
complete(hba->dev_cmd.complete);
+ }
}
}
diff --git a/include/trace/events/ufs.h b/include/trace/events/ufs.h
index e9634df..bf6f826 100644
--- a/include/trace/events/ufs.h
+++ b/include/trace/events/ufs.h
@@ -219,6 +219,44 @@
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
+
+TRACE_EVENT(ufshcd_command,
+ TP_PROTO(const char *dev_name, const char *str, unsigned int tag,
+ u32 doorbell, int transfer_len, u32 intr, u64 lba,
+ u8 opcode),
+
+ TP_ARGS(dev_name, str, tag, doorbell, transfer_len, intr, lba, opcode),
+
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name)
+ __string(str, str)
+ __field(unsigned int, tag)
+ __field(u32, doorbell)
+ __field(int, transfer_len)
+ __field(u32, intr)
+ __field(u64, lba)
+ __field(u8, opcode)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev_name, dev_name);
+ __assign_str(str, str);
+ __entry->tag = tag;
+ __entry->doorbell = doorbell;
+ __entry->transfer_len = transfer_len;
+ __entry->intr = intr;
+ __entry->lba = lba;
+ __entry->opcode = opcode;
+ ),
+
+ TP_printk(
+ "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x",
+ __get_str(str), __get_str(dev_name), __entry->tag,
+ __entry->doorbell, __entry->transfer_len,
+ __entry->intr, __entry->lba, (u32)__entry->opcode
+ )
+);
+
#endif /* if !defined(_TRACE_UFS_H) || defined(TRACE_HEADER_MULTI_READ) */
/* This part must be outside protection */
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 10/12] scsi: ufs: add time profiling support
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, Steven Rostedt, Ingo Molnar,
Sahitya Tummala, Lee Susman, Venkat Gopalakrishnan, open list
This patch adds the profiling support for some of the time critical
operations like hibern8 enter/exit, clock gating & clock scaling.
Reviewed-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 24 ++++++++++++++++++++++++
include/trace/events/ufs.h | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2d3ca18..36d239d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3021,11 +3021,14 @@ static int __ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
{
int ret;
struct uic_command uic_cmd = {0};
+ ktime_t start = ktime_get();
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_ENTER, PRE_CHANGE);
uic_cmd.command = UIC_CMD_DME_HIBER_ENTER;
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+ trace_ufshcd_profile_hibern8(dev_name(hba->dev), "enter",
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
if (ret) {
dev_err(hba->dev, "%s: hibern8 enter failed. ret = %d\n",
@@ -3061,11 +3064,15 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
{
struct uic_command uic_cmd = {0};
int ret;
+ ktime_t start = ktime_get();
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT, PRE_CHANGE);
uic_cmd.command = UIC_CMD_DME_HIBER_EXIT;
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+ trace_ufshcd_profile_hibern8(dev_name(hba->dev), "exit",
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
+
if (ret) {
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d\n",
__func__, ret);
@@ -5950,6 +5957,8 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
unsigned long flags;
+ ktime_t start = ktime_get();
+ bool clk_state_changed = false;
if (!head || list_empty(head))
goto out;
@@ -5963,6 +5972,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
continue;
+ clk_state_changed = on ^ clki->enabled;
if (on && !clki->enabled) {
ret = clk_prepare_enable(clki->clk);
if (ret) {
@@ -5997,6 +6007,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
spin_unlock_irqrestore(hba->host->host_lock, flags);
}
+ if (clk_state_changed)
+ trace_ufshcd_profile_clk_gating(dev_name(hba->dev),
+ (on ? "on" : "off"),
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
return ret;
}
@@ -6996,6 +7010,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
int ret = 0;
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
+ ktime_t start = ktime_get();
+ bool clk_state_changed = false;
if (!head || list_empty(head))
goto out;
@@ -7009,6 +7025,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
if (scale_up && clki->max_freq) {
if (clki->curr_freq == clki->max_freq)
continue;
+
+ clk_state_changed = true;
ret = clk_set_rate(clki->clk, clki->max_freq);
if (ret) {
dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
@@ -7026,6 +7044,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
} else if (!scale_up && clki->min_freq) {
if (clki->curr_freq == clki->min_freq)
continue;
+
+ clk_state_changed = true;
ret = clk_set_rate(clki->clk, clki->min_freq);
if (ret) {
dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
@@ -7047,6 +7067,10 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE);
out:
+ if (clk_state_changed)
+ trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
+ (scale_up ? "up" : "down"),
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
return ret;
}
diff --git a/include/trace/events/ufs.h b/include/trace/events/ufs.h
index 57743b6..e9634df 100644
--- a/include/trace/events/ufs.h
+++ b/include/trace/events/ufs.h
@@ -123,6 +123,46 @@
__get_str(dev_name), __get_str(state))
);
+DECLARE_EVENT_CLASS(ufshcd_profiling_template,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+
+ TP_ARGS(dev_name, profile_info, time_us, err),
+
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name)
+ __string(profile_info, profile_info)
+ __field(s64, time_us)
+ __field(int, err)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev_name, dev_name);
+ __assign_str(profile_info, profile_info);
+ __entry->time_us = time_us;
+ __entry->err = err;
+ ),
+
+ TP_printk("%s: %s: took %lld usecs, err %d",
+ __get_str(dev_name), __get_str(profile_info),
+ __entry->time_us, __entry->err)
+);
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_hibern8,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_clk_gating,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_clk_scaling,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
DECLARE_EVENT_CLASS(ufshcd_template,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 10/12] scsi: ufs: add time profiling support
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, Steven Rostedt, Ingo Molnar,
Sahitya Tummala, Lee Susman, Venkat Gopalakrishnan, open list
This patch adds the profiling support for some of the time critical
operations like hibern8 enter/exit, clock gating & clock scaling.
Reviewed-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 24 ++++++++++++++++++++++++
include/trace/events/ufs.h | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2d3ca18..36d239d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3021,11 +3021,14 @@ static int __ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
{
int ret;
struct uic_command uic_cmd = {0};
+ ktime_t start = ktime_get();
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_ENTER, PRE_CHANGE);
uic_cmd.command = UIC_CMD_DME_HIBER_ENTER;
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+ trace_ufshcd_profile_hibern8(dev_name(hba->dev), "enter",
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
if (ret) {
dev_err(hba->dev, "%s: hibern8 enter failed. ret = %d\n",
@@ -3061,11 +3064,15 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
{
struct uic_command uic_cmd = {0};
int ret;
+ ktime_t start = ktime_get();
ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT, PRE_CHANGE);
uic_cmd.command = UIC_CMD_DME_HIBER_EXIT;
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+ trace_ufshcd_profile_hibern8(dev_name(hba->dev), "exit",
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
+
if (ret) {
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d\n",
__func__, ret);
@@ -5950,6 +5957,8 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
unsigned long flags;
+ ktime_t start = ktime_get();
+ bool clk_state_changed = false;
if (!head || list_empty(head))
goto out;
@@ -5963,6 +5972,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
continue;
+ clk_state_changed = on ^ clki->enabled;
if (on && !clki->enabled) {
ret = clk_prepare_enable(clki->clk);
if (ret) {
@@ -5997,6 +6007,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
spin_unlock_irqrestore(hba->host->host_lock, flags);
}
+ if (clk_state_changed)
+ trace_ufshcd_profile_clk_gating(dev_name(hba->dev),
+ (on ? "on" : "off"),
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
return ret;
}
@@ -6996,6 +7010,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
int ret = 0;
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
+ ktime_t start = ktime_get();
+ bool clk_state_changed = false;
if (!head || list_empty(head))
goto out;
@@ -7009,6 +7025,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
if (scale_up && clki->max_freq) {
if (clki->curr_freq == clki->max_freq)
continue;
+
+ clk_state_changed = true;
ret = clk_set_rate(clki->clk, clki->max_freq);
if (ret) {
dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
@@ -7026,6 +7044,8 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
} else if (!scale_up && clki->min_freq) {
if (clki->curr_freq == clki->min_freq)
continue;
+
+ clk_state_changed = true;
ret = clk_set_rate(clki->clk, clki->min_freq);
if (ret) {
dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
@@ -7047,6 +7067,10 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE);
out:
+ if (clk_state_changed)
+ trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
+ (scale_up ? "up" : "down"),
+ ktime_to_us(ktime_sub(ktime_get(), start)), ret);
return ret;
}
diff --git a/include/trace/events/ufs.h b/include/trace/events/ufs.h
index 57743b6..e9634df 100644
--- a/include/trace/events/ufs.h
+++ b/include/trace/events/ufs.h
@@ -123,6 +123,46 @@
__get_str(dev_name), __get_str(state))
);
+DECLARE_EVENT_CLASS(ufshcd_profiling_template,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+
+ TP_ARGS(dev_name, profile_info, time_us, err),
+
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name)
+ __string(profile_info, profile_info)
+ __field(s64, time_us)
+ __field(int, err)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev_name, dev_name);
+ __assign_str(profile_info, profile_info);
+ __entry->time_us = time_us;
+ __entry->err = err;
+ ),
+
+ TP_printk("%s: %s: took %lld usecs, err %d",
+ __get_str(dev_name), __get_str(profile_info),
+ __entry->time_us, __entry->err)
+);
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_hibern8,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_clk_gating,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
+DEFINE_EVENT(ufshcd_profiling_template, ufshcd_profile_clk_scaling,
+ TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
+ int err),
+ TP_ARGS(dev_name, profile_info, time_us, err));
+
DECLARE_EVENT_CLASS(ufshcd_template,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 09/12] scsi: ufs: fix setting init power mode
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
Immediately after successful UFS link startup, UFS link power mode would
be in PWM-G1, 1-lane, SLOW-AUTO mode. But currently we are doing few
of the DME local/peer attributes access before setting the "hba->pwr_info"
to default power mode. If we are doing link startup as part of error
recovery then old power mode might be set to FAST mode and doing DME peer
access (after link startup but before updating "hba->pwr_info" to default
power mode) unintentionally tries to switch from FAST to FAST_AUTO mode (if
UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE quirk is enabled).
Above issue is fixed by setting the default power mode immediately after
successful link startup.
Reviewed-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a647bcf..2d3ca18 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3512,6 +3512,10 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
goto link_startup;
}
+ /* Mark that link is up in PWM-G1, 1-lane, SLOW-AUTO mode */
+ ufshcd_init_pwr_info(hba);
+ ufshcd_print_pwr_info(hba);
+
if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
ret = ufshcd_disable_device_tx_lcc(hba);
if (ret)
@@ -5547,9 +5551,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret)
goto out;
- ufshcd_init_pwr_info(hba);
- ufshcd_print_pwr_info(hba);
-
/* set the default level for urgent bkops */
hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
hba->is_urgent_bkops_lvl_checked = false;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* master - lvchange: allow a transiently failed RaidLV to be refreshed
From: Heinz Mauelshagen @ 2016-12-23 2:41 UTC (permalink / raw)
To: lvm-devel
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=95d68f1d0e16f553f4f12046ceb7b6ff8d251336
Commit: 95d68f1d0e16f553f4f12046ceb7b6ff8d251336
Parent: 62be9c8de430a054d5de9b652949f58a684a0cf6
Author: Heinz Mauelshagen <heinzm@redhat.com>
AuthorDate: Fri Dec 23 03:35:13 2016 +0100
Committer: Heinz Mauelshagen <heinzm@redhat.com>
CommitterDate: Fri Dec 23 03:41:32 2016 +0100
lvchange: allow a transiently failed RaidLV to be refreshed
Add to commits 87117c2b2546 and 0b8bf73a63d8 to avoid refreshing two
times altogether, thus avoiding issues related to clustered, remotely
activated RaidLV. Avoid need to repeat "lvchange --refresh RaidLV"
two times as a workaround to refresh a RaidLV. Fix handles removal
of temporary *-missing-* devices created for any missing segments
in RAID SubLVs during activation.
Because the kernel dm-raid target isn't able to handle transiently
failing devices properly we need
"[dm-devel][PATCH] dm raid: fix transient device failure processing"
as well.
test: add lvchange-raid-transient-failures.sh
and enhance lvconvert-raid.sh
Resolves: rhbz1025322
Related: rhbz1265191
Related: rhbz1399844
Related: rhbz1404425
---
lib/activate/activate.c | 75 ++++++++++++++++++++++++
lib/activate/activate.h | 2 +
lib/metadata/lv_manip.c | 34 +++--------
test/shell/lvchange-raid-transient-failures.sh | 69 ++++++++++++++++++++++
test/shell/lvconvert-raid.sh | 19 +++++-
5 files changed, 171 insertions(+), 28 deletions(-)
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
index b7009e6..742d838 100644
--- a/lib/activate/activate.c
+++ b/lib/activate/activate.c
@@ -358,6 +358,10 @@ int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
{
return 1;
}
+int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv)
+{
+ return 1;
+}
int pv_uses_vg(struct physical_volume *pv,
struct volume_group *vg)
{
@@ -2573,6 +2577,77 @@ int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
return r;
}
+/* Remove any existing, closed mapped device by @name */
+static int _remove_dm_dev_by_name(const char *name)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ return_0;
+
+ /* Check, if the device exists. */
+ if (dm_task_set_name(dmt, name) && dm_task_run(dmt) && dm_task_get_info(dmt, &info)) {
+ dm_task_destroy(dmt);
+
+ /* Ignore non-existing or open dm devices */
+ if (!info.exists || info.open_count)
+ return 1;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
+ return_0;
+
+ if (dm_task_set_name(dmt, name))
+ r = dm_task_run(dmt);
+ }
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/* Work all segments of @lv removing any existing, closed "*-missing_N_0" sub devices. */
+static int _lv_remove_any_missing_subdevs(struct logical_volume *lv)
+{
+ if (lv) {
+ uint32_t seg_no = 0;
+ char name[257];
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (seg->area_count != 1)
+ return_0;
+ if (dm_snprintf(name, sizeof(name), "%s-%s-missing_%u_0", seg->lv->vg->name, seg->lv->name, seg_no) < 0)
+ return 0;
+ if (!_remove_dm_dev_by_name(name))
+ return 0;
+
+ seg_no++;
+ }
+ }
+
+ return 1;
+}
+
+/* Remove any "*-missing_*" sub devices added by the activation layer for an rmate/rimage missing PV mapping */
+int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv)
+{
+ uint32_t s;
+ struct lv_segment *seg = first_seg(lv);
+
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) == AREA_LV &&
+ !_lv_remove_any_missing_subdevs(seg_lv(seg, s)))
+ return 0;
+ if (seg->meta_areas && seg_metatype(seg, s) == AREA_LV &&
+ !_lv_remove_any_missing_subdevs(seg_metalv(seg, s)))
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* Does PV use VG somewhere in its construction?
* Returns 1 on failure.
diff --git a/lib/activate/activate.h b/lib/activate/activate.h
index db8d997..85c1521 100644
--- a/lib/activate/activate.h
+++ b/lib/activate/activate.h
@@ -124,6 +124,8 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
+int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv);
+
/*
* Returns 1 if info structure has been populated, else 0 on failure.
* When lvinfo* is NULL, it returns 1 if the device is locally active, 0 otherwise.
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 3862f11..e5808ec 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -1419,35 +1419,19 @@ static int _lv_refresh_suspend_resume(const struct logical_volume *lv)
int lv_refresh_suspend_resume(const struct logical_volume *lv)
{
+ if (!_lv_refresh_suspend_resume(lv))
+ return 0;
+
/*
- * FIXME:
- *
- * in case of RAID, refresh the SubLVs before
- * refreshing the top-level one in order to cope
- * with transient failures of SubLVs.
+ * Remove any transiently activated error
+ * devices which arean't used any more.
*/
- if (lv_is_raid(lv)) {
- if (vg_is_clustered(lv->vg) &&
- lv_is_active_remotely(lv)) {
- if (!_lv_refresh_suspend_resume(lv))
- return 0;
- } else {
- uint32_t s;
- struct lv_segment *seg = first_seg(lv);
-
- for (s = 0; s < seg->area_count; s++) {
- if (seg_type(seg, s) == AREA_LV &&
- !_lv_refresh_suspend_resume(seg_lv(seg, s)))
- return 0;
- if (seg->meta_areas &&
- seg_metatype(seg, s) == AREA_LV &&
- !_lv_refresh_suspend_resume(seg_metalv(seg, s)))
- return 0;
- }
- }
+ if (lv_is_raid(lv) && !lv_deactivate_any_missing_subdevs(lv)) {
+ log_error("Failed to remove temporary SubLVs from %s", display_lvname(lv));
+ return 0;
}
- return _lv_refresh_suspend_resume(lv);
+ return 1;
}
/*
diff --git a/test/shell/lvchange-raid-transient-failures.sh b/test/shell/lvchange-raid-transient-failures.sh
new file mode 100644
index 0000000..844f217
--- /dev/null
+++ b/test/shell/lvchange-raid-transient-failures.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+SKIP_WITH_LVMLOCKD=1
+SKIP_WITH_LVMPOLLD=1
+
+. lib/inittest
+
+aux have_raid 1 10 1 || skip
+aux prepare_vg 6
+
+#
+# FIXME: add multi-segment leg tests
+#
+
+function _check_raid
+{
+ local vg=$1
+ shift
+ local lv=$1
+ shift
+ local fail=$1
+ shift
+ local good=$1
+ shift
+ local devs=$*
+
+ aux wait_for_sync $vg $lv
+ aux disable_dev --error --silent $devs
+ mkfs.ext4 "$DM_DEV_DIR/$vg/$lv"
+ fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv"
+ check raid_leg_status $vg $lv "$fail"
+ aux enable_dev --silent $devs
+ lvs -a -o +devices $vg | tee out
+ not grep unknown out
+ lvchange --refresh $vg/$lv
+ fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv"
+ aux wait_for_sync $vg $lv
+ fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv"
+ check raid_leg_status $vg $lv "$good"
+}
+
+# raid1 with transiently failing devices
+lv=4way
+lvcreate -aey --type raid1 -m 3 --ignoremonitoring -L 1 -n $lv $vg
+_check_raid $vg $lv "ADAD" "AAAA" $dev2 $dev4
+lvremove -y $vg/$lv
+
+# raid6 with transiently failing devices
+lv=6way
+lvcreate -aey --type raid6 -i 4 --ignoremonitoring -L 1 -n $lv $vg
+_check_raid $vg $lv "ADADAA" "AAAAAA" $dev2 $dev4
+lvremove -y $vg/$lv
+
+# raid10 with transiently failing devices
+lv=6way
+lvcreate -aey --type raid10 -i 3 -m 1 --ignoremonitoring -L 1 -n $lv $vg
+_check_raid $vg $lv "ADADDA" "AAAAAA" $dev2 $dev4 $dev5
+lvremove -y $vg/$lv
+
+vgremove -f $vg
diff --git a/test/shell/lvconvert-raid.sh b/test/shell/lvconvert-raid.sh
index 25bc4a8..8538c41 100644
--- a/test/shell/lvconvert-raid.sh
+++ b/test/shell/lvconvert-raid.sh
@@ -32,7 +32,8 @@ get_image_pvs() {
aux have_raid 1 3 0 || skip
aux prepare_pvs 9
-vgcreate -s 256k $vg $(cat DEVICES)
+# vgcreate -s 256k $vg $(cat DEVICES)
+vgcreate -s 2m $vg $(cat DEVICES)
###########################################
# RAID1 convert tests
@@ -135,15 +136,27 @@ lvconvert --yes --splitmirrors 1 --name $lv2 $vg/$lv1 "$dev2"
lvremove -ff $vg
###########################################
-# RAID1 split + trackchanges / merge
+# RAID1 split + trackchanges / merge with content check
###########################################
# 3-way to 2-way/linear
-lvcreate --type raid1 -m 2 -l 2 -n $lv1 $vg
+lvcreate --type raid1 -m 2 -l 1 -n $lv1 $vg
+mkfs.ext4 "$DM_DEV_DIR/$vg/$lv1"
+fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv1"
aux wait_for_sync $vg $lv1
+fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv1"
lvconvert --splitmirrors 1 --trackchanges $vg/$lv1
check lv_exists $vg $lv1
check linear $vg ${lv1}_rimage_2
+fsck.ext4 -fn "$DM_DEV_DIR/mapper/$vg-${lv1}_rimage_2"
+dd of="$DM_DEV_DIR/$vg/$lv1" if=/dev/zero bs=512 oflag=direct count=`blockdev --getsz "$DM_DEV_DIR/$vg/$lv1"`
+not fsck.ext4 -fn "$DM_DEV_DIR/$vg/$lv1"
+fsck.ext4 -fn "$DM_DEV_DIR/mapper/$vg-${lv1}_rimage_2"
+# FIXME: needed on tiny loop but not on real block backend ?
+lvchange --refresh $vg/$lv1
lvconvert --merge $vg/${lv1}_rimage_2
+aux wait_for_sync $vg $lv1
+lvconvert --splitmirrors 1 --trackchanges $vg/$lv1
+not fsck.ext4 -fn "$DM_DEV_DIR/mapper/$vg-${lv1}_rimage_2"
# FIXME: ensure no residual devices
lvremove -ff $vg
^ permalink raw reply related
* [PATCH v3 08/12] scsi: ufs: add capability to keep auto bkops always enabled
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
UFS device requires to perform bkops (back ground operations) periodically
but host can control (via auto-bkops parameter of device) when device can
perform bkops based on its performance requirements. In general, host
would like to enable the device's auto-bkops only when it's not doing any
regular data transfer but sometimes device may not behave properly if host
keeps the auto-bkops disabled. This change adds the capability to let the
device auto-bkops always enabled except suspend.
Reviewed-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 33 ++++++++++++++++++++++-----------
drivers/scsi/ufs/ufshcd.h | 13 +++++++++++++
2 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 12a2250..a647bcf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4134,18 +4134,25 @@ static int ufshcd_disable_auto_bkops(struct ufs_hba *hba)
}
/**
- * ufshcd_force_reset_auto_bkops - force enable of auto bkops
+ * ufshcd_force_reset_auto_bkops - force reset auto bkops state
* @hba: per adapter instance
*
* After a device reset the device may toggle the BKOPS_EN flag
* to default value. The s/w tracking variables should be updated
- * as well. Do this by forcing enable of auto bkops.
+ * as well. This function would change the auto-bkops state based on
+ * UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND.
*/
-static void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
+static void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
{
- hba->auto_bkops_enabled = false;
- hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
- ufshcd_enable_auto_bkops(hba);
+ if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) {
+ hba->auto_bkops_enabled = false;
+ hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
+ ufshcd_enable_auto_bkops(hba);
+ } else {
+ hba->auto_bkops_enabled = true;
+ hba->ee_ctrl_mask &= ~MASK_EE_URGENT_BKOPS;
+ ufshcd_disable_auto_bkops(hba);
+ }
}
static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status)
@@ -6564,11 +6571,15 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
goto set_old_link_state;
}
- /*
- * If BKOPs operations are urgently needed at this moment then
- * keep auto-bkops enabled or else disable it.
- */
- ufshcd_urgent_bkops(hba);
+ if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+ ufshcd_enable_auto_bkops(hba);
+ else
+ /*
+ * If BKOPs operations are urgently needed at this moment then
+ * keep auto-bkops enabled or else disable it.
+ */
+ ufshcd_urgent_bkops(hba);
+
hba->clk_gating.is_suspended = false;
if (hba->clk_scaling.is_allowed)
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 787323b..f25d468 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -566,6 +566,14 @@ struct ufs_hba {
* CAUTION: Enabling this might reduce overall UFS throughput.
*/
#define UFSHCD_CAP_INTR_AGGR (1 << 4)
+ /*
+ * This capability allows the device auto-bkops to be always enabled
+ * except during suspend (both runtime and suspend).
+ * Enabling this capability means that device will always be allowed
+ * to do background operation when it's active but it might degrade
+ * the performance of ongoing read/write operations.
+ */
+#define UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND (1 << 5)
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
@@ -663,6 +671,11 @@ static inline void *ufshcd_get_variant(struct ufs_hba *hba)
BUG_ON(!hba);
return hba->priv;
}
+static inline bool ufshcd_keep_autobkops_enabled_except_suspend(
+ struct ufs_hba *hba)
+{
+ return hba->caps & UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND;
+}
extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
extern int ufshcd_runtime_resume(struct ufs_hba *hba);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 09/12] scsi: ufs: fix setting init power mode
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
Immediately after successful UFS link startup, UFS link power mode would
be in PWM-G1, 1-lane, SLOW-AUTO mode. But currently we are doing few
of the DME local/peer attributes access before setting the "hba->pwr_info"
to default power mode. If we are doing link startup as part of error
recovery then old power mode might be set to FAST mode and doing DME peer
access (after link startup but before updating "hba->pwr_info" to default
power mode) unintentionally tries to switch from FAST to FAST_AUTO mode (if
UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE quirk is enabled).
Above issue is fixed by setting the default power mode immediately after
successful link startup.
Reviewed-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a647bcf..2d3ca18 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3512,6 +3512,10 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
goto link_startup;
}
+ /* Mark that link is up in PWM-G1, 1-lane, SLOW-AUTO mode */
+ ufshcd_init_pwr_info(hba);
+ ufshcd_print_pwr_info(hba);
+
if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
ret = ufshcd_disable_device_tx_lcc(hba);
if (ret)
@@ -5547,9 +5551,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret)
goto out;
- ufshcd_init_pwr_info(hba);
- ufshcd_print_pwr_info(hba);
-
/* set the default level for urgent bkops */
hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
hba->is_urgent_bkops_lvl_checked = false;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 08/12] scsi: ufs: add capability to keep auto bkops always enabled
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
UFS device requires to perform bkops (back ground operations) periodically
but host can control (via auto-bkops parameter of device) when device can
perform bkops based on its performance requirements. In general, host
would like to enable the device's auto-bkops only when it's not doing any
regular data transfer but sometimes device may not behave properly if host
keeps the auto-bkops disabled. This change adds the capability to let the
device auto-bkops always enabled except suspend.
Reviewed-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 33 ++++++++++++++++++++++-----------
drivers/scsi/ufs/ufshcd.h | 13 +++++++++++++
2 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 12a2250..a647bcf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4134,18 +4134,25 @@ static int ufshcd_disable_auto_bkops(struct ufs_hba *hba)
}
/**
- * ufshcd_force_reset_auto_bkops - force enable of auto bkops
+ * ufshcd_force_reset_auto_bkops - force reset auto bkops state
* @hba: per adapter instance
*
* After a device reset the device may toggle the BKOPS_EN flag
* to default value. The s/w tracking variables should be updated
- * as well. Do this by forcing enable of auto bkops.
+ * as well. This function would change the auto-bkops state based on
+ * UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND.
*/
-static void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
+static void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
{
- hba->auto_bkops_enabled = false;
- hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
- ufshcd_enable_auto_bkops(hba);
+ if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) {
+ hba->auto_bkops_enabled = false;
+ hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
+ ufshcd_enable_auto_bkops(hba);
+ } else {
+ hba->auto_bkops_enabled = true;
+ hba->ee_ctrl_mask &= ~MASK_EE_URGENT_BKOPS;
+ ufshcd_disable_auto_bkops(hba);
+ }
}
static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status)
@@ -6564,11 +6571,15 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
goto set_old_link_state;
}
- /*
- * If BKOPs operations are urgently needed at this moment then
- * keep auto-bkops enabled or else disable it.
- */
- ufshcd_urgent_bkops(hba);
+ if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+ ufshcd_enable_auto_bkops(hba);
+ else
+ /*
+ * If BKOPs operations are urgently needed at this moment then
+ * keep auto-bkops enabled or else disable it.
+ */
+ ufshcd_urgent_bkops(hba);
+
hba->clk_gating.is_suspended = false;
if (hba->clk_scaling.is_allowed)
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 787323b..f25d468 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -566,6 +566,14 @@ struct ufs_hba {
* CAUTION: Enabling this might reduce overall UFS throughput.
*/
#define UFSHCD_CAP_INTR_AGGR (1 << 4)
+ /*
+ * This capability allows the device auto-bkops to be always enabled
+ * except during suspend (both runtime and suspend).
+ * Enabling this capability means that device will always be allowed
+ * to do background operation when it's active but it might degrade
+ * the performance of ongoing read/write operations.
+ */
+#define UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND (1 << 5)
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
@@ -663,6 +671,11 @@ static inline void *ufshcd_get_variant(struct ufs_hba *hba)
BUG_ON(!hba);
return hba->priv;
}
+static inline bool ufshcd_keep_autobkops_enabled_except_suspend(
+ struct ufs_hba *hba)
+{
+ return hba->caps & UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND;
+}
extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
extern int ufshcd_runtime_resume(struct ufs_hba *hba);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 07/12] scsi: ufs: set default UFS power management level
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
UFS device and link can be put in multiple different low power modes hence
UFS driver supports multiple different low power modes.
This change sets the default UFS power management level which should put
the link hibernate state and device in sleep state. This default power
management level gives good power savings with relatively less enter/exit
latencies.
Reviewed-by: Yaniv Gardi <ygardi@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v2 -> v3:
- Simplified patch to just set the default power management level.
Device tree capability to specify broken hardware will be taken up as
separate patch in different patch series than current one.
---
drivers/scsi/ufs/ufshcd.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 30ee04a..12a2250 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -191,6 +191,22 @@ enum {
return ufs_pm_lvl_states[lvl].link_state;
}
+static inline enum ufs_pm_level
+ufs_get_desired_pm_lvl_for_dev_link_state(enum ufs_dev_pwr_mode dev_state,
+ enum uic_link_state link_state)
+{
+ enum ufs_pm_level lvl;
+
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) {
+ if ((ufs_pm_lvl_states[lvl].dev_state == dev_state) &&
+ (ufs_pm_lvl_states[lvl].link_state == link_state))
+ return lvl;
+ }
+
+ /* if no match found, return the level 0 */
+ return UFS_PM_LVL_0;
+}
+
static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
@@ -7264,6 +7280,18 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_clkscaling_init_sysfs(hba);
}
+ /*
+ * Set the default power management level for runtime and system PM.
+ * Default power saving mode is to keep UFS link in Hibern8 state
+ * and UFS device in sleep state.
+ */
+ hba->rpm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+ hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 07/12] scsi: ufs: set default UFS power management level
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
UFS device and link can be put in multiple different low power modes hence
UFS driver supports multiple different low power modes.
This change sets the default UFS power management level which should put
the link hibernate state and device in sleep state. This default power
management level gives good power savings with relatively less enter/exit
latencies.
Reviewed-by: Yaniv Gardi <ygardi@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
Changes from v2 -> v3:
- Simplified patch to just set the default power management level.
Device tree capability to specify broken hardware will be taken up as
separate patch in different patch series than current one.
---
drivers/scsi/ufs/ufshcd.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 30ee04a..12a2250 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -191,6 +191,22 @@ enum {
return ufs_pm_lvl_states[lvl].link_state;
}
+static inline enum ufs_pm_level
+ufs_get_desired_pm_lvl_for_dev_link_state(enum ufs_dev_pwr_mode dev_state,
+ enum uic_link_state link_state)
+{
+ enum ufs_pm_level lvl;
+
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) {
+ if ((ufs_pm_lvl_states[lvl].dev_state == dev_state) &&
+ (ufs_pm_lvl_states[lvl].link_state == link_state))
+ return lvl;
+ }
+
+ /* if no match found, return the level 0 */
+ return UFS_PM_LVL_0;
+}
+
static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
@@ -7264,6 +7280,18 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_clkscaling_init_sysfs(hba);
}
+ /*
+ * Set the default power management level for runtime and system PM.
+ * Default power saving mode is to keep UFS link in Hibern8 state
+ * and UFS device in sleep state.
+ */
+ hba->rpm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+ hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_SLEEP_PWR_MODE,
+ UIC_LINK_HIBERN8_STATE);
+
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 06/12] scsi: ufs: provide sysfs attribute to select the PM level
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
This patch provides the sysfs attribute to choose the power management
level for UFS runtime and system suspend.
Reviewed-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufshcd.h | 2 +
2 files changed, 146 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5932936..30ee04a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -686,6 +686,28 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
}
+static const char *ufschd_uic_link_state_to_string(
+ enum uic_link_state state)
+{
+ switch (state) {
+ case UIC_LINK_OFF_STATE: return "OFF";
+ case UIC_LINK_ACTIVE_STATE: return "ACTIVE";
+ case UIC_LINK_HIBERN8_STATE: return "HIBERN8";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *ufschd_ufs_dev_pwr_mode_to_string(
+ enum ufs_dev_pwr_mode state)
+{
+ switch (state) {
+ case UFS_ACTIVE_PWR_MODE: return "ACTIVE";
+ case UFS_SLEEP_PWR_MODE: return "SLEEP";
+ case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN";
+ default: return "UNKNOWN";
+ }
+}
+
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
{
/* HCI version 1.0 and 1.1 supports UniPro 1.41 */
@@ -6709,6 +6731,127 @@ int ufshcd_runtime_idle(struct ufs_hba *hba)
}
EXPORT_SYMBOL(ufshcd_runtime_idle);
+static inline ssize_t ufshcd_pm_lvl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ bool rpm)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags, value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if ((value < UFS_PM_LVL_0) || (value >= UFS_PM_LVL_MAX))
+ return -EINVAL;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (rpm)
+ hba->rpm_lvl = value;
+ else
+ hba->spm_lvl = value;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return count;
+}
+
+static ssize_t ufshcd_rpm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int curr_len;
+ u8 lvl;
+
+ curr_len = snprintf(buf, PAGE_SIZE,
+ "\nCurrent Runtime PM level [%d] => dev_state [%s] link_state [%s]\n",
+ hba->rpm_lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].link_state));
+
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\nAll available Runtime PM levels info:\n");
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\tRuntime PM level [%d] => dev_state [%s] link_state [%s]\n",
+ lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[lvl].link_state));
+
+ return curr_len;
+}
+
+static ssize_t ufshcd_rpm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufshcd_pm_lvl_store(dev, attr, buf, count, true);
+}
+
+static void ufshcd_add_rpm_lvl_sysfs_nodes(struct ufs_hba *hba)
+{
+ hba->rpm_lvl_attr.show = ufshcd_rpm_lvl_show;
+ hba->rpm_lvl_attr.store = ufshcd_rpm_lvl_store;
+ sysfs_attr_init(&hba->rpm_lvl_attr.attr);
+ hba->rpm_lvl_attr.attr.name = "rpm_lvl";
+ hba->rpm_lvl_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->rpm_lvl_attr))
+ dev_err(hba->dev, "Failed to create sysfs for rpm_lvl\n");
+}
+
+static ssize_t ufshcd_spm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int curr_len;
+ u8 lvl;
+
+ curr_len = snprintf(buf, PAGE_SIZE,
+ "\nCurrent System PM level [%d] => dev_state [%s] link_state [%s]\n",
+ hba->spm_lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].link_state));
+
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\nAll available System PM levels info:\n");
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\tSystem PM level [%d] => dev_state [%s] link_state [%s]\n",
+ lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[lvl].link_state));
+
+ return curr_len;
+}
+
+static ssize_t ufshcd_spm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufshcd_pm_lvl_store(dev, attr, buf, count, false);
+}
+
+static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba)
+{
+ hba->spm_lvl_attr.show = ufshcd_spm_lvl_show;
+ hba->spm_lvl_attr.store = ufshcd_spm_lvl_store;
+ sysfs_attr_init(&hba->spm_lvl_attr.attr);
+ hba->spm_lvl_attr.attr.name = "spm_lvl";
+ hba->spm_lvl_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->spm_lvl_attr))
+ dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n");
+}
+
+static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
+{
+ ufshcd_add_rpm_lvl_sysfs_nodes(hba);
+ ufshcd_add_spm_lvl_sysfs_nodes(hba);
+}
+
/**
* ufshcd_shutdown - shutdown routine
* @hba: per adapter instance
@@ -7133,6 +7276,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_set_ufs_dev_active(hba);
async_schedule(ufshcd_async_scan, hba);
+ ufshcd_add_sysfs_nodes(hba);
return 0;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index bdd2284..787323b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -435,6 +435,8 @@ struct ufs_hba {
enum ufs_pm_level rpm_lvl;
/* Desired UFS power management level during system PM */
enum ufs_pm_level spm_lvl;
+ struct device_attribute rpm_lvl_attr;
+ struct device_attribute spm_lvl_attr;
int pm_op_in_progress;
struct ufshcd_lrb *lrb;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 06/12] scsi: ufs: provide sysfs attribute to select the PM level
From: Subhash Jadavani @ 2016-12-23 2:41 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Subhash Jadavani, open list
This patch provides the sysfs attribute to choose the power management
level for UFS runtime and system suspend.
Reviewed-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufshcd.h | 2 +
2 files changed, 146 insertions(+)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5932936..30ee04a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -686,6 +686,28 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
}
+static const char *ufschd_uic_link_state_to_string(
+ enum uic_link_state state)
+{
+ switch (state) {
+ case UIC_LINK_OFF_STATE: return "OFF";
+ case UIC_LINK_ACTIVE_STATE: return "ACTIVE";
+ case UIC_LINK_HIBERN8_STATE: return "HIBERN8";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *ufschd_ufs_dev_pwr_mode_to_string(
+ enum ufs_dev_pwr_mode state)
+{
+ switch (state) {
+ case UFS_ACTIVE_PWR_MODE: return "ACTIVE";
+ case UFS_SLEEP_PWR_MODE: return "SLEEP";
+ case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN";
+ default: return "UNKNOWN";
+ }
+}
+
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
{
/* HCI version 1.0 and 1.1 supports UniPro 1.41 */
@@ -6709,6 +6731,127 @@ int ufshcd_runtime_idle(struct ufs_hba *hba)
}
EXPORT_SYMBOL(ufshcd_runtime_idle);
+static inline ssize_t ufshcd_pm_lvl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ bool rpm)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags, value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if ((value < UFS_PM_LVL_0) || (value >= UFS_PM_LVL_MAX))
+ return -EINVAL;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (rpm)
+ hba->rpm_lvl = value;
+ else
+ hba->spm_lvl = value;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return count;
+}
+
+static ssize_t ufshcd_rpm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int curr_len;
+ u8 lvl;
+
+ curr_len = snprintf(buf, PAGE_SIZE,
+ "\nCurrent Runtime PM level [%d] => dev_state [%s] link_state [%s]\n",
+ hba->rpm_lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].link_state));
+
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\nAll available Runtime PM levels info:\n");
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\tRuntime PM level [%d] => dev_state [%s] link_state [%s]\n",
+ lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[lvl].link_state));
+
+ return curr_len;
+}
+
+static ssize_t ufshcd_rpm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufshcd_pm_lvl_store(dev, attr, buf, count, true);
+}
+
+static void ufshcd_add_rpm_lvl_sysfs_nodes(struct ufs_hba *hba)
+{
+ hba->rpm_lvl_attr.show = ufshcd_rpm_lvl_show;
+ hba->rpm_lvl_attr.store = ufshcd_rpm_lvl_store;
+ sysfs_attr_init(&hba->rpm_lvl_attr.attr);
+ hba->rpm_lvl_attr.attr.name = "rpm_lvl";
+ hba->rpm_lvl_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->rpm_lvl_attr))
+ dev_err(hba->dev, "Failed to create sysfs for rpm_lvl\n");
+}
+
+static ssize_t ufshcd_spm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int curr_len;
+ u8 lvl;
+
+ curr_len = snprintf(buf, PAGE_SIZE,
+ "\nCurrent System PM level [%d] => dev_state [%s] link_state [%s]\n",
+ hba->spm_lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].link_state));
+
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\nAll available System PM levels info:\n");
+ for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
+ curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
+ "\tSystem PM level [%d] => dev_state [%s] link_state [%s]\n",
+ lvl,
+ ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[lvl].dev_state),
+ ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[lvl].link_state));
+
+ return curr_len;
+}
+
+static ssize_t ufshcd_spm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufshcd_pm_lvl_store(dev, attr, buf, count, false);
+}
+
+static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba)
+{
+ hba->spm_lvl_attr.show = ufshcd_spm_lvl_show;
+ hba->spm_lvl_attr.store = ufshcd_spm_lvl_store;
+ sysfs_attr_init(&hba->spm_lvl_attr.attr);
+ hba->spm_lvl_attr.attr.name = "spm_lvl";
+ hba->spm_lvl_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->spm_lvl_attr))
+ dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n");
+}
+
+static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
+{
+ ufshcd_add_rpm_lvl_sysfs_nodes(hba);
+ ufshcd_add_spm_lvl_sysfs_nodes(hba);
+}
+
/**
* ufshcd_shutdown - shutdown routine
* @hba: per adapter instance
@@ -7133,6 +7276,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_set_ufs_dev_active(hba);
async_schedule(ufshcd_async_scan, hba);
+ ufshcd_add_sysfs_nodes(hba);
return 0;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index bdd2284..787323b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -435,6 +435,8 @@ struct ufs_hba {
enum ufs_pm_level rpm_lvl;
/* Desired UFS power management level during system PM */
enum ufs_pm_level spm_lvl;
+ struct device_attribute rpm_lvl_attr;
+ struct device_attribute spm_lvl_attr;
int pm_op_in_progress;
struct ufshcd_lrb *lrb;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 05/12] scsi: ufs: Add sysfs node to dynamically control clock scaling
From: Subhash Jadavani @ 2016-12-23 2:40 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Sahitya Tummala, Subhash Jadavani, open list
From: Sahitya Tummala <stummala@codeaurora.org>
Provide an option to enable/disable clock scaling during runtime.
Write 1/0 to "clkscale_enable" sysfs node to enable/disable clock
scaling.
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 95 +++++++++++++++++++++++++++++++++++++++--------
drivers/scsi/ufs/ufshcd.h | 4 +-
2 files changed, 82 insertions(+), 17 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2ed82c2..5932936 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -230,6 +230,9 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
+static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
+static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
+static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *desired_pwr_mode);
@@ -713,16 +716,58 @@ static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
{
- if (ufshcd_is_clkscaling_enabled(hba)) {
- devfreq_suspend_device(hba->devfreq);
- hba->clk_scaling.window_start_t = 0;
- }
+ if (!ufshcd_is_clkscaling_supported(hba))
+ return;
+
+ devfreq_suspend_device(hba->devfreq);
+ hba->clk_scaling.window_start_t = 0;
}
static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
{
- if (ufshcd_is_clkscaling_enabled(hba))
- devfreq_resume_device(hba->devfreq);
+ devfreq_resume_device(hba->devfreq);
+}
+
+static ssize_t ufshcd_clkscale_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_scaling.is_allowed);
+}
+
+static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int err;
+
+ if (kstrtou32(buf, 0, &value))
+ return -EINVAL;
+
+ value = !!value;
+ if (value == hba->clk_scaling.is_allowed)
+ goto out;
+
+ pm_runtime_get_sync(hba->dev);
+ ufshcd_hold(hba, false);
+
+ if (value) {
+ ufshcd_resume_clkscaling(hba);
+ } else {
+ ufshcd_suspend_clkscaling(hba);
+ err = ufshcd_scale_clks(hba, true);
+ if (err)
+ dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
+ __func__, err);
+ }
+ hba->clk_scaling.is_allowed = value;
+
+ ufshcd_release(hba);
+ pm_runtime_put_sync(hba->dev);
+out:
+ return count;
}
static void ufshcd_ungate_work(struct work_struct *work)
@@ -758,7 +803,8 @@ static void ufshcd_ungate_work(struct work_struct *work)
hba->clk_gating.is_suspended = false;
}
unblock_reqs:
- ufshcd_resume_clkscaling(hba);
+ if (hba->clk_scaling.is_allowed)
+ ufshcd_resume_clkscaling(hba);
scsi_unblock_requests(hba->host);
}
@@ -1046,7 +1092,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
/* Must be called with host lock acquired */
static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
{
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return;
if (!hba->clk_scaling.is_busy_started) {
@@ -1059,7 +1105,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
{
struct ufs_clk_scaling *scaling = &hba->clk_scaling;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return;
if (!hba->outstanding_reqs && scaling->is_busy_started) {
@@ -5526,12 +5572,15 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
pm_runtime_put_sync(hba->dev);
}
+ /* Resume devfreq after UFS device is detected */
+ if (ufshcd_is_clkscaling_supported(hba)) {
+ ufshcd_resume_clkscaling(hba);
+ hba->clk_scaling.is_allowed = true;
+ }
+
if (!hba->is_init_prefetch)
hba->is_init_prefetch = true;
- /* Resume devfreq after UFS device is detected */
- ufshcd_resume_clkscaling(hba);
-
out:
/*
* If we failed to initialize the device or the device is not
@@ -6484,7 +6533,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
ufshcd_urgent_bkops(hba);
hba->clk_gating.is_suspended = false;
- ufshcd_resume_clkscaling(hba);
+ if (hba->clk_scaling.is_allowed)
+ ufshcd_resume_clkscaling(hba);
/* Schedule clock gating in case of no access to UFS device yet */
ufshcd_release(hba);
@@ -6702,6 +6752,8 @@ void ufshcd_remove(struct ufs_hba *hba)
ufshcd_hba_stop(hba, true);
ufshcd_exit_clk_gating(hba);
+ if (ufshcd_is_clkscaling_supported(hba))
+ device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
ufshcd_hba_exit(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -6835,7 +6887,7 @@ static int ufshcd_devfreq_target(struct device *dev,
bool release_clk_hold = false;
unsigned long irq_flags;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return -EINVAL;
spin_lock_irqsave(hba->host->host_lock, irq_flags);
@@ -6883,7 +6935,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
struct ufs_clk_scaling *scaling = &hba->clk_scaling;
unsigned long flags;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return -EINVAL;
memset(stat, 0, sizeof(*stat));
@@ -6919,6 +6971,16 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
.target = ufshcd_devfreq_target,
.get_dev_status = ufshcd_devfreq_get_dev_status,
};
+static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
+{
+ hba->clk_scaling.enable_attr.show = ufshcd_clkscale_enable_show;
+ hba->clk_scaling.enable_attr.store = ufshcd_clkscale_enable_store;
+ sysfs_attr_init(&hba->clk_scaling.enable_attr.attr);
+ hba->clk_scaling.enable_attr.attr.name = "clkscale_enable";
+ hba->clk_scaling.enable_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->clk_scaling.enable_attr))
+ dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n");
+}
/**
* ufshcd_init - Driver initialization routine
@@ -7045,7 +7107,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
goto out_remove_scsi_host;
}
- if (ufshcd_is_clkscaling_enabled(hba)) {
+ if (ufshcd_is_clkscaling_supported(hba)) {
hba->devfreq = devm_devfreq_add_device(dev, &ufs_devfreq_profile,
"simple_ondemand", NULL);
if (IS_ERR(hba->devfreq)) {
@@ -7056,6 +7118,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
}
/* Suspend devfreq until the UFS device is detected */
ufshcd_suspend_clkscaling(hba);
+ ufshcd_clkscaling_init_sysfs(hba);
}
/* Hold auto suspend until async scan completes */
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 0882ba6..bdd2284 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -342,6 +342,8 @@ struct ufs_clk_scaling {
bool is_busy_started;
unsigned long tot_busy_t;
unsigned long window_start_t;
+ struct device_attribute enable_attr;
+ bool is_allowed;
};
/**
@@ -580,7 +582,7 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
}
-static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
+static inline int ufshcd_is_clkscaling_supported(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_CLK_SCALING;
}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 05/12] scsi: ufs: Add sysfs node to dynamically control clock scaling
From: Subhash Jadavani @ 2016-12-23 2:40 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Sahitya Tummala, Subhash Jadavani, open list
From: Sahitya Tummala <stummala@codeaurora.org>
Provide an option to enable/disable clock scaling during runtime.
Write 1/0 to "clkscale_enable" sysfs node to enable/disable clock
scaling.
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 95 +++++++++++++++++++++++++++++++++++++++--------
drivers/scsi/ufs/ufshcd.h | 4 +-
2 files changed, 82 insertions(+), 17 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2ed82c2..5932936 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -230,6 +230,9 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
+static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
+static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
+static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *desired_pwr_mode);
@@ -713,16 +716,58 @@ static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
{
- if (ufshcd_is_clkscaling_enabled(hba)) {
- devfreq_suspend_device(hba->devfreq);
- hba->clk_scaling.window_start_t = 0;
- }
+ if (!ufshcd_is_clkscaling_supported(hba))
+ return;
+
+ devfreq_suspend_device(hba->devfreq);
+ hba->clk_scaling.window_start_t = 0;
}
static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
{
- if (ufshcd_is_clkscaling_enabled(hba))
- devfreq_resume_device(hba->devfreq);
+ devfreq_resume_device(hba->devfreq);
+}
+
+static ssize_t ufshcd_clkscale_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_scaling.is_allowed);
+}
+
+static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int err;
+
+ if (kstrtou32(buf, 0, &value))
+ return -EINVAL;
+
+ value = !!value;
+ if (value == hba->clk_scaling.is_allowed)
+ goto out;
+
+ pm_runtime_get_sync(hba->dev);
+ ufshcd_hold(hba, false);
+
+ if (value) {
+ ufshcd_resume_clkscaling(hba);
+ } else {
+ ufshcd_suspend_clkscaling(hba);
+ err = ufshcd_scale_clks(hba, true);
+ if (err)
+ dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
+ __func__, err);
+ }
+ hba->clk_scaling.is_allowed = value;
+
+ ufshcd_release(hba);
+ pm_runtime_put_sync(hba->dev);
+out:
+ return count;
}
static void ufshcd_ungate_work(struct work_struct *work)
@@ -758,7 +803,8 @@ static void ufshcd_ungate_work(struct work_struct *work)
hba->clk_gating.is_suspended = false;
}
unblock_reqs:
- ufshcd_resume_clkscaling(hba);
+ if (hba->clk_scaling.is_allowed)
+ ufshcd_resume_clkscaling(hba);
scsi_unblock_requests(hba->host);
}
@@ -1046,7 +1092,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
/* Must be called with host lock acquired */
static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
{
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return;
if (!hba->clk_scaling.is_busy_started) {
@@ -1059,7 +1105,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
{
struct ufs_clk_scaling *scaling = &hba->clk_scaling;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return;
if (!hba->outstanding_reqs && scaling->is_busy_started) {
@@ -5526,12 +5572,15 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
pm_runtime_put_sync(hba->dev);
}
+ /* Resume devfreq after UFS device is detected */
+ if (ufshcd_is_clkscaling_supported(hba)) {
+ ufshcd_resume_clkscaling(hba);
+ hba->clk_scaling.is_allowed = true;
+ }
+
if (!hba->is_init_prefetch)
hba->is_init_prefetch = true;
- /* Resume devfreq after UFS device is detected */
- ufshcd_resume_clkscaling(hba);
-
out:
/*
* If we failed to initialize the device or the device is not
@@ -6484,7 +6533,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
ufshcd_urgent_bkops(hba);
hba->clk_gating.is_suspended = false;
- ufshcd_resume_clkscaling(hba);
+ if (hba->clk_scaling.is_allowed)
+ ufshcd_resume_clkscaling(hba);
/* Schedule clock gating in case of no access to UFS device yet */
ufshcd_release(hba);
@@ -6702,6 +6752,8 @@ void ufshcd_remove(struct ufs_hba *hba)
ufshcd_hba_stop(hba, true);
ufshcd_exit_clk_gating(hba);
+ if (ufshcd_is_clkscaling_supported(hba))
+ device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
ufshcd_hba_exit(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -6835,7 +6887,7 @@ static int ufshcd_devfreq_target(struct device *dev,
bool release_clk_hold = false;
unsigned long irq_flags;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return -EINVAL;
spin_lock_irqsave(hba->host->host_lock, irq_flags);
@@ -6883,7 +6935,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
struct ufs_clk_scaling *scaling = &hba->clk_scaling;
unsigned long flags;
- if (!ufshcd_is_clkscaling_enabled(hba))
+ if (!ufshcd_is_clkscaling_supported(hba))
return -EINVAL;
memset(stat, 0, sizeof(*stat));
@@ -6919,6 +6971,16 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
.target = ufshcd_devfreq_target,
.get_dev_status = ufshcd_devfreq_get_dev_status,
};
+static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
+{
+ hba->clk_scaling.enable_attr.show = ufshcd_clkscale_enable_show;
+ hba->clk_scaling.enable_attr.store = ufshcd_clkscale_enable_store;
+ sysfs_attr_init(&hba->clk_scaling.enable_attr.attr);
+ hba->clk_scaling.enable_attr.attr.name = "clkscale_enable";
+ hba->clk_scaling.enable_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->clk_scaling.enable_attr))
+ dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n");
+}
/**
* ufshcd_init - Driver initialization routine
@@ -7045,7 +7107,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
goto out_remove_scsi_host;
}
- if (ufshcd_is_clkscaling_enabled(hba)) {
+ if (ufshcd_is_clkscaling_supported(hba)) {
hba->devfreq = devm_devfreq_add_device(dev, &ufs_devfreq_profile,
"simple_ondemand", NULL);
if (IS_ERR(hba->devfreq)) {
@@ -7056,6 +7118,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
}
/* Suspend devfreq until the UFS device is detected */
ufshcd_suspend_clkscaling(hba);
+ ufshcd_clkscaling_init_sysfs(hba);
}
/* Hold auto suspend until async scan completes */
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 0882ba6..bdd2284 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -342,6 +342,8 @@ struct ufs_clk_scaling {
bool is_busy_started;
unsigned long tot_busy_t;
unsigned long window_start_t;
+ struct device_attribute enable_attr;
+ bool is_allowed;
};
/**
@@ -580,7 +582,7 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
}
-static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
+static inline int ufshcd_is_clkscaling_supported(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_CLK_SCALING;
}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v3 04/12] scsi: ufs: Add sysfs node to dynamically control clock gating
From: Subhash Jadavani @ 2016-12-23 2:40 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Sahitya Tummala, Subhash Jadavani, open list
From: Sahitya Tummala <stummala@codeaurora.org>
Provide an option to enable/disable clock gating during runtime.
Write 1 or 0 to "clkgate_enable" sysfs node to enable/disable
clock gating.
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/ufs/ufshcd.h | 4 ++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2e7c172..2ed82c2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -970,6 +970,41 @@ static ssize_t ufshcd_clkgate_delay_store(struct device *dev,
return count;
}
+static ssize_t ufshcd_clkgate_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_gating.is_enabled);
+}
+
+static ssize_t ufshcd_clkgate_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags;
+ u32 value;
+
+ if (kstrtou32(buf, 0, &value))
+ return -EINVAL;
+
+ value = !!value;
+ if (value == hba->clk_gating.is_enabled)
+ goto out;
+
+ if (value) {
+ ufshcd_release(hba);
+ } else {
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.active_reqs++;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ }
+
+ hba->clk_gating.is_enabled = value;
+out:
+ return count;
+}
+
static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
if (!ufshcd_is_clkgating_allowed(hba))
@@ -979,13 +1014,23 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba)
INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work);
INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work);
+ hba->clk_gating.is_enabled = true;
+
hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show;
hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store;
sysfs_attr_init(&hba->clk_gating.delay_attr.attr);
hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms";
- hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR;
+ hba->clk_gating.delay_attr.attr.mode = 0644;
if (device_create_file(hba->dev, &hba->clk_gating.delay_attr))
dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n");
+
+ hba->clk_gating.enable_attr.show = ufshcd_clkgate_enable_show;
+ hba->clk_gating.enable_attr.store = ufshcd_clkgate_enable_store;
+ sysfs_attr_init(&hba->clk_gating.enable_attr.attr);
+ hba->clk_gating.enable_attr.attr.name = "clkgate_enable";
+ hba->clk_gating.enable_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->clk_gating.enable_attr))
+ dev_err(hba->dev, "Failed to create sysfs for clkgate_enable\n");
}
static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
@@ -993,6 +1038,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
if (!ufshcd_is_clkgating_allowed(hba))
return;
device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
+ device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
cancel_work_sync(&hba->clk_gating.ungate_work);
cancel_delayed_work_sync(&hba->clk_gating.gate_work);
}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 08cd26e..0882ba6 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -320,6 +320,8 @@ enum clk_gating_state {
* @is_suspended: clk gating is suspended when set to 1 which can be used
* during suspend/resume
* @delay_attr: sysfs attribute to control delay_attr
+ * @enable_attr: sysfs attribute to enable/disable clock gating
+ * @is_enabled: Indicates the current status of clock gating
* @active_reqs: number of requests that are pending and should be waited for
* completion before gating clocks.
*/
@@ -330,6 +332,8 @@ struct ufs_clk_gating {
unsigned long delay_ms;
bool is_suspended;
struct device_attribute delay_attr;
+ struct device_attribute enable_attr;
+ bool is_enabled;
int active_reqs;
};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* RE: A small window for a race condition in mm/rmap.c:page_lock_anon_vma_read
From: Dashi DS1 Cao @ 2016-12-23 2:38 UTC (permalink / raw)
To: Hugh Dickins, Peter Zijlstra
Cc: Michal Hocko, linux-mm@kvack.org, linux-kernel@vger.kernel.org
In-Reply-To: <alpine.LSU.2.11.1612221351340.1744@eggly.anvils>
I'd expected that one or more tasks doing the free were the current task of other cpu cores, but only one of the four dumps has two swapd task that are concurrently at execution on different cpu.
This is the task leading to the crash:
PID: 247 TASK: ffff881fcfad8000 CPU: 14 COMMAND: "kswapd1"
#0 [ffff881fcfad7978] machine_kexec at ffffffff81051e9b
#1 [ffff881fcfad79d8] crash_kexec at ffffffff810f27e2
#2 [ffff881fcfad7aa8] oops_end at ffffffff8163f448
#3 [ffff881fcfad7ad0] die at ffffffff8101859b
#4 [ffff881fcfad7b00] do_general_protection at ffffffff8163ed3e
#5 [ffff881fcfad7b30] general_protection at ffffffff8163e5e8
[exception RIP: down_read_trylock+9]
RIP: ffffffff810aa9f9 RSP: ffff881fcfad7be0 RFLAGS: 00010286
RAX: 0000000000000000 RBX: ffff882b47ddadc0 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 91550b2b32f5a3e8
RBP: ffff881fcfad7be0 R8: ffffea00ecc28860 R9: ffff883fcffeae28
R10: ffffffff81a691a0 R11: 0000000000000001 R12: ffff882b47ddadc1
R13: ffffea00ecc28840 R14: 91550b2b32f5a3e8 R15: ffffea00ecc28840
ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0000
#6 [ffff881fcfad7be8] page_lock_anon_vma_read at ffffffff811a3365
#7 [ffff881fcfad7c18] page_referenced at ffffffff811a35e7
#8 [ffff881fcfad7c90] shrink_active_list at ffffffff8117e8cc
#9 [ffff881fcfad7d48] balance_pgdat at ffffffff81180288
#10 [ffff881fcfad7e20] kswapd at ffffffff81180813
#11 [ffff881fcfad7ec8] kthread at ffffffff810a5b8f
#12 [ffff881fcfad7f50] ret_from_fork at ffffffff81646a98
And this is the one at the same time:
PID: 246 TASK: ffff881fd27af300 CPU: 20 COMMAND: "kswapd0"
#0 [ffff881fffd05e70] crash_nmi_callback at ffffffff81045982
#1 [ffff881fffd05e80] nmi_handle at ffffffff8163f5d9
#2 [ffff881fffd05ec8] do_nmi at ffffffff8163f6f0
#3 [ffff881fffd05ef0] end_repeat_nmi at ffffffff8163ea13
[exception RIP: free_pcppages_bulk+529]
RIP: ffffffff81171ae1 RSP: ffff881fcfad38f0 RFLAGS: 00000087
RAX: 002fffff0000002c RBX: ffffea007606ae40 RCX: 0000000000000000
RDX: ffffea007606ae00 RSI: 00000000000002b9 RDI: 0000000000000000
RBP: ffff881fcfad3978 R8: 0000000000000000 R9: 0000000000000001
R10: ffff88207ffda000 R11: 0000000000000002 R12: ffffea007606ae40
R13: 0000000000000002 R14: ffff88207ffda000 R15: 00000000000002b8
ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018
--- <NMI exception stack> ---
#4 [ffff881fcfad38f0] free_pcppages_bulk at ffffffff81171ae1
#5 [ffff881fcfad3980] free_hot_cold_page at ffffffff81171f08
#6 [ffff881fcfad39b8] free_hot_cold_page_list at ffffffff81171f76
#7 [ffff881fcfad39f0] shrink_page_list at ffffffff8117d71e
#8 [ffff881fcfad3b28] shrink_inactive_list at ffffffff8117e37a
#9 [ffff881fcfad3bf0] shrink_lruvec at ffffffff8117ee45
#10 [ffff881fcfad3cf0] shrink_zone at ffffffff8117f2a6
#11 [ffff881fcfad3d48] balance_pgdat at ffffffff8118054c
#12 [ffff881fcfad3e20] kswapd at ffffffff81180813
#13 [ffff881fcfad3ec8] kthread at ffffffff810a5b8f
#14 [ffff881fcfad3f50] ret_from_fork at ffffffff81646a98
I hope the information would be useful.
Dashi Cao
-----Original Message-----
From: Hugh Dickins [mailto:hughd@google.com]
Sent: Friday, December 23, 2016 6:27 AM
To: Peter Zijlstra <peterz@infradead.org>
Cc: Michal Hocko <mhocko@kernel.org>; Dashi DS1 Cao <caods1@lenovo.com>; linux-mm@kvack.org; linux-kernel@vger.kernel.org; Hugh Dickins <hughd@google.com>
Subject: Re: A small window for a race condition in mm/rmap.c:page_lock_anon_vma_read
On Thu, 22 Dec 2016, Peter Zijlstra wrote:
> On Wed, Dec 21, 2016 at 03:43:43PM +0100, Michal Hocko wrote:
> > anon_vma locking is clever^Wsubtle as hell. CC Peter...
> >
> > On Tue 20-12-16 09:32:27, Dashi DS1 Cao wrote:
> > > I've collected four crash dumps with similar backtrace.
> > >
> > > PID: 247 TASK: ffff881fcfad8000 CPU: 14 COMMAND: "kswapd1"
> > > #0 [ffff881fcfad7978] machine_kexec at ffffffff81051e9b
> > > #1 [ffff881fcfad79d8] crash_kexec at ffffffff810f27e2
> > > #2 [ffff881fcfad7aa8] oops_end at ffffffff8163f448
> > > #3 [ffff881fcfad7ad0] die at ffffffff8101859b
> > > #4 [ffff881fcfad7b00] do_general_protection at ffffffff8163ed3e
> > > #5 [ffff881fcfad7b30] general_protection at ffffffff8163e5e8
> > > [exception RIP: down_read_trylock+9]
> > > RIP: ffffffff810aa9f9 RSP: ffff881fcfad7be0 RFLAGS: 00010286
> > > RAX: 0000000000000000 RBX: ffff882b47ddadc0 RCX: 0000000000000000
> > > RDX: 0000000000000000 RSI: 0000000000000000 RDI:
> > > 91550b2b32f5a3e8
> >
> > rdi is obviously a mess - smells like a string. So either sombody
> > has overwritten root_anon_vma or this is really a use after free...
>
> e8 - ???
> a3 - ???
> f5 - ???
> 32 - 2
> 2b - +
> b -
>
> 55 - U
> 91 - ???
>
> Not a string..
>
> > > RBP: ffff881fcfad7be0 R8: ffffea00ecc28860 R9: ffff883fcffeae28
> > > R10: ffffffff81a691a0 R11: 0000000000000001 R12: ffff882b47ddadc1
> > > R13: ffffea00ecc28840 R14: 91550b2b32f5a3e8 R15: ffffea00ecc28840
> > > ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0000
> > > #6 [ffff881fcfad7be8] page_lock_anon_vma_read at ffffffff811a3365
> > > #7 [ffff881fcfad7c18] page_referenced at ffffffff811a35e7
> > > #8 [ffff881fcfad7c90] shrink_active_list at ffffffff8117e8cc
> > > #9 [ffff881fcfad7d48] balance_pgdat at ffffffff81180288
> > > #10 [ffff881fcfad7e20] kswapd at ffffffff81180813
> > > #11 [ffff881fcfad7ec8] kthread at ffffffff810a5b8f
> > > #12 [ffff881fcfad7f50] ret_from_fork at ffffffff81646a98
> > >
> > > I suspect my customer hits into a small window of a race condition in mm/rmap.c: page_lock_anon_vma_read.
> > > struct anon_vma *page_lock_anon_vma_read(struct page *page) {
> > > struct anon_vma *anon_vma = NULL;
> > > struct anon_vma *root_anon_vma;
> > > unsigned long anon_mapping;
> > >
> > > rcu_read_lock();
> > > anon_mapping = (unsigned long)READ_ONCE(page->mapping);
> > > if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON)
> > > goto out;
> > > if (!page_mapped(page))
> > > goto out;
> > >
> > > anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON);
> > > root_anon_vma = READ_ONCE(anon_vma->root);
> >
> > Could you dump the anon_vma and struct page as well?
> >
> > > if (down_read_trylock(&root_anon_vma->rwsem)) {
> > > /*
> > > * If the page is still mapped, then this anon_vma is still
> > > * its anon_vma, and holding the mutex ensures that it will
> > > * not go away, see anon_vma_free().
> > > */
> > > if (!page_mapped(page)) {
> > > up_read(&root_anon_vma->rwsem);
> > > anon_vma = NULL;
> > > }
> > > goto out;
> > > }
> > > ...
> > > }
> > >
> > > Between the time the two "page_mapped(page)" are checked, the
> > > address (anon_mapping - PAGE_MAPPING_ANON) is unmapped! However it
> > > seems that anon_vma->root could still be read in but the value is
> > > wild. So the kernel crashes in down_read_trylock. But it's weird
> > > that all the "struct page" has its member "_mapcount" still with
> > > value 0, not -1, in the four crashes.
>
> So the point is that while we hold rcu_read_lock() the actual memory
> backing the anon_vmas cannot be freed. It can be reused, but only for
> another anon_vma.
>
> Now, anon_vma_alloc() sets ->root to self, while anon_vma_free()
> leaves
> ->root set to whatever. And any other ->root assignment is to a valid
> anon_vma.
>
> Therefore, the same rules that ensure anon_vma stays valid, should
> also ensure anon_vma->root stays valid.
>
> Now, one thing that might go wobbly is that ->root assignments are not
> done using WRITE_ONCE(), this means a naughty compiler can miscompile
> those stores and introduce store-tearing, if our READ_ONCE() would
> observe such a tear, we'd be up some creek without a paddle.
We would indeed. And this being the season of goodwill, I'm biting my tongue not to say what I think of the prospect of store tearing.
But that zeroed anon_vma implies tearing not the problem here anyway.
>
> Now, its been a long time since I looked at any of this code, and I
> see that Hugh has fixed at least two wobblies in my original code.
Nothing much, and this (admittedly subtle) technique has been working well for years, so I'm sceptical about "a small window for a race condition".
But Dashi's right to point out that the struct page has _mapcount 0 (not -1 for logical 0) in these cases: it looks as if something is freeing (or corrupting) the anon_vma despite it still having pages mapped, or something is misaccounting (or corrupting) the _mapcount.
But I've no idea what, and we have not heard such reports elsewhere.
We don't even know what kernel this is - something special, perhaps?
Hugh
^ permalink raw reply
* [PATCH v3 04/12] scsi: ufs: Add sysfs node to dynamically control clock gating
From: Subhash Jadavani @ 2016-12-23 2:40 UTC (permalink / raw)
To: vinholikatti, jejb, martin.petersen
Cc: linux-scsi, Sahitya Tummala, Subhash Jadavani, open list
From: Sahitya Tummala <stummala@codeaurora.org>
Provide an option to enable/disable clock gating during runtime.
Write 1 or 0 to "clkgate_enable" sysfs node to enable/disable
clock gating.
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
---
drivers/scsi/ufs/ufshcd.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/ufs/ufshcd.h | 4 ++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2e7c172..2ed82c2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -970,6 +970,41 @@ static ssize_t ufshcd_clkgate_delay_store(struct device *dev,
return count;
}
+static ssize_t ufshcd_clkgate_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_gating.is_enabled);
+}
+
+static ssize_t ufshcd_clkgate_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags;
+ u32 value;
+
+ if (kstrtou32(buf, 0, &value))
+ return -EINVAL;
+
+ value = !!value;
+ if (value == hba->clk_gating.is_enabled)
+ goto out;
+
+ if (value) {
+ ufshcd_release(hba);
+ } else {
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.active_reqs++;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ }
+
+ hba->clk_gating.is_enabled = value;
+out:
+ return count;
+}
+
static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
if (!ufshcd_is_clkgating_allowed(hba))
@@ -979,13 +1014,23 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba)
INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work);
INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work);
+ hba->clk_gating.is_enabled = true;
+
hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show;
hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store;
sysfs_attr_init(&hba->clk_gating.delay_attr.attr);
hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms";
- hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR;
+ hba->clk_gating.delay_attr.attr.mode = 0644;
if (device_create_file(hba->dev, &hba->clk_gating.delay_attr))
dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n");
+
+ hba->clk_gating.enable_attr.show = ufshcd_clkgate_enable_show;
+ hba->clk_gating.enable_attr.store = ufshcd_clkgate_enable_store;
+ sysfs_attr_init(&hba->clk_gating.enable_attr.attr);
+ hba->clk_gating.enable_attr.attr.name = "clkgate_enable";
+ hba->clk_gating.enable_attr.attr.mode = 0644;
+ if (device_create_file(hba->dev, &hba->clk_gating.enable_attr))
+ dev_err(hba->dev, "Failed to create sysfs for clkgate_enable\n");
}
static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
@@ -993,6 +1038,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
if (!ufshcd_is_clkgating_allowed(hba))
return;
device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
+ device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
cancel_work_sync(&hba->clk_gating.ungate_work);
cancel_delayed_work_sync(&hba->clk_gating.gate_work);
}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 08cd26e..0882ba6 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -320,6 +320,8 @@ enum clk_gating_state {
* @is_suspended: clk gating is suspended when set to 1 which can be used
* during suspend/resume
* @delay_attr: sysfs attribute to control delay_attr
+ * @enable_attr: sysfs attribute to enable/disable clock gating
+ * @is_enabled: Indicates the current status of clock gating
* @active_reqs: number of requests that are pending and should be waited for
* completion before gating clocks.
*/
@@ -330,6 +332,8 @@ struct ufs_clk_gating {
unsigned long delay_ms;
bool is_suspended;
struct device_attribute delay_attr;
+ struct device_attribute enable_attr;
+ bool is_enabled;
int active_reqs;
};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.