7686729069
Extract protocol-agnostic FenceTimeline from Intel to shared src/drivers/fence.rs — atomic-based fence tracking suitable for Intel, VIRGL, and AMD drivers. Extract protocol-agnostic SyncobjManager from Intel to shared src/drivers/syncobj.rs — syncobj create/destroy/signal/reset/ wait/query and sync_file fd export/import. Wire both into VirtioDriver: - Add FenceTimeline + SyncobjManager fields - Implement all 5 GpuDriver syncobj trait methods (create, destroy, wait, export_fd, import_fd) - Track fence seqnos in virgl_submit_3d (allocate before submit, signal after completion) Intel fence.rs and syncobj.rs converted to thin re-export modules pointing at shared sources — no behavioral change for Intel driver. This gives Mesa VIRGL userspace the standard DRM syncobj API for GPU/compositor synchronization.
535 lines
12 KiB
C
535 lines
12 KiB
C
/* Functions from hack's utils library.
|
|
Copyright (C) 1989-2017 Free Software Foundation, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
|
|
#include "unlocked-io.h"
|
|
#include "utils.h"
|
|
#include "fwriting.h"
|
|
|
|
const char *myname;
|
|
|
|
/* Store information about files opened with ck_fopen
|
|
so that error messages from ck_fread, ck_fwrite, etc. can print the
|
|
name of the file that had the error */
|
|
|
|
struct open_file
|
|
{
|
|
FILE *fp;
|
|
char *name;
|
|
struct open_file *link;
|
|
unsigned temp : 1;
|
|
};
|
|
|
|
static struct open_file *open_files = NULL;
|
|
static void do_ck_fclose (FILE *fp);
|
|
|
|
/* Print an error message and exit */
|
|
|
|
void
|
|
panic(const char *str, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "%s: ", myname);
|
|
va_start(ap, str);
|
|
vfprintf(stderr, str, ap);
|
|
va_end(ap);
|
|
putc('\n', stderr);
|
|
|
|
/* Unlink the temporary files. */
|
|
while (open_files)
|
|
{
|
|
if (open_files->temp)
|
|
{
|
|
fclose (open_files->fp);
|
|
errno = 0;
|
|
unlink (open_files->name);
|
|
if (errno != 0)
|
|
fprintf (stderr, _("cannot remove %s: %s"), open_files->name,
|
|
strerror (errno));
|
|
}
|
|
|
|
open_files = open_files->link;
|
|
}
|
|
|
|
exit(EXIT_PANIC);
|
|
}
|
|
|
|
|
|
/* Internal routine to get a filename from open_files */
|
|
static const char * _GL_ATTRIBUTE_PURE
|
|
utils_fp_name(FILE *fp)
|
|
{
|
|
struct open_file *p;
|
|
|
|
for (p=open_files; p; p=p->link)
|
|
if (p->fp == fp)
|
|
return p->name;
|
|
if (fp == stdin)
|
|
return "stdin";
|
|
else if (fp == stdout)
|
|
return "stdout";
|
|
else if (fp == stderr)
|
|
return "stderr";
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static void
|
|
register_open_file (FILE *fp, const char *name)
|
|
{
|
|
struct open_file *p;
|
|
for (p=open_files; p; p=p->link)
|
|
{
|
|
if (fp == p->fp)
|
|
{
|
|
free(p->name);
|
|
break;
|
|
}
|
|
}
|
|
if (!p)
|
|
{
|
|
p = MALLOC(1, struct open_file);
|
|
p->link = open_files;
|
|
open_files = p;
|
|
}
|
|
p->name = ck_strdup(name);
|
|
p->fp = fp;
|
|
p->temp = false;
|
|
}
|
|
|
|
/* Panic on failing fopen */
|
|
FILE *
|
|
ck_fopen(const char *name, const char *mode, int fail)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fopen (name, mode);
|
|
if (!fp)
|
|
{
|
|
if (fail)
|
|
panic(_("couldn't open file %s: %s"), name, strerror(errno));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
register_open_file (fp, name);
|
|
return fp;
|
|
}
|
|
|
|
/* Panic on failing fdopen */
|
|
FILE *
|
|
ck_fdopen( int fd, const char *name, const char *mode, int fail)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fdopen (fd, mode);
|
|
if (!fp)
|
|
{
|
|
if (fail)
|
|
panic(_("couldn't attach to %s: %s"), name, strerror(errno));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
register_open_file (fp, name);
|
|
return fp;
|
|
}
|
|
|
|
FILE *
|
|
ck_mkstemp (char **p_filename, const char *tmpdir,
|
|
const char *base, const char *mode)
|
|
{
|
|
char *template = xmalloc (strlen (tmpdir) + strlen (base) + 8);
|
|
sprintf (template, "%s/%sXXXXXX", tmpdir, base);
|
|
|
|
/* The ownership might change, so omit some permissions at first
|
|
so unauthorized users cannot nip in before the file is ready.
|
|
mkstemp forces O_BINARY on cygwin, so use mkostemp instead. */
|
|
mode_t save_umask = umask (0700);
|
|
int fd = mkostemp (template, 0);
|
|
umask (save_umask);
|
|
if (fd == -1)
|
|
panic(_("couldn't open temporary file %s: %s"), template, strerror(errno));
|
|
|
|
*p_filename = template;
|
|
FILE *fp = fdopen (fd, mode);
|
|
register_open_file (fp, template);
|
|
return fp;
|
|
}
|
|
|
|
/* Panic on failing fwrite */
|
|
void
|
|
ck_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
|
|
{
|
|
clearerr(stream);
|
|
if (size && fwrite(ptr, size, nmemb, stream) != nmemb)
|
|
panic(ngettext("couldn't write %llu item to %s: %s",
|
|
"couldn't write %llu items to %s: %s", nmemb),
|
|
(unsigned long long) nmemb, utils_fp_name(stream),
|
|
strerror(errno));
|
|
}
|
|
|
|
/* Panic on failing fread */
|
|
size_t
|
|
ck_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
|
|
{
|
|
clearerr(stream);
|
|
if (size && (nmemb=fread(ptr, size, nmemb, stream)) <= 0 && ferror(stream))
|
|
panic(_("read error on %s: %s"), utils_fp_name(stream), strerror(errno));
|
|
|
|
return nmemb;
|
|
}
|
|
|
|
size_t
|
|
ck_getdelim(char **text, size_t *buflen, char buffer_delimiter, FILE *stream)
|
|
{
|
|
ssize_t result;
|
|
bool error;
|
|
|
|
error = ferror (stream);
|
|
if (!error)
|
|
{
|
|
result = getdelim (text, buflen, buffer_delimiter, stream);
|
|
error = ferror (stream);
|
|
}
|
|
|
|
if (error)
|
|
panic (_("read error on %s: %s"), utils_fp_name(stream), strerror(errno));
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Panic on failing fflush */
|
|
void
|
|
ck_fflush(FILE *stream)
|
|
{
|
|
if (!fwriting(stream))
|
|
return;
|
|
|
|
clearerr(stream);
|
|
if (fflush(stream) == EOF && errno != EBADF)
|
|
panic("couldn't flush %s: %s", utils_fp_name(stream), strerror(errno));
|
|
}
|
|
|
|
/* Panic on failing fclose */
|
|
void
|
|
ck_fclose(FILE *stream)
|
|
{
|
|
struct open_file r;
|
|
struct open_file *prev;
|
|
struct open_file *cur;
|
|
|
|
/* a NULL stream means to close all files */
|
|
r.link = open_files;
|
|
prev = &r;
|
|
while ( (cur = prev->link) )
|
|
{
|
|
if (!stream || stream == cur->fp)
|
|
{
|
|
do_ck_fclose (cur->fp);
|
|
prev->link = cur->link;
|
|
free(cur->name);
|
|
free(cur);
|
|
}
|
|
else
|
|
prev = cur;
|
|
}
|
|
|
|
open_files = r.link;
|
|
|
|
/* Also care about stdout, because if it is redirected the
|
|
last output operations might fail and it is important
|
|
to signal this as an error (perhaps to make). */
|
|
if (!stream)
|
|
{
|
|
do_ck_fclose (stdout);
|
|
do_ck_fclose (stderr);
|
|
}
|
|
}
|
|
|
|
/* Close a single file. */
|
|
void
|
|
do_ck_fclose(FILE *fp)
|
|
{
|
|
ck_fflush(fp);
|
|
clearerr(fp);
|
|
|
|
if (fclose(fp) == EOF)
|
|
panic("couldn't close %s: %s", utils_fp_name(fp), strerror(errno));
|
|
}
|
|
|
|
|
|
/* Follow symlink and panic if something fails. Return the ultimate
|
|
symlink target, stored in a temporary buffer that the caller should
|
|
not free. */
|
|
const char *
|
|
follow_symlink(const char *fname)
|
|
{
|
|
#ifdef ENABLE_FOLLOW_SYMLINKS
|
|
static char *buf1, *buf2;
|
|
static int buf_size;
|
|
|
|
struct stat statbuf;
|
|
const char *buf = fname, *c;
|
|
int rc;
|
|
|
|
if (buf_size == 0)
|
|
{
|
|
buf1 = ck_malloc (PATH_MAX + 1);
|
|
buf2 = ck_malloc (PATH_MAX + 1);
|
|
buf_size = PATH_MAX + 1;
|
|
}
|
|
|
|
while ((rc = lstat (buf, &statbuf)) == 0
|
|
&& (statbuf.st_mode & S_IFLNK) == S_IFLNK)
|
|
{
|
|
if (buf == buf2)
|
|
{
|
|
strcpy (buf1, buf2);
|
|
buf = buf1;
|
|
}
|
|
|
|
while ((rc = readlink (buf, buf2, buf_size)) == buf_size)
|
|
{
|
|
buf_size *= 2;
|
|
buf1 = ck_realloc (buf1, buf_size);
|
|
buf2 = ck_realloc (buf2, buf_size);
|
|
}
|
|
if (rc < 0)
|
|
panic (_("couldn't follow symlink %s: %s"), buf, strerror(errno));
|
|
else
|
|
buf2 [rc] = '\0';
|
|
|
|
if (buf2[0] != '/' && (c = strrchr (buf, '/')) != NULL)
|
|
{
|
|
/* Need to handle relative paths with care. Reallocate buf1 and
|
|
buf2 to be big enough. */
|
|
int len = c - buf + 1;
|
|
if (len + rc + 1 > buf_size)
|
|
{
|
|
buf_size = len + rc + 1;
|
|
buf1 = ck_realloc (buf1, buf_size);
|
|
buf2 = ck_realloc (buf2, buf_size);
|
|
}
|
|
|
|
/* Always store the new path in buf1. */
|
|
if (buf != buf1)
|
|
memcpy (buf1, buf, len);
|
|
|
|
/* Tack the relative symlink at the end of buf1. */
|
|
memcpy (buf1 + len, buf2, rc + 1);
|
|
buf = buf1;
|
|
}
|
|
else
|
|
{
|
|
/* Use buf2 as the buffer, it saves a strcpy if it is not pointing to
|
|
another link. It works for absolute symlinks, and as long as
|
|
symlinks do not leave the current directory. */
|
|
buf = buf2;
|
|
}
|
|
}
|
|
|
|
if (rc < 0)
|
|
panic (_("cannot stat %s: %s"), buf, strerror(errno));
|
|
|
|
return buf;
|
|
#else
|
|
return fname;
|
|
#endif /* ENABLE_FOLLOW_SYMLINKS */
|
|
}
|
|
|
|
/* Panic on failing rename */
|
|
void
|
|
ck_rename (const char *from, const char *to, const char *unlink_if_fail)
|
|
{
|
|
int rd = rename (from, to);
|
|
if (rd != -1)
|
|
return;
|
|
|
|
if (unlink_if_fail)
|
|
{
|
|
int save_errno = errno;
|
|
errno = 0;
|
|
unlink (unlink_if_fail);
|
|
|
|
/* Failure to remove the temporary file is more severe,
|
|
so trigger it first. */
|
|
if (errno != 0)
|
|
panic (_("cannot remove %s: %s"), unlink_if_fail, strerror (errno));
|
|
|
|
errno = save_errno;
|
|
}
|
|
|
|
panic (_("cannot rename %s: %s"), from, strerror (errno));
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Panic on failing malloc */
|
|
void *
|
|
ck_malloc(size_t size)
|
|
{
|
|
void *ret = calloc(1, size ? size : 1);
|
|
if (!ret)
|
|
panic("couldn't allocate memory");
|
|
return ret;
|
|
}
|
|
|
|
/* Panic on failing realloc */
|
|
void *
|
|
ck_realloc(void *ptr, size_t size)
|
|
{
|
|
void *ret;
|
|
|
|
if (size == 0)
|
|
{
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
if (!ptr)
|
|
return ck_malloc(size);
|
|
ret = realloc(ptr, size);
|
|
if (!ret)
|
|
panic("couldn't re-allocate memory");
|
|
return ret;
|
|
}
|
|
|
|
/* Return a malloc()'d copy of a string */
|
|
char *
|
|
ck_strdup(const char *str)
|
|
{
|
|
char *ret = MALLOC(strlen(str)+1, char);
|
|
return strcpy(ret, str);
|
|
}
|
|
|
|
/* Return a malloc()'d copy of a block of memory */
|
|
void *
|
|
ck_memdup(const void *buf, size_t len)
|
|
{
|
|
void *ret = ck_malloc(len);
|
|
return memcpy(ret, buf, len);
|
|
}
|
|
|
|
|
|
/* Implement a variable sized buffer of `stuff'. We don't know what it is,
|
|
nor do we care, as long as it doesn't mind being aligned by malloc. */
|
|
|
|
struct buffer
|
|
{
|
|
size_t allocated;
|
|
size_t length;
|
|
char *b;
|
|
};
|
|
|
|
#define MIN_ALLOCATE 50
|
|
|
|
struct buffer *
|
|
init_buffer(void)
|
|
{
|
|
struct buffer *b = MALLOC(1, struct buffer);
|
|
b->b = MALLOC(MIN_ALLOCATE, char);
|
|
b->allocated = MIN_ALLOCATE;
|
|
b->length = 0;
|
|
return b;
|
|
}
|
|
|
|
char *
|
|
get_buffer(struct buffer const *b)
|
|
{
|
|
return b->b;
|
|
}
|
|
|
|
size_t
|
|
size_buffer(struct buffer const *b)
|
|
{
|
|
return b->length;
|
|
}
|
|
|
|
static void
|
|
resize_buffer(struct buffer *b, size_t newlen)
|
|
{
|
|
char *try = NULL;
|
|
size_t alen = b->allocated;
|
|
|
|
if (newlen <= alen)
|
|
return;
|
|
alen *= 2;
|
|
if (newlen < alen)
|
|
try = realloc(b->b, alen); /* Note: *not* the REALLOC() macro! */
|
|
if (!try)
|
|
{
|
|
alen = newlen;
|
|
try = REALLOC(b->b, alen, char);
|
|
}
|
|
b->allocated = alen;
|
|
b->b = try;
|
|
}
|
|
|
|
char *
|
|
add_buffer(struct buffer *b, const char *p, size_t n)
|
|
{
|
|
char *result;
|
|
if (b->allocated - b->length < n)
|
|
resize_buffer(b, b->length+n);
|
|
result = memcpy(b->b + b->length, p, n);
|
|
b->length += n;
|
|
return result;
|
|
}
|
|
|
|
char *
|
|
add1_buffer(struct buffer *b, int c)
|
|
{
|
|
/* This special case should be kept cheap;
|
|
* don't make it just a mere convenience
|
|
* wrapper for add_buffer() -- even "builtin"
|
|
* versions of memcpy(a, b, 1) can become
|
|
* expensive when called too often.
|
|
*/
|
|
if (c != EOF)
|
|
{
|
|
char *result;
|
|
if (b->allocated - b->length < 1)
|
|
resize_buffer(b, b->length+1);
|
|
result = b->b + b->length++;
|
|
*result = c;
|
|
return result;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
free_buffer(struct buffer *b)
|
|
{
|
|
if (b)
|
|
free(b->b);
|
|
free(b);
|
|
}
|