From 1a4493584078be87fa9dcbdd8def3cb53d41720c Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 31 Aug 2005 05:48:59 -0400 Subject: [netdrvr tulip] new PCI ID Noted in BZ# 2960. --- drivers/net/tulip/tulip_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 05da5bea564c..6266a9a7e6e3 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -238,6 +238,7 @@ static struct pci_device_id tulip_pci_tbl[] = { { 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */ { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */ + { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); -- cgit v1.2.3 From afc7097f45bdfddc2a0d375ef4a2c38b6e09c339 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 31 Aug 2005 06:11:16 -0400 Subject: [netdrvr de2104x] store PCI bus addresses in unsigned long BZ# 4475. --- drivers/net/tulip/de2104x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index fc353e348f9a..a22d00198e4d 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -1934,7 +1934,7 @@ static int __init de_init_one (struct pci_dev *pdev, struct de_private *de; int rc; void __iomem *regs; - long pciaddr; + unsigned long pciaddr; static int board_idx = -1; board_idx++; -- cgit v1.2.3 From 61a3c6966158dfd9b1279c10ea8eeb3bc7acdef4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:28:57 +1000 Subject: [PATCH] iseries_veth: Cleanup error and debug messages Currently the iseries_veth driver prints the file name and line number in its error messages. This isn't very useful for most users, so just print "iseries_veth: message" instead. - convert uses of veth_printk() to veth_debug()/veth_error()/veth_info() - make terminology consistent, ie. always refer to LPAR not lpar - be consistent about printing return codes as %d not %x - make format strings fit in 80 columns Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 98 ++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 183ba97785b0..d5b08dc8a9f8 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -79,6 +79,8 @@ #include #include +#undef DEBUG + #include "iseries_veth.h" MODULE_AUTHOR("Kyle Lucke "); @@ -176,11 +178,18 @@ static void veth_timed_ack(unsigned long connectionPtr); * Utility functions */ -#define veth_printk(prio, fmt, args...) \ - printk(prio "%s: " fmt, __FILE__, ## args) +#define veth_info(fmt, args...) \ + printk(KERN_INFO "iseries_veth: " fmt, ## args) #define veth_error(fmt, args...) \ - printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) + printk(KERN_ERR "iseries_veth: Error: " fmt, ## args) + +#ifdef DEBUG +#define veth_debug(fmt, args...) \ + printk(KERN_DEBUG "iseries_veth: " fmt, ## args) +#else +#define veth_debug(fmt, args...) do {} while (0) +#endif static inline void veth_stack_push(struct veth_lpar_connection *cnx, struct veth_msg *msg) @@ -278,7 +287,7 @@ static void veth_take_cap(struct veth_lpar_connection *cnx, HvLpEvent_Type_VirtualLan); if (cnx->state & VETH_STATE_GOTCAPS) { - veth_error("Received a second capabilities from lpar %d\n", + veth_error("Received a second capabilities from LPAR %d.\n", cnx->remote_lp); event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable; HvCallEvent_ackLpEvent((struct HvLpEvent *) event); @@ -297,7 +306,7 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx, spin_lock_irqsave(&cnx->lock, flags); if (cnx->state & VETH_STATE_GOTCAPACK) { - veth_error("Received a second capabilities ack from lpar %d\n", + veth_error("Received a second capabilities ack from LPAR %d.\n", cnx->remote_lp); } else { memcpy(&cnx->cap_ack_event, event, @@ -314,8 +323,7 @@ static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, unsigned long flags; spin_lock_irqsave(&cnx->lock, flags); - veth_printk(KERN_DEBUG, "Monitor ack returned for lpar %d\n", - cnx->remote_lp); + veth_debug("cnx %d: lost connection.\n", cnx->remote_lp); cnx->state |= VETH_STATE_RESET; veth_kick_statemachine(cnx); spin_unlock_irqrestore(&cnx->lock, flags); @@ -336,8 +344,8 @@ static void veth_handle_ack(struct VethLpEvent *event) veth_take_monitor_ack(cnx, event); break; default: - veth_error("Unknown ack type %d from lpar %d\n", - event->base_event.xSubtype, rlp); + veth_error("Unknown ack type %d from LPAR %d.\n", + event->base_event.xSubtype, rlp); }; } @@ -373,8 +381,8 @@ static void veth_handle_int(struct VethLpEvent *event) veth_receive(cnx, event); break; default: - veth_error("Unknown interrupt type %d from lpar %d\n", - event->base_event.xSubtype, rlp); + veth_error("Unknown interrupt type %d from LPAR %d.\n", + event->base_event.xSubtype, rlp); }; } @@ -400,8 +408,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx) || (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) || (remote_caps->ack_threshold == 0) || (cnx->ack_timeout == 0) ) { - veth_error("Received incompatible capabilities from lpar %d\n", - cnx->remote_lp); + veth_error("Received incompatible capabilities from LPAR %d.\n", + cnx->remote_lp); return HvLpEvent_Rc_InvalidSubtypeData; } @@ -418,8 +426,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx) cnx->num_ack_events += num; if (cnx->num_ack_events < num_acks_needed) { - veth_error("Couldn't allocate enough ack events for lpar %d\n", - cnx->remote_lp); + veth_error("Couldn't allocate enough ack events " + "for LPAR %d.\n", cnx->remote_lp); return HvLpEvent_Rc_BufferNotAvailable; } @@ -498,9 +506,8 @@ static void veth_statemachine(void *p) } else { if ( (rc != HvLpEvent_Rc_PartitionDead) && (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending monitor to " - "lpar %d, rc=%x\n", - rlp, (int) rc); + veth_error("Error sending monitor to LPAR %d, " + "rc = %d\n", rlp, rc); /* Oh well, hope we get a cap from the other * end and do better when that kicks us */ @@ -523,9 +530,9 @@ static void veth_statemachine(void *p) } else { if ( (rc != HvLpEvent_Rc_PartitionDead) && (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending caps to " - "lpar %d, rc=%x\n", - rlp, (int) rc); + veth_error("Error sending caps to LPAR %d, " + "rc = %d\n", rlp, rc); + /* Oh well, hope we get a cap from the other * end and do better when that kicks us */ goto out; @@ -565,10 +572,8 @@ static void veth_statemachine(void *p) add_timer(&cnx->ack_timer); cnx->state |= VETH_STATE_READY; } else { - veth_printk(KERN_ERR, "Caps rejected (rc=%d) by " - "lpar %d\n", - cnx->cap_ack_event.base_event.xRc, - rlp); + veth_error("Caps rejected by LPAR %d, rc = %d\n", + rlp, cnx->cap_ack_event.base_event.xRc); goto cant_cope; } } @@ -581,8 +586,8 @@ static void veth_statemachine(void *p) /* FIXME: we get here if something happens we really can't * cope with. The link will never work once we get here, and * all we can do is not lock the rest of the system up */ - veth_error("Badness on connection to lpar %d (state=%04lx) " - " - shutting down\n", rlp, cnx->state); + veth_error("Unrecoverable error on connection to LPAR %d, shutting down" + " (state = 0x%04lx)\n", rlp, cnx->state); cnx->state |= VETH_STATE_SHUTDOWN; spin_unlock_irq(&cnx->lock); } @@ -614,7 +619,7 @@ static int veth_init_connection(u8 rlp) msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL); if (! msgs) { - veth_error("Can't allocate buffers for lpar %d\n", rlp); + veth_error("Can't allocate buffers for LPAR %d.\n", rlp); return -ENOMEM; } @@ -630,8 +635,7 @@ static int veth_init_connection(u8 rlp) cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS); if (cnx->num_events < (2 + VETH_NUMBUFFERS)) { - veth_error("Can't allocate events for lpar %d, only got %d\n", - rlp, cnx->num_events); + veth_error("Can't allocate enough events for LPAR %d.\n", rlp); return -ENOMEM; } @@ -889,15 +893,13 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) rc = register_netdev(dev); if (rc != 0) { - veth_printk(KERN_ERR, - "Failed to register ethernet device for vlan %d\n", - vlan); + veth_error("Failed registering net device for vlan%d.\n", vlan); free_netdev(dev); return NULL; } - veth_printk(KERN_DEBUG, "%s attached to iSeries vlan %d (lpar_map=0x%04x)\n", - dev->name, vlan, port->lpar_map); + veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n", + dev->name, vlan, port->lpar_map); return dev; } @@ -1030,7 +1032,7 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb); } else { if (port->pending_skb) { - veth_error("%s: Tx while skb was pending!\n", + veth_error("%s: TX while skb was pending!\n", dev->name); dev_kfree_skb(skb); spin_unlock_irqrestore(&port->pending_gate, flags); @@ -1066,10 +1068,10 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, memset(&msg->data, 0, sizeof(msg->data)); veth_stack_push(cnx, msg); - } else - if (cnx->state & VETH_STATE_OPEN) - veth_error("Bogus frames ack from lpar %d (#%d)\n", - cnx->remote_lp, msg->token); + } else if (cnx->state & VETH_STATE_OPEN) { + veth_error("Non-pending frame (# %d) acked by LPAR %d.\n", + cnx->remote_lp, msg->token); + } } static void veth_flush_pending(struct veth_lpar_connection *cnx) @@ -1179,8 +1181,8 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx) 0, &cnx->pending_acks); if (rc != HvLpEvent_Rc_Good) - veth_error("Error 0x%x acking frames from lpar %d!\n", - (unsigned)rc, cnx->remote_lp); + veth_error("Failed acking frames from LPAR %d, rc = %d\n", + cnx->remote_lp, (int)rc); cnx->num_pending_acks = 0; memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks)); @@ -1216,9 +1218,10 @@ static void veth_receive(struct veth_lpar_connection *cnx, /* make sure that we have at least 1 EOF entry in the * remaining entries */ if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) { - veth_error("missing EOF frag in event " - "eofmask=0x%x startchunk=%d\n", - (unsigned) senddata->eofmask, startchunk); + veth_error("Missing EOF fragment in event " + "eofmask = 0x%x startchunk = %d\n", + (unsigned)senddata->eofmask, + startchunk); break; } @@ -1237,8 +1240,9 @@ static void veth_receive(struct veth_lpar_connection *cnx, /* nchunks == # of chunks in this frame */ if ((length - ETH_HLEN) > VETH_MAX_MTU) { - veth_error("Received oversize frame from lpar %d " - "(length=%d)\n", cnx->remote_lp, length); + veth_error("Received oversize frame from LPAR %d " + "(length = %d)\n", + cnx->remote_lp, length); continue; } -- cgit v1.2.3 From abfda4719c61550be4efaf277d4a904a7930d410 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:28:59 +1000 Subject: [PATCH] iseries_veth: Remove a FIXME WRT deletion of the ack_timer The iseries_veth driver has a timer which we use to send acks. When the connection is reset or stopped we need to delete the timer. Currently we only call del_timer() when resetting a connection, which means the timer might run again while the connection is being re-setup. As it turns out that's ok, because the flags the timer consults have been reset. It's cleaner though to call del_timer_sync() once we've dropped the lock, although the timer may still run between us dropping the lock and calling del_timer_sync(), but as above that's ok. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index d5b08dc8a9f8..c19b32e0a5ad 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -450,13 +450,15 @@ static void veth_statemachine(void *p) if (cnx->state & VETH_STATE_RESET) { int i; - del_timer(&cnx->ack_timer); - if (cnx->state & VETH_STATE_OPEN) HvCallEvent_closeLpEventPath(cnx->remote_lp, HvLpEvent_Type_VirtualLan); - /* reset ack data */ + /* + * Reset ack data. This prevents the ack_timer actually + * doing anything, even if it runs one more time when + * we drop the lock below. + */ memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); cnx->num_pending_acks = 0; @@ -469,9 +471,16 @@ static void veth_statemachine(void *p) if (cnx->msgs) for (i = 0; i < VETH_NUMBUFFERS; ++i) veth_recycle_msg(cnx, cnx->msgs + i); + + /* Drop the lock so we can do stuff that might sleep or + * take other locks. */ spin_unlock_irq(&cnx->lock); + + del_timer_sync(&cnx->ack_timer); veth_flush_pending(cnx); + spin_lock_irq(&cnx->lock); + if (cnx->state & VETH_STATE_RESET) goto restart; } @@ -658,13 +667,9 @@ static void veth_stop_connection(u8 rlp) veth_kick_statemachine(cnx); spin_unlock_irq(&cnx->lock); + /* Wait for the state machine to run. */ flush_scheduled_work(); - /* FIXME: not sure if this is necessary - will already have - * been deleted by the state machine, just want to make sure - * its not running any more */ - del_timer_sync(&cnx->ack_timer); - if (cnx->num_events > 0) mf_deallocate_lp_events(cnx->remote_lp, HvLpEvent_Type_VirtualLan, -- cgit v1.2.3 From 58c5900bdaffbf76afd7ad5e053410cb95eb3169 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:00 +1000 Subject: [PATCH] iseries_veth: Try to avoid pathological reset behaviour The iseries_veth driver contains a state machine which is used to manage how connections are setup and neogotiated between LPARs. If one side of a connection resets for some reason, the two LPARs can get stuck in a race to re-setup the connection. This can lead to the connection being declared dead by one or both ends. In practice the connection is declared dead by one or both ends approximately 8/10 times a connection is reset, although it is rare for connections to be reset. (an example here: http://michael.ellerman.id.au/files/misc/veth-trace.html) The core of the problem is that the end that resets the connection doesn't wait for the other end to become aware of the reset. So the resetting end starts setting the connection back up, and then receives a reset from the other end (which is the response to the initial reset). And so on. We're severely limited in what we can do to fix this. The protocol between LPARs is essentially fixed, as we have to interoperate with both OS/400 and old Linux drivers. Which also means we need a fix that only changes the code on one end. The only fix I've found given that, is to just blindly sleep for a bit when resetting the connection, in the hope that the other end will get itself sorted. Needless to say I'd love it if someone has a better idea. This does work, I've so far been unable to get it to break, whereas without the fix a reset of one end will lead to a dead connection ~8/10 times. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index c19b32e0a5ad..db83b0d31327 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -324,8 +324,14 @@ static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, spin_lock_irqsave(&cnx->lock, flags); veth_debug("cnx %d: lost connection.\n", cnx->remote_lp); - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); + + /* Avoid kicking the statemachine once we're shutdown. + * It's unnecessary and it could break veth_stop_connection(). */ + + if (! (cnx->state & VETH_STATE_SHUTDOWN)) { + cnx->state |= VETH_STATE_RESET; + veth_kick_statemachine(cnx); + } spin_unlock_irqrestore(&cnx->lock, flags); } @@ -483,6 +489,12 @@ static void veth_statemachine(void *p) if (cnx->state & VETH_STATE_RESET) goto restart; + + /* Hack, wait for the other end to reset itself. */ + if (! (cnx->state & VETH_STATE_SHUTDOWN)) { + schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ); + goto out; + } } if (cnx->state & VETH_STATE_SHUTDOWN) @@ -667,6 +679,15 @@ static void veth_stop_connection(u8 rlp) veth_kick_statemachine(cnx); spin_unlock_irq(&cnx->lock); + /* There's a slim chance the reset code has just queued the + * statemachine to run in five seconds. If so we need to cancel + * that and requeue the work to run now. */ + if (cancel_delayed_work(&cnx->statemachine_wq)) { + spin_lock_irq(&cnx->lock); + veth_kick_statemachine(cnx); + spin_unlock_irq(&cnx->lock); + } + /* Wait for the state machine to run. */ flush_scheduled_work(); -- cgit v1.2.3 From 2a5391a12297d1759b1c736634acb95793d43fb3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:02 +1000 Subject: [PATCH] iseries_veth: Fix broken promiscuous handling Due to a logic bug, once promiscuous mode is enabled in the iseries_veth driver it is never disabled. The driver keeps two flags, promiscuous and all_mcast which have exactly the same effect. This is because we only ever receive packets destined for us, or multicast packets. So consolidate them into one promiscuous flag for simplicity. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index db83b0d31327..74ee937c4606 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -159,7 +159,6 @@ struct veth_port { rwlock_t mcast_gate; int promiscuous; - int all_mcast; int num_mcast; u64 mcast_addr[VETH_MAX_MCAST]; }; @@ -756,17 +755,15 @@ static void veth_set_multicast_list(struct net_device *dev) write_lock_irqsave(&port->mcast_gate, flags); - if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ - printk(KERN_INFO "%s: Promiscuous mode enabled.\n", - dev->name); + if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || + (dev->mc_count > VETH_MAX_MCAST)) { port->promiscuous = 1; - } else if ( (dev->flags & IFF_ALLMULTI) - || (dev->mc_count > VETH_MAX_MCAST) ) { - port->all_mcast = 1; } else { struct dev_mc_list *dmi = dev->mc_list; int i; + port->promiscuous = 0; + /* Update table */ port->num_mcast = 0; @@ -1145,12 +1142,9 @@ static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr) if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) ) return 1; - if (! (((char *) &mac_addr)[0] & 0x01)) - return 0; - read_lock_irqsave(&port->mcast_gate, flags); - if (port->promiscuous || port->all_mcast) { + if (port->promiscuous) { wanted = 1; goto out; } -- cgit v1.2.3 From d7893ddd1b2110a54a5b1773a405748172ba1fe5 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:05 +1000 Subject: [PATCH] iseries_veth: Remove redundant message stack lock The iseries_veth driver keeps a stack of messages for each connection and a lock to protect the stack. However there is also a per-connection lock which makes the message stack lock redundant. Remove the message stack lock and document the fact that callers of the stack-manipulation functions must hold the connection's lock. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 74ee937c4606..231b2d2d3264 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -143,7 +143,6 @@ struct veth_lpar_connection { struct VethCapData remote_caps; u32 ack_timeout; - spinlock_t msg_stack_lock; struct veth_msg *msg_stack_head; }; @@ -190,27 +189,23 @@ static void veth_timed_ack(unsigned long connectionPtr); #define veth_debug(fmt, args...) do {} while (0) #endif +/* You must hold the connection's lock when you call this function. */ static inline void veth_stack_push(struct veth_lpar_connection *cnx, struct veth_msg *msg) { - unsigned long flags; - - spin_lock_irqsave(&cnx->msg_stack_lock, flags); msg->next = cnx->msg_stack_head; cnx->msg_stack_head = msg; - spin_unlock_irqrestore(&cnx->msg_stack_lock, flags); } +/* You must hold the connection's lock when you call this function. */ static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx) { - unsigned long flags; struct veth_msg *msg; - spin_lock_irqsave(&cnx->msg_stack_lock, flags); msg = cnx->msg_stack_head; if (msg) cnx->msg_stack_head = cnx->msg_stack_head->next; - spin_unlock_irqrestore(&cnx->msg_stack_lock, flags); + return msg; } @@ -645,7 +640,6 @@ static int veth_init_connection(u8 rlp) cnx->msgs = msgs; memset(msgs, 0, VETH_NUMBUFFERS * sizeof(struct veth_msg)); - spin_lock_init(&cnx->msg_stack_lock); for (i = 0; i < VETH_NUMBUFFERS; i++) { msgs[i].token = i; -- cgit v1.2.3 From b08bd5c0a3110f143faeef9cd057d9d8ff2f0714 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:06 +1000 Subject: [PATCH] iseries_veth: Replace lock-protected atomic with an ordinary variable The iseries_veth driver uses atomic ops to manipulate the in_use field of one of its per-connection structures. However all references to the flag occur while the connection's lock is held, so the atomic ops aren't necessary. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 231b2d2d3264..427ac8cc56be 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -117,7 +117,7 @@ struct veth_msg { struct veth_msg *next; struct VethFramesData data; int token; - unsigned long in_use; + int in_use; struct sk_buff *skb; struct device *dev; }; @@ -957,6 +957,8 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, goto drop; } + msg->in_use = 1; + dma_length = skb->len; dma_address = dma_map_single(port->dev, skb->data, dma_length, DMA_TO_DEVICE); @@ -971,7 +973,6 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->data.addr[0] = dma_address; msg->data.len[0] = dma_length; msg->data.eofmask = 1 << VETH_EOF_SHIFT; - set_bit(0, &(msg->in_use)); rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data); if (rc != HvLpEvent_Rc_Good) @@ -981,10 +982,8 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, return 0; recycle_and_drop: + /* we free the skb below, so tell veth_recycle_msg() not to. */ msg->skb = NULL; - /* need to set in use to make veth_recycle_msg in case this - * was a mapping failure */ - set_bit(0, &msg->in_use); veth_recycle_msg(cnx, msg); drop: port->stats.tx_errors++; @@ -1066,12 +1065,14 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } +/* You must hold the connection's lock when you call this function. */ static void veth_recycle_msg(struct veth_lpar_connection *cnx, struct veth_msg *msg) { u32 dma_address, dma_length; - if (test_and_clear_bit(0, &msg->in_use)) { + if (msg->in_use) { + msg->in_use = 0; dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; -- cgit v1.2.3 From cbf9074cc30ca0eee19c9bd7304faf9f1beb1e76 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:07 +1000 Subject: [PATCH] iseries_veth: Only call dma_unmap_single() if dma_map_single() succeeded The iseries_veth driver unconditionally calls dma_unmap_single() even when the corresponding dma_map_single() may have failed. Rework the code a bit to keep the return value from dma_unmap_single() around, and then check if it's a dma_mapping_error() before we do the dma_unmap_single(). Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 427ac8cc56be..91d79db96e82 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -931,7 +931,6 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, struct veth_lpar_connection *cnx = veth_cnx[rlp]; struct veth_port *port = (struct veth_port *) dev->priv; HvLpEvent_Rc rc; - u32 dma_address, dma_length; struct veth_msg *msg = NULL; int err = 0; unsigned long flags; @@ -959,20 +958,19 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->in_use = 1; - dma_length = skb->len; - dma_address = dma_map_single(port->dev, skb->data, - dma_length, DMA_TO_DEVICE); + msg->data.addr[0] = dma_map_single(port->dev, skb->data, + skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_address)) + if (dma_mapping_error(msg->data.addr[0])) goto recycle_and_drop; /* Is it really necessary to check the length and address * fields of the first entry here? */ msg->skb = skb; msg->dev = port->dev; - msg->data.addr[0] = dma_address; - msg->data.len[0] = dma_length; + msg->data.len[0] = skb->len; msg->data.eofmask = 1 << VETH_EOF_SHIFT; + rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data); if (rc != HvLpEvent_Rc_Good) @@ -1076,8 +1074,9 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; - dma_unmap_single(msg->dev, dma_address, dma_length, - DMA_TO_DEVICE); + if (!dma_mapping_error(dma_address)) + dma_unmap_single(msg->dev, dma_address, dma_length, + DMA_TO_DEVICE); if (msg->skb) { dev_kfree_skb_any(msg->skb); -- cgit v1.2.3 From ec60beebed497691c97d674c1facac5ca3d7a4b3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:08 +1000 Subject: [PATCH] iseries_veth: Make init_connection() & destroy_connection() symmetrical This patch makes veth_init_connection() and veth_destroy_connection() symmetrical in that they allocate/deallocate the same data. Currently if there's an error while initialising connections (ie. ENOMEM) we call veth_module_cleanup(), however this will oops because we call driver_unregister() before we've called driver_register(). I've never seen this actually happen though. So instead we explicitly call veth_destroy_connection() for each connection, any that have been set up will be deallocated. We also fix a potential leak if vio_register_driver() fails. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 91d79db96e82..ab9fb218d111 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -683,6 +683,14 @@ static void veth_stop_connection(u8 rlp) /* Wait for the state machine to run. */ flush_scheduled_work(); +} + +static void veth_destroy_connection(u8 rlp) +{ + struct veth_lpar_connection *cnx = veth_cnx[rlp]; + + if (! cnx) + return; if (cnx->num_events > 0) mf_deallocate_lp_events(cnx->remote_lp, @@ -694,14 +702,6 @@ static void veth_stop_connection(u8 rlp) HvLpEvent_Type_VirtualLan, cnx->num_ack_events, NULL, NULL); -} - -static void veth_destroy_connection(u8 rlp) -{ - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - if (! cnx) - return; kfree(cnx->msgs); kfree(cnx); @@ -1441,15 +1441,24 @@ int __init veth_module_init(void) for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { rc = veth_init_connection(i); - if (rc != 0) { - veth_module_cleanup(); - return rc; - } + if (rc != 0) + goto error; } HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handle_event); - return vio_register_driver(&veth_driver); + rc = vio_register_driver(&veth_driver); + if (rc != 0) + goto error; + + return 0; + +error: + for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { + veth_destroy_connection(i); + } + + return rc; } module_init(veth_module_init); -- cgit v1.2.3 From f0c129caa34b4bb0944bbb758b56c3d85b105557 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:09 +1000 Subject: [PATCH] iseries_veth: Use kobjects to track lifecycle of connection structs The iseries_veth driver can attach to multiple vlans, which correspond to multiple net devices. However there is only 1 connection between each LPAR, so the connection structure may be shared by multiple net devices. This makes module removal messy, because we can't deallocate the connections until we know there are no net devices still using them. The solution is to use ref counts on the connections, so we can delete them (actually stop) as soon as the ref count hits zero. This patch fixes (part of) a bug we were seeing with IPv6 sending probes to a dead LPAR, which would then hang us forever due to leftover skbs. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 121 +++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 38 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index ab9fb218d111..5ce9f9348042 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -129,6 +129,7 @@ struct veth_lpar_connection { int num_events; struct VethCapData local_caps; + struct kobject kobject; struct timer_list ack_timer; spinlock_t lock; @@ -171,6 +172,11 @@ static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); static void veth_flush_pending(struct veth_lpar_connection *cnx); static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); static void veth_timed_ack(unsigned long connectionPtr); +static void veth_release_connection(struct kobject *kobject); + +static struct kobj_type veth_lpar_connection_ktype = { + .release = veth_release_connection +}; /* * Utility functions @@ -611,7 +617,7 @@ static int veth_init_connection(u8 rlp) { struct veth_lpar_connection *cnx; struct veth_msg *msgs; - int i; + int i, rc; if ( (rlp == this_lp) || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) @@ -632,6 +638,14 @@ static int veth_init_connection(u8 rlp) veth_cnx[rlp] = cnx; + /* This gets us 1 reference, which is held on behalf of the driver + * infrastructure. It's released at module unload. */ + kobject_init(&cnx->kobject); + cnx->kobject.ktype = &veth_lpar_connection_ktype; + rc = kobject_set_name(&cnx->kobject, "cnx%.2d", rlp); + if (rc != 0) + return rc; + msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL); if (! msgs) { veth_error("Can't allocate buffers for LPAR %d.\n", rlp); @@ -660,11 +674,9 @@ static int veth_init_connection(u8 rlp) return 0; } -static void veth_stop_connection(u8 rlp) +static void veth_stop_connection(struct veth_lpar_connection *cnx) { - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - if (! cnx) + if (!cnx) return; spin_lock_irq(&cnx->lock); @@ -685,11 +697,9 @@ static void veth_stop_connection(u8 rlp) flush_scheduled_work(); } -static void veth_destroy_connection(u8 rlp) +static void veth_destroy_connection(struct veth_lpar_connection *cnx) { - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - if (! cnx) + if (!cnx) return; if (cnx->num_events > 0) @@ -704,8 +714,16 @@ static void veth_destroy_connection(u8 rlp) NULL, NULL); kfree(cnx->msgs); + veth_cnx[cnx->remote_lp] = NULL; kfree(cnx); - veth_cnx[rlp] = NULL; +} + +static void veth_release_connection(struct kobject *kobj) +{ + struct veth_lpar_connection *cnx; + cnx = container_of(kobj, struct veth_lpar_connection, kobject); + veth_stop_connection(cnx); + veth_destroy_connection(cnx); } /* @@ -1349,15 +1367,31 @@ static void veth_timed_ack(unsigned long ptr) static int veth_remove(struct vio_dev *vdev) { - int i = vdev->unit_address; + struct veth_lpar_connection *cnx; struct net_device *dev; + struct veth_port *port; + int i; - dev = veth_dev[i]; - if (dev != NULL) { - veth_dev[i] = NULL; - unregister_netdev(dev); - free_netdev(dev); + dev = veth_dev[vdev->unit_address]; + + if (! dev) + return 0; + + port = netdev_priv(dev); + + for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { + cnx = veth_cnx[i]; + + if (cnx && (port->lpar_map & (1 << i))) { + /* Drop our reference to connections on our VLAN */ + kobject_put(&cnx->kobject); + } } + + veth_dev[vdev->unit_address] = NULL; + unregister_netdev(dev); + free_netdev(dev); + return 0; } @@ -1365,6 +1399,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) { int i = vdev->unit_address; struct net_device *dev; + struct veth_port *port; dev = veth_probe_one(i, &vdev->dev); if (dev == NULL) { @@ -1373,11 +1408,23 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) } veth_dev[i] = dev; - /* Start the state machine on each connection, to commence - * link negotiation */ - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) - if (veth_cnx[i]) - veth_kick_statemachine(veth_cnx[i]); + port = (struct veth_port*)netdev_priv(dev); + + /* Start the state machine on each connection on this vlan. If we're + * the first dev to do so this will commence link negotiation */ + for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { + struct veth_lpar_connection *cnx; + + if (! (port->lpar_map & (1 << i))) + continue; + + cnx = veth_cnx[i]; + if (!cnx) + continue; + + kobject_get(&cnx->kobject); + veth_kick_statemachine(cnx); + } return 0; } @@ -1406,29 +1453,27 @@ static struct vio_driver veth_driver = { void __exit veth_module_cleanup(void) { int i; + struct veth_lpar_connection *cnx; - /* Stop the queues first to stop any new packets being sent. */ - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) - if (veth_dev[i]) - netif_stop_queue(veth_dev[i]); - - /* Stop the connections before we unregister the driver. This - * ensures there's no skbs lying around holding the device open. */ - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) - veth_stop_connection(i); - + /* Disconnect our "irq" to stop events coming from the Hypervisor. */ HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - /* Hypervisor callbacks may have scheduled more work while we - * were stoping connections. Now that we've disconnected from - * the hypervisor make sure everything's finished. */ + /* Make sure any work queued from Hypervisor callbacks is finished. */ flush_scheduled_work(); - vio_unregister_driver(&veth_driver); + for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { + cnx = veth_cnx[i]; + + if (!cnx) + continue; - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) - veth_destroy_connection(i); + /* Drop the driver's reference to the connection */ + kobject_put(&cnx->kobject); + } + /* Unregister the driver, which will close all the netdevs and stop + * the connections when they're no longer referenced. */ + vio_unregister_driver(&veth_driver); } module_exit(veth_module_cleanup); @@ -1456,7 +1501,7 @@ int __init veth_module_init(void) error: for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - veth_destroy_connection(i); + veth_destroy_connection(veth_cnx[i]); } return rc; -- cgit v1.2.3 From 48683d72f8146dfb896e05c90d3544bbad63778c Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:12 +1000 Subject: [PATCH] iseries_veth: Remove TX timeout code The iseries_veth driver uses the generic TX timeout watchdog, however a better solution is in the works, so remove this code. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 48 ---------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 5ce9f9348042..7d6ba5114a1e 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -830,49 +830,6 @@ static struct ethtool_ops ops = { .get_link = veth_get_link, }; -static void veth_tx_timeout(struct net_device *dev) -{ - struct veth_port *port = (struct veth_port *)dev->priv; - struct net_device_stats *stats = &port->stats; - unsigned long flags; - int i; - - stats->tx_errors++; - - spin_lock_irqsave(&port->pending_gate, flags); - - if (!port->pending_lpmask) { - spin_unlock_irqrestore(&port->pending_gate, flags); - return; - } - - printk(KERN_WARNING "%s: Tx timeout! Resetting lp connections: %08x\n", - dev->name, port->pending_lpmask); - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - struct veth_lpar_connection *cnx = veth_cnx[i]; - - if (! (port->pending_lpmask & (1<lock); - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - spin_unlock(&cnx->lock); - } - - spin_unlock_irqrestore(&port->pending_gate, flags); -} - static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) { struct net_device *dev; @@ -921,9 +878,6 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) dev->set_multicast_list = veth_set_multicast_list; SET_ETHTOOL_OPS(dev, &ops); - dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000); - dev->tx_timeout = veth_tx_timeout; - SET_NETDEV_DEV(dev, vdev); rc = register_netdev(dev); @@ -1058,8 +1012,6 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) lpmask = veth_transmit_to_many(skb, lpmask, dev); - dev->trans_start = jiffies; - if (! lpmask) { dev_kfree_skb(skb); } else { -- cgit v1.2.3 From 24562ffa8bdf3a111278a8b93ab92837b9ec9113 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:17 +1000 Subject: [PATCH] iseries_veth: Add a per-connection ack timer Currently the iseries_veth driver contravenes the specification in Documentation/networking/driver.txt, in that if packets are not acked by the other LPAR they will sit around forever. This patch adds a per-connection timer which fires if we've had no acks for five seconds. This is superior to the generic TX timer because it catches the case of a small number of packets being sent and never acked. This fixes a bug we were seeing on real systems, where some IPv6 neighbour discovery packets would not be acked and then prevent the module from being removed, due to skbs lying around. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 75 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 7d6ba5114a1e..122d60db4ff7 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -132,6 +132,11 @@ struct veth_lpar_connection { struct kobject kobject; struct timer_list ack_timer; + struct timer_list reset_timer; + unsigned int reset_timeout; + unsigned long last_contact; + int outstanding_tx; + spinlock_t lock; unsigned long state; HvLpInstanceId src_inst; @@ -171,8 +176,9 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); static void veth_flush_pending(struct veth_lpar_connection *cnx); static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); -static void veth_timed_ack(unsigned long connectionPtr); static void veth_release_connection(struct kobject *kobject); +static void veth_timed_ack(unsigned long ptr); +static void veth_timed_reset(unsigned long ptr); static struct kobj_type veth_lpar_connection_ktype = { .release = veth_release_connection @@ -360,7 +366,7 @@ static void veth_handle_int(struct VethLpEvent *event) HvLpIndex rlp = event->base_event.xSourceLp; struct veth_lpar_connection *cnx = veth_cnx[rlp]; unsigned long flags; - int i; + int i, acked = 0; BUG_ON(! cnx); @@ -374,13 +380,22 @@ static void veth_handle_int(struct VethLpEvent *event) break; case VethEventTypeFramesAck: spin_lock_irqsave(&cnx->lock, flags); + for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { u16 msgnum = event->u.frames_ack_data.token[i]; - if (msgnum < VETH_NUMBUFFERS) + if (msgnum < VETH_NUMBUFFERS) { veth_recycle_msg(cnx, cnx->msgs + msgnum); + cnx->outstanding_tx--; + acked++; + } } + + if (acked > 0) + cnx->last_contact = jiffies; + spin_unlock_irqrestore(&cnx->lock, flags); + veth_flush_pending(cnx); break; case VethEventTypeFrames: @@ -454,8 +469,6 @@ static void veth_statemachine(void *p) restart: if (cnx->state & VETH_STATE_RESET) { - int i; - if (cnx->state & VETH_STATE_OPEN) HvCallEvent_closeLpEventPath(cnx->remote_lp, HvLpEvent_Type_VirtualLan); @@ -474,15 +487,20 @@ static void veth_statemachine(void *p) | VETH_STATE_SENTCAPACK | VETH_STATE_READY); /* Clean up any leftover messages */ - if (cnx->msgs) + if (cnx->msgs) { + int i; for (i = 0; i < VETH_NUMBUFFERS; ++i) veth_recycle_msg(cnx, cnx->msgs + i); + } + cnx->outstanding_tx = 0; /* Drop the lock so we can do stuff that might sleep or * take other locks. */ spin_unlock_irq(&cnx->lock); del_timer_sync(&cnx->ack_timer); + del_timer_sync(&cnx->reset_timer); + veth_flush_pending(cnx); spin_lock_irq(&cnx->lock); @@ -631,9 +649,16 @@ static int veth_init_connection(u8 rlp) cnx->remote_lp = rlp; spin_lock_init(&cnx->lock); INIT_WORK(&cnx->statemachine_wq, veth_statemachine, cnx); + init_timer(&cnx->ack_timer); cnx->ack_timer.function = veth_timed_ack; cnx->ack_timer.data = (unsigned long) cnx; + + init_timer(&cnx->reset_timer); + cnx->reset_timer.function = veth_timed_reset; + cnx->reset_timer.data = (unsigned long) cnx; + cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000); + memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); veth_cnx[rlp] = cnx; @@ -948,6 +973,13 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, if (rc != HvLpEvent_Rc_Good) goto recycle_and_drop; + /* If the timer's not already running, start it now. */ + if (0 == cnx->outstanding_tx) + mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout); + + cnx->last_contact = jiffies; + cnx->outstanding_tx++; + spin_unlock_irqrestore(&cnx->lock, flags); return 0; @@ -1093,6 +1125,37 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx) } } +static void veth_timed_reset(unsigned long ptr) +{ + struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr; + unsigned long trigger_time, flags; + + /* FIXME is it possible this fires after veth_stop_connection()? + * That would reschedule the statemachine for 5 seconds and probably + * execute it after the module's been unloaded. Hmm. */ + + spin_lock_irqsave(&cnx->lock, flags); + + if (cnx->outstanding_tx > 0) { + trigger_time = cnx->last_contact + cnx->reset_timeout; + + if (trigger_time < jiffies) { + cnx->state |= VETH_STATE_RESET; + veth_kick_statemachine(cnx); + veth_error("%d packets not acked by LPAR %d within %d " + "seconds, resetting.\n", + cnx->outstanding_tx, cnx->remote_lp, + cnx->reset_timeout / HZ); + } else { + /* Reschedule the timer */ + trigger_time = jiffies + cnx->reset_timeout; + mod_timer(&cnx->reset_timer, trigger_time); + } + } + + spin_unlock_irqrestore(&cnx->lock, flags); +} + /* * Rx path */ -- cgit v1.2.3 From e0808494ff44d5cedcaf286bb8a93d08e8d9af49 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:18 +1000 Subject: [PATCH] iseries_veth: Simplify full-queue handling The iseries_veth driver often has multiple netdevices sending packets over a single connection to another LPAR. If the bandwidth to the other LPAR is exceeded, all the netdevices must have their queues stopped. The current code achieves this by queueing one incoming skb on the per-netdevice port structure. When the connection is able to send more packets we iterate through the port structs and flush any packet that is queued, as well as restarting the associated netdevice's queue. This arrangement makes less sense now that we have per-connection TX timers, rather than the per-netdevice generic TX timer. The new code simply detects when one of the connections is full, and stops the queue of all associated netdevices. Then when a packet is acked on that connection (ie. there is space again) all the queues are woken up. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 108 +++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 122d60db4ff7..eaff17cc9fb8 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -158,10 +158,11 @@ struct veth_port { u64 mac_addr; HvLpIndexMap lpar_map; - spinlock_t pending_gate; - struct sk_buff *pending_skb; - HvLpIndexMap pending_lpmask; + /* queue_lock protects the stopped_map and dev's queue. */ + spinlock_t queue_lock; + HvLpIndexMap stopped_map; + /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */ rwlock_t mcast_gate; int promiscuous; int num_mcast; @@ -174,7 +175,8 @@ static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); -static void veth_flush_pending(struct veth_lpar_connection *cnx); +static void veth_wake_queues(struct veth_lpar_connection *cnx); +static void veth_stop_queues(struct veth_lpar_connection *cnx); static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); static void veth_release_connection(struct kobject *kobject); static void veth_timed_ack(unsigned long ptr); @@ -221,6 +223,12 @@ static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx) return msg; } +/* You must hold the connection's lock when you call this function. */ +static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx) +{ + return cnx->msg_stack_head == NULL; +} + static inline HvLpEvent_Rc veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype, HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype, @@ -391,12 +399,12 @@ static void veth_handle_int(struct VethLpEvent *event) } } - if (acked > 0) + if (acked > 0) { cnx->last_contact = jiffies; + veth_wake_queues(cnx); + } spin_unlock_irqrestore(&cnx->lock, flags); - - veth_flush_pending(cnx); break; case VethEventTypeFrames: veth_receive(cnx, event); @@ -492,7 +500,9 @@ static void veth_statemachine(void *p) for (i = 0; i < VETH_NUMBUFFERS; ++i) veth_recycle_msg(cnx, cnx->msgs + i); } + cnx->outstanding_tx = 0; + veth_wake_queues(cnx); /* Drop the lock so we can do stuff that might sleep or * take other locks. */ @@ -501,8 +511,6 @@ static void veth_statemachine(void *p) del_timer_sync(&cnx->ack_timer); del_timer_sync(&cnx->reset_timer); - veth_flush_pending(cnx); - spin_lock_irq(&cnx->lock); if (cnx->state & VETH_STATE_RESET) @@ -869,8 +877,9 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) port = (struct veth_port *) dev->priv; - spin_lock_init(&port->pending_gate); + spin_lock_init(&port->queue_lock); rwlock_init(&port->mcast_gate); + port->stopped_map = 0; for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { HvLpVirtualLanIndexMap map; @@ -980,6 +989,9 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, cnx->last_contact = jiffies; cnx->outstanding_tx++; + if (veth_stack_is_empty(cnx)) + veth_stop_queues(cnx); + spin_unlock_irqrestore(&cnx->lock, flags); return 0; @@ -1023,7 +1035,6 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned char *frame = skb->data; struct veth_port *port = (struct veth_port *) dev->priv; - unsigned long flags; HvLpIndexMap lpmask; if (! (frame[0] & 0x01)) { @@ -1040,27 +1051,9 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) lpmask = port->lpar_map; } - spin_lock_irqsave(&port->pending_gate, flags); - - lpmask = veth_transmit_to_many(skb, lpmask, dev); + veth_transmit_to_many(skb, lpmask, dev); - if (! lpmask) { - dev_kfree_skb(skb); - } else { - if (port->pending_skb) { - veth_error("%s: TX while skb was pending!\n", - dev->name); - dev_kfree_skb(skb); - spin_unlock_irqrestore(&port->pending_gate, flags); - return 1; - } - - port->pending_skb = skb; - port->pending_lpmask = lpmask; - netif_stop_queue(dev); - } - - spin_unlock_irqrestore(&port->pending_gate, flags); + dev_kfree_skb(skb); return 0; } @@ -1093,9 +1086,10 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, } } -static void veth_flush_pending(struct veth_lpar_connection *cnx) +static void veth_wake_queues(struct veth_lpar_connection *cnx) { int i; + for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { struct net_device *dev = veth_dev[i]; struct veth_port *port; @@ -1109,19 +1103,45 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx) if (! (port->lpar_map & (1<remote_lp))) continue; - spin_lock_irqsave(&port->pending_gate, flags); - if (port->pending_skb) { - port->pending_lpmask = - veth_transmit_to_many(port->pending_skb, - port->pending_lpmask, - dev); - if (! port->pending_lpmask) { - dev_kfree_skb_any(port->pending_skb); - port->pending_skb = NULL; - netif_wake_queue(dev); - } + spin_lock_irqsave(&port->queue_lock, flags); + + port->stopped_map &= ~(1 << cnx->remote_lp); + + if (0 == port->stopped_map && netif_queue_stopped(dev)) { + veth_debug("cnx %d: woke queue for %s.\n", + cnx->remote_lp, dev->name); + netif_wake_queue(dev); } - spin_unlock_irqrestore(&port->pending_gate, flags); + spin_unlock_irqrestore(&port->queue_lock, flags); + } +} + +static void veth_stop_queues(struct veth_lpar_connection *cnx) +{ + int i; + + for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { + struct net_device *dev = veth_dev[i]; + struct veth_port *port; + + if (! dev) + continue; + + port = (struct veth_port *)dev->priv; + + /* If this cnx is not on the vlan for this port, continue */ + if (! (port->lpar_map & (1 << cnx->remote_lp))) + continue; + + spin_lock(&port->queue_lock); + + netif_stop_queue(dev); + port->stopped_map |= (1 << cnx->remote_lp); + + veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n", + cnx->remote_lp, dev->name, port->stopped_map); + + spin_unlock(&port->queue_lock); } } -- cgit v1.2.3 From db5e8718eac0b8166d6fd05b1ed7f8114c243988 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:19 +1000 Subject: [PATCH] iseries_veth: Fix bogus counting of TX errors There's a number of problems with the way iseries_veth counts TX errors. Firstly it counts conditions which aren't really errors as TX errors. This includes if we don't have a connection struct for the other LPAR, or if the other LPAR is currently down (or just doesn't want to talk to us). Neither of these should count as TX errors. Secondly, it counts one TX error for each LPAR that fails to accept the packet. This can lead to TX error counts higher than the total number of packets sent through the interface. This is confusing for users. This patch fixes that behaviour. The non-error conditions are no longer counted, and we introduce a new and I think saner meaning to the TX counts. If a packet is successfully transmitted to any LPAR then it is transmitted and tx_packets is incremented by 1. If there is an error transmitting a packet to any LPAR then that is counted as one error, ie. tx_errors is incremented by 1. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 47 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index eaff17cc9fb8..b945bf02d257 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -938,31 +938,25 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, struct veth_port *port = (struct veth_port *) dev->priv; HvLpEvent_Rc rc; struct veth_msg *msg = NULL; - int err = 0; unsigned long flags; - if (! cnx) { - port->stats.tx_errors++; - dev_kfree_skb(skb); + if (! cnx) return 0; - } spin_lock_irqsave(&cnx->lock, flags); if (! (cnx->state & VETH_STATE_READY)) - goto drop; + goto no_error; - if ((skb->len - 14) > VETH_MAX_MTU) + if ((skb->len - ETH_HLEN) > VETH_MAX_MTU) goto drop; msg = veth_stack_pop(cnx); - - if (! msg) { - err = 1; + if (! msg) goto drop; - } msg->in_use = 1; + msg->skb = skb_get(skb); msg->data.addr[0] = dma_map_single(port->dev, skb->data, skb->len, DMA_TO_DEVICE); @@ -970,9 +964,6 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, if (dma_mapping_error(msg->data.addr[0])) goto recycle_and_drop; - /* Is it really necessary to check the length and address - * fields of the first entry here? */ - msg->skb = skb; msg->dev = port->dev; msg->data.len[0] = skb->len; msg->data.eofmask = 1 << VETH_EOF_SHIFT; @@ -992,43 +983,43 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, if (veth_stack_is_empty(cnx)) veth_stop_queues(cnx); + no_error: spin_unlock_irqrestore(&cnx->lock, flags); return 0; recycle_and_drop: - /* we free the skb below, so tell veth_recycle_msg() not to. */ - msg->skb = NULL; veth_recycle_msg(cnx, msg); drop: - port->stats.tx_errors++; - dev_kfree_skb(skb); spin_unlock_irqrestore(&cnx->lock, flags); - return err; + return 1; } -static HvLpIndexMap veth_transmit_to_many(struct sk_buff *skb, +static void veth_transmit_to_many(struct sk_buff *skb, HvLpIndexMap lpmask, struct net_device *dev) { struct veth_port *port = (struct veth_port *) dev->priv; - int i; - int rc; + int i, success, error; + + success = error = 0; for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { if ((lpmask & (1 << i)) == 0) continue; - rc = veth_transmit_to_one(skb_get(skb), i, dev); - if (! rc) - lpmask &= ~(1<stats.tx_errors++; + + if (success) { port->stats.tx_packets++; port->stats.tx_bytes += skb->len; } - - return lpmask; } static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) -- cgit v1.2.3 From 76812d81238cda5c5c4060da27517a08287620fc Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:20 +1000 Subject: [PATCH] iseries_veth: Add sysfs support for connection structs To aid in field debugging, add sysfs support for iseries_veth's connection structures. At the moment this is all read-only, however we could think about adding write support for some attributes in future. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 94 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index b945bf02d257..25481ea67474 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -182,10 +182,6 @@ static void veth_release_connection(struct kobject *kobject); static void veth_timed_ack(unsigned long ptr); static void veth_timed_reset(unsigned long ptr); -static struct kobj_type veth_lpar_connection_ktype = { - .release = veth_release_connection -}; - /* * Utility functions */ @@ -279,6 +275,81 @@ static int veth_allocate_events(HvLpIndex rlp, int number) return vc.num; } +/* + * sysfs support + */ + +struct veth_cnx_attribute { + struct attribute attr; + ssize_t (*show)(struct veth_lpar_connection *, char *buf); + ssize_t (*store)(struct veth_lpar_connection *, const char *buf); +}; + +static ssize_t veth_cnx_attribute_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct veth_cnx_attribute *cnx_attr; + struct veth_lpar_connection *cnx; + + cnx_attr = container_of(attr, struct veth_cnx_attribute, attr); + cnx = container_of(kobj, struct veth_lpar_connection, kobject); + + if (!cnx_attr->show) + return -EIO; + + return cnx_attr->show(cnx, buf); +} + +#define CUSTOM_CNX_ATTR(_name, _format, _expression) \ +static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\ +{ \ + return sprintf(buf, _format, _expression); \ +} \ +struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name) + +#define SIMPLE_CNX_ATTR(_name) \ + CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name) + +SIMPLE_CNX_ATTR(outstanding_tx); +SIMPLE_CNX_ATTR(remote_lp); +SIMPLE_CNX_ATTR(num_events); +SIMPLE_CNX_ATTR(src_inst); +SIMPLE_CNX_ATTR(dst_inst); +SIMPLE_CNX_ATTR(num_pending_acks); +SIMPLE_CNX_ATTR(num_ack_events); +CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout)); +CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout)); +CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state); +CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ? + jiffies_to_msecs(jiffies - cnx->last_contact) : 0); + +#define GET_CNX_ATTR(_name) (&veth_cnx_attr_##_name.attr) + +static struct attribute *veth_cnx_default_attrs[] = { + GET_CNX_ATTR(outstanding_tx), + GET_CNX_ATTR(remote_lp), + GET_CNX_ATTR(num_events), + GET_CNX_ATTR(reset_timeout), + GET_CNX_ATTR(last_contact), + GET_CNX_ATTR(state), + GET_CNX_ATTR(src_inst), + GET_CNX_ATTR(dst_inst), + GET_CNX_ATTR(num_pending_acks), + GET_CNX_ATTR(num_ack_events), + GET_CNX_ATTR(ack_timeout), + NULL +}; + +static struct sysfs_ops veth_cnx_sysfs_ops = { + .show = veth_cnx_attribute_show +}; + +static struct kobj_type veth_lpar_connection_ktype = { + .release = veth_release_connection, + .sysfs_ops = &veth_cnx_sysfs_ops, + .default_attrs = veth_cnx_default_attrs +}; + /* * LPAR connection code */ @@ -1493,6 +1564,8 @@ void __exit veth_module_cleanup(void) if (!cnx) continue; + /* Remove the connection from sysfs */ + kobject_del(&cnx->kobject); /* Drop the driver's reference to the connection */ kobject_put(&cnx->kobject); } @@ -1523,6 +1596,19 @@ int __init veth_module_init(void) if (rc != 0) goto error; + for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { + struct kobject *kobj; + + if (!veth_cnx[i]) + continue; + + kobj = &veth_cnx[i]->kobject; + kobj->parent = &veth_driver.driver.kobj; + /* If the add failes, complain but otherwise continue */ + if (0 != kobject_add(kobj)) + veth_error("cnx %d: Failed adding to sysfs.\n", i); + } + return 0; error: -- cgit v1.2.3 From 07a5c1727d6bf5c917034fe4006acf726cb158bf Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:21 +1000 Subject: [PATCH] iseries_veth: Add sysfs support for port structs Also to aid debugging, add sysfs support for iseries_veth's port structures. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 25481ea67474..9536962f5cd2 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -167,6 +167,8 @@ struct veth_port { int promiscuous; int num_mcast; u64 mcast_addr[VETH_MAX_MCAST]; + + struct kobject kobject; }; static HvLpIndex this_lp; @@ -350,6 +352,62 @@ static struct kobj_type veth_lpar_connection_ktype = { .default_attrs = veth_cnx_default_attrs }; +struct veth_port_attribute { + struct attribute attr; + ssize_t (*show)(struct veth_port *, char *buf); + ssize_t (*store)(struct veth_port *, const char *buf); +}; + +static ssize_t veth_port_attribute_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct veth_port_attribute *port_attr; + struct veth_port *port; + + port_attr = container_of(attr, struct veth_port_attribute, attr); + port = container_of(kobj, struct veth_port, kobject); + + if (!port_attr->show) + return -EIO; + + return port_attr->show(port, buf); +} + +#define CUSTOM_PORT_ATTR(_name, _format, _expression) \ +static ssize_t _name##_show(struct veth_port *port, char *buf) \ +{ \ + return sprintf(buf, _format, _expression); \ +} \ +struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name) + +#define SIMPLE_PORT_ATTR(_name) \ + CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name) + +SIMPLE_PORT_ATTR(promiscuous); +SIMPLE_PORT_ATTR(num_mcast); +CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map); +CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map); +CUSTOM_PORT_ATTR(mac_addr, "0x%lX\n", port->mac_addr); + +#define GET_PORT_ATTR(_name) (&veth_port_attr_##_name.attr) +static struct attribute *veth_port_default_attrs[] = { + GET_PORT_ATTR(mac_addr), + GET_PORT_ATTR(lpar_map), + GET_PORT_ATTR(stopped_map), + GET_PORT_ATTR(promiscuous), + GET_PORT_ATTR(num_mcast), + NULL +}; + +static struct sysfs_ops veth_port_sysfs_ops = { + .show = veth_port_attribute_show +}; + +static struct kobj_type veth_port_ktype = { + .sysfs_ops = &veth_port_sysfs_ops, + .default_attrs = veth_port_default_attrs +}; + /* * LPAR connection code */ @@ -992,6 +1050,13 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) return NULL; } + kobject_init(&port->kobject); + port->kobject.parent = &dev->class_dev.kobj; + port->kobject.ktype = &veth_port_ktype; + kobject_set_name(&port->kobject, "veth_port"); + if (0 != kobject_add(&port->kobject)) + veth_error("Failed adding port for %s to sysfs.\n", dev->name); + veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n", dev->name, vlan, port->lpar_map); @@ -1486,6 +1551,8 @@ static int veth_remove(struct vio_dev *vdev) } veth_dev[vdev->unit_address] = NULL; + kobject_del(&port->kobject); + kobject_put(&port->kobject); unregister_netdev(dev); free_netdev(dev); -- cgit v1.2.3 From 642d1a4c36b9002a45ea6498bda5d1e911eeb933 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:25 +1000 Subject: [PATCH] iseries_veth: Incorporate iseries_veth.h in iseries_veth.c iseries_veth.h is only used by iseries_veth.c, so merge the former into the latter. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 42 ++++++++++++++++++++++++++++++++++++++++-- drivers/net/iseries_veth.h | 46 ---------------------------------------------- 2 files changed, 40 insertions(+), 48 deletions(-) delete mode 100644 drivers/net/iseries_veth.h diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 9536962f5cd2..2e54c85bc099 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -81,12 +81,50 @@ #undef DEBUG -#include "iseries_veth.h" - MODULE_AUTHOR("Kyle Lucke "); MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); MODULE_LICENSE("GPL"); +#define VethEventTypeCap (0) +#define VethEventTypeFrames (1) +#define VethEventTypeMonitor (2) +#define VethEventTypeFramesAck (3) + +#define VETH_MAX_ACKS_PER_MSG (20) +#define VETH_MAX_FRAMES_PER_MSG (6) + +struct VethFramesData { + u32 addr[VETH_MAX_FRAMES_PER_MSG]; + u16 len[VETH_MAX_FRAMES_PER_MSG]; + u32 eofmask; +}; +#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) + +struct VethFramesAckData { + u16 token[VETH_MAX_ACKS_PER_MSG]; +}; + +struct VethCapData { + u8 caps_version; + u8 rsvd1; + u16 num_buffers; + u16 ack_threshold; + u16 rsvd2; + u32 ack_timeout; + u32 rsvd3; + u64 rsvd4[3]; +}; + +struct VethLpEvent { + struct HvLpEvent base_event; + union { + struct VethCapData caps_data; + struct VethFramesData frames_data; + struct VethFramesAckData frames_ack_data; + } u; + +}; + #define VETH_NUMBUFFERS (120) #define VETH_ACKTIMEOUT (1000000) /* microseconds */ #define VETH_MAX_MCAST (12) diff --git a/drivers/net/iseries_veth.h b/drivers/net/iseries_veth.h deleted file mode 100644 index d9370f79b83e..000000000000 --- a/drivers/net/iseries_veth.h +++ /dev/null @@ -1,46 +0,0 @@ -/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */ - -#ifndef _ISERIES_VETH_H -#define _ISERIES_VETH_H - -#define VethEventTypeCap (0) -#define VethEventTypeFrames (1) -#define VethEventTypeMonitor (2) -#define VethEventTypeFramesAck (3) - -#define VETH_MAX_ACKS_PER_MSG (20) -#define VETH_MAX_FRAMES_PER_MSG (6) - -struct VethFramesData { - u32 addr[VETH_MAX_FRAMES_PER_MSG]; - u16 len[VETH_MAX_FRAMES_PER_MSG]; - u32 eofmask; -}; -#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) - -struct VethFramesAckData { - u16 token[VETH_MAX_ACKS_PER_MSG]; -}; - -struct VethCapData { - u8 caps_version; - u8 rsvd1; - u16 num_buffers; - u16 ack_threshold; - u16 rsvd2; - u32 ack_timeout; - u32 rsvd3; - u64 rsvd4[3]; -}; - -struct VethLpEvent { - struct HvLpEvent base_event; - union { - struct VethCapData caps_data; - struct VethFramesData frames_data; - struct VethFramesAckData frames_ack_data; - } u; - -}; - -#endif /* _ISERIES_VETH_H */ -- cgit v1.2.3 From 59f17aebabef709a32a8fc09b4cd3507f32dea01 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:27 +1000 Subject: [PATCH] iseries_veth: Remove studly caps from iseries_veth.c Having merged iseries_veth.h, let's remove some of the studly caps that came with it. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 74 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 2e54c85bc099..a3855e94fbc5 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -85,26 +85,26 @@ MODULE_AUTHOR("Kyle Lucke "); MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); MODULE_LICENSE("GPL"); -#define VethEventTypeCap (0) -#define VethEventTypeFrames (1) -#define VethEventTypeMonitor (2) -#define VethEventTypeFramesAck (3) +#define VETH_EVENT_CAP (0) +#define VETH_EVENT_FRAMES (1) +#define VETH_EVENT_MONITOR (2) +#define VETH_EVENT_FRAMES_ACK (3) #define VETH_MAX_ACKS_PER_MSG (20) #define VETH_MAX_FRAMES_PER_MSG (6) -struct VethFramesData { +struct veth_frames_data { u32 addr[VETH_MAX_FRAMES_PER_MSG]; u16 len[VETH_MAX_FRAMES_PER_MSG]; u32 eofmask; }; #define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) -struct VethFramesAckData { +struct veth_frames_ack_data { u16 token[VETH_MAX_ACKS_PER_MSG]; }; -struct VethCapData { +struct veth_cap_data { u8 caps_version; u8 rsvd1; u16 num_buffers; @@ -115,12 +115,12 @@ struct VethCapData { u64 rsvd4[3]; }; -struct VethLpEvent { +struct veth_lpevent { struct HvLpEvent base_event; union { - struct VethCapData caps_data; - struct VethFramesData frames_data; - struct VethFramesAckData frames_ack_data; + struct veth_cap_data caps_data; + struct veth_frames_data frames_data; + struct veth_frames_ack_data frames_ack_data; } u; }; @@ -153,7 +153,7 @@ struct VethLpEvent { struct veth_msg { struct veth_msg *next; - struct VethFramesData data; + struct veth_frames_data data; int token; int in_use; struct sk_buff *skb; @@ -165,7 +165,7 @@ struct veth_lpar_connection { struct work_struct statemachine_wq; struct veth_msg *msgs; int num_events; - struct VethCapData local_caps; + struct veth_cap_data local_caps; struct kobject kobject; struct timer_list ack_timer; @@ -179,12 +179,12 @@ struct veth_lpar_connection { unsigned long state; HvLpInstanceId src_inst; HvLpInstanceId dst_inst; - struct VethLpEvent cap_event, cap_ack_event; + struct veth_lpevent cap_event, cap_ack_event; u16 pending_acks[VETH_MAX_ACKS_PER_MSG]; u32 num_pending_acks; int num_ack_events; - struct VethCapData remote_caps; + struct veth_cap_data remote_caps; u32 ack_timeout; struct veth_msg *msg_stack_head; @@ -217,7 +217,7 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); static void veth_wake_queues(struct veth_lpar_connection *cnx); static void veth_stop_queues(struct veth_lpar_connection *cnx); -static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); +static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *); static void veth_release_connection(struct kobject *kobject); static void veth_timed_ack(unsigned long ptr); static void veth_timed_reset(unsigned long ptr); @@ -308,7 +308,7 @@ static int veth_allocate_events(HvLpIndex rlp, int number) struct veth_allocation vc = { COMPLETION_INITIALIZER(vc.c), 0 }; mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan, - sizeof(struct VethLpEvent), number, + sizeof(struct veth_lpevent), number, &veth_complete_allocation, &vc); wait_for_completion(&vc.c); @@ -456,7 +456,7 @@ static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx) } static void veth_take_cap(struct veth_lpar_connection *cnx, - struct VethLpEvent *event) + struct veth_lpevent *event) { unsigned long flags; @@ -481,7 +481,7 @@ static void veth_take_cap(struct veth_lpar_connection *cnx, } static void veth_take_cap_ack(struct veth_lpar_connection *cnx, - struct VethLpEvent *event) + struct veth_lpevent *event) { unsigned long flags; @@ -499,7 +499,7 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx, } static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, - struct VethLpEvent *event) + struct veth_lpevent *event) { unsigned long flags; @@ -516,7 +516,7 @@ static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, spin_unlock_irqrestore(&cnx->lock, flags); } -static void veth_handle_ack(struct VethLpEvent *event) +static void veth_handle_ack(struct veth_lpevent *event) { HvLpIndex rlp = event->base_event.xTargetLp; struct veth_lpar_connection *cnx = veth_cnx[rlp]; @@ -524,10 +524,10 @@ static void veth_handle_ack(struct VethLpEvent *event) BUG_ON(! cnx); switch (event->base_event.xSubtype) { - case VethEventTypeCap: + case VETH_EVENT_CAP: veth_take_cap_ack(cnx, event); break; - case VethEventTypeMonitor: + case VETH_EVENT_MONITOR: veth_take_monitor_ack(cnx, event); break; default: @@ -536,7 +536,7 @@ static void veth_handle_ack(struct VethLpEvent *event) }; } -static void veth_handle_int(struct VethLpEvent *event) +static void veth_handle_int(struct veth_lpevent *event) { HvLpIndex rlp = event->base_event.xSourceLp; struct veth_lpar_connection *cnx = veth_cnx[rlp]; @@ -546,14 +546,14 @@ static void veth_handle_int(struct VethLpEvent *event) BUG_ON(! cnx); switch (event->base_event.xSubtype) { - case VethEventTypeCap: + case VETH_EVENT_CAP: veth_take_cap(cnx, event); break; - case VethEventTypeMonitor: + case VETH_EVENT_MONITOR: /* do nothing... this'll hang out here til we're dead, * and the hypervisor will return it for us. */ break; - case VethEventTypeFramesAck: + case VETH_EVENT_FRAMES_ACK: spin_lock_irqsave(&cnx->lock, flags); for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { @@ -573,7 +573,7 @@ static void veth_handle_int(struct VethLpEvent *event) spin_unlock_irqrestore(&cnx->lock, flags); break; - case VethEventTypeFrames: + case VETH_EVENT_FRAMES: veth_receive(cnx, event); break; default: @@ -584,7 +584,7 @@ static void veth_handle_int(struct VethLpEvent *event) static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs) { - struct VethLpEvent *veth_event = (struct VethLpEvent *)event; + struct veth_lpevent *veth_event = (struct veth_lpevent *)event; if (event->xFlags.xFunction == HvLpEvent_Function_Ack) veth_handle_ack(veth_event); @@ -594,7 +594,7 @@ static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs) static int veth_process_caps(struct veth_lpar_connection *cnx) { - struct VethCapData *remote_caps = &cnx->remote_caps; + struct veth_cap_data *remote_caps = &cnx->remote_caps; int num_acks_needed; /* Convert timer to jiffies */ @@ -710,7 +710,7 @@ static void veth_statemachine(void *p) if ( (cnx->state & VETH_STATE_OPEN) && !(cnx->state & VETH_STATE_SENTMON) ) { - rc = veth_signalevent(cnx, VethEventTypeMonitor, + rc = veth_signalevent(cnx, VETH_EVENT_MONITOR, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_DeferredAck, 0, 0, 0, 0, 0, 0); @@ -733,7 +733,7 @@ static void veth_statemachine(void *p) && !(cnx->state & VETH_STATE_SENTCAPS)) { u64 *rawcap = (u64 *)&cnx->local_caps; - rc = veth_signalevent(cnx, VethEventTypeCap, + rc = veth_signalevent(cnx, VETH_EVENT_CAP, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, 0, rawcap[0], rawcap[1], rawcap[2], @@ -755,7 +755,7 @@ static void veth_statemachine(void *p) if ((cnx->state & VETH_STATE_GOTCAPS) && !(cnx->state & VETH_STATE_SENTCAPACK)) { - struct VethCapData *remote_caps = &cnx->remote_caps; + struct veth_cap_data *remote_caps = &cnx->remote_caps; memcpy(remote_caps, &cnx->cap_event.u.caps_data, sizeof(*remote_caps)); @@ -1142,7 +1142,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->data.len[0] = skb->len; msg->data.eofmask = 1 << VETH_EOF_SHIFT; - rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data); + rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data); if (rc != HvLpEvent_Rc_Good) goto recycle_and_drop; @@ -1409,7 +1409,7 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx) { HvLpEvent_Rc rc; - rc = veth_signaldata(cnx, VethEventTypeFramesAck, + rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK, 0, &cnx->pending_acks); if (rc != HvLpEvent_Rc_Good) @@ -1421,9 +1421,9 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx) } static void veth_receive(struct veth_lpar_connection *cnx, - struct VethLpEvent *event) + struct veth_lpevent *event) { - struct VethFramesData *senddata = &event->u.frames_data; + struct veth_frames_data *senddata = &event->u.frames_data; int startchunk = 0; int nchunks; unsigned long flags; -- cgit v1.2.3 From ee05f031ec72cc06abc4002992649c3a8344d246 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 1 Sep 2005 11:29:29 +1000 Subject: [PATCH] iseries_veth: Be consistent about driver name, increment version The iseries_veth driver tells sysfs that it's called 'iseries_veth', but if you ask it via ethtool it thinks it's called 'veth'. I think this comes from 2.4 when the driver was called 'veth', but it's definitely called 'iseries_veth' now, so fix it. To make sure we don't do it again define DRV_NAME and use it everywhere. While we're at it, change the version number to 2.0, to reflect the changes made in this patch series. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik --- drivers/net/iseries_veth.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index a3855e94fbc5..dc5d089bf184 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -125,6 +125,9 @@ struct veth_lpevent { }; +#define DRV_NAME "iseries_veth" +#define DRV_VERSION "2.0" + #define VETH_NUMBUFFERS (120) #define VETH_ACKTIMEOUT (1000000) /* microseconds */ #define VETH_MAX_MCAST (12) @@ -227,14 +230,14 @@ static void veth_timed_reset(unsigned long ptr); */ #define veth_info(fmt, args...) \ - printk(KERN_INFO "iseries_veth: " fmt, ## args) + printk(KERN_INFO DRV_NAME ": " fmt, ## args) #define veth_error(fmt, args...) \ - printk(KERN_ERR "iseries_veth: Error: " fmt, ## args) + printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args) #ifdef DEBUG #define veth_debug(fmt, args...) \ - printk(KERN_DEBUG "iseries_veth: " fmt, ## args) + printk(KERN_DEBUG DRV_NAME ": " fmt, ## args) #else #define veth_debug(fmt, args...) do {} while (0) #endif @@ -997,9 +1000,10 @@ static void veth_set_multicast_list(struct net_device *dev) static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, "veth", sizeof(info->driver) - 1); + strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); info->driver[sizeof(info->driver) - 1] = '\0'; - strncpy(info->version, "1.0", sizeof(info->version) - 1); + strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); + info->version[sizeof(info->version) - 1] = '\0'; } static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) @@ -1642,7 +1646,7 @@ static struct vio_device_id veth_device_table[] __devinitdata = { MODULE_DEVICE_TABLE(vio, veth_device_table); static struct vio_driver veth_driver = { - .name = "iseries_veth", + .name = DRV_NAME, .id_table = veth_device_table, .probe = veth_probe, .remove = veth_remove -- cgit v1.2.3