Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 22 May 2000 16:22:56 -0400 (EDT)
From:      mike ryan <mike+fbsd@medianstrip.net>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/18756: [PATCH] fxp device causes lockups after suspend/resume
Message-ID:  <200005222022.QAA00520@vapre>

next in thread | raw e-mail | index | archive | help

>Number:         18756
>Category:       kern
>Synopsis:       [PATCH] fxp device causes lockups after suspend/resume
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon May 22 13:30:01 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     mike ryan
>Release:        FreeBSD 4.0-STABLE i386
>Organization:
>Environment:

	sony vaio z505hs, recent 4.0-STABLE GENERIC kernel.

>Description:

	with the internal fxp device up and running, the machine will
	lock up after suspend/resume.  a DDB trace indicates an infinite
	loop while waiting for a DMA to complete within the fxp_init
	function in sys/pci/if_fxp.c at line 1518:

   1511         /*
   1512          * Start the config command/DMA.
   1513          */
   1514         fxp_scb_wait(sc);
   1515         CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
   1516         CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
   1517         /* ...and wait for it to complete. */
   1518         while (!(cbp->cb_status & FXP_CB_STATUS_C));

>How-To-Repeat:

	ifconfig fxp0 up, suspend, and resume.

>Fix:

	the patch below includes code translated from netbsd to avoid
	and log a few infinite loops, including the one above, and to
	avoid trying to handle interrupts before the device is resumed,
	and from linux to save and restore some necessary PCI data.  it
	makes suspend/resume work for me, but i haven't tested it
	anywhere but my laptop.

Index: sys/pci/if_fxp.c
===================================================================
RCS file: /usr/local/src/FREEBSD/src/sys/pci/if_fxp.c,v
retrieving revision 1.77.2.1
diff -c -r1.77.2.1 if_fxp.c
*** sys/pci/if_fxp.c	2000/03/29 02:02:38	1.77.2.1
--- sys/pci/if_fxp.c	2000/05/22 20:01:22
***************
*** 222,227 ****
--- 222,228 ----
  static void fxp_mediastatus	__P((struct ifnet *, struct ifmediareq *));
  static void fxp_set_media	__P((struct fxp_softc *, int));
  static __inline void fxp_scb_wait __P((struct fxp_softc *));
+ static __inline void fxp_dma_wait __P((volatile u_int16_t *, struct fxp_softc *sc));
  static FXP_INTR_TYPE fxp_intr	__P((void *));
  static void fxp_start		__P((struct ifnet *));
  static int fxp_ioctl		__P((struct ifnet *,
***************
*** 291,299 ****
  {
  	int i = 10000;
  
! 	while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i);
  }
  
  /*************************************************************
   * Operating system-specific autoconfiguration glue
   *************************************************************/
--- 292,316 ----
  {
  	int i = 10000;
  
! 	while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i)
! 		DELAY(2);
! 	if (i == 0)
! 		printf(FXP_FORMAT ": SCB timeout\n", FXP_ARGS(sc));
  }
  
+ static __inline void
+ fxp_dma_wait(status, sc)
+ 	volatile u_int16_t *status;
+ 	struct fxp_softc *sc;
+ {
+ 	int i = 10000;
+ 
+ 	while (!(*status & FXP_CB_STATUS_C) && --i)
+ 		DELAY(2);
+ 	if (i == 0)
+ 		printf(FXP_FORMAT ": DMA timeout\n", FXP_ARGS(sc));
+ }
+ 
  /*************************************************************
   * Operating system-specific autoconfiguration glue
   *************************************************************/
***************
*** 680,691 ****
--- 697,773 ----
  	return 0;
  }
  
+ /*
+  * Device suspend routine.  Stop the interface and save some PCI
+  * settings in case the BIOS doesn't restore them properly on
+  * resume.
+  */
+ static int
+ fxp_suspend(device_t dev)
+ {
+ 	struct fxp_softc *sc = device_get_softc(dev);
+ 	int i, s = splimp();
+ 
+ 	fxp_stop(sc);
+ 	
+ 	for (i=0; i<5; i++)
+ 		sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i*4, 4);
+ 	sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4);
+ 	sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1);
+ 	sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+ 	sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1);
+ 
+ 	splx(s);
+ 
+ 	return 0;
+ }
+ 
+ /*
+  * Device resume routine.  Restore some PCI settings in case the BIOS
+  * doesn't, re-enable busmastering, and restart the interface if
+  * appropriate.
+  */
+ static int
+ fxp_resume(device_t dev)
+ {
+ 	struct fxp_softc *sc = device_get_softc(dev);
+ 	struct ifnet *ifp = &sc->sc_if;
+ 	u_int16_t pci_command;
+ 	int i, s = splimp();
+ 
+ 	/* better way to do this? */
+ 	for (i=0; i<5; i++)
+ 		pci_write_config(dev, PCIR_MAPS + i*4, sc->saved_maps[i], 4);
+ 	pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4);
+ 	pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1);
+ 	pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1);
+ 	pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1);
+ 
+ 	/* reenable busmastering */
+ 	pci_command = pci_read_config(dev, PCIR_COMMAND, 2);
+ 	pci_command |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ 	pci_write_config(dev, PCIR_COMMAND, pci_command, 2);
+ 
+ 	CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
+ 	DELAY(10);
+ 
+ 	/* reinitialize interface if necessary */
+ 	if (ifp->if_flags & IFF_UP)
+ 		fxp_init(sc);
+ 
+ 	splx(s);
+ 
+ 	return 0;
+ }
+ 
  static device_method_t fxp_methods[] = {
  	/* Device interface */
  	DEVMETHOD(device_probe,		fxp_probe),
  	DEVMETHOD(device_attach,	fxp_attach),
  	DEVMETHOD(device_detach,	fxp_detach),
  	DEVMETHOD(device_shutdown,	fxp_shutdown),
+ 	DEVMETHOD(device_suspend,	fxp_suspend),
+ 	DEVMETHOD(device_resume,	fxp_resume),
  
  	{ 0, 0 }
  };
***************
*** 1096,1101 ****
--- 1178,1188 ----
  	int claimed = 0;
  #endif
  
+ 	/* don't try to service interrupts when the interface isn't running */
+ 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ 		return;
+ 	}
+ 
  	while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
  #if defined(__NetBSD__)
  		claimed = 1;
***************
*** 1358,1363 ****
--- 1445,1453 ----
  	struct fxp_cb_tx *txp;
  	int i;
  
+ 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ 	ifp->if_timer = 0;
+ 
  	/*
  	 * Cancel stats updater.
  	 */
***************
*** 1400,1408 ****
  			panic("fxp_stop: no buffers!");
  		}
  	}
- 
- 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
- 	ifp->if_timer = 0;
  }
  
  /*
--- 1490,1495 ----
***************
*** 1515,1521 ****
  	CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
  	CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
  	/* ...and wait for it to complete. */
! 	while (!(cbp->cb_status & FXP_CB_STATUS_C));
  
  	/*
  	 * Now initialize the station address. Temporarily use the TxCB
--- 1602,1608 ----
  	CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
  	CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
  	/* ...and wait for it to complete. */
! 	fxp_dma_wait(&cbp->cb_status, sc);
  
  	/*
  	 * Now initialize the station address. Temporarily use the TxCB
***************
*** 1538,1544 ****
  	fxp_scb_wait(sc);
  	CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
  	/* ...and wait for it to complete. */
! 	while (!(cb_ias->cb_status & FXP_CB_STATUS_C));
  
  	/*
  	 * Initialize transmit control block (TxCB) list.
--- 1625,1631 ----
  	fxp_scb_wait(sc);
  	CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
  	/* ...and wait for it to complete. */
! 	fxp_dma_wait(&cb_ias->cb_status, sc);
  
  	/*
  	 * Initialize transmit control block (TxCB) list.
***************
*** 1977,1982 ****
--- 2064,2070 ----
  	struct ifnet *ifp = &sc->sc_if;
  	struct ifmultiaddr *ifma;
  	int nmcasts;
+ 	int count;
  
  	/*
  	 * If there are queued commands, we must wait until they are all
***************
*** 2058,2065 ****
  	 * Wait until command unit is not active. This should never
  	 * be the case when nothing is queued, but make sure anyway.
  	 */
  	while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) ==
! 	    FXP_SCB_CUS_ACTIVE) ;
  
  	/*
  	 * Start the multicast setup command.
--- 2146,2159 ----
  	 * Wait until command unit is not active. This should never
  	 * be the case when nothing is queued, but make sure anyway.
  	 */
+ 	count = 100;
  	while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) ==
! 	    FXP_SCB_CUS_ACTIVE && --count)
! 		DELAY(10);
! 	if (count == 0) {
! 		printf(FXP_FORMAT ": command queue timeout\n", FXP_ARGS(sc));
! 		return;
! 	}
  
  	/*
  	 * Start the multicast setup command.
Index: sys/pci/if_fxpvar.h
===================================================================
RCS file: /usr/local/src/FREEBSD/src/sys/pci/if_fxpvar.h,v
retrieving revision 1.9.2.1
diff -c -r1.9.2.1 if_fxpvar.h
*** sys/pci/if_fxpvar.h	2000/03/29 02:02:39	1.9.2.1
--- sys/pci/if_fxpvar.h	2000/05/22 19:58:05
***************
*** 68,73 ****
--- 68,78 ----
  	int phy_primary_device;		/* device type of primary PHY */
  	int phy_10Mbps_only;		/* PHY is 10Mbps-only device */
  	int eeprom_size;		/* size of serial EEPROM */
+ 	u_int32_t saved_maps[5];	/* pci data */
+ 	u_int32_t saved_biosaddr;
+ 	u_int8_t saved_intline;
+ 	u_int8_t saved_cachelnsz;
+ 	u_int8_t saved_lattimer;
  };
  
  /* Macros to ease CSR access. */

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200005222022.QAA00520>