summaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorStefano Stabellini <sstabellini@kernel.org>2017-10-30 18:40:58 -0400
committerBoris Ostrovsky <boris.ostrovsky@oracle.com>2017-10-31 09:05:53 -0400
commit9774c6cca26610d065a75d2ac8c5e3fcf0a209b3 (patch)
treed46c59c17e79e05cd72c749b659c65d781da0c14 /drivers/xen
parent1853f11d72ed46310ff3c5a60264b2f8a53f8c25 (diff)
xen/pvcalls: implement accept command
Introduce a waitqueue to allow only one outstanding accept command at any given time and to implement polling on the passive socket. Introduce a flags field to keep track of in-flight accept and poll commands. Send PVCALLS_ACCEPT to the backend. Allocate a new active socket. Make sure that only one accept command is executed at any given time by setting PVCALLS_FLAG_ACCEPT_INFLIGHT and waiting on the inflight_accept_req waitqueue. Convert the new struct sock_mapping pointer into an uintptr_t and use it as id for the new socket to pass to the backend. Check if the accept call is non-blocking: in that case after sending the ACCEPT command to the backend store the sock_mapping pointer of the new struct and the inflight req_id then return -EAGAIN (which will respond only when there is something to accept). Next time accept is called, we'll check if the ACCEPT command has been answered, if so we'll pick up where we left off, otherwise we return -EAGAIN again. Note that, differently from the other commands, we can use wait_event_interruptible (instead of wait_event) in the case of accept as we are able to track the req_id of the ACCEPT response that we are waiting. Signed-off-by: Stefano Stabellini <stefano@aporeto.com> Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com> CC: boris.ostrovsky@oracle.com CC: jgross@suse.com Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/pvcalls-front.c145
-rw-r--r--drivers/xen/pvcalls-front.h3
2 files changed, 148 insertions, 0 deletions
diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c
index 09b2a64fef91..d781ac4bcbde 100644
--- a/drivers/xen/pvcalls-front.c
+++ b/drivers/xen/pvcalls-front.c
@@ -76,6 +76,16 @@ struct sock_mapping {
76#define PVCALLS_STATUS_BIND 1 76#define PVCALLS_STATUS_BIND 1
77#define PVCALLS_STATUS_LISTEN 2 77#define PVCALLS_STATUS_LISTEN 2
78 uint8_t status; 78 uint8_t status;
79 /*
80 * Internal state-machine flags.
81 * Only one accept operation can be inflight for a socket.
82 * Only one poll operation can be inflight for a given socket.
83 */
84#define PVCALLS_FLAG_ACCEPT_INFLIGHT 0
85 uint8_t flags;
86 uint32_t inflight_req_id;
87 struct sock_mapping *accept_map;
88 wait_queue_head_t inflight_accept_req;
79 } passive; 89 } passive;
80 }; 90 };
81}; 91};
@@ -391,6 +401,8 @@ int pvcalls_front_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
391 memcpy(req->u.bind.addr, addr, sizeof(*addr)); 401 memcpy(req->u.bind.addr, addr, sizeof(*addr));
392 req->u.bind.len = addr_len; 402 req->u.bind.len = addr_len;
393 403
404 init_waitqueue_head(&map->passive.inflight_accept_req);
405
394 map->active_socket = false; 406 map->active_socket = false;
395 407
396 bedata->ring.req_prod_pvt++; 408 bedata->ring.req_prod_pvt++;
@@ -469,6 +481,139 @@ int pvcalls_front_listen(struct socket *sock, int backlog)
469 return ret; 481 return ret;
470} 482}
471 483
484int pvcalls_front_accept(struct socket *sock, struct socket *newsock, int flags)
485{
486 struct pvcalls_bedata *bedata;
487 struct sock_mapping *map;
488 struct sock_mapping *map2 = NULL;
489 struct xen_pvcalls_request *req;
490 int notify, req_id, ret, evtchn, nonblock;
491
492 pvcalls_enter();
493 if (!pvcalls_front_dev) {
494 pvcalls_exit();
495 return -ENOTCONN;
496 }
497 bedata = dev_get_drvdata(&pvcalls_front_dev->dev);
498
499 map = (struct sock_mapping *) sock->sk->sk_send_head;
500 if (!map) {
501 pvcalls_exit();
502 return -ENOTSOCK;
503 }
504
505 if (map->passive.status != PVCALLS_STATUS_LISTEN) {
506 pvcalls_exit();
507 return -EINVAL;
508 }
509
510 nonblock = flags & SOCK_NONBLOCK;
511 /*
512 * Backend only supports 1 inflight accept request, will return
513 * errors for the others
514 */
515 if (test_and_set_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
516 (void *)&map->passive.flags)) {
517 req_id = READ_ONCE(map->passive.inflight_req_id);
518 if (req_id != PVCALLS_INVALID_ID &&
519 READ_ONCE(bedata->rsp[req_id].req_id) == req_id) {
520 map2 = map->passive.accept_map;
521 goto received;
522 }
523 if (nonblock) {
524 pvcalls_exit();
525 return -EAGAIN;
526 }
527 if (wait_event_interruptible(map->passive.inflight_accept_req,
528 !test_and_set_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
529 (void *)&map->passive.flags))) {
530 pvcalls_exit();
531 return -EINTR;
532 }
533 }
534
535 spin_lock(&bedata->socket_lock);
536 ret = get_request(bedata, &req_id);
537 if (ret < 0) {
538 clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
539 (void *)&map->passive.flags);
540 spin_unlock(&bedata->socket_lock);
541 pvcalls_exit();
542 return ret;
543 }
544 map2 = kzalloc(sizeof(*map2), GFP_KERNEL);
545 if (map2 == NULL) {
546 clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
547 (void *)&map->passive.flags);
548 spin_unlock(&bedata->socket_lock);
549 pvcalls_exit();
550 return -ENOMEM;
551 }
552 ret = create_active(map2, &evtchn);
553 if (ret < 0) {
554 kfree(map2);
555 clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
556 (void *)&map->passive.flags);
557 spin_unlock(&bedata->socket_lock);
558 pvcalls_exit();
559 return ret;
560 }
561 list_add_tail(&map2->list, &bedata->socket_mappings);
562
563 req = RING_GET_REQUEST(&bedata->ring, req_id);
564 req->req_id = req_id;
565 req->cmd = PVCALLS_ACCEPT;
566 req->u.accept.id = (uintptr_t) map;
567 req->u.accept.ref = map2->active.ref;
568 req->u.accept.id_new = (uintptr_t) map2;
569 req->u.accept.evtchn = evtchn;
570 map->passive.accept_map = map2;
571
572 bedata->ring.req_prod_pvt++;
573 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&bedata->ring, notify);
574 spin_unlock(&bedata->socket_lock);
575 if (notify)
576 notify_remote_via_irq(bedata->irq);
577 /* We could check if we have received a response before returning. */
578 if (nonblock) {
579 WRITE_ONCE(map->passive.inflight_req_id, req_id);
580 pvcalls_exit();
581 return -EAGAIN;
582 }
583
584 if (wait_event_interruptible(bedata->inflight_req,
585 READ_ONCE(bedata->rsp[req_id].req_id) == req_id)) {
586 pvcalls_exit();
587 return -EINTR;
588 }
589 /* read req_id, then the content */
590 smp_rmb();
591
592received:
593 map2->sock = newsock;
594 newsock->sk = kzalloc(sizeof(*newsock->sk), GFP_KERNEL);
595 if (!newsock->sk) {
596 bedata->rsp[req_id].req_id = PVCALLS_INVALID_ID;
597 map->passive.inflight_req_id = PVCALLS_INVALID_ID;
598 clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT,
599 (void *)&map->passive.flags);
600 pvcalls_front_free_map(bedata, map2);
601 pvcalls_exit();
602 return -ENOMEM;
603 }
604 newsock->sk->sk_send_head = (void *)map2;
605
606 ret = bedata->rsp[req_id].ret;
607 bedata->rsp[req_id].req_id = PVCALLS_INVALID_ID;
608 map->passive.inflight_req_id = PVCALLS_INVALID_ID;
609
610 clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, (void *)&map->passive.flags);
611 wake_up(&map->passive.inflight_accept_req);
612
613 pvcalls_exit();
614 return ret;
615}
616
472static const struct xenbus_device_id pvcalls_front_ids[] = { 617static const struct xenbus_device_id pvcalls_front_ids[] = {
473 { "pvcalls" }, 618 { "pvcalls" },
474 { "" } 619 { "" }
diff --git a/drivers/xen/pvcalls-front.h b/drivers/xen/pvcalls-front.h
index aa8fe1007da5..ab4f1dad3142 100644
--- a/drivers/xen/pvcalls-front.h
+++ b/drivers/xen/pvcalls-front.h
@@ -10,5 +10,8 @@ int pvcalls_front_bind(struct socket *sock,
10 struct sockaddr *addr, 10 struct sockaddr *addr,
11 int addr_len); 11 int addr_len);
12int pvcalls_front_listen(struct socket *sock, int backlog); 12int pvcalls_front_listen(struct socket *sock, int backlog);
13int pvcalls_front_accept(struct socket *sock,
14 struct socket *newsock,
15 int flags);
13 16
14#endif 17#endif