[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 2/2] PV framebuffer



Markus Armbruster <armbru@xxxxxxxxxx> writes:

> Steven Smith <sos22@xxxxxxxxx> writes:
>
> [...]
>>> >> +        if (xenfb_hotplug(xsh, vfb_backdir) < 0)
>>> >> +                goto error;
>>> >> +        if (xenfb_hotplug(xsh, vkbd_backdir) < 0)
>>> >> +                goto error;
>>> >> +
>>> >> +        if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", 
>>> >> "1"))
>>> >> +                goto error;
>>> >> +        if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d",
>>> >> +                            XenbusStateInitWait))
>>> >> +                goto error;
>>> >> +        if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d",
>>> >> +                            XenbusStateInitWait))
>>> >> +                goto error;
>>> >> +
>>> > I'd probably reorder this a little to look more like this:
>>> >
>>> > (1) Set feature-abs-pointer
>>> > (2) Set state to InitWait
>>> > (3) Set hotplug status
>>> >
>>> > The only actual *required* constraint is (1) before (2), so that the
>>> > frontend doesn't initialise before we've set the feature and
>>> > potentially miss it.
>>> >
>>> > (2) before (3) is kind of nice, in that it makes sure that the backend
>>> > is ready before xend unpauses the domain, and so the frontend'll be
>>> > able to connect the first time it tries, but that's a lot less
>>> > important here than for e.g. block devices.
>>> Based on our previous discussions, I designed the startup protocol
>>> this way:
>>> 
>>>     backend                          frontend
>>>     ------------------------------------------------------------------
>>>     hotplug_status = connected
>>>     [makes xend populate xenstore, set fe and be state = Initialising]
>>>     wait for be state = Initialising
>>>       [i.e. wait for xend]
>>>     write xs: feature-abs-pointer    write xs: feature-update
>>>     be state = InitWait              fe state = Initialised
>>>     ------------------------------ sync ------------------------------
>>>     wait for fe state = Initialised  wait for be state = InitWait
>>>     ------------------------------ sync ------------------------------
>>>     read xs: feature-update          read xs: feature-abs-pointer
>>>     write xs: request-update         write xs: request-abs-pointer
>>>     be state = Connected             fe state = Connected
>>>     ------------------------------ sync ------------------------------
>>>     wait for fe state = Connected    wait for be state = Connected
>>>     ------------------------------ sync ------------------------------
>>>     read xs: request-abs-pointer     read xs: request-update
>>> 
>>> The symmetry made sense to me.
>> Ah, sorry, I wasn't clear enough.  You've got everything right after
>> the first sync line, but the bit before that isn't quite right.  Xend
>> creates xenstore area when the domain is created, and then waits until
>> hotplug-status is set before starting the domain running.  The idea is
>> that the backend driver should be watching its area of xenbus (so
>> /local/domain/0/backend/vif, say), so that it notices when the area is
>> created and creates the appropriate backend devices.  Creating the
>> backend devices triggers the hotplug scripts via some udev magic which
>> I've never quite understood, and they then e.g. connect vifs up to the
>> bridge.  Once they've finished, the backends are all ready, and so
>> it's safe to start the guest.  If you start the guest before the
>> backends are ready, you potentially have issues like your root
>> fileystem not becoming available until after the guest has booted,
>> which tends to make Linux unhappy.
>>
>> xend                         backend driver          hotplug scripts
>> Creates a new domain
>>      paused
>> Creates backend area
>>                              Notices new backend
>>                                      area, creates
>>                                      backend device
>>                              Does some basic setup
>>                                      on backend device
>>                              Go to state InitWait
>>                              Kicks udev
>>                                                      Does a bit more setup
>>                                                              on backend
>>                                                              device
>>                                                      Sets hotplug-status
>> Notices hotplug status,
>>      unpauses domain
>>
>> Now, the obvious mapping of this protocol onto the PVFB would have
>> xend create the xenstore area when the guest is created and spawn the
>> backend itself.  The backend could then set hotplug-status to indicate
>> that it's ready, which would unpause the guest and things would then
>> proceed in the usual way.
>
> I coded this, and it works.

Not quite.  I fear there's a race.

My xenkbd otherend_changed callback runs twice with backend_state ==
XenbusStateConnected instead of first with XenbusStateInitWait and
then with XenbusStateConnected.

Here's what I think happens.  First how backend, xend and frontend
synchronize:


    backend               xend                  frontend
    ------------------------------------------------------------------
                          create domain paused
                              create xenstore,
                          fe and be state =
                              Initialising
               ________ sync _______/
              /                                
    wait for be state                           
        Initialising
    write xs: feature-*                         
    be state = InitWait
    hotplug_status =                            
        connected                               
              \________ sync _______
                                    \          
                          wait for
                              hotplug_status
                                  connected
                          unpause domain
                                    \________ sync _______
                                                          \          
                                                write xs: feature-*
                                                fe state = Initialised
              \___________________ sync __________________/
              /                                           \
    wait for fe state                           wait for be state
        Initialised                                 InitWait
    read xs: feature-*                          read xs: feature-*
    write xs: request-*                         write xs: request-*
    be state = Connected                        fe state = Connected
              \___________________ sync __________________/
              /                                           \
    wait for fe state                           wait for be state
        Connected                                   Connected
    read xs: request-*                          read xs: request-*

Except the frontend doesn't actually wait for be state, it runs a
callback (otherend_changed) when the state changes.

Now, when the this callback in the frontend is delayed sufficiently,
the order of events could be:

    frontend                                    backend
    ------------------------------------------------------------------
                             [...]
    be state = InitWait
    hotplug_status =
        connected
                             [...]
                                                fe state = Initialised
    wait for fe state
        Initialised
        completes
    read xs: feature-*  
    write xs: request-* 
    be state = Connected
                                                otherend_changed runs
                                                    and sees be state
                                                       Connected
                                                    (actual trigger
                                                        was InitWait)
                                                otherend_changed runs
                                                    and sees be state
                                                       Connected

How do you want me to deal with this?

Note the funny code in drivers/xen/blkback/xenbus.c's
frontend_changed:

        case XenbusStateInitialised:
        case XenbusStateConnected:
                /* Ensure we connect even when two watches fire in 
                   close successsion and we miss the intermediate value 
                   of frontend_state. */

If this is the right thing to do there, why isn't it done elsewhere?

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.