aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/iseries_veth.c
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2005-08-31 21:29:17 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-08-31 22:42:45 -0400
commit24562ffa8bdf3a111278a8b93ab92837b9ec9113 (patch)
treefd61f42ecad239577edf680efe3f93ac95b70a26 /drivers/net/iseries_veth.c
parent48683d72f8146dfb896e05c90d3544bbad63778c (diff)
[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 <michael@ellerman.id.au> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/net/iseries_veth.c')
-rw-r--r--drivers/net/iseries_veth.c75
1 files 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 {
132 struct kobject kobject; 132 struct kobject kobject;
133 struct timer_list ack_timer; 133 struct timer_list ack_timer;
134 134
135 struct timer_list reset_timer;
136 unsigned int reset_timeout;
137 unsigned long last_contact;
138 int outstanding_tx;
139
135 spinlock_t lock; 140 spinlock_t lock;
136 unsigned long state; 141 unsigned long state;
137 HvLpInstanceId src_inst; 142 HvLpInstanceId src_inst;
@@ -171,8 +176,9 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev);
171static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); 176static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *);
172static void veth_flush_pending(struct veth_lpar_connection *cnx); 177static void veth_flush_pending(struct veth_lpar_connection *cnx);
173static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); 178static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *);
174static void veth_timed_ack(unsigned long connectionPtr);
175static void veth_release_connection(struct kobject *kobject); 179static void veth_release_connection(struct kobject *kobject);
180static void veth_timed_ack(unsigned long ptr);
181static void veth_timed_reset(unsigned long ptr);
176 182
177static struct kobj_type veth_lpar_connection_ktype = { 183static struct kobj_type veth_lpar_connection_ktype = {
178 .release = veth_release_connection 184 .release = veth_release_connection
@@ -360,7 +366,7 @@ static void veth_handle_int(struct VethLpEvent *event)
360 HvLpIndex rlp = event->base_event.xSourceLp; 366 HvLpIndex rlp = event->base_event.xSourceLp;
361 struct veth_lpar_connection *cnx = veth_cnx[rlp]; 367 struct veth_lpar_connection *cnx = veth_cnx[rlp];
362 unsigned long flags; 368 unsigned long flags;
363 int i; 369 int i, acked = 0;
364 370
365 BUG_ON(! cnx); 371 BUG_ON(! cnx);
366 372
@@ -374,13 +380,22 @@ static void veth_handle_int(struct VethLpEvent *event)
374 break; 380 break;
375 case VethEventTypeFramesAck: 381 case VethEventTypeFramesAck:
376 spin_lock_irqsave(&cnx->lock, flags); 382 spin_lock_irqsave(&cnx->lock, flags);
383
377 for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { 384 for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) {
378 u16 msgnum = event->u.frames_ack_data.token[i]; 385 u16 msgnum = event->u.frames_ack_data.token[i];
379 386
380 if (msgnum < VETH_NUMBUFFERS) 387 if (msgnum < VETH_NUMBUFFERS) {
381 veth_recycle_msg(cnx, cnx->msgs + msgnum); 388 veth_recycle_msg(cnx, cnx->msgs + msgnum);
389 cnx->outstanding_tx--;
390 acked++;
391 }
382 } 392 }
393
394 if (acked > 0)
395 cnx->last_contact = jiffies;
396
383 spin_unlock_irqrestore(&cnx->lock, flags); 397 spin_unlock_irqrestore(&cnx->lock, flags);
398
384 veth_flush_pending(cnx); 399 veth_flush_pending(cnx);
385 break; 400 break;
386 case VethEventTypeFrames: 401 case VethEventTypeFrames:
@@ -454,8 +469,6 @@ static void veth_statemachine(void *p)
454 469
455 restart: 470 restart:
456 if (cnx->state & VETH_STATE_RESET) { 471 if (cnx->state & VETH_STATE_RESET) {
457 int i;
458
459 if (cnx->state & VETH_STATE_OPEN) 472 if (cnx->state & VETH_STATE_OPEN)
460 HvCallEvent_closeLpEventPath(cnx->remote_lp, 473 HvCallEvent_closeLpEventPath(cnx->remote_lp,
461 HvLpEvent_Type_VirtualLan); 474 HvLpEvent_Type_VirtualLan);
@@ -474,15 +487,20 @@ static void veth_statemachine(void *p)
474 | VETH_STATE_SENTCAPACK | VETH_STATE_READY); 487 | VETH_STATE_SENTCAPACK | VETH_STATE_READY);
475 488
476 /* Clean up any leftover messages */ 489 /* Clean up any leftover messages */
477 if (cnx->msgs) 490 if (cnx->msgs) {
491 int i;
478 for (i = 0; i < VETH_NUMBUFFERS; ++i) 492 for (i = 0; i < VETH_NUMBUFFERS; ++i)
479 veth_recycle_msg(cnx, cnx->msgs + i); 493 veth_recycle_msg(cnx, cnx->msgs + i);
494 }
495 cnx->outstanding_tx = 0;
480 496
481 /* Drop the lock so we can do stuff that might sleep or 497 /* Drop the lock so we can do stuff that might sleep or
482 * take other locks. */ 498 * take other locks. */
483 spin_unlock_irq(&cnx->lock); 499 spin_unlock_irq(&cnx->lock);
484 500
485 del_timer_sync(&cnx->ack_timer); 501 del_timer_sync(&cnx->ack_timer);
502 del_timer_sync(&cnx->reset_timer);
503
486 veth_flush_pending(cnx); 504 veth_flush_pending(cnx);
487 505
488 spin_lock_irq(&cnx->lock); 506 spin_lock_irq(&cnx->lock);
@@ -631,9 +649,16 @@ static int veth_init_connection(u8 rlp)
631 cnx->remote_lp = rlp; 649 cnx->remote_lp = rlp;
632 spin_lock_init(&cnx->lock); 650 spin_lock_init(&cnx->lock);
633 INIT_WORK(&cnx->statemachine_wq, veth_statemachine, cnx); 651 INIT_WORK(&cnx->statemachine_wq, veth_statemachine, cnx);
652
634 init_timer(&cnx->ack_timer); 653 init_timer(&cnx->ack_timer);
635 cnx->ack_timer.function = veth_timed_ack; 654 cnx->ack_timer.function = veth_timed_ack;
636 cnx->ack_timer.data = (unsigned long) cnx; 655 cnx->ack_timer.data = (unsigned long) cnx;
656
657 init_timer(&cnx->reset_timer);
658 cnx->reset_timer.function = veth_timed_reset;
659 cnx->reset_timer.data = (unsigned long) cnx;
660 cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000);
661
637 memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); 662 memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
638 663
639 veth_cnx[rlp] = cnx; 664 veth_cnx[rlp] = cnx;
@@ -948,6 +973,13 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
948 if (rc != HvLpEvent_Rc_Good) 973 if (rc != HvLpEvent_Rc_Good)
949 goto recycle_and_drop; 974 goto recycle_and_drop;
950 975
976 /* If the timer's not already running, start it now. */
977 if (0 == cnx->outstanding_tx)
978 mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout);
979
980 cnx->last_contact = jiffies;
981 cnx->outstanding_tx++;
982
951 spin_unlock_irqrestore(&cnx->lock, flags); 983 spin_unlock_irqrestore(&cnx->lock, flags);
952 return 0; 984 return 0;
953 985
@@ -1093,6 +1125,37 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx)
1093 } 1125 }
1094} 1126}
1095 1127
1128static void veth_timed_reset(unsigned long ptr)
1129{
1130 struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr;
1131 unsigned long trigger_time, flags;
1132
1133 /* FIXME is it possible this fires after veth_stop_connection()?
1134 * That would reschedule the statemachine for 5 seconds and probably
1135 * execute it after the module's been unloaded. Hmm. */
1136
1137 spin_lock_irqsave(&cnx->lock, flags);
1138
1139 if (cnx->outstanding_tx > 0) {
1140 trigger_time = cnx->last_contact + cnx->reset_timeout;
1141
1142 if (trigger_time < jiffies) {
1143 cnx->state |= VETH_STATE_RESET;
1144 veth_kick_statemachine(cnx);
1145 veth_error("%d packets not acked by LPAR %d within %d "
1146 "seconds, resetting.\n",
1147 cnx->outstanding_tx, cnx->remote_lp,
1148 cnx->reset_timeout / HZ);
1149 } else {
1150 /* Reschedule the timer */
1151 trigger_time = jiffies + cnx->reset_timeout;
1152 mod_timer(&cnx->reset_timer, trigger_time);
1153 }
1154 }
1155
1156 spin_unlock_irqrestore(&cnx->lock, flags);
1157}
1158
1096/* 1159/*
1097 * Rx path 1160 * Rx path
1098 */ 1161 */