1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation;
5 * version 2.1 of the License.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include <unistd.h>
17 #include <fcntl.h>
18
19 #include <xenevtchn.h>
20
21 #include "xc_private.h"
22 #include "xenguest.h"
23
24 #define SUSPEND_LOCK_FILE XEN_RUN_DIR "/suspend-evtchn-%d.lock"
25
26 /*
27 * locking
28 */
29
30 #define ERR(x) do{ \
31 ERROR("Can't " #x " lock file for suspend event channel %s: %s\n", \
32 suspend_file, strerror(errno)); \
33 goto err; \
34 }while(0)
35
36 #define SUSPEND_FILE_BUFLEN (sizeof(SUSPEND_LOCK_FILE) + 10)
37
get_suspend_file(char buf[],uint32_t domid)38 static void get_suspend_file(char buf[], uint32_t domid)
39 {
40 snprintf(buf, SUSPEND_FILE_BUFLEN, SUSPEND_LOCK_FILE, domid);
41 }
42
lock_suspend_event(xc_interface * xch,uint32_t domid,int * lockfd)43 static int lock_suspend_event(xc_interface *xch, uint32_t domid, int *lockfd)
44 {
45 int fd = -1, r;
46 char suspend_file[SUSPEND_FILE_BUFLEN];
47 struct stat ours, theirs;
48 struct flock fl;
49
50 get_suspend_file(suspend_file, domid);
51
52 *lockfd = -1;
53
54 for (;;) {
55 if (fd >= 0)
56 close (fd);
57
58 fd = open(suspend_file, O_CREAT | O_RDWR, 0600);
59 if (fd < 0)
60 ERR("create");
61
62 r = fcntl(fd, F_SETFD, FD_CLOEXEC);
63 if (r)
64 ERR("fcntl F_SETFD FD_CLOEXEC");
65
66 memset(&fl, 0, sizeof(fl));
67 fl.l_type = F_WRLCK;
68 fl.l_whence = SEEK_SET;
69 fl.l_len = 1;
70 r = fcntl(fd, F_SETLK, &fl);
71 if (r)
72 ERR("fcntl F_SETLK");
73
74 r = fstat(fd, &ours);
75 if (r)
76 ERR("fstat");
77
78 r = stat(suspend_file, &theirs);
79 if (r) {
80 if (errno == ENOENT)
81 /* try again */
82 continue;
83 ERR("stat");
84 }
85
86 if (ours.st_ino != theirs.st_ino)
87 /* someone else must have removed it while we were locking it */
88 continue;
89
90 break;
91 }
92
93 *lockfd = fd;
94 return 0;
95
96 err:
97 if (fd >= 0)
98 close(fd);
99
100 return -1;
101 }
102
unlock_suspend_event(xc_interface * xch,uint32_t domid,int * lockfd)103 static int unlock_suspend_event(xc_interface *xch, uint32_t domid, int *lockfd)
104 {
105 int r;
106 char suspend_file[SUSPEND_FILE_BUFLEN];
107
108 if (*lockfd < 0)
109 return 0;
110
111 get_suspend_file(suspend_file, domid);
112
113 r = unlink(suspend_file);
114 if (r)
115 ERR("unlink");
116
117 r = close(*lockfd);
118 *lockfd = -1;
119 if (r)
120 ERR("close");
121
122 err:
123 if (*lockfd >= 0)
124 close(*lockfd);
125
126 return -1;
127 }
128
xc_await_suspend(xc_interface * xch,xenevtchn_handle * xce,int suspend_evtchn)129 int xc_await_suspend(xc_interface *xch, xenevtchn_handle *xce, int suspend_evtchn)
130 {
131 int rc;
132
133 do {
134 rc = xenevtchn_pending(xce);
135 if (rc < 0) {
136 ERROR("error polling suspend notification channel: %d", rc);
137 return -1;
138 }
139 } while (rc != suspend_evtchn);
140
141 /* harmless for one-off suspend */
142 if (xenevtchn_unmask(xce, suspend_evtchn) < 0)
143 ERROR("failed to unmask suspend notification channel: %d", rc);
144
145 return 0;
146 }
147
148 /* Internal callers are allowed to call this with suspend_evtchn<0
149 * but *lockfd>0. */
xc_suspend_evtchn_release(xc_interface * xch,xenevtchn_handle * xce,uint32_t domid,int suspend_evtchn,int * lockfd)150 int xc_suspend_evtchn_release(xc_interface *xch, xenevtchn_handle *xce,
151 uint32_t domid, int suspend_evtchn, int *lockfd)
152 {
153 if (suspend_evtchn >= 0)
154 xenevtchn_unbind(xce, suspend_evtchn);
155
156 return unlock_suspend_event(xch, domid, lockfd);
157 }
158
xc_suspend_evtchn_init_sane(xc_interface * xch,xenevtchn_handle * xce,uint32_t domid,int port,int * lockfd)159 int xc_suspend_evtchn_init_sane(xc_interface *xch, xenevtchn_handle *xce,
160 uint32_t domid, int port, int *lockfd)
161 {
162 int rc, suspend_evtchn = -1;
163
164 if (lock_suspend_event(xch, domid, lockfd)) {
165 errno = EINVAL;
166 goto cleanup;
167 }
168
169 suspend_evtchn = xenevtchn_bind_interdomain(xce, domid, port);
170 if (suspend_evtchn < 0) {
171 ERROR("failed to bind suspend event channel: %d", suspend_evtchn);
172 goto cleanup;
173 }
174
175 rc = xc_domain_subscribe_for_suspend(xch, domid, port);
176 if (rc < 0) {
177 ERROR("failed to subscribe to domain: %d", rc);
178 goto cleanup;
179 }
180
181 return suspend_evtchn;
182
183 cleanup:
184 xc_suspend_evtchn_release(xch, xce, domid, suspend_evtchn, lockfd);
185
186 return -1;
187 }
188
xc_suspend_evtchn_init_exclusive(xc_interface * xch,xenevtchn_handle * xce,uint32_t domid,int port,int * lockfd)189 int xc_suspend_evtchn_init_exclusive(xc_interface *xch, xenevtchn_handle *xce,
190 uint32_t domid, int port, int *lockfd)
191 {
192 int suspend_evtchn;
193
194 suspend_evtchn = xc_suspend_evtchn_init_sane(xch, xce, domid, port, lockfd);
195 if (suspend_evtchn < 0)
196 return suspend_evtchn;
197
198 /* event channel is pending immediately after binding */
199 xc_await_suspend(xch, xce, suspend_evtchn);
200
201 return suspend_evtchn;
202 }
203