diff options
author | Stefano Stabellini <sstabellini@kernel.org> | 2017-10-30 18:41:02 -0400 |
---|---|---|
committer | Boris Ostrovsky <boris.ostrovsky@oracle.com> | 2017-10-31 09:05:53 -0400 |
commit | 235a71c5390316429e2c7823119c5ea439816beb (patch) | |
tree | 20ab8b2a3827d6dbce43bf026121e4bb36cfc908 | |
parent | 5842c83596fcfa742978ec2840440ab56c7fdf79 (diff) |
xen/pvcalls: implement release command
Send PVCALLS_RELEASE to the backend and wait for a reply. Take both
in_mutex and out_mutex to avoid concurrent accesses. Then, free the
socket.
For passive sockets, check whether we have already pre-allocated an
active socket for the purpose of being accepted. If so, free that as
well.
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>
-rw-r--r-- | drivers/xen/pvcalls-front.c | 98 | ||||
-rw-r--r-- | drivers/xen/pvcalls-front.h | 1 |
2 files changed, 99 insertions, 0 deletions
diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c index c7d4251c3678..0c1ec6894cc4 100644 --- a/drivers/xen/pvcalls-front.c +++ b/drivers/xen/pvcalls-front.c | |||
@@ -199,6 +199,21 @@ again: | |||
199 | static void pvcalls_front_free_map(struct pvcalls_bedata *bedata, | 199 | static void pvcalls_front_free_map(struct pvcalls_bedata *bedata, |
200 | struct sock_mapping *map) | 200 | struct sock_mapping *map) |
201 | { | 201 | { |
202 | int i; | ||
203 | |||
204 | unbind_from_irqhandler(map->active.irq, map); | ||
205 | |||
206 | spin_lock(&bedata->socket_lock); | ||
207 | if (!list_empty(&map->list)) | ||
208 | list_del_init(&map->list); | ||
209 | spin_unlock(&bedata->socket_lock); | ||
210 | |||
211 | for (i = 0; i < (1 << PVCALLS_RING_ORDER); i++) | ||
212 | gnttab_end_foreign_access(map->active.ring->ref[i], 0, 0); | ||
213 | gnttab_end_foreign_access(map->active.ref, 0, 0); | ||
214 | free_page((unsigned long)map->active.ring); | ||
215 | |||
216 | kfree(map); | ||
202 | } | 217 | } |
203 | 218 | ||
204 | static irqreturn_t pvcalls_front_conn_handler(int irq, void *sock_map) | 219 | static irqreturn_t pvcalls_front_conn_handler(int irq, void *sock_map) |
@@ -972,6 +987,89 @@ unsigned int pvcalls_front_poll(struct file *file, struct socket *sock, | |||
972 | return ret; | 987 | return ret; |
973 | } | 988 | } |
974 | 989 | ||
990 | int pvcalls_front_release(struct socket *sock) | ||
991 | { | ||
992 | struct pvcalls_bedata *bedata; | ||
993 | struct sock_mapping *map; | ||
994 | int req_id, notify, ret; | ||
995 | struct xen_pvcalls_request *req; | ||
996 | |||
997 | if (sock->sk == NULL) | ||
998 | return 0; | ||
999 | |||
1000 | pvcalls_enter(); | ||
1001 | if (!pvcalls_front_dev) { | ||
1002 | pvcalls_exit(); | ||
1003 | return -EIO; | ||
1004 | } | ||
1005 | |||
1006 | bedata = dev_get_drvdata(&pvcalls_front_dev->dev); | ||
1007 | |||
1008 | map = (struct sock_mapping *) sock->sk->sk_send_head; | ||
1009 | if (map == NULL) { | ||
1010 | pvcalls_exit(); | ||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | spin_lock(&bedata->socket_lock); | ||
1015 | ret = get_request(bedata, &req_id); | ||
1016 | if (ret < 0) { | ||
1017 | spin_unlock(&bedata->socket_lock); | ||
1018 | pvcalls_exit(); | ||
1019 | return ret; | ||
1020 | } | ||
1021 | sock->sk->sk_send_head = NULL; | ||
1022 | |||
1023 | req = RING_GET_REQUEST(&bedata->ring, req_id); | ||
1024 | req->req_id = req_id; | ||
1025 | req->cmd = PVCALLS_RELEASE; | ||
1026 | req->u.release.id = (uintptr_t)map; | ||
1027 | |||
1028 | bedata->ring.req_prod_pvt++; | ||
1029 | RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&bedata->ring, notify); | ||
1030 | spin_unlock(&bedata->socket_lock); | ||
1031 | if (notify) | ||
1032 | notify_remote_via_irq(bedata->irq); | ||
1033 | |||
1034 | wait_event(bedata->inflight_req, | ||
1035 | READ_ONCE(bedata->rsp[req_id].req_id) == req_id); | ||
1036 | |||
1037 | if (map->active_socket) { | ||
1038 | /* | ||
1039 | * Set in_error and wake up inflight_conn_req to force | ||
1040 | * recvmsg waiters to exit. | ||
1041 | */ | ||
1042 | map->active.ring->in_error = -EBADF; | ||
1043 | wake_up_interruptible(&map->active.inflight_conn_req); | ||
1044 | |||
1045 | /* | ||
1046 | * Wait until there are no more waiters on the mutexes. | ||
1047 | * We know that no new waiters can be added because sk_send_head | ||
1048 | * is set to NULL -- we only need to wait for the existing | ||
1049 | * waiters to return. | ||
1050 | */ | ||
1051 | while (!mutex_trylock(&map->active.in_mutex) || | ||
1052 | !mutex_trylock(&map->active.out_mutex)) | ||
1053 | cpu_relax(); | ||
1054 | |||
1055 | pvcalls_front_free_map(bedata, map); | ||
1056 | } else { | ||
1057 | spin_lock(&bedata->socket_lock); | ||
1058 | list_del(&map->list); | ||
1059 | spin_unlock(&bedata->socket_lock); | ||
1060 | if (READ_ONCE(map->passive.inflight_req_id) != | ||
1061 | PVCALLS_INVALID_ID) { | ||
1062 | pvcalls_front_free_map(bedata, | ||
1063 | map->passive.accept_map); | ||
1064 | } | ||
1065 | kfree(map); | ||
1066 | } | ||
1067 | WRITE_ONCE(bedata->rsp[req_id].req_id, PVCALLS_INVALID_ID); | ||
1068 | |||
1069 | pvcalls_exit(); | ||
1070 | return 0; | ||
1071 | } | ||
1072 | |||
975 | static const struct xenbus_device_id pvcalls_front_ids[] = { | 1073 | static const struct xenbus_device_id pvcalls_front_ids[] = { |
976 | { "pvcalls" }, | 1074 | { "pvcalls" }, |
977 | { "" } | 1075 | { "" } |
diff --git a/drivers/xen/pvcalls-front.h b/drivers/xen/pvcalls-front.h index 25e05b8f2d72..3332978f4fcd 100644 --- a/drivers/xen/pvcalls-front.h +++ b/drivers/xen/pvcalls-front.h | |||
@@ -23,5 +23,6 @@ int pvcalls_front_recvmsg(struct socket *sock, | |||
23 | unsigned int pvcalls_front_poll(struct file *file, | 23 | unsigned int pvcalls_front_poll(struct file *file, |
24 | struct socket *sock, | 24 | struct socket *sock, |
25 | poll_table *wait); | 25 | poll_table *wait); |
26 | int pvcalls_front_release(struct socket *sock); | ||
26 | 27 | ||
27 | #endif | 28 | #endif |