]>
Commit | Line | Data |
---|---|---|
4bac07c9 JF |
1 | /****************************************************************************** |
2 | * xenbus_comms.c | |
3 | * | |
4 | * Low level code to talks to Xen Store: ringbuffer and event channel. | |
5 | * | |
6 | * Copyright (C) 2005 Rusty Russell, IBM Corporation | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License version 2 | |
10 | * as published by the Free Software Foundation; or, when distributed | |
11 | * separately from the Linux kernel or incorporated into other | |
12 | * software packages, subject to the following license: | |
13 | * | |
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
15 | * of this source file (the "Software"), to deal in the Software without | |
16 | * restriction, including without limitation the rights to use, copy, modify, | |
17 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
18 | * and to permit persons to whom the Software is furnished to do so, subject to | |
19 | * the following conditions: | |
20 | * | |
21 | * The above copyright notice and this permission notice shall be included in | |
22 | * all copies or substantial portions of the Software. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
29 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
30 | * IN THE SOFTWARE. | |
31 | */ | |
32 | ||
283c0972 JP |
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
34 | ||
4bac07c9 JF |
35 | #include <linux/wait.h> |
36 | #include <linux/interrupt.h> | |
37 | #include <linux/sched.h> | |
38 | #include <linux/err.h> | |
39 | #include <xen/xenbus.h> | |
40 | #include <asm/xen/hypervisor.h> | |
41 | #include <xen/events.h> | |
42 | #include <xen/page.h> | |
43 | #include "xenbus_comms.h" | |
44 | ||
45 | static int xenbus_irq; | |
46 | ||
47 | static DECLARE_WORK(probe_work, xenbus_probe); | |
48 | ||
49 | static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); | |
50 | ||
51 | static irqreturn_t wake_waiting(int irq, void *unused) | |
52 | { | |
53 | if (unlikely(xenstored_ready == 0)) { | |
54 | xenstored_ready = 1; | |
55 | schedule_work(&probe_work); | |
56 | } | |
57 | ||
58 | wake_up(&xb_waitq); | |
59 | return IRQ_HANDLED; | |
60 | } | |
61 | ||
62 | static int check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) | |
63 | { | |
64 | return ((prod - cons) <= XENSTORE_RING_SIZE); | |
65 | } | |
66 | ||
67 | static void *get_output_chunk(XENSTORE_RING_IDX cons, | |
68 | XENSTORE_RING_IDX prod, | |
69 | char *buf, uint32_t *len) | |
70 | { | |
71 | *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); | |
72 | if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) | |
73 | *len = XENSTORE_RING_SIZE - (prod - cons); | |
74 | return buf + MASK_XENSTORE_IDX(prod); | |
75 | } | |
76 | ||
77 | static const void *get_input_chunk(XENSTORE_RING_IDX cons, | |
78 | XENSTORE_RING_IDX prod, | |
79 | const char *buf, uint32_t *len) | |
80 | { | |
81 | *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); | |
82 | if ((prod - cons) < *len) | |
83 | *len = prod - cons; | |
84 | return buf + MASK_XENSTORE_IDX(cons); | |
85 | } | |
86 | ||
87 | /** | |
88 | * xb_write - low level write | |
89 | * @data: buffer to send | |
90 | * @len: length of buffer | |
91 | * | |
92 | * Returns 0 on success, error otherwise. | |
93 | */ | |
94 | int xb_write(const void *data, unsigned len) | |
95 | { | |
96 | struct xenstore_domain_interface *intf = xen_store_interface; | |
97 | XENSTORE_RING_IDX cons, prod; | |
98 | int rc; | |
99 | ||
100 | while (len != 0) { | |
101 | void *dst; | |
102 | unsigned int avail; | |
103 | ||
104 | rc = wait_event_interruptible( | |
105 | xb_waitq, | |
106 | (intf->req_prod - intf->req_cons) != | |
107 | XENSTORE_RING_SIZE); | |
108 | if (rc < 0) | |
109 | return rc; | |
110 | ||
111 | /* Read indexes, then verify. */ | |
112 | cons = intf->req_cons; | |
113 | prod = intf->req_prod; | |
114 | if (!check_indexes(cons, prod)) { | |
115 | intf->req_cons = intf->req_prod = 0; | |
116 | return -EIO; | |
117 | } | |
118 | ||
119 | dst = get_output_chunk(cons, prod, intf->req, &avail); | |
120 | if (avail == 0) | |
121 | continue; | |
122 | if (avail > len) | |
123 | avail = len; | |
124 | ||
125 | /* Must write data /after/ reading the consumer index. */ | |
126 | mb(); | |
127 | ||
128 | memcpy(dst, data, avail); | |
129 | data += avail; | |
130 | len -= avail; | |
131 | ||
132 | /* Other side must not see new producer until data is there. */ | |
133 | wmb(); | |
134 | intf->req_prod += avail; | |
135 | ||
136 | /* Implies mb(): other side will see the updated producer. */ | |
137 | notify_remote_via_evtchn(xen_store_evtchn); | |
138 | } | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | int xb_data_to_read(void) | |
144 | { | |
145 | struct xenstore_domain_interface *intf = xen_store_interface; | |
146 | return (intf->rsp_cons != intf->rsp_prod); | |
147 | } | |
148 | ||
149 | int xb_wait_for_data_to_read(void) | |
150 | { | |
151 | return wait_event_interruptible(xb_waitq, xb_data_to_read()); | |
152 | } | |
153 | ||
154 | int xb_read(void *data, unsigned len) | |
155 | { | |
156 | struct xenstore_domain_interface *intf = xen_store_interface; | |
157 | XENSTORE_RING_IDX cons, prod; | |
158 | int rc; | |
159 | ||
160 | while (len != 0) { | |
161 | unsigned int avail; | |
162 | const char *src; | |
163 | ||
164 | rc = xb_wait_for_data_to_read(); | |
165 | if (rc < 0) | |
166 | return rc; | |
167 | ||
168 | /* Read indexes, then verify. */ | |
169 | cons = intf->rsp_cons; | |
170 | prod = intf->rsp_prod; | |
171 | if (!check_indexes(cons, prod)) { | |
172 | intf->rsp_cons = intf->rsp_prod = 0; | |
173 | return -EIO; | |
174 | } | |
175 | ||
176 | src = get_input_chunk(cons, prod, intf->rsp, &avail); | |
177 | if (avail == 0) | |
178 | continue; | |
179 | if (avail > len) | |
180 | avail = len; | |
181 | ||
182 | /* Must read data /after/ reading the producer index. */ | |
183 | rmb(); | |
184 | ||
185 | memcpy(data, src, avail); | |
186 | data += avail; | |
187 | len -= avail; | |
188 | ||
189 | /* Other side must not see free space until we've copied out */ | |
190 | mb(); | |
191 | intf->rsp_cons += avail; | |
192 | ||
193 | pr_debug("Finished read of %i bytes (%i to go)\n", avail, len); | |
194 | ||
195 | /* Implies mb(): other side will see the updated consumer. */ | |
196 | notify_remote_via_evtchn(xen_store_evtchn); | |
197 | } | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | /** | |
203 | * xb_init_comms - Set up interrupt handler off store event channel. | |
204 | */ | |
205 | int xb_init_comms(void) | |
206 | { | |
207 | struct xenstore_domain_interface *intf = xen_store_interface; | |
4bac07c9 JF |
208 | |
209 | if (intf->req_prod != intf->req_cons) | |
283c0972 JP |
210 | pr_err("request ring is not quiescent (%08x:%08x)!\n", |
211 | intf->req_cons, intf->req_prod); | |
4bac07c9 JF |
212 | |
213 | if (intf->rsp_prod != intf->rsp_cons) { | |
283c0972 JP |
214 | pr_warn("response ring is not quiescent (%08x:%08x): fixing up\n", |
215 | intf->rsp_cons, intf->rsp_prod); | |
116df6f0 OH |
216 | /* breaks kdump */ |
217 | if (!reset_devices) | |
218 | intf->rsp_cons = intf->rsp_prod; | |
4bac07c9 JF |
219 | } |
220 | ||
7d88d32a JF |
221 | if (xenbus_irq) { |
222 | /* Already have an irq; assume we're resuming */ | |
223 | rebind_evtchn_irq(xen_store_evtchn, xenbus_irq); | |
224 | } else { | |
225 | int err; | |
226 | err = bind_evtchn_to_irqhandler(xen_store_evtchn, wake_waiting, | |
227 | 0, "xenbus", &xb_waitq); | |
ecc635f9 | 228 | if (err < 0) { |
283c0972 | 229 | pr_err("request irq failed %i\n", err); |
7d88d32a JF |
230 | return err; |
231 | } | |
4bac07c9 | 232 | |
7d88d32a | 233 | xenbus_irq = err; |
4bac07c9 JF |
234 | } |
235 | ||
4bac07c9 JF |
236 | return 0; |
237 | } | |
d2fb4c51 DDG |
238 | |
239 | void xb_deinit_comms(void) | |
240 | { | |
241 | unbind_from_irqhandler(xenbus_irq, &xb_waitq); | |
242 | xenbus_irq = 0; | |
243 | } |