summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiloslav Trmač <mitr@redhat.com>2010-09-14 18:39:43 +0200
committerMiloslav Trmač <mitr@redhat.com>2010-09-14 18:39:43 +0200
commitf91e641ee86217ff28a30057e7774fab3cc2b097 (patch)
tree0a9eda61844f2b609005a3fb5613c16b061f99b8
parentcba2b0cd7ba7368eb209be50d8be237cf7778eab (diff)
downloadcryptodev-linux-f91e641ee86217ff28a30057e7774fab3cc2b097.tar.gz
cryptodev-linux-f91e641ee86217ff28a30057e7774fab3cc2b097.tar.xz
cryptodev-linux-f91e641ee86217ff28a30057e7774fab3cc2b097.zip
Clean up data when last reference goes away
In particular, don't free hash context until both master and slave are closed.
-rw-r--r--cryptodev_main.c58
1 files changed, 32 insertions, 26 deletions
diff --git a/cryptodev_main.c b/cryptodev_main.c
index f90dd45..c52210d 100644
--- a/cryptodev_main.c
+++ b/cryptodev_main.c
@@ -97,7 +97,7 @@ int __get_userbuf(uint8_t __user * addr, uint32_t len, int write,
/* A socket merely used for I/O. */
struct data_sock {
struct sock sk;
- struct alg_sock *master; /* See below for reference counting */
+ struct alg_sock *master; /* NULL until master's accept() */
unsigned index;
};
@@ -235,6 +235,15 @@ static struct proto alg_proto = {
.name = "ALG",
};
+/* We only call sock_put() from task context, so this is running in task
+ context as well. */
+static void data_destruct(struct sock *sk)
+{
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), &data_proto, -1);
+ local_bh_enable();
+}
+
static int data_release(struct socket *sock)
{
struct sock *sk;
@@ -247,10 +256,6 @@ static int data_release(struct socket *sock)
dsk = data_sk(sk);
DUMP_DSK(dsk);
- local_bh_disable();
- sock_prot_inuse_add(sock_net(sk), &data_proto, -1);
- local_bh_enable();
-
BUG_ON(dsk->master == NULL);
sock_put(&dsk->master->sk);
DUMP_DSK(dsk);
@@ -271,7 +276,8 @@ static int do_data_sendmsg(struct kiocb *iocb, struct alg_sock *ask,
BUG_ON(index != 0);
BUG_ON(ask->hash.init == 0);
- // FIXME: limit size, or use socket buffer
+ // FIXME: limit size, or use socket buffer, or refer to userspace
+ // pages directly
buf = kmalloc(total_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -372,6 +378,22 @@ static const struct proto_ops data_proto_ops = {
.splice_read = NULL,
};
+/* We only call sock_put() from task context, so this is running in task
+ context as well. */
+static void alg_destruct(struct sock *sk)
+{
+ struct alg_sock *ask;
+
+ ask = alg_sk(sk);
+
+ if (ask->hash.init != 0)
+ cryptodev_hash_deinit(&ask->hash);
+
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), &alg_proto, -1);
+ local_bh_enable();
+}
+
static int alg_release(struct socket *sock)
{
struct sock *sk;
@@ -396,24 +418,10 @@ static int alg_release(struct socket *sock)
BUG_ON(dsk == NULL);
DUMP_DSK(dsk);
- if (i >= ask->accept_idx) { // FIXME: cleaner - on last sock_put
- local_bh_disable();
- sock_prot_inuse_add(sock_net(&dsk->sk), &data_proto,
- -1);
- local_bh_enable();
- }
sock_put(&dsk->sk);
DUMP_DSK(dsk);
}
- if (ask->hash.init != 0)
- cryptodev_hash_deinit(&ask->hash);
-
- // FIXME: on last sock_put
- local_bh_disable();
- sock_prot_inuse_add(sock_net(sk), &alg_proto, -1);
- local_bh_enable();
-
sock_put(sk);
return 0;
@@ -509,6 +517,8 @@ static int alg_listen(struct socket *sock, int len)
sock_prot_inuse_add(net, &data_proto, 1);
local_bh_enable();
+ newsk->sk_destruct = data_destruct;
+
dsk = data_sk(newsk);
dsk->index = i;
DUMP_DSK(dsk);
@@ -529,12 +539,6 @@ err_partial:
BUG_ON(dsk == NULL);
DUMP_DSK(dsk);
-
- // FIXME: cleaner - on last sock_put
- local_bh_disable();
- sock_prot_inuse_add(sock_net(&dsk->sk), &data_proto, -1);
- local_bh_enable();
-
sock_put(&dsk->sk);
DUMP_DSK(dsk);
}
@@ -617,6 +621,8 @@ static int alg_create(struct net *net, struct socket *sock, int protocol,
sock_prot_inuse_add(net, &alg_proto, 1);
local_bh_enable();
+ sk->sk_destruct = alg_destruct;
+
return 0;
}