On Tue, 25 Jun 2013, I wrote:

My current best design:
- use ordinary mutexes to protect counter fetches in non-per-CPU contexts.
- use native-sized or always 32-bit counters.  Counter updates are done
 by a single addl on i386.  Fix pcpu.h on arches other than amd64 and
 i386 and use the same method as there.
- counter fetches add the native-sized pcpu counters to 64-bit non-pcpu
 counters, when the native-size counters are in danger of overflowing
 or always, under the mutex.  Transferring data uses an ordinary
 atomic_cmpset.  To avoid ifdefs, always use u_int pcpu counters.
 The 64-bit non-pcpu counters can easily be changed to pcpu counters
 if the API is extended to support pcpu data.
- run a daemon every few minutes to fetch all the counters, so that
 the native-sized counters are in no danger of overflowing on systems
 that don't run statistics programs often enough to fetch the counters
 to actually use.

There is at least 1 counter decrement (add -1) in tcp, so the native counters
need to be signed.

...
With my design:

        extern struct mtx counter_locks[];
        extern uint64_t counters[];

This is pseudo-code.  The extra structure must be dynamically allocated
with each counter.  I'm not sure how to do that.  uint64_t_pcpu_zone
is specialized for pcpu counters, and a non-pcpu part is needed.

        uint64_t r;
        volatile u_int *p;
        u_int v;

Change to int.

        int cnum;

        cnum = ctonum(c);
        mtx_lock(&counter_locks[cnum]);     /* might not need 1 per counter */
        r = counters[cnum];
        for (i = 0; i < mp_ncpus; i++) {
                p = (u_int *)((char *)c + sizeof(struct pcpu) * i);

Change to int *.

                v = *p;                 /* don't care if it is stale */
                if (v >= 0x80000000) {

Change the critical level to 2 critical levels, 0x40000000 for positive
values and -0x40000000 for negative values.

                        /* Transfer only when above critical level. */
                        while (atomic_cmpset_rel_int(p, v, 0) == 0)
                                v = *p; /* still don't care if it is stale */
                        counters[cnum] += v;

Even though full counter values are not expected to become negative,
the native counters can easily become negative when a decrement occurs
after the transfer resets them to 0.

                }
                r += v;
        }
        mtx_unlock(&counter_locks[cnum]);
        return (r);

Mutexes give some slowness in the fetching code, but fetches eare expected
to be relatively rare.

I added the complication to usually avoid atomic ops at the last minute.
The complication might not be worth it.

The complication is to test v so as to normally avoid doing the transfer
and its atomic ops (and to not use atomic ops for loading v).  The
complication is larger with 2 thresholds.  If we always transferred,
then *p would usually be small and often 0, so that decrementing it
would often make it -1.  This -1 must be transferred by adding -1, not
by adding 0xfffffffff.  Changing the type of the native counter to int
gives this.

Bruce
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to