This is the mail archive of the ecos-discuss@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: How to debug synchronisation in the usbs.c in a new usb-driver for the ARM at91sam7s...


>>>>> "Nick" == Nick Garnett <nickg@ecoscentric.com> writes:

    <snip>
    Nick> This corresponds almost exactly to what I would expect to be
    Nick> done, and have implemented in the past. I would use a
    Nick> separate condition variable to implement the gate-keeper to
    Nick> keep response time on the event condition variable low.

    Nick> Also, the completed variable is seldom needed. Instead the
    Nick> thread will be testing whatever changes the ISR/DSR have
    Nick> made to the state of the device. This is particularly
    Nick> important when there might be threads waiting for a variety
    Nick> of different conditions to become true.

True for the case where multiple threads may be manipulating a device,
but not for the simple cases I have come across in USB/SPI/I2C. If the
DSR decides to post the semaphore/condition variable then the thread
knows the I/O has completed. There is no need to check the device
state. In fact, depending on the device there may be no easy way to
detect completion at the thread level. In this circumstance the
completed flag is necessary to avoid race conditions between the DSR
and thread.

    <snip>

    >> A semaphore and a condition variable are the same size
    >> (assuming 32-bit pointers), so the second approach saves space
    >> for the busy and completed flags. That may be two ints, two
    >> bytes, or just two bits in a flags field (although testing bits
    >> is likely to impose a code size penalty). It is still an
    >> overhead.

    Nick> This is not actually true. a condition variable is just a
    Nick> thread queue. A semaphore is a thread queue plus a counter.
    Nick> The counter is effectively the equivalent of the completed
    Nick> variable above.

Actually, a condition variable is thread queue plus pointer to mutex,
whereas a semaphore is thread queue plus counter.

    >> Re. code size, the second approach eliminates four function
    >> calls, four assignments, and two loops. A fairly clear gain. If
    >> there are multiple threads attempting to perform I/O to the
    >> same device then the cyg_drv_cond_broadcast() call will also
    >> result in threads waking up and having to go to sleep again
    >> immediately because the device is still busy. This could be
    >> eliminated using a separate condition variable, but that adds
    >> other overhead.

    Nick> The difference is less than you think. The completed
    Nick> variable is logically unnecessary, the semaphore has the
    Nick> additional counter. Internally semaphore wait is logically
    Nick> equvalent to

    Nick>     while( counter <= 0 )
    Nick>         cyg_drv_cond_wait( sem );
    Nick>     counter--;

    Nick> It just moves the loop elsewhere.

They are logically equivalent, but condition variables are still more
expensive. Inside the driver it is:

   (start the I/O operation)			(start the I/O operation)
   cyg_drv_dsr_lock();			VS	cyg_drv_sem_wait(&wait);
   while (!<test for completion>) {
      cyg_drv_cond_wait(&wait);
   }
   cyg_drv_dsr_unlock();
   (perform any clean-ups)                      (perform any clean-ups);

plus cyg_drv_cond_wait() is itself more expensive than a semaphore
wait because it needs to manipulate the mutex twice.

    >> In terms of real-time behaviour, the condition variable
    >> approach has to lock the scheduler albeit for only a short
    >> time.

    Nick> This will always be necessary, if the thread is to examine
    Nick> and manipulate data shared with the ISR/DSR it is going to
    Nick> have to either disable interrupts or lock the scheduler.

With a semaphore there is no need for the thread to examine shared
state. The semaphore itself acts as the only shared state of interest:
whether or not the operation has completed. Obviously for devices like
serial something more complicated will be needed, but for the
USB/SPI/I2C devices I have come across a semaphore suffices.

    >> More worryingly mutex priority inversion protection is not
    >> going to help. If you have a low priority thread currently
    >> performing I/O, a high priority thread wanting access to the
    >> same device, and an intermediate thread hogging the cpu, then
    >> when the I/O has completed you have a priority inversion
    >> situation: the high priority thread is blocked on a condition
    >> variable, not a semaphore.

    Nick> Mutexes should not be used to queue threads for access to
    Nick> critical sections in which the current owner suspends using
    Nick> something other than a condition variable. Mutexes are for
    Nick> mutual exclusion, and should be treated more like spinlocks.
    Nick> Long-term waiters should be transferred to condition
    Nick> variables, and not left on the mutex.

    Nick> From a real time point of view, IO should be performed at
    Nick> the priority of the requesting thread.

I am not convinced that is universally true. In the case of an I2C bus
where a high priority thread wants to access device A and a low
priority thread wants to access device B, do we really want the high
priority thread blocked indefinitely because some intermediate thread
is hogging the cpu?

I think the answer depends on both the application's requirements and
the implementation of the device driver. If most of the I/O happens
via a DMA engine or in ISRs, with the thread only woken up once the
I/O has completed, then boosting the low priority's thread seems
very sensible. If the driver involves bit-banging and/or polling it
may be better to stick at the lower priority - depending on just how
important it is that the high priority thread can access its device.

Obviously the application can and probably should resolve any problems
by careful use of priorities, but preferring an approach that risks
priority inversion problems seems wrong. Using a mutex/semaphore
combination gives reasonable behaviour, even if it does mean a mutex
is claimed for rather longer than usual.

Bart

-- 
Bart Veer                       eCos Configuration Architect
http://www.ecoscentric.com/     The eCos and RedBoot experts


-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]