diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2010-06-29 11:26:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-30 02:14:21 -0400 |
commit | de847272149365363a6043a963a6f42fb91566e2 (patch) | |
tree | 51a1c4bbf9baec1d9949816734a869706a9b48cf /drivers/net/3c59x.c | |
parent | 5a86f28f954c3841d3a6df4d07d2ed17088c3711 (diff) |
3c59x: Use fine-grained locks for MII and windowed register access
This avoids scheduling in atomic context and also means that IRQs
will only be deferred for relatively short periods of time.
Previously discussed in:
http://article.gmane.org/gmane.linux.network/155024
Reported-by: Arne Nordmark <nordmark@mech.kth.se>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/3c59x.c')
-rw-r--r-- | drivers/net/3c59x.c | 68 |
1 files changed, 41 insertions, 27 deletions
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index beddef98ad92..069a03f717d3 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c | |||
@@ -644,9 +644,15 @@ struct vortex_private { | |||
644 | u16 deferred; /* Resend these interrupts when we | 644 | u16 deferred; /* Resend these interrupts when we |
645 | * bale from the ISR */ | 645 | * bale from the ISR */ |
646 | u16 io_size; /* Size of PCI region (for release_region) */ | 646 | u16 io_size; /* Size of PCI region (for release_region) */ |
647 | spinlock_t lock; /* Serialise access to device & its vortex_private */ | 647 | |
648 | struct mii_if_info mii; /* MII lib hooks/info */ | 648 | /* Serialises access to hardware other than MII and variables below. |
649 | int window; /* Register window */ | 649 | * The lock hierarchy is rtnl_lock > lock > mii_lock > window_lock. */ |
650 | spinlock_t lock; | ||
651 | |||
652 | spinlock_t mii_lock; /* Serialises access to MII */ | ||
653 | struct mii_if_info mii; /* MII lib hooks/info */ | ||
654 | spinlock_t window_lock; /* Serialises access to windowed regs */ | ||
655 | int window; /* Register window */ | ||
650 | }; | 656 | }; |
651 | 657 | ||
652 | static void window_set(struct vortex_private *vp, int window) | 658 | static void window_set(struct vortex_private *vp, int window) |
@@ -661,15 +667,23 @@ static void window_set(struct vortex_private *vp, int window) | |||
661 | static u ## size \ | 667 | static u ## size \ |
662 | window_read ## size(struct vortex_private *vp, int window, int addr) \ | 668 | window_read ## size(struct vortex_private *vp, int window, int addr) \ |
663 | { \ | 669 | { \ |
670 | unsigned long flags; \ | ||
671 | u ## size ret; \ | ||
672 | spin_lock_irqsave(&vp->window_lock, flags); \ | ||
664 | window_set(vp, window); \ | 673 | window_set(vp, window); \ |
665 | return ioread ## size(vp->ioaddr + addr); \ | 674 | ret = ioread ## size(vp->ioaddr + addr); \ |
675 | spin_unlock_irqrestore(&vp->window_lock, flags); \ | ||
676 | return ret; \ | ||
666 | } \ | 677 | } \ |
667 | static void \ | 678 | static void \ |
668 | window_write ## size(struct vortex_private *vp, u ## size value, \ | 679 | window_write ## size(struct vortex_private *vp, u ## size value, \ |
669 | int window, int addr) \ | 680 | int window, int addr) \ |
670 | { \ | 681 | { \ |
682 | unsigned long flags; \ | ||
683 | spin_lock_irqsave(&vp->window_lock, flags); \ | ||
671 | window_set(vp, window); \ | 684 | window_set(vp, window); \ |
672 | iowrite ## size(value, vp->ioaddr + addr); \ | 685 | iowrite ## size(value, vp->ioaddr + addr); \ |
686 | spin_unlock_irqrestore(&vp->window_lock, flags); \ | ||
673 | } | 687 | } |
674 | DEFINE_WINDOW_IO(8) | 688 | DEFINE_WINDOW_IO(8) |
675 | DEFINE_WINDOW_IO(16) | 689 | DEFINE_WINDOW_IO(16) |
@@ -1181,6 +1195,8 @@ static int __devinit vortex_probe1(struct device *gendev, | |||
1181 | } | 1195 | } |
1182 | 1196 | ||
1183 | spin_lock_init(&vp->lock); | 1197 | spin_lock_init(&vp->lock); |
1198 | spin_lock_init(&vp->mii_lock); | ||
1199 | spin_lock_init(&vp->window_lock); | ||
1184 | vp->gendev = gendev; | 1200 | vp->gendev = gendev; |
1185 | vp->mii.dev = dev; | 1201 | vp->mii.dev = dev; |
1186 | vp->mii.mdio_read = mdio_read; | 1202 | vp->mii.mdio_read = mdio_read; |
@@ -1784,7 +1800,6 @@ vortex_timer(unsigned long data) | |||
1784 | pr_debug("dev->watchdog_timeo=%d\n", dev->watchdog_timeo); | 1800 | pr_debug("dev->watchdog_timeo=%d\n", dev->watchdog_timeo); |
1785 | } | 1801 | } |
1786 | 1802 | ||
1787 | disable_irq_lockdep(dev->irq); | ||
1788 | media_status = window_read16(vp, 4, Wn4_Media); | 1803 | media_status = window_read16(vp, 4, Wn4_Media); |
1789 | switch (dev->if_port) { | 1804 | switch (dev->if_port) { |
1790 | case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx: | 1805 | case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx: |
@@ -1805,10 +1820,7 @@ vortex_timer(unsigned long data) | |||
1805 | case XCVR_MII: case XCVR_NWAY: | 1820 | case XCVR_MII: case XCVR_NWAY: |
1806 | { | 1821 | { |
1807 | ok = 1; | 1822 | ok = 1; |
1808 | /* Interrupts are already disabled */ | ||
1809 | spin_lock(&vp->lock); | ||
1810 | vortex_check_media(dev, 0); | 1823 | vortex_check_media(dev, 0); |
1811 | spin_unlock(&vp->lock); | ||
1812 | } | 1824 | } |
1813 | break; | 1825 | break; |
1814 | default: /* Other media types handled by Tx timeouts. */ | 1826 | default: /* Other media types handled by Tx timeouts. */ |
@@ -1827,6 +1839,8 @@ vortex_timer(unsigned long data) | |||
1827 | if (!ok) { | 1839 | if (!ok) { |
1828 | unsigned int config; | 1840 | unsigned int config; |
1829 | 1841 | ||
1842 | spin_lock_irq(&vp->lock); | ||
1843 | |||
1830 | do { | 1844 | do { |
1831 | dev->if_port = media_tbl[dev->if_port].next; | 1845 | dev->if_port = media_tbl[dev->if_port].next; |
1832 | } while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); | 1846 | } while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); |
@@ -1855,6 +1869,8 @@ vortex_timer(unsigned long data) | |||
1855 | if (vortex_debug > 1) | 1869 | if (vortex_debug > 1) |
1856 | pr_debug("wrote 0x%08x to Wn3_Config\n", config); | 1870 | pr_debug("wrote 0x%08x to Wn3_Config\n", config); |
1857 | /* AKPM: FIXME: Should reset Rx & Tx here. P60 of 3c90xc.pdf */ | 1871 | /* AKPM: FIXME: Should reset Rx & Tx here. P60 of 3c90xc.pdf */ |
1872 | |||
1873 | spin_unlock_irq(&vp->lock); | ||
1858 | } | 1874 | } |
1859 | 1875 | ||
1860 | leave_media_alone: | 1876 | leave_media_alone: |
@@ -1862,7 +1878,6 @@ leave_media_alone: | |||
1862 | pr_debug("%s: Media selection timer finished, %s.\n", | 1878 | pr_debug("%s: Media selection timer finished, %s.\n", |
1863 | dev->name, media_tbl[dev->if_port].name); | 1879 | dev->name, media_tbl[dev->if_port].name); |
1864 | 1880 | ||
1865 | enable_irq_lockdep(dev->irq); | ||
1866 | mod_timer(&vp->timer, RUN_AT(next_tick)); | 1881 | mod_timer(&vp->timer, RUN_AT(next_tick)); |
1867 | if (vp->deferred) | 1882 | if (vp->deferred) |
1868 | iowrite16(FakeIntr, ioaddr + EL3_CMD); | 1883 | iowrite16(FakeIntr, ioaddr + EL3_CMD); |
@@ -2051,9 +2066,11 @@ vortex_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
2051 | int len = (skb->len + 3) & ~3; | 2066 | int len = (skb->len + 3) & ~3; |
2052 | vp->tx_skb_dma = pci_map_single(VORTEX_PCI(vp), skb->data, len, | 2067 | vp->tx_skb_dma = pci_map_single(VORTEX_PCI(vp), skb->data, len, |
2053 | PCI_DMA_TODEVICE); | 2068 | PCI_DMA_TODEVICE); |
2069 | spin_lock_irq(&vp->window_lock); | ||
2054 | window_set(vp, 7); | 2070 | window_set(vp, 7); |
2055 | iowrite32(vp->tx_skb_dma, ioaddr + Wn7_MasterAddr); | 2071 | iowrite32(vp->tx_skb_dma, ioaddr + Wn7_MasterAddr); |
2056 | iowrite16(len, ioaddr + Wn7_MasterLen); | 2072 | iowrite16(len, ioaddr + Wn7_MasterLen); |
2073 | spin_unlock_irq(&vp->window_lock); | ||
2057 | vp->tx_skb = skb; | 2074 | vp->tx_skb = skb; |
2058 | iowrite16(StartDMADown, ioaddr + EL3_CMD); | 2075 | iowrite16(StartDMADown, ioaddr + EL3_CMD); |
2059 | /* netif_wake_queue() will be called at the DMADone interrupt. */ | 2076 | /* netif_wake_queue() will be called at the DMADone interrupt. */ |
@@ -2225,6 +2242,7 @@ vortex_interrupt(int irq, void *dev_id) | |||
2225 | pr_debug("%s: interrupt, status %4.4x, latency %d ticks.\n", | 2242 | pr_debug("%s: interrupt, status %4.4x, latency %d ticks.\n", |
2226 | dev->name, status, ioread8(ioaddr + Timer)); | 2243 | dev->name, status, ioread8(ioaddr + Timer)); |
2227 | 2244 | ||
2245 | spin_lock(&vp->window_lock); | ||
2228 | window_set(vp, 7); | 2246 | window_set(vp, 7); |
2229 | 2247 | ||
2230 | do { | 2248 | do { |
@@ -2285,6 +2303,8 @@ vortex_interrupt(int irq, void *dev_id) | |||
2285 | iowrite16(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); | 2303 | iowrite16(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); |
2286 | } while ((status = ioread16(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); | 2304 | } while ((status = ioread16(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); |
2287 | 2305 | ||
2306 | spin_unlock(&vp->window_lock); | ||
2307 | |||
2288 | if (vortex_debug > 4) | 2308 | if (vortex_debug > 4) |
2289 | pr_debug("%s: exiting interrupt, status %4.4x.\n", | 2309 | pr_debug("%s: exiting interrupt, status %4.4x.\n", |
2290 | dev->name, status); | 2310 | dev->name, status); |
@@ -2806,37 +2826,22 @@ static void update_stats(void __iomem *ioaddr, struct net_device *dev) | |||
2806 | static int vortex_nway_reset(struct net_device *dev) | 2826 | static int vortex_nway_reset(struct net_device *dev) |
2807 | { | 2827 | { |
2808 | struct vortex_private *vp = netdev_priv(dev); | 2828 | struct vortex_private *vp = netdev_priv(dev); |
2809 | unsigned long flags; | ||
2810 | int rc; | ||
2811 | 2829 | ||
2812 | spin_lock_irqsave(&vp->lock, flags); | 2830 | return mii_nway_restart(&vp->mii); |
2813 | rc = mii_nway_restart(&vp->mii); | ||
2814 | spin_unlock_irqrestore(&vp->lock, flags); | ||
2815 | return rc; | ||
2816 | } | 2831 | } |
2817 | 2832 | ||
2818 | static int vortex_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | 2833 | static int vortex_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) |
2819 | { | 2834 | { |
2820 | struct vortex_private *vp = netdev_priv(dev); | 2835 | struct vortex_private *vp = netdev_priv(dev); |
2821 | unsigned long flags; | ||
2822 | int rc; | ||
2823 | 2836 | ||
2824 | spin_lock_irqsave(&vp->lock, flags); | 2837 | return mii_ethtool_gset(&vp->mii, cmd); |
2825 | rc = mii_ethtool_gset(&vp->mii, cmd); | ||
2826 | spin_unlock_irqrestore(&vp->lock, flags); | ||
2827 | return rc; | ||
2828 | } | 2838 | } |
2829 | 2839 | ||
2830 | static int vortex_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) | 2840 | static int vortex_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) |
2831 | { | 2841 | { |
2832 | struct vortex_private *vp = netdev_priv(dev); | 2842 | struct vortex_private *vp = netdev_priv(dev); |
2833 | unsigned long flags; | ||
2834 | int rc; | ||
2835 | 2843 | ||
2836 | spin_lock_irqsave(&vp->lock, flags); | 2844 | return mii_ethtool_sset(&vp->mii, cmd); |
2837 | rc = mii_ethtool_sset(&vp->mii, cmd); | ||
2838 | spin_unlock_irqrestore(&vp->lock, flags); | ||
2839 | return rc; | ||
2840 | } | 2845 | } |
2841 | 2846 | ||
2842 | static u32 vortex_get_msglevel(struct net_device *dev) | 2847 | static u32 vortex_get_msglevel(struct net_device *dev) |
@@ -3059,6 +3064,8 @@ static int mdio_read(struct net_device *dev, int phy_id, int location) | |||
3059 | int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; | 3064 | int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; |
3060 | unsigned int retval = 0; | 3065 | unsigned int retval = 0; |
3061 | 3066 | ||
3067 | spin_lock_bh(&vp->mii_lock); | ||
3068 | |||
3062 | if (mii_preamble_required) | 3069 | if (mii_preamble_required) |
3063 | mdio_sync(vp, 32); | 3070 | mdio_sync(vp, 32); |
3064 | 3071 | ||
@@ -3082,6 +3089,9 @@ static int mdio_read(struct net_device *dev, int phy_id, int location) | |||
3082 | 4, Wn4_PhysicalMgmt); | 3089 | 4, Wn4_PhysicalMgmt); |
3083 | mdio_delay(vp); | 3090 | mdio_delay(vp); |
3084 | } | 3091 | } |
3092 | |||
3093 | spin_unlock_bh(&vp->mii_lock); | ||
3094 | |||
3085 | return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff; | 3095 | return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff; |
3086 | } | 3096 | } |
3087 | 3097 | ||
@@ -3091,6 +3101,8 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val | |||
3091 | int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; | 3101 | int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; |
3092 | int i; | 3102 | int i; |
3093 | 3103 | ||
3104 | spin_lock_bh(&vp->mii_lock); | ||
3105 | |||
3094 | if (mii_preamble_required) | 3106 | if (mii_preamble_required) |
3095 | mdio_sync(vp, 32); | 3107 | mdio_sync(vp, 32); |
3096 | 3108 | ||
@@ -3111,6 +3123,8 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val | |||
3111 | 4, Wn4_PhysicalMgmt); | 3123 | 4, Wn4_PhysicalMgmt); |
3112 | mdio_delay(vp); | 3124 | mdio_delay(vp); |
3113 | } | 3125 | } |
3126 | |||
3127 | spin_unlock_bh(&vp->mii_lock); | ||
3114 | } | 3128 | } |
3115 | 3129 | ||
3116 | /* ACPI: Advanced Configuration and Power Interface. */ | 3130 | /* ACPI: Advanced Configuration and Power Interface. */ |