diff options
Diffstat (limited to 'drivers/net/iseries_veth.c')
-rw-r--r-- | drivers/net/iseries_veth.c | 75 |
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); | |||
171 | static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); | 176 | static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); |
172 | static void veth_flush_pending(struct veth_lpar_connection *cnx); | 177 | static void veth_flush_pending(struct veth_lpar_connection *cnx); |
173 | static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); | 178 | static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); |
174 | static void veth_timed_ack(unsigned long connectionPtr); | ||
175 | static void veth_release_connection(struct kobject *kobject); | 179 | static void veth_release_connection(struct kobject *kobject); |
180 | static void veth_timed_ack(unsigned long ptr); | ||
181 | static void veth_timed_reset(unsigned long ptr); | ||
176 | 182 | ||
177 | static struct kobj_type veth_lpar_connection_ktype = { | 183 | static 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 | ||
1128 | static 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 | */ |