Subject: [PATCH] tg3 : avoid an expensive divide

From: Eric Dumazet <[email-masked]>
Date: Tue, 06 Feb 2007 21:53:24 +0100

Hi David

During an oprofile session of linux-2.6.20 on a dual opteron system, I noticed 
an expensive divide was done in tg3_poll().

I am using gcc-4.1.1, so the following comment from drivers/net/tg3.c seems 
over-optimistic :

/* Do not place this n-ring entries value into the tp struct itself,
  * we really want to expose these constants to GCC so that modulo et
  * al.  operations are done with shifts and masks instead of with
  * hw multiply/modulo instructions.  Another solution would be to
  * replace things like '% foo' with '& (foo - 1)'.
  */
#define TG3_RX_RCB_RING_SIZE(tp)        \
         ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ?  512 : 1024)

Assembly code before patch :
(oprofile results included)
   6434  0.0088 :ffffffff803684b9:       mov    0x6f0(%r15),%eax
    587 8.0e-04 :ffffffff803684c0:       and    $0x40000,%eax
   2170  0.0030 :ffffffff803684c5:       cmp    $0x1,%eax
                :ffffffff803684c8:       lea    0x1(%r13),%eax
                :ffffffff803684cc:       sbb    %ecx,%ecx
   2051  0.0028 :ffffffff803684ce:       xor    %edx,%edx
                :ffffffff803684d0:       and    $0x200,%ecx
     20 2.7e-05 :ffffffff803684d6:       add    $0x200,%ecx
   1986  0.0027 :ffffffff803684dc:       div    %ecx
103427  0.1410 :ffffffff803684de:       cmp    %edx,0xffffffffffffff7c(%rbp)

Assembly code after the suggested patch :

ffffffff803684b9:           mov    0x6f0(%r15),%eax
ffffffff803684c0:           and    $0x40000,%eax
ffffffff803684c5:           cmp    $0x1,%eax
ffffffff803684c8:           sbb    %eax,%eax
ffffffff803684ca:           inc    %r13d
ffffffff803684cd:           and    $0x200,%eax
ffffffff803684d2:           add    $0x1ff,%eax
ffffffff803684d7:           and    %eax,%r13d
ffffffff803684da:           cmp    %r13d,0xffffffffffffff7c(%rbp)

Signed-off-by: Eric Dumazet <[email-masked]>
--- linux-2.6.20/drivers/net/tg3.c.orig	2007-02-06 22:30:39.000000000 +0100
+++ linux-2.6.20-ed/drivers/net/tg3.c	2007-02-06 22:32:42.000000000 +0100
@@ -3384,7 +3384,7 @@
 		}
 next_pkt_nopost:
 		sw_idx++;
-		sw_idx %= TG3_RX_RCB_RING_SIZE(tp);
+		sw_idx &= (TG3_RX_RCB_RING_SIZE(tp) - 1);
 
 		/* Refresh hw_idx to see if there is new work */
 		if (sw_idx == hw_idx) {