Merge pull request #2519 from cgwalters/syncfs-only
deploy: Add a 5s max timeout on global filesystem `sync()`
This commit is contained in:
commit
a05b02fa9c
|
|
@ -26,6 +26,7 @@
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
@ -1557,6 +1558,39 @@ typedef struct {
|
||||||
guint64 extra_syncfs_msec;
|
guint64 extra_syncfs_msec;
|
||||||
} SyncStats;
|
} SyncStats;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ref_count; /* atomic */
|
||||||
|
bool success;
|
||||||
|
GMutex mutex;
|
||||||
|
GCond cond;
|
||||||
|
} SyncData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sync_data_unref (SyncData *data)
|
||||||
|
{
|
||||||
|
if (!g_atomic_int_dec_and_test (&data->ref_count))
|
||||||
|
return;
|
||||||
|
g_mutex_clear (&data->mutex);
|
||||||
|
g_cond_clear (&data->cond);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An asynchronous sync() call. See below.
|
||||||
|
static void *
|
||||||
|
sync_in_thread (void *ptr)
|
||||||
|
{
|
||||||
|
SyncData *syncdata = ptr;
|
||||||
|
// Ensure that the caller is blocked waiting
|
||||||
|
g_mutex_lock (&syncdata->mutex);
|
||||||
|
sync ();
|
||||||
|
// Signal success
|
||||||
|
syncdata->success = true;
|
||||||
|
g_cond_broadcast (&syncdata->cond);
|
||||||
|
g_mutex_unlock (&syncdata->mutex);
|
||||||
|
sync_data_unref (syncdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* First, sync the root directory as well as /var and /boot which may
|
/* First, sync the root directory as well as /var and /boot which may
|
||||||
* be separate mount points. Then *in addition*, do a global
|
* be separate mount points. Then *in addition*, do a global
|
||||||
* `sync()`.
|
* `sync()`.
|
||||||
|
|
@ -1589,9 +1623,41 @@ full_system_sync (OstreeSysroot *self,
|
||||||
* actually get error codes out of that API, and we more clearly
|
* actually get error codes out of that API, and we more clearly
|
||||||
* delineate what we actually want to sync in the future when this
|
* delineate what we actually want to sync in the future when this
|
||||||
* global sync call is removed.
|
* global sync call is removed.
|
||||||
|
*
|
||||||
|
* Due to https://bugzilla.redhat.com/show_bug.cgi?id=2003532 which is
|
||||||
|
* a bug in the interaction between Ceph and systemd, we have a capped
|
||||||
|
* timeout of 5s on the sync here. In general, we don't actually want
|
||||||
|
* to "own" flushing data on *all* mounted filesystems as part of
|
||||||
|
* our process. Again, we're only keeping the `sync` here out of
|
||||||
|
* conservatisim. We could likely just remove it and e.g. systemd
|
||||||
|
* would take care of sync as part of unmounting individual filesystems.
|
||||||
*/
|
*/
|
||||||
start_msec = g_get_monotonic_time () / 1000;
|
start_msec = g_get_monotonic_time () / 1000;
|
||||||
sync ();
|
if (!(self->opt_flags & OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC))
|
||||||
|
{
|
||||||
|
SyncData *syncdata = g_new (SyncData, 1);
|
||||||
|
syncdata->ref_count = 2; // One for us, one for thread
|
||||||
|
syncdata->success = FALSE;
|
||||||
|
g_mutex_init (&syncdata->mutex);
|
||||||
|
g_cond_init (&syncdata->cond);
|
||||||
|
g_mutex_lock (&syncdata->mutex);
|
||||||
|
GThread *worker = g_thread_new ("ostree sync", sync_in_thread, syncdata);
|
||||||
|
// Wait up to 5 seconds for sync() to finish
|
||||||
|
gint64 end_time = g_get_monotonic_time () + (5 * G_TIME_SPAN_SECOND);
|
||||||
|
while (!syncdata->success)
|
||||||
|
{
|
||||||
|
if (!g_cond_wait_until (&syncdata->cond, &syncdata->mutex, end_time))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&syncdata->mutex);
|
||||||
|
sync_data_unref (syncdata);
|
||||||
|
// We can't join the thread here, for two reasons. First, the whole
|
||||||
|
// point here is to avoid blocking on `sync`. The other reason is
|
||||||
|
// today there is no return value on success from `sync`, so there's
|
||||||
|
// no point to joining the thread even if we had a nonblocking mechanism
|
||||||
|
// to do so.
|
||||||
|
g_thread_unref (worker);
|
||||||
|
}
|
||||||
end_msec = g_get_monotonic_time () / 1000;
|
end_msec = g_get_monotonic_time () / 1000;
|
||||||
out_stats->extra_syncfs_msec = (end_msec - start_msec);
|
out_stats->extra_syncfs_msec = (end_msec - start_msec);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,11 @@ typedef enum {
|
||||||
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 3, /* https://github.com/ostreedev/ostree/issues/2154 */
|
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 3, /* https://github.com/ostreedev/ostree/issues/2154 */
|
||||||
} OstreeSysrootDebugFlags;
|
} OstreeSysrootDebugFlags;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* Skip invoking `sync()` */
|
||||||
|
OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC = 1 << 0,
|
||||||
|
} OstreeSysrootGlobalOptFlags;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OSTREE_SYSROOT_LOAD_STATE_NONE, /* ostree_sysroot_new() was called */
|
OSTREE_SYSROOT_LOAD_STATE_NONE, /* ostree_sysroot_new() was called */
|
||||||
OSTREE_SYSROOT_LOAD_STATE_INIT, /* We've loaded basic sysroot state and have an fd */
|
OSTREE_SYSROOT_LOAD_STATE_INIT, /* We've loaded basic sysroot state and have an fd */
|
||||||
|
|
@ -75,6 +80,7 @@ struct OstreeSysroot {
|
||||||
/* Only access through ostree_sysroot_[_get]repo() */
|
/* Only access through ostree_sysroot_[_get]repo() */
|
||||||
OstreeRepo *repo;
|
OstreeRepo *repo;
|
||||||
|
|
||||||
|
OstreeSysrootGlobalOptFlags opt_flags;
|
||||||
OstreeSysrootDebugFlags debug_flags;
|
OstreeSysrootDebugFlags debug_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,9 @@ ostree_sysroot_class_init (OstreeSysrootClass *klass)
|
||||||
static void
|
static void
|
||||||
ostree_sysroot_init (OstreeSysroot *self)
|
ostree_sysroot_init (OstreeSysroot *self)
|
||||||
{
|
{
|
||||||
|
const GDebugKey globalopt_keys[] = {
|
||||||
|
{ "skip-sync", OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC },
|
||||||
|
};
|
||||||
const GDebugKey keys[] = {
|
const GDebugKey keys[] = {
|
||||||
{ "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS },
|
{ "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS },
|
||||||
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
|
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
|
||||||
|
|
@ -191,6 +194,8 @@ ostree_sysroot_init (OstreeSysroot *self)
|
||||||
{ "no-dtb", OSTREE_SYSROOT_DEBUG_TEST_NO_DTB },
|
{ "no-dtb", OSTREE_SYSROOT_DEBUG_TEST_NO_DTB },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self->opt_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_OPTS"),
|
||||||
|
globalopt_keys, G_N_ELEMENTS (globalopt_keys));
|
||||||
self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"),
|
self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"),
|
||||||
keys, G_N_ELEMENTS (keys));
|
keys, G_N_ELEMENTS (keys));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue