aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefano Stabellini <sstabellini@kernel.org>2017-07-06 14:01:06 -0400
committerBoris Ostrovsky <boris.ostrovsky@oracle.com>2017-08-31 09:45:55 -0400
commit5db4d286a8ef88a04247a005df9cdd4bce3c7673 (patch)
tree770bd3fe65e1f840f94948c84f68c58c4ae4e6c5
parentfb0298754ab79f0aca1a8162f9aeb5b097c0a1b1 (diff)
xen/pvcalls: implement connect command
Allocate a socket. Keep track of socket <-> ring mappings with a new data structure, called sock_mapping. Implement the connect command by calling inet_stream_connect, and mapping the new indexes page and data ring. Allocate a workqueue and a work_struct, called ioworker, to perform reads and writes to the socket. When an active socket is closed (sk_state_change), set in_error to -ENOTCONN and notify the other end, as specified by the protocol. sk_data_ready and pvcalls_back_ioworker will be implemented later. [ boris: fixed whitespaces ] Signed-off-by: Stefano Stabellini <stefano@aporeto.com> Reviewed-by: Juergen Gross <jgross@suse.com> CC: boris.ostrovsky@oracle.com CC: jgross@suse.com Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
-rw-r--r--drivers/xen/pvcalls-back.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/xen/pvcalls-back.c b/drivers/xen/pvcalls-back.c
index 8f436e51053a..c0fc774221ca 100644
--- a/drivers/xen/pvcalls-back.c
+++ b/drivers/xen/pvcalls-back.c
@@ -54,6 +54,39 @@ struct pvcalls_fedata {
54 struct semaphore socket_lock; 54 struct semaphore socket_lock;
55}; 55};
56 56
57struct pvcalls_ioworker {
58 struct work_struct register_work;
59 struct workqueue_struct *wq;
60};
61
62struct sock_mapping {
63 struct list_head list;
64 struct pvcalls_fedata *fedata;
65 struct socket *sock;
66 uint64_t id;
67 grant_ref_t ref;
68 struct pvcalls_data_intf *ring;
69 void *bytes;
70 struct pvcalls_data data;
71 uint32_t ring_order;
72 int irq;
73 atomic_t read;
74 atomic_t write;
75 atomic_t io;
76 atomic_t release;
77 void (*saved_data_ready)(struct sock *sk);
78 struct pvcalls_ioworker ioworker;
79};
80
81static irqreturn_t pvcalls_back_conn_event(int irq, void *sock_map);
82static int pvcalls_back_release_active(struct xenbus_device *dev,
83 struct pvcalls_fedata *fedata,
84 struct sock_mapping *map);
85
86static void pvcalls_back_ioworker(struct work_struct *work)
87{
88}
89
57static int pvcalls_back_socket(struct xenbus_device *dev, 90static int pvcalls_back_socket(struct xenbus_device *dev,
58 struct xen_pvcalls_request *req) 91 struct xen_pvcalls_request *req)
59{ 92{
@@ -82,9 +115,150 @@ static int pvcalls_back_socket(struct xenbus_device *dev,
82 return 0; 115 return 0;
83} 116}
84 117
118static void pvcalls_sk_state_change(struct sock *sock)
119{
120 struct sock_mapping *map = sock->sk_user_data;
121 struct pvcalls_data_intf *intf;
122
123 if (map == NULL)
124 return;
125
126 intf = map->ring;
127 intf->in_error = -ENOTCONN;
128 notify_remote_via_irq(map->irq);
129}
130
131static void pvcalls_sk_data_ready(struct sock *sock)
132{
133}
134
135static struct sock_mapping *pvcalls_new_active_socket(
136 struct pvcalls_fedata *fedata,
137 uint64_t id,
138 grant_ref_t ref,
139 uint32_t evtchn,
140 struct socket *sock)
141{
142 int ret;
143 struct sock_mapping *map;
144 void *page;
145
146 map = kzalloc(sizeof(*map), GFP_KERNEL);
147 if (map == NULL)
148 return NULL;
149
150 map->fedata = fedata;
151 map->sock = sock;
152 map->id = id;
153 map->ref = ref;
154
155 ret = xenbus_map_ring_valloc(fedata->dev, &ref, 1, &page);
156 if (ret < 0)
157 goto out;
158 map->ring = page;
159 map->ring_order = map->ring->ring_order;
160 /* first read the order, then map the data ring */
161 virt_rmb();
162 if (map->ring_order > MAX_RING_ORDER) {
163 pr_warn("%s frontend requested ring_order %u, which is > MAX (%u)\n",
164 __func__, map->ring_order, MAX_RING_ORDER);
165 goto out;
166 }
167 ret = xenbus_map_ring_valloc(fedata->dev, map->ring->ref,
168 (1 << map->ring_order), &page);
169 if (ret < 0)
170 goto out;
171 map->bytes = page;
172
173 ret = bind_interdomain_evtchn_to_irqhandler(fedata->dev->otherend_id,
174 evtchn,
175 pvcalls_back_conn_event,
176 0,
177 "pvcalls-backend",
178 map);
179 if (ret < 0)
180 goto out;
181 map->irq = ret;
182
183 map->data.in = map->bytes;
184 map->data.out = map->bytes + XEN_FLEX_RING_SIZE(map->ring_order);
185
186 map->ioworker.wq = alloc_workqueue("pvcalls_io", WQ_UNBOUND, 1);
187 if (!map->ioworker.wq)
188 goto out;
189 atomic_set(&map->io, 1);
190 INIT_WORK(&map->ioworker.register_work, pvcalls_back_ioworker);
191
192 down(&fedata->socket_lock);
193 list_add_tail(&map->list, &fedata->socket_mappings);
194 up(&fedata->socket_lock);
195
196 write_lock_bh(&map->sock->sk->sk_callback_lock);
197 map->saved_data_ready = map->sock->sk->sk_data_ready;
198 map->sock->sk->sk_user_data = map;
199 map->sock->sk->sk_data_ready = pvcalls_sk_data_ready;
200 map->sock->sk->sk_state_change = pvcalls_sk_state_change;
201 write_unlock_bh(&map->sock->sk->sk_callback_lock);
202
203 return map;
204out:
205 down(&fedata->socket_lock);
206 list_del(&map->list);
207 pvcalls_back_release_active(fedata->dev, fedata, map);
208 up(&fedata->socket_lock);
209 return NULL;
210}
211
85static int pvcalls_back_connect(struct xenbus_device *dev, 212static int pvcalls_back_connect(struct xenbus_device *dev,
86 struct xen_pvcalls_request *req) 213 struct xen_pvcalls_request *req)
87{ 214{
215 struct pvcalls_fedata *fedata;
216 int ret = -EINVAL;
217 struct socket *sock;
218 struct sock_mapping *map;
219 struct xen_pvcalls_response *rsp;
220 struct sockaddr *sa = (struct sockaddr *)&req->u.connect.addr;
221
222 fedata = dev_get_drvdata(&dev->dev);
223
224 if (req->u.connect.len < sizeof(sa->sa_family) ||
225 req->u.connect.len > sizeof(req->u.connect.addr) ||
226 sa->sa_family != AF_INET)
227 goto out;
228
229 ret = sock_create(AF_INET, SOCK_STREAM, 0, &sock);
230 if (ret < 0)
231 goto out;
232 ret = inet_stream_connect(sock, sa, req->u.connect.len, 0);
233 if (ret < 0) {
234 sock_release(sock);
235 goto out;
236 }
237
238 map = pvcalls_new_active_socket(fedata,
239 req->u.connect.id,
240 req->u.connect.ref,
241 req->u.connect.evtchn,
242 sock);
243 if (!map) {
244 ret = -EFAULT;
245 sock_release(map->sock);
246 }
247
248out:
249 rsp = RING_GET_RESPONSE(&fedata->ring, fedata->ring.rsp_prod_pvt++);
250 rsp->req_id = req->req_id;
251 rsp->cmd = req->cmd;
252 rsp->u.connect.id = req->u.connect.id;
253 rsp->ret = ret;
254
255 return 0;
256}
257
258static int pvcalls_back_release_active(struct xenbus_device *dev,
259 struct pvcalls_fedata *fedata,
260 struct sock_mapping *map)
261{
88 return 0; 262 return 0;
89} 263}
90 264
@@ -206,6 +380,11 @@ static irqreturn_t pvcalls_back_event(int irq, void *dev_id)
206 return IRQ_HANDLED; 380 return IRQ_HANDLED;
207} 381}
208 382
383static irqreturn_t pvcalls_back_conn_event(int irq, void *sock_map)
384{
385 return IRQ_HANDLED;
386}
387
209static int backend_connect(struct xenbus_device *dev) 388static int backend_connect(struct xenbus_device *dev)
210{ 389{
211 int err, evtchn; 390 int err, evtchn;