/* * 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 . */ #include #include #include #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[], uint32_t domid) { snprintf(buf, SUSPEND_FILE_BUFLEN, SUSPEND_LOCK_FILE, domid); } static int lock_suspend_event(xc_interface *xch, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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; }