util/log: Limit RCUCloseFILE to file closing

Use FILE* for global_file.  We can perform an rcu_read on that
just as easily as RCUCloseFILE*.  This simplifies a couple of
places, where previously we required taking the rcu_read_lock
simply to avoid racing to dereference RCUCloseFile->fd.

Only allocate the RCUCloseFile prior to call_rcu.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20220417183019.755276-39-richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-17 11:30:18 -07:00
parent d5f55fff34
commit 30f5a73ac3
1 changed files with 32 additions and 36 deletions

View File

@ -37,7 +37,7 @@ typedef struct RCUCloseFILE {
/* Mutex covering the other global_* variables. */ /* Mutex covering the other global_* variables. */
static QemuMutex global_mutex; static QemuMutex global_mutex;
static char *global_filename; static char *global_filename;
static RCUCloseFILE *global_file; static FILE *global_file;
int qemu_loglevel; int qemu_loglevel;
static int log_append = 0; static int log_append = 0;
@ -46,46 +46,44 @@ static GArray *debug_regions;
/* Returns true if qemu_log() will really write somewhere. */ /* Returns true if qemu_log() will really write somewhere. */
bool qemu_log_enabled(void) bool qemu_log_enabled(void)
{ {
return global_file != NULL; return qatomic_read(&global_file) != NULL;
} }
/* Returns true if qemu_log() will write somewhere other than stderr. */ /* Returns true if qemu_log() will write somewhere other than stderr. */
bool qemu_log_separate(void) bool qemu_log_separate(void)
{ {
RCUCloseFILE *logfile; FILE *logfile = qatomic_read(&global_file);
bool res = false; return logfile && logfile != stderr;
rcu_read_lock();
logfile = qatomic_rcu_read(&global_file);
if (logfile && logfile->fd != stderr) {
res = true;
}
rcu_read_unlock();
return res;
} }
/* Lock/unlock output. */ /* Lock/unlock output. */
FILE *qemu_log_trylock(void) FILE *qemu_log_trylock(void)
{ {
RCUCloseFILE *logfile; FILE *logfile;
rcu_read_lock(); rcu_read_lock();
logfile = qatomic_rcu_read(&global_file); /*
* FIXME: typeof_strip_qual, as used by qatomic_rcu_read,
* does not work with pointers to undefined structures,
* such as we have with struct _IO_FILE and musl libc.
* Since all we want is a read of a pointer, cast to void**,
* which does work with typeof_strip_qual.
*/
logfile = qatomic_rcu_read((void **)&global_file);
if (logfile) { if (logfile) {
qemu_flockfile(logfile->fd); qemu_flockfile(logfile);
return logfile->fd;
} else { } else {
rcu_read_unlock(); rcu_read_unlock();
return NULL;
} }
return logfile;
} }
void qemu_log_unlock(FILE *fd) void qemu_log_unlock(FILE *logfile)
{ {
if (fd) { if (logfile) {
fflush(fd); fflush(logfile);
qemu_funlockfile(fd); qemu_funlockfile(logfile);
rcu_read_unlock(); rcu_read_unlock();
} }
} }
@ -110,9 +108,7 @@ static void __attribute__((__constructor__)) startup(void)
static void rcu_close_file(RCUCloseFILE *r) static void rcu_close_file(RCUCloseFILE *r)
{ {
if (r->fd != stderr) { fclose(r->fd);
fclose(r->fd);
}
g_free(r); g_free(r);
} }
@ -122,7 +118,7 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name,
{ {
bool need_to_open_file; bool need_to_open_file;
bool daemonized; bool daemonized;
RCUCloseFILE *logfile; FILE *logfile;
QEMU_LOCK_GUARD(&global_mutex); QEMU_LOCK_GUARD(&global_mutex);
logfile = global_file; logfile = global_file;
@ -176,37 +172,37 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name,
if (logfile && (!need_to_open_file || changed_name)) { if (logfile && (!need_to_open_file || changed_name)) {
qatomic_rcu_set(&global_file, NULL); qatomic_rcu_set(&global_file, NULL);
call_rcu(logfile, rcu_close_file, rcu); if (logfile != stderr) {
RCUCloseFILE *r = g_new0(RCUCloseFILE, 1);
r->fd = logfile;
call_rcu(r, rcu_close_file, rcu);
}
logfile = NULL; logfile = NULL;
} }
if (!logfile && need_to_open_file) { if (!logfile && need_to_open_file) {
FILE *fd;
if (filename) { if (filename) {
fd = fopen(filename, log_append ? "a" : "w"); logfile = fopen(filename, log_append ? "a" : "w");
if (!fd) { if (!logfile) {
error_setg_errno(errp, errno, "Error opening logfile %s", error_setg_errno(errp, errno, "Error opening logfile %s",
filename); filename);
return false; return false;
} }
/* In case we are a daemon redirect stderr to logfile */ /* In case we are a daemon redirect stderr to logfile */
if (daemonized) { if (daemonized) {
dup2(fileno(fd), STDERR_FILENO); dup2(fileno(logfile), STDERR_FILENO);
fclose(fd); fclose(logfile);
/* This will skip closing logfile in rcu_close_file. */ /* This will skip closing logfile in rcu_close_file. */
fd = stderr; logfile = stderr;
} }
} else { } else {
/* Default to stderr if no log file specified */ /* Default to stderr if no log file specified */
assert(!daemonized); assert(!daemonized);
fd = stderr; logfile = stderr;
} }
log_append = 1; log_append = 1;
logfile = g_new0(RCUCloseFILE, 1);
logfile->fd = fd;
qatomic_rcu_set(&global_file, logfile); qatomic_rcu_set(&global_file, logfile);
} }
return true; return true;