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

Re: [Libguestfs] Proposed changes for OpenStack



On 12/15/2011 08:35 AM, Richard W.M. Jones wrote:
On Wed, Dec 14, 2011 at 05:07:04PM +0000, Matthew Booth wrote:
guestmount_mount(g.sock_path(), '/tmp/dir')
...filesystem stuff...
guestmount_umount('/tmp/dir')

g.close()

Note that there are no callbacks, no threads and no races. The code
can be written sequentially.

There are certainly threads here, although they would have to be
hidden behind the scenes, with a barrier to provide synchronization
before "filesystem stuff" can run.

No, there are no threads and (in this example, anyway) no synchronization is required. There *is* a fork, though. guestmount_mount() would:

1. Create a new guestfs connection to the existing appliance.
2. Create a new filesystem using the new connection.
3. Mount it.
4. fork() and run the FUSE event loop from the child.

In this case, guestmount_mount() would effectively return after 3, at which point the filesystem is already safe to use. Although the above example doesn't require this, it would also be 'safe' to interleave filesystem access and guestfs commands. The limitations would be that changing the appliance mounts while it is guestmounted would have undesirable consequences and, as with all filesystem access, there would be the potential for read->modify->write races. However, a simple use case like the one you proposed won't hit these limitations. If required, a simple locking scheme could be added later. This could be as simple as total exclusivity for a single client until released.

In working out what guestmount_mount would do, it occurs to me that you can do this without requiring either multiple connections to the appliance or a callback mechanism, as long as the caller takes responsibility for the guestfs handle. Consider guestfsmount_mount_handle(g, mountpoint), which does:

1. Create a new filesystem using the given handle.
2. Mount it.
3. Create a new thread to run the FUSE event loop.

You could then do:

g.foo()
h = guestmount_mount_handle(g, mp) (h is an opaque handle)

... it is the responsibility of the caller not to use g here ...

guestmount_unmount(mh)
g.bar()

h would contain at least the mountpoint and the thread handle. guestmount_unmount(mh) would therefore be:

umount(mh->mp)
mh->thread.join()

Also, we're not bringing FUSE into the main API.

Is this such a problem?  We could dlopen libfuse if the dependency on
libfuse was a problem ...

Actually, I may be softening to this if the API can be more like a regular API call. I think my guestmount_mount_handle example could be turned into something usable:

g.foo()
g.mount_local(mp)
... g cannot be used here ...
g.unmount_local()
g.bar()

The handle would be stored in g itself, so only 1 mount would be supported at any one time.

This could be usefully extended in 2 ways I can think of:

Firstly, guestfs could be made threadsafe by simply putting a mutex around all api calls. That is, calls can be made simultaneously from 2 different threads, but they will run sequentially. This could be improved upon by redesigning the protocol, but I suspect it would be perfectly adequate in practice. This would make g usable between calls to mount_local and unmount_local(), as long as mount points are not modified and the handle is not closed.

Secondly, the mechanism I described before for connecting to a running appliance remains generally useful. guestmount could still be updated to create a handle which uses an existing appliance.

On the face of it, neither of these extensions seems particularly onerous to implement either.

Matt
--
Matthew Booth, RHCA, RHCSS
Red Hat Engineering, Virtualisation Team

GPG ID:  D33C3490
GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490


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