mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
libxenevtchn will provide a stable API and ABI for accessing the evtchn device. The functions are moved into the xenevtchn namespace to make a clean break from libxc and avoid ambiguity regarding which interfaces are stable. All in-tree users are updated to use the new names. Upon request (via #define XC_WANT_COMPAT_EVTCHN_API) libxenctrl will provide a compat API for the old names. This is used by qemu-xen for the time being. qemu-xen-traditional is updated in lockstep. This leaves a few event channel related functions which go via privcmd (EVTCHNOP) rather than ioctls on the /dev/xen/evtchn device in libxenctrl. Specifically: - xc_evtchn_alloc_unbound - xc_evtchn_reset - xc_evtchn_status Note that xc_evtchn_alloc_unbound's functionality is also provided by xenevtchn_bind_unbound_port() (née xc_evtchn_bind_unbound_port) and is probably redundant. These functions do not appear to be needed by qemu-dm, qemu-pv (provision of device model to HVM guests and PV backends respectively) or by libvchan suggesting they are not needed by non-toolstack uses of event channels. QEMU does use these in hw/xenpv/xen_domainbuild.c but that is a "toolstack use". The new library uses a version script to ensure that only expected symbols are exported and to version them such that ABI guarantees can be kept in the future. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> [ ijc -- updated MINIOS_UPSTREAM_REVISION and QEMU_TRADITIONAL_REVISION ]
203 lines
5.0 KiB
C
203 lines
5.0 KiB
C
/*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <xenevtchn.h>
|
|
|
|
#include "xc_private.h"
|
|
#include "xenguest.h"
|
|
|
|
#define SUSPEND_LOCK_FILE XEN_RUN_DIR "/suspend-evtchn-%d.lock"
|
|
|
|
/*
|
|
* locking
|
|
*/
|
|
|
|
#define ERR(x) do{ \
|
|
ERROR("Can't " #x " lock file for suspend event channel %s: %s\n", \
|
|
suspend_file, strerror(errno)); \
|
|
goto err; \
|
|
}while(0)
|
|
|
|
#define SUSPEND_FILE_BUFLEN (sizeof(SUSPEND_LOCK_FILE) + 10)
|
|
|
|
static void get_suspend_file(char buf[], int domid)
|
|
{
|
|
snprintf(buf, SUSPEND_FILE_BUFLEN, SUSPEND_LOCK_FILE, domid);
|
|
}
|
|
|
|
static int lock_suspend_event(xc_interface *xch, int domid, int *lockfd)
|
|
{
|
|
int fd = -1, r;
|
|
char suspend_file[SUSPEND_FILE_BUFLEN];
|
|
struct stat ours, theirs;
|
|
struct flock fl;
|
|
|
|
get_suspend_file(suspend_file, domid);
|
|
|
|
*lockfd = -1;
|
|
|
|
for (;;) {
|
|
if (fd >= 0)
|
|
close (fd);
|
|
|
|
fd = open(suspend_file, O_CREAT | O_RDWR, 0600);
|
|
if (fd < 0)
|
|
ERR("create");
|
|
|
|
r = fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
if (r)
|
|
ERR("fcntl F_SETFD FD_CLOEXEC");
|
|
|
|
memset(&fl, 0, sizeof(fl));
|
|
fl.l_type = F_WRLCK;
|
|
fl.l_whence = SEEK_SET;
|
|
fl.l_len = 1;
|
|
r = fcntl(fd, F_SETLK, &fl);
|
|
if (r)
|
|
ERR("fcntl F_SETLK");
|
|
|
|
r = fstat(fd, &ours);
|
|
if (r)
|
|
ERR("fstat");
|
|
|
|
r = stat(suspend_file, &theirs);
|
|
if (r) {
|
|
if (errno == ENOENT)
|
|
/* try again */
|
|
continue;
|
|
ERR("stat");
|
|
}
|
|
|
|
if (ours.st_ino != theirs.st_ino)
|
|
/* someone else must have removed it while we were locking it */
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
*lockfd = fd;
|
|
return 0;
|
|
|
|
err:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int unlock_suspend_event(xc_interface *xch, int domid, int *lockfd)
|
|
{
|
|
int r;
|
|
char suspend_file[SUSPEND_FILE_BUFLEN];
|
|
|
|
if (*lockfd < 0)
|
|
return 0;
|
|
|
|
get_suspend_file(suspend_file, domid);
|
|
|
|
r = unlink(suspend_file);
|
|
if (r)
|
|
ERR("unlink");
|
|
|
|
r = close(*lockfd);
|
|
*lockfd = -1;
|
|
if (r)
|
|
ERR("close");
|
|
|
|
err:
|
|
if (*lockfd >= 0)
|
|
close(*lockfd);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int xc_await_suspend(xc_interface *xch, xenevtchn_handle *xce, int suspend_evtchn)
|
|
{
|
|
int rc;
|
|
|
|
do {
|
|
rc = xenevtchn_pending(xce);
|
|
if (rc < 0) {
|
|
ERROR("error polling suspend notification channel: %d", rc);
|
|
return -1;
|
|
}
|
|
} while (rc != suspend_evtchn);
|
|
|
|
/* harmless for one-off suspend */
|
|
if (xenevtchn_unmask(xce, suspend_evtchn) < 0)
|
|
ERROR("failed to unmask suspend notification channel: %d", rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Internal callers are allowed to call this with suspend_evtchn<0
|
|
* but *lockfd>0. */
|
|
int xc_suspend_evtchn_release(xc_interface *xch, xenevtchn_handle *xce,
|
|
int domid, int suspend_evtchn, int *lockfd)
|
|
{
|
|
if (suspend_evtchn >= 0)
|
|
xenevtchn_unbind(xce, suspend_evtchn);
|
|
|
|
return unlock_suspend_event(xch, domid, lockfd);
|
|
}
|
|
|
|
int xc_suspend_evtchn_init_sane(xc_interface *xch, xenevtchn_handle *xce,
|
|
int domid, int port, int *lockfd)
|
|
{
|
|
int rc, suspend_evtchn = -1;
|
|
|
|
if (lock_suspend_event(xch, domid, lockfd)) {
|
|
errno = EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
suspend_evtchn = xenevtchn_bind_interdomain(xce, domid, port);
|
|
if (suspend_evtchn < 0) {
|
|
ERROR("failed to bind suspend event channel: %d", suspend_evtchn);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = xc_domain_subscribe_for_suspend(xch, domid, port);
|
|
if (rc < 0) {
|
|
ERROR("failed to subscribe to domain: %d", rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
return suspend_evtchn;
|
|
|
|
cleanup:
|
|
xc_suspend_evtchn_release(xch, xce, domid, suspend_evtchn, lockfd);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int xc_suspend_evtchn_init_exclusive(xc_interface *xch, xenevtchn_handle *xce,
|
|
int domid, int port, int *lockfd)
|
|
{
|
|
int suspend_evtchn;
|
|
|
|
suspend_evtchn = xc_suspend_evtchn_init_sane(xch, xce, domid, port, lockfd);
|
|
if (suspend_evtchn < 0)
|
|
return suspend_evtchn;
|
|
|
|
/* event channel is pending immediately after binding */
|
|
xc_await_suspend(xch, xce, suspend_evtchn);
|
|
|
|
return suspend_evtchn;
|
|
}
|