diff mbox series

[v2] net: fec: Use unlocked timecounter reads for saving state

Message ID 20220830111516.82875-1-csokas.bence@prolan.hu (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [v2] net: fec: Use unlocked timecounter reads for saving state | expand

Checks

Context Check Description
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix warning Target tree name not specified in the subject
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 4 this patch: 10
netdev/cc_maintainers warning 2 maintainers not CCed: edumazet@google.com pabeni@redhat.com
netdev/build_clang fail Errors and warnings before: 7 this patch: 11
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes fail Problems with Fixes tag: 1
netdev/build_allmodconfig_warn fail Errors and warnings before: 4 this patch: 10
netdev/checkpatch warning WARNING: line length of 88 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Csókás Bence Aug. 30, 2022, 11:15 a.m. UTC
`fec_ptp_save_state()` may be called from an atomic context,
which makes `fec_ptp_gettime()` unable to acquire a mutex.
Using the lower-level timecounter ops remedies the problem.

Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
Fixes: f79959220fa5

Signed-off-by: Csókás Bence <csokas.bence@prolan.hu>
---
 drivers/net/ethernet/freescale/fec.h     |  4 ++--
 drivers/net/ethernet/freescale/fec_ptp.c | 19 +++++++++++++------
 2 files changed, 15 insertions(+), 8 deletions(-)

Comments

Andrew Lunn Aug. 30, 2022, 12:29 p.m. UTC | #1
> -	fec_ptp_gettime(&fep->ptp_caps, &fep->ptp_saved_state.ts_phc);
> +	if (preempt_count_equals(0)) {

~/linux/drivers$ grep -r preempt_count_equals *
~/linux/drivers$ 

No other driver plays games like this.

Why not unconditionally take the lock?

    Andrew
Csókás Bence Aug. 30, 2022, 3:05 p.m. UTC | #2
On 2022. 08. 30. 14:29, Andrew Lunn wrote:
>> -	fec_ptp_gettime(&fep->ptp_caps, &fep->ptp_saved_state.ts_phc);
>> +	if (preempt_count_equals(0)) {
> 
> ~/linux/drivers$ grep -r preempt_count_equals *
> ~/linux/drivers$
> 
> No other driver plays games like this.
> 
> Why not unconditionally take the lock?

Because then we would be back at the original problem (see Marc's message):

| [   14.001542] BUG: sleeping function called from invalid context at 
kernel/locking/mutex.c:283 

| [   14.010604] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 
13, name: kworker/0:1 

| [   14.018737] preempt_count: 201, expected: 0

We cannot take a mutex in atomic context. However, we also don't *need 
to* take a mutex in atomic context.

> 
>      Andrew

If someone has a better solution, I'm open to suggestions. But to me, it 
seems that there are only 3 options:

1. Unconditionally taking the mutex was what I originally did, but that 
caused issues in Marc's setup.
2. Not taking the mutex at all is what I proposed in v1 of this patch. 
But as Richard pointed out, `timecounter_read()` actually does a 
Read-Modify-Write on the `FEC_ATIME_CTRL` register, that *could* get 
interrupted if not guarded by the mutex (or atomic context).
3. The final option, check if we are in an atomic or otherwise 
non-interruptible context, and if not, take a mutex. Otherwise, proceed 
normally. Which is this version of the patch.

Bence
Andrew Lunn Aug. 30, 2022, 3:21 p.m. UTC | #3
On Tue, Aug 30, 2022 at 05:05:24PM +0200, Csókás Bence wrote:
> 
> 
> On 2022. 08. 30. 14:29, Andrew Lunn wrote:
> > > -	fec_ptp_gettime(&fep->ptp_caps, &fep->ptp_saved_state.ts_phc);
> > > +	if (preempt_count_equals(0)) {
> > 
> > ~/linux/drivers$ grep -r preempt_count_equals *
> > ~/linux/drivers$
> > 
> > No other driver plays games like this.
> > 
> > Why not unconditionally take the lock?
> 
> Because then we would be back at the original problem (see Marc's message):
> 
> | [   14.001542] BUG: sleeping function called from invalid context at
> kernel/locking/mutex.c:283
> 
> | [   14.010604] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 13,
> name: kworker/0:1
> 
> | [   14.018737] preempt_count: 201, expected: 0
> 
> We cannot take a mutex in atomic context. However, we also don't *need to*
> take a mutex in atomic context.

You are not taking a mutex, you are taking a spinlock. You can do that
in atomic context. Can you protect everything which needs protecting
with a spinlock? And avoid sleeping...

     Andrew
Richard Cochran Aug. 31, 2022, 3:41 a.m. UTC | #4
On Tue, Aug 30, 2022 at 05:05:24PM +0200, Csókás Bence wrote:

> 3. The final option, check if we are in an atomic or otherwise
> non-interruptible context, and if not, take a mutex. Otherwise, proceed
> normally. Which is this version of the patch.

Just replace the mutex with a spinlock.

Thanks,
Richard
kernel test robot Aug. 31, 2022, 10:20 a.m. UTC | #5
Hi "Csókás,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net/master]
[also build test ERROR on net-next/master linus/master v6.0-rc3 next-20220831]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cs-k-s-Bence/net-fec-Use-unlocked-timecounter-reads-for-saving-state/20220830-191644
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git b05972f01e7d30419987a1f221b5593668fd6448
config: arm-mxs_defconfig
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 1c66bacd6cde1f37d6ac96c45b389666a1334ec0)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm cross compiling tool for clang build
        # apt-get install binutils-arm-linux-gnueabi
        # https://github.com/intel-lab-lkp/linux/commit/1b907e75a5f827529bfe24d68038c69fac840901
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Cs-k-s-Bence/net-fec-Use-unlocked-timecounter-reads-for-saving-state/20220830-191644
        git checkout 1b907e75a5f827529bfe24d68038c69fac840901
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> drivers/net/ethernet/freescale/fec_ptp.c:648:6: error: call to undeclared function 'preempt_count_equals'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
           if (preempt_count_equals(0)) {
               ^
>> drivers/net/ethernet/freescale/fec_ptp.c:649:39: error: use of undeclared identifier 'flags'
                   spin_lock_irqsave(&fep->tmreg_lock, flags);
                                                       ^
>> drivers/net/ethernet/freescale/fec_ptp.c:649:39: error: use of undeclared identifier 'flags'
   drivers/net/ethernet/freescale/fec_ptp.c:651:44: error: use of undeclared identifier 'flags'
                   spin_unlock_irqrestore(&fep->tmreg_lock, flags);
                                                            ^
   4 errors generated.


vim +/preempt_count_equals +648 drivers/net/ethernet/freescale/fec_ptp.c

   643	
   644	void fec_ptp_save_state(struct fec_enet_private *fep)
   645	{
   646		u32 atime_inc_corr;
   647	
 > 648		if (preempt_count_equals(0)) {
 > 649			spin_lock_irqsave(&fep->tmreg_lock, flags);
   650			fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
   651			spin_unlock_irqrestore(&fep->tmreg_lock, flags);
   652		} else {
   653			fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
   654		}
   655		fep->ptp_saved_state.ns_sys = ktime_get_ns();
   656	
   657		fep->ptp_saved_state.at_corr = readl(fep->hwp + FEC_ATIME_CORR);
   658		atime_inc_corr = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_CORR_MASK;
   659		fep->ptp_saved_state.at_inc_corr = (u8)(atime_inc_corr >> FEC_T_INC_CORR_OFFSET);
   660	}
   661
Csókás Bence Aug. 31, 2022, 1:09 p.m. UTC | #6
On 2022. 08. 30. 17:21, Andrew Lunn wrote:
> You are not taking a mutex, you are taking a spinlock. You can do that
> in atomic context. Can you protect everything which needs protecting
> with a spinlock? And avoid sleeping...

You are correct, the issue Marc experienced turns out to be caused by a 
`mutex_lock()` introduced in 6a4d7234ae9a3bb31181f348ade9bbdb55aeb5c5...

On 2022. 08. 31. 5:41, Richard Cochran wrote:
 > Just replace the mutex with a spinlock.

Will do. Disregard this patch and see "[PATCH] Use a spinlock to guard 
`fep->ptp_clk_on`" instead

Bence
kernel test robot Sept. 1, 2022, 9:52 p.m. UTC | #7
Hi "Csókás,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net/master]
[also build test ERROR on net-next/master linus/master v6.0-rc3 next-20220901]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cs-k-s-Bence/net-fec-Use-unlocked-timecounter-reads-for-saving-state/20220830-191644
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git b05972f01e7d30419987a1f221b5593668fd6448
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20220902/202209020550.JEmUsnnl-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/1b907e75a5f827529bfe24d68038c69fac840901
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Cs-k-s-Bence/net-fec-Use-unlocked-timecounter-reads-for-saving-state/20220830-191644
        git checkout 1b907e75a5f827529bfe24d68038c69fac840901
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/net/ethernet/freescale/fec_ptp.c: In function 'fec_ptp_save_state':
>> drivers/net/ethernet/freescale/fec_ptp.c:648:13: error: implicit declaration of function 'preempt_count_equals'; did you mean 'preempt_count_sub'? [-Werror=implicit-function-declaration]
     648 |         if (preempt_count_equals(0)) {
         |             ^~~~~~~~~~~~~~~~~~~~
         |             preempt_count_sub
   In file included from include/linux/bitops.h:7,
                    from include/linux/thread_info.h:27,
                    from arch/s390/include/asm/preempt.h:6,
                    from include/linux/preempt.h:78,
                    from arch/s390/include/asm/timex.h:13,
                    from include/linux/timex.h:67,
                    from include/linux/time32.h:13,
                    from include/linux/time.h:60,
                    from include/linux/stat.h:19,
                    from include/linux/module.h:13,
                    from drivers/net/ethernet/freescale/fec_ptp.c:10:
>> drivers/net/ethernet/freescale/fec_ptp.c:649:53: error: 'flags' undeclared (first use in this function)
     649 |                 spin_lock_irqsave(&fep->tmreg_lock, flags);
         |                                                     ^~~~~
   include/linux/typecheck.h:11:16: note: in definition of macro 'typecheck'
      11 |         typeof(x) __dummy2; \
         |                ^
   include/linux/spinlock.h:379:9: note: in expansion of macro 'raw_spin_lock_irqsave'
     379 |         raw_spin_lock_irqsave(spinlock_check(lock), flags);     \
         |         ^~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/freescale/fec_ptp.c:649:17: note: in expansion of macro 'spin_lock_irqsave'
     649 |                 spin_lock_irqsave(&fep->tmreg_lock, flags);
         |                 ^~~~~~~~~~~~~~~~~
   drivers/net/ethernet/freescale/fec_ptp.c:649:53: note: each undeclared identifier is reported only once for each function it appears in
     649 |                 spin_lock_irqsave(&fep->tmreg_lock, flags);
         |                                                     ^~~~~
   include/linux/typecheck.h:11:16: note: in definition of macro 'typecheck'
      11 |         typeof(x) __dummy2; \
         |                ^
   include/linux/spinlock.h:379:9: note: in expansion of macro 'raw_spin_lock_irqsave'
     379 |         raw_spin_lock_irqsave(spinlock_check(lock), flags);     \
         |         ^~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/freescale/fec_ptp.c:649:17: note: in expansion of macro 'spin_lock_irqsave'
     649 |                 spin_lock_irqsave(&fep->tmreg_lock, flags);
         |                 ^~~~~~~~~~~~~~~~~
   include/linux/typecheck.h:12:25: warning: comparison of distinct pointer types lacks a cast
      12 |         (void)(&__dummy == &__dummy2); \
         |                         ^~
   include/linux/spinlock.h:241:17: note: in expansion of macro 'typecheck'
     241 |                 typecheck(unsigned long, flags);        \
         |                 ^~~~~~~~~
   include/linux/spinlock.h:379:9: note: in expansion of macro 'raw_spin_lock_irqsave'
     379 |         raw_spin_lock_irqsave(spinlock_check(lock), flags);     \
         |         ^~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/freescale/fec_ptp.c:649:17: note: in expansion of macro 'spin_lock_irqsave'
     649 |                 spin_lock_irqsave(&fep->tmreg_lock, flags);
         |                 ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +648 drivers/net/ethernet/freescale/fec_ptp.c

   643	
   644	void fec_ptp_save_state(struct fec_enet_private *fep)
   645	{
   646		u32 atime_inc_corr;
   647	
 > 648		if (preempt_count_equals(0)) {
 > 649			spin_lock_irqsave(&fep->tmreg_lock, flags);
   650			fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
   651			spin_unlock_irqrestore(&fep->tmreg_lock, flags);
   652		} else {
   653			fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
   654		}
   655		fep->ptp_saved_state.ns_sys = ktime_get_ns();
   656	
   657		fep->ptp_saved_state.at_corr = readl(fep->hwp + FEC_ATIME_CORR);
   658		atime_inc_corr = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_CORR_MASK;
   659		fep->ptp_saved_state.at_inc_corr = (u8)(atime_inc_corr >> FEC_T_INC_CORR_OFFSET);
   660	}
   661
diff mbox series

Patch

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index b656cda75c92..7bc7ab4b5d3a 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -597,7 +597,7 @@  struct fec_enet_private {
 	unsigned int next_counter;
 
 	struct {
-		struct timespec64 ts_phc;
+		u64 ns_phc;
 		u64 ns_sys;
 		u32 at_corr;
 		u8 at_inc_corr;
@@ -613,7 +613,7 @@  int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr);
 int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr);
 
 void fec_ptp_save_state(struct fec_enet_private *fep);
-int fec_ptp_restore_state(struct fec_enet_private *fep);
+void fec_ptp_restore_state(struct fec_enet_private *fep);
 
 /****************************************************************************/
 #endif /* FEC_H */
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 78fb8818d168..2ad93668844a 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -636,7 +636,13 @@  void fec_ptp_save_state(struct fec_enet_private *fep)
 {
 	u32 atime_inc_corr;
 
-	fec_ptp_gettime(&fep->ptp_caps, &fep->ptp_saved_state.ts_phc);
+	if (preempt_count_equals(0)) {
+		spin_lock_irqsave(&fep->tmreg_lock, flags);
+		fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
+		spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+	} else {
+		fep->ptp_saved_state.ns_phc = timecounter_read(&fep->tc);
+	}
 	fep->ptp_saved_state.ns_sys = ktime_get_ns();
 
 	fep->ptp_saved_state.at_corr = readl(fep->hwp + FEC_ATIME_CORR);
@@ -644,16 +650,17 @@  void fec_ptp_save_state(struct fec_enet_private *fep)
 	fep->ptp_saved_state.at_inc_corr = (u8)(atime_inc_corr >> FEC_T_INC_CORR_OFFSET);
 }
 
-int fec_ptp_restore_state(struct fec_enet_private *fep)
+void fec_ptp_restore_state(struct fec_enet_private *fep)
 {
 	u32 atime_inc = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK;
-	u64 ns_sys;
+	u64 ns;
 
 	writel(fep->ptp_saved_state.at_corr, fep->hwp + FEC_ATIME_CORR);
 	atime_inc |= ((u32)fep->ptp_saved_state.at_inc_corr) << FEC_T_INC_CORR_OFFSET;
 	writel(atime_inc, fep->hwp + FEC_ATIME_INC);
 
-	ns_sys = ktime_get_ns() - fep->ptp_saved_state.ns_sys;
-	timespec64_add_ns(&fep->ptp_saved_state.ts_phc, ns_sys);
-	return fec_ptp_settime(&fep->ptp_caps, &fep->ptp_saved_state.ts_phc);
+	ns = ktime_get_ns() - fep->ptp_saved_state.ns_sys + fep->ptp_saved_state.ns_phc;
+
+	writel(ns & fep->cc.mask, fep->hwp + FEC_ATIME);
+	timecounter_init(&fep->tc, &fep->cc, ns);
 }