From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
To: James Morris <jmorris@redhat.com>
Cc: netdev@oss.sgi.com, cryptoapi@lists.logix.cz,
jamal <hadi@cyberus.ca>, Michal Ludvig <michal@logix.cz>
Subject: Asynchronous crypto layer. Reincarnation #1.
Date: Tue, 14 Dec 2004 19:03:56 +0300 [thread overview]
Message-ID: <1103040236.3430.54.camel@uganda> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 1331 bytes --]
Changed locking schema, coding style, symbol exporting.
HIFN driver is ready and is waiting the latest test.
It works several hours on a 4-way system without any problem
(test crypto_provider and crypto consumer are also in patch).
BTW, little note:
System crypts with 128 bits aes in ecb mode, since only one CPU in a
time performs
crypto operations(crypto_provider is a wrapper over synchronous crypto
layer)
numbers are not so exciting: about 250-350 sessions per second, each one
works
with 32 bytes block. Test crypto consumer injects new session each 3
milliseconds.
An interesting note about linux work_queues: when I inject new
crypto_session each 2
msecs - work_queue's threads for 2 of 4 CPUs are loaded about the same
3-10% of
the CPU time, but when I inject sessions each 3 or 1 msec - only one
queue's thread
gets all sessions callbacks and takes about 100% of the CPU time.
With 2msec delay I get about 500 sessions per second, with 1msec -
little more than 1000.
pcix$ cat /sys/class/acrypto/crypto_provider/scompleted && sleep 1
&&cat /sys/class/acrypto/crypto_provider/scompleted
7044717
7045728
Please test, review and comment.
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
--
Evgeniy Polyakov
Crash is better than data corruption -- Arthur Grabowski
[-- Attachment #1.2: acrypto.patch --]
[-- Type: text/x-patch, Size: 88333 bytes --]
diff -Nru /tmp/empty/acrypto.h linux-2.6/drivers/acrypto/acrypto.h
--- /tmp/empty/acrypto.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/acrypto.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,226 @@
+/*
+ * acrypto.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ACRYPTO_H
+#define __ACRYPTO_H
+
+#define SCACHE_NAMELEN 32
+
+struct crypto_session_initializer;
+struct crypto_data;
+typedef void (*crypto_callback_t) (struct crypto_session_initializer *,
+ struct crypto_data *);
+
+extern void crypto_wake_lb(void);
+
+#define SESSION_COMPLETED (1<<15)
+#define SESSION_FINISHED (1<<14)
+#define SESSION_STARTED (1<<13)
+#define SESSION_PROCESSED (1<<12)
+#define SESSION_BINDED (1<<11)
+#define SESSION_BROKEN (1<<10)
+
+#define session_completed(s) (s->ci.flags & SESSION_COMPLETED)
+#define complete_session(s) do {s->ci.flags |= SESSION_COMPLETED;} while(0)
+#define uncomplete_session(s) do {s->ci.flags &= ~SESSION_COMPLETED;} while (0)
+
+#define session_finished(s) (s->ci.flags & SESSION_FINISHED)
+#define finish_session(s) do {s->ci.flags |= SESSION_FINISHED;} while(0)
+#define unfinish_session(s) do {s->ci.flags &= ~SESSION_FINISHED;} while (0)
+
+#define session_started(s) (s->ci.flags & SESSION_STARTED)
+#define start_session(s) do {s->ci.flags |= SESSION_STARTED;} while(0)
+#define unstart_session(s) do {s->ci.flags &= ~SESSION_STARTED;} while (0)
+
+#define session_is_processed(s) (s->ci.flags & SESSION_PROCESSED)
+#define start_process_session(s) do {s->ci.flags |= SESSION_PROCESSED; s->ci.ptime = jiffies;} while(0)
+#define stop_process_session(s) do {s->ci.flags &= ~SESSION_PROCESSED; s->ci.ptime = jiffies - s->ci.ptime; crypto_wake_lb();} while (0)
+
+#define session_binded(s) (s->ci.flags & SESSION_BINDED)
+#define bind_session(s) do {s->ci.flags |= SESSION_BINDED;} while(0)
+#define unbind_session(s) do {s->ci.flags &= ~SESSION_BINDED;} while (0)
+#define sci_binded(ci) (ci->flags & SESSION_BINDED)
+
+#define session_broken(s) (s->ci.flags & SESSION_BROKEN)
+#define broke_session(s) do {s->ci.flags |= SESSION_BROKEN;} while(0)
+#define unbroke_session(s) do {s->ci.flags &= ~SESSION_BROKEN;} while (0)
+
+struct crypto_device_stat {
+ __u64 scompleted;
+ __u64 sfinished;
+ __u64 sstarted;
+ __u64 kmem_failed;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+
+#include <asm/scatterlist.h>
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(f, a...) printk("%d " f, smp_processor_id(), ##a)
+#else
+#define dprintk(f, a...)
+#endif
+
+#define CRYPTO_MAX_PRIV_SIZE 1024
+
+#define DEVICE_BROKEN (1<<0)
+
+#define device_broken(dev) (dev->flags & DEVICE_BROKEN)
+#define broke_device(dev) do {dev->flags |= DEVICE_BROKEN;} while(0)
+#define repair_device(dev) do {dev->flags &= ~DEVICE_BROKEN;} while(0)
+
+struct crypto_capability {
+ u16 operation;
+ u16 type;
+ u16 mode;
+ u16 qlen;
+ u64 ptime;
+ u64 scomp;
+};
+
+struct crypto_session_initializer {
+ u16 operation;
+ u16 type;
+ u16 mode;
+ u16 priority;
+
+ u64 id;
+ u64 dev_id;
+
+ u32 flags;
+
+ u32 bdev;
+
+ u64 ptime;
+
+ crypto_callback_t callback;
+};
+
+struct crypto_data {
+ int sg_src_num;
+ struct scatterlist sg_src;
+ struct scatterlist sg_dst;
+ struct scatterlist sg_key;
+ struct scatterlist sg_iv;
+
+ void *priv;
+ unsigned int priv_size;
+};
+
+struct crypto_device {
+ char name[SCACHE_NAMELEN];
+
+ spinlock_t session_lock;
+ struct list_head session_list;
+
+ u64 sid;
+ spinlock_t lock;
+
+ atomic_t refcnt;
+
+ u32 flags;
+
+ u32 id;
+
+ struct list_head cdev_entry;
+
+ void (*data_ready)(struct crypto_device *);
+
+ struct device_driver *driver;
+ struct device device;
+ struct class_device class_device;
+ struct completion dev_released;
+
+ spinlock_t stat_lock;
+ struct crypto_device_stat stat;
+
+ struct crypto_capability *cap;
+ int cap_number;
+
+ void *priv;
+};
+
+struct crypto_route_head {
+ struct crypto_route *next;
+ struct crypto_route *prev;
+
+ __u32 qlen;
+ spinlock_t lock;
+};
+
+struct crypto_route {
+ struct crypto_route *next;
+ struct crypto_route *prev;
+
+ struct crypto_route_head *list;
+ struct crypto_device *dev;
+
+ struct crypto_session_initializer ci;
+};
+
+struct crypto_session {
+ struct list_head dev_queue_entry;
+ struct list_head main_queue_entry;
+
+ struct crypto_session_initializer ci;
+
+ struct crypto_data data;
+
+ spinlock_t lock;
+
+ struct work_struct work;
+
+ struct crypto_route_head route_list;
+};
+
+struct crypto_session *crypto_session_alloc(struct crypto_session_initializer *, struct crypto_data *);
+struct crypto_session *crypto_session_create(struct crypto_session_initializer *, struct crypto_data *);
+void crypto_session_add(struct crypto_session *);
+void crypto_session_dequeue_main(struct crypto_session *);
+void __crypto_session_dequeue_main(struct crypto_session *);
+void __crypto_session_dequeue_route(struct crypto_session *);
+void crypto_session_dequeue_route(struct crypto_session *);
+
+void crypto_device_get(struct crypto_device *);
+void crypto_device_put(struct crypto_device *);
+struct crypto_device *crypto_device_get_name(char *);
+
+int __crypto_device_add(struct crypto_device *);
+int crypto_device_add(struct crypto_device *);
+void __crypto_device_remove(struct crypto_device *);
+void crypto_device_remove(struct crypto_device *);
+int match_initializer(struct crypto_device *, struct crypto_session_initializer *);
+int __match_initializer(struct crypto_capability *, struct crypto_session_initializer *);
+
+void crypto_session_insert_main(struct crypto_device *dev, struct crypto_session *s);
+void crypto_session_insert(struct crypto_device *dev, struct crypto_session *s);
+
+#endif /* __KERNEL__ */
+#endif /* __ACRYPTO_H */
diff -Nru /tmp/empty/.arch-ids/acrypto.h.id linux-2.6/drivers/acrypto/.arch-ids/acrypto.h.id
--- /tmp/empty/.arch-ids/acrypto.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/acrypto.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 20:33:25 2004 2957.0
diff -Nru /tmp/empty/.arch-ids/consumer.c.id linux-2.6/drivers/acrypto/.arch-ids/consumer.c.id
--- /tmp/empty/.arch-ids/consumer.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/consumer.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Wed Oct 6 02:34:04 2004 4902.0
diff -Nru /tmp/empty/.arch-ids/crypto_conn.c.id linux-2.6/drivers/acrypto/.arch-ids/crypto_conn.c.id
--- /tmp/empty/.arch-ids/crypto_conn.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_conn.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Wed Oct 6 03:19:44 2004 5558.0
diff -Nru /tmp/empty/.arch-ids/crypto_conn.h.id linux-2.6/drivers/acrypto/.arch-ids/crypto_conn.h.id
--- /tmp/empty/.arch-ids/crypto_conn.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_conn.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Wed Oct 6 03:19:44 2004 5558.1
diff -Nru /tmp/empty/.arch-ids/crypto_def.h.id linux-2.6/drivers/acrypto/.arch-ids/crypto_def.h.id
--- /tmp/empty/.arch-ids/crypto_def.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_def.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Tue Oct 26 20:26:53 2004 2903.0
diff -Nru /tmp/empty/.arch-ids/crypto_dev.c.id linux-2.6/drivers/acrypto/.arch-ids/crypto_dev.c.id
--- /tmp/empty/.arch-ids/crypto_dev.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_dev.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 22:27:13 2004 4211.0
diff -Nru /tmp/empty/.arch-ids/crypto_lb.c.id linux-2.6/drivers/acrypto/.arch-ids/crypto_lb.c.id
--- /tmp/empty/.arch-ids/crypto_lb.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_lb.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 22:27:09 2004 4210.0
diff -Nru /tmp/empty/.arch-ids/crypto_lb.h.id linux-2.6/drivers/acrypto/.arch-ids/crypto_lb.h.id
--- /tmp/empty/.arch-ids/crypto_lb.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_lb.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 22:27:09 2004 4210.1
diff -Nru /tmp/empty/.arch-ids/crypto_main.c.id linux-2.6/drivers/acrypto/.arch-ids/crypto_main.c.id
--- /tmp/empty/.arch-ids/crypto_main.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_main.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 20:28:05 2004 2950.1
diff -Nru /tmp/empty/.arch-ids/crypto_route.h.id linux-2.6/drivers/acrypto/.arch-ids/crypto_route.h.id
--- /tmp/empty/.arch-ids/crypto_route.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_route.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Tue Oct 12 19:25:27 2004 2518.0
diff -Nru /tmp/empty/.arch-ids/crypto_stat.c.id linux-2.6/drivers/acrypto/.arch-ids/crypto_stat.c.id
--- /tmp/empty/.arch-ids/crypto_stat.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_stat.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Thu Oct 7 23:08:48 2004 4384.0
diff -Nru /tmp/empty/.arch-ids/crypto_stat.h.id linux-2.6/drivers/acrypto/.arch-ids/crypto_stat.h.id
--- /tmp/empty/.arch-ids/crypto_stat.h.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/crypto_stat.h.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Thu Oct 7 23:08:48 2004 4384.1
diff -Nru /tmp/empty/.arch-ids/Makefile.id linux-2.6/drivers/acrypto/.arch-ids/Makefile.id
--- /tmp/empty/.arch-ids/Makefile.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/Makefile.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Sun Oct 3 20:28:05 2004 2950.0
diff -Nru /tmp/empty/.arch-ids/provider.c.id linux-2.6/drivers/acrypto/.arch-ids/provider.c.id
--- /tmp/empty/.arch-ids/provider.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/provider.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Wed Oct 6 02:34:04 2004 4902.1
diff -Nru /tmp/empty/.arch-ids/sha1_provider.c.id linux-2.6/drivers/acrypto/.arch-ids/sha1_provider.c.id
--- /tmp/empty/.arch-ids/sha1_provider.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/sha1_provider.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Wed Oct 20 20:26:53 2004 25654.0
diff -Nru /tmp/empty/.arch-ids/simple_lb.c.id linux-2.6/drivers/acrypto/.arch-ids/simple_lb.c.id
--- /tmp/empty/.arch-ids/simple_lb.c.id 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/.arch-ids/simple_lb.c.id 2004-12-14 17:50:04.000000000 +0300
@@ -0,0 +1 @@
+Evgeniy Polyakov <johnpol@2ka.mipt.ru> Mon Oct 4 00:58:07 2004 4691.0
diff -Nru /tmp/empty/consumer.c linux-2.6/drivers/acrypto/consumer.c
--- /tmp/empty/consumer.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/consumer.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,288 @@
+/*
+ * consumer.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/completion.h>
+
+#undef DEBUG
+#include "acrypto.h"
+#include "crypto_def.h"
+#include "via-padlock/padlock.h"
+
+#undef dprintk
+#define dprintk(f, a...) do {} while(0)
+
+#define KEY_LENGTH 16
+static char ckey[KEY_LENGTH];
+static int key_length = sizeof(ckey);
+
+static void ctest_callback(struct crypto_session_initializer *ci,
+ struct crypto_data *data);
+
+static struct crypto_session_initializer ci = {
+ .operation = CRYPTO_OP_ENCRYPT,
+ .type = CRYPTO_TYPE_AES_128,
+ .mode = CRYPTO_MODE_ECB,
+ .priority = 4,
+ .callback = ctest_callback,
+};
+
+static struct crypto_data cdata;
+
+#define CSESSION_MAX 1000
+static struct crypto_session *s;
+static int watermark;
+//static struct completion callback_completed;
+static struct timer_list ctimer;
+
+static void ctest_callback(struct crypto_session_initializer *ci,
+ struct crypto_data *data)
+{
+ int i, off, size, ssize;
+ struct scatterlist *sg;
+ unsigned char *ptr;
+
+ watermark--;
+
+ printk("%s: session %llu [%llu]\n", __func__, ci->id, ci->dev_id);
+ dprintk("src=%s, len=%d.\n",
+ ((char *)page_address(data->sg_src.page)) + data->sg_src.offset,
+ data->sg_src.length);
+
+ sg = &data->sg_key;
+ ptr = (unsigned char *)page_address(sg->page);
+ off = sg->offset;
+ size = sg->length;
+
+ if (size > 16) {
+ dprintk("key sg is broken, size=%d.\n", size);
+ goto err_out;
+ }
+
+ dprintk("key[%d]=", size);
+ for (i = 0; i < size; ++i)
+ dprintk("0x%02x, ", ptr[i + off]);
+ dprintk("\n");
+
+ sg = &data->sg_src;
+ ptr = (unsigned char *)page_address(sg->page);
+ off = sg->offset;
+ ssize = size = sg->length;
+ if (size > 32) {
+ dprintk("src sg is broken, size=%d.\n", size);
+ goto err_out;
+ }
+
+ dprintk("src[%d]=", size);
+ for (i = 0; i < size; ++i)
+ dprintk("0x%02x, ", ptr[i + off]);
+ dprintk("\n");
+
+ sg = &data->sg_dst;
+ ptr = (unsigned char *)page_address(sg->page);
+ off = sg->offset;
+ size = sg->length;
+ if (size > 32) {
+ dprintk("dst sg is broken, size=%d.\n", size);
+ goto err_out;
+ }
+
+ if (size == 0) {
+ dprintk("size=0, setting to %d.\n", ssize);
+ size = ssize;
+ }
+
+ dprintk("dst[%d]=", size);
+ for (i = 0; i < size; ++i)
+ dprintk("0x%02x, ", ptr[i + off]);
+ dprintk("\n");
+
+err_out:
+ //complete(&callback_completed);
+ return;
+}
+
+int ctimer_func(void *data, int size, int op)
+{
+ u8 *ptr;
+
+ if (size > PAGE_SIZE)
+ size = PAGE_SIZE;
+
+ ptr = (u8 *)page_address(cdata.sg_src.page) + cdata.sg_src.offset;
+ memcpy(ptr, data, size);
+ cdata.sg_src.length = size;
+ cdata.sg_dst.length = size;
+
+ ci.operation = op;
+ s = crypto_session_alloc(&ci, &cdata);
+ if (s)
+ watermark++;
+ else
+ dprintk("allocation failed.\n");
+
+ return (s) ? 0 : -EINVAL;
+}
+
+static int alloc_sg(struct scatterlist *sg, void *data, int size)
+{
+ sg->offset = 0;
+ sg->page = alloc_pages(GFP_KERNEL, get_order(size));
+ if (!sg->page) {
+ printk(KERN_ERR "Failed to allocate page.\n");
+ return -ENOMEM;
+ }
+
+ memset(page_address(sg->page), 0, PAGE_SIZE);
+
+ if (data)
+ memcpy(page_address(sg->page), data, size);
+
+ sg->length = size;
+
+ return 0;
+}
+
+static void ctimerf(unsigned long data)
+{
+ char str[] = "test message qwerty asdzxc\n";
+ int size = sizeof(str), err;
+
+ printk("%s started\n", __func__);
+
+ err = ctimer_func(str, size, CRYPTO_OP_ENCRYPT);
+
+ if (!err)
+ mod_timer(&ctimer, jiffies + 3);
+ else
+ mod_timer(&ctimer, jiffies + HZ);
+
+ printk("%s finished.\n", __func__);
+}
+
+int consumer_init(void)
+{
+ int err;
+
+ cdata.sg_src_num = 1;
+
+ err = alloc_sg(&cdata.sg_src, NULL, PAGE_SIZE);
+ if (err)
+ goto err_out_return;
+
+ err = alloc_sg(&cdata.sg_dst, NULL, PAGE_SIZE);
+ if (err)
+ goto err_out_src;
+
+ err = alloc_sg(&cdata.sg_key, ckey, key_length);
+ if (err)
+ goto err_out_dst;
+
+ err = alloc_sg(&cdata.sg_iv, NULL, PAGE_SIZE);
+ if (err)
+ goto err_out_key;
+
+ cdata.priv_size = sizeof(struct aes_ctx);
+
+ init_timer(&ctimer);
+ ctimer.function = &ctimerf;
+ ctimer.expires = jiffies + HZ;
+ ctimer.data = 0;
+
+ add_timer(&ctimer);
+
+ return 0;
+#if 0
+ char str[] = "test message qwerty asdzxc\n";
+ int size;
+
+ init_completion(&callback_completed);
+
+ size = 32;
+ err = ctimer_func(str, size, CRYPTO_OP_ENCRYPT);
+ if (err)
+ goto err_out_iv;
+
+ wait_for_completion(&callback_completed);
+ init_completion(&callback_completed);
+
+ err =
+ ctimer_func(page_address(cdata.sg_dst.page), cdata.sg_dst.length,
+ CRYPTO_OP_DECRYPT);
+ if (err)
+ goto err_out_iv;
+
+ wait_for_completion(&callback_completed);
+
+ dprintk("dst: %s\n",
+ ((char *)page_address(cdata.sg_dst.page)) +
+ cdata.sg_dst.offset);
+ err_out_iv:
+#endif
+
+ __free_pages(cdata.sg_iv.page, get_order(1));
+ err_out_key:
+ __free_pages(cdata.sg_key.page, get_order(key_length));
+ err_out_dst:
+ __free_pages(cdata.sg_dst.page, get_order(1));
+ err_out_src:
+ __free_pages(cdata.sg_src.page, get_order(1));
+ err_out_return:
+ return -ENODEV;
+}
+
+void consumer_fini(void)
+{
+ del_timer_sync(&ctimer);
+ while (watermark) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ printk(KERN_INFO "Waiting for sessions to be freed: watermark=%d.\n",
+ watermark);
+ }
+
+ if (!cdata.sg_key.length)
+ printk("BUG: key length is 0 in %s.\n", __func__);
+
+ __free_pages(cdata.sg_iv.page, get_order(1));
+ __free_pages(cdata.sg_key.page, get_order(key_length));
+ __free_pages(cdata.sg_dst.page, get_order(1));
+ __free_pages(cdata.sg_src.page, get_order(1));
+
+ printk(KERN_INFO "Test crypto module consumer is unloaded.\n");
+}
+
+module_init(consumer_init);
+module_exit(consumer_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Test crypto module consumer.");
Binary files /tmp/empty/.consumer.c.swp and linux-2.6/drivers/acrypto/.consumer.c.swp differ
diff -Nru /tmp/empty/crypto_conn.c linux-2.6/drivers/acrypto/crypto_conn.c
--- /tmp/empty/crypto_conn.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_conn.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,191 @@
+/*
+ * crypto_conn.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+
+#include "acrypto.h"
+#include "crypto_lb.h"
+
+#include "../connector/connector.h"
+
+#include "crypto_conn.h"
+
+static struct cb_id crypto_conn_id = { 0xdead, 0x0000 };
+static char crypto_conn_name[] = "crconn";
+
+static void crypto_conn_callback(void *data)
+{
+ struct cn_msg *msg, *reply;
+ struct crypto_conn_data *d, *cmd;
+ struct crypto_device *dev;
+ u32 sessions;
+
+ msg = (struct cn_msg *)data;
+ d = (struct crypto_conn_data *)msg->data;
+
+ if (msg->len < sizeof(*d)) {
+ dprintk(KERN_ERR "Wrong message to crypto connector: msg->len=%u < %u.\n",
+ msg->len, sizeof(*d));
+ return;
+ }
+
+ if (msg->len != sizeof(*d) + d->len) {
+ dprintk(KERN_ERR "Wrong message to crypto connector: msg->len=%u != %u.\n",
+ msg->len, sizeof(*d) + d->len);
+ return;
+ }
+
+ dev = crypto_device_get_name(d->name);
+ if (!dev) {
+ dprintk(KERN_INFO "Crypto device %s was not found.\n", d->name);
+ return;
+ }
+
+ switch (d->cmd) {
+ case CRYPTO_CONN_READ_SESSIONS:
+ reply = kmalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(sessions), GFP_ATOMIC);
+ if (reply) {
+ memcpy(reply, msg, sizeof(*reply));
+ reply->len = sizeof(*cmd) + sizeof(sessions);
+
+ /*
+ * See protocol description in connector.c
+ */
+ reply->ack++;
+
+ cmd = (struct crypto_conn_data *)(reply + 1);
+ memcpy(cmd, d, sizeof(*cmd));
+ cmd->len = sizeof(sessions);
+
+ sessions = atomic_read(&dev->refcnt);
+
+ memcpy(cmd + 1, &sessions, sizeof(sessions));
+
+ cn_netlink_send(reply, 0);
+
+ kfree(reply);
+ } else
+ dprintk(KERN_ERR "Failed to allocate %d bytes in reply to comamnd 0x%x.\n",
+ sizeof(*msg) + sizeof(*cmd), d->cmd);
+ break;
+ case CRYPTO_CONN_DUMP_QUEUE:
+ reply = kmalloc(sizeof(*msg) + sizeof(*cmd) +
+ 1024 * sizeof(struct crypto_session_initializer), GFP_ATOMIC);
+ if (reply) {
+ struct crypto_session *s;
+ struct crypto_session_initializer *ptr;
+
+ memcpy(reply, msg, sizeof(*reply));
+
+ /*
+ * See protocol description in connector.c
+ */
+ reply->ack++;
+
+ cmd = (struct crypto_conn_data *)(reply + 1);
+ memcpy(cmd, d, sizeof(*cmd));
+
+ ptr = (struct crypto_session_initializer *)(cmd + 1);
+
+ sessions = 0;
+ spin_lock_irq(&dev->session_lock);
+ list_for_each_entry(s, &dev->session_list, dev_queue_entry) {
+ memcpy(ptr, &s->ci, sizeof(*ptr));
+ sessions++;
+ ptr++;
+
+ if (sessions >= 1024)
+ break;
+ }
+ spin_unlock_irq(&dev->session_lock);
+
+ cmd->len = sizeof(*ptr) * sessions;
+ reply->len = sizeof(*cmd) + cmd->len;
+
+ cn_netlink_send(reply, 0);
+
+ kfree(reply);
+ } else
+ dprintk(KERN_ERR "Failed to allocate %d bytes in reply to comamnd 0x%x.\n",
+ sizeof(*msg) + sizeof(*cmd), d->cmd);
+ break;
+ case CRYPTO_GET_STAT:
+ reply =
+ kmalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(struct crypto_device_stat), GFP_ATOMIC);
+ if (reply) {
+ struct crypto_device_stat *ptr;
+
+ memcpy(reply, msg, sizeof(*reply));
+ reply->len = sizeof(*cmd) + sizeof(*ptr);
+
+ /*
+ * See protocol description in connector.c
+ */
+ reply->ack++;
+
+ cmd = (struct crypto_conn_data *)(reply + 1);
+ memcpy(cmd, d, sizeof(*cmd));
+ cmd->len = sizeof(*ptr);
+
+ ptr = (struct crypto_device_stat *)(cmd + 1);
+ memcpy(ptr, &dev->stat, sizeof(*ptr));
+
+ cn_netlink_send(reply, 0);
+
+ kfree(reply);
+ } else
+ dprintk(KERN_ERR "Failed to allocate %d bytes in reply to comamnd 0x%x.\n",
+ sizeof(*msg) + sizeof(*cmd), d->cmd);
+ break;
+ default:
+ dprintk(KERN_ERR "Wrong operation 0x%04x for crypto connector.\n",
+ d->cmd);
+ return;
+ }
+
+ crypto_device_put(dev);
+}
+
+int crypto_conn_init(void)
+{
+ int err;
+
+ err = cn_add_callback(&crypto_conn_id, crypto_conn_name, crypto_conn_callback);
+ if (err)
+ return err;
+
+ dprintk(KERN_INFO "Crypto connector callback is registered.\n");
+
+ return 0;
+}
+
+void crypto_conn_fini(void)
+{
+ cn_del_callback(&crypto_conn_id);
+ dprintk(KERN_INFO "Crypto connector callback is unregistered.\n");
+}
diff -Nru /tmp/empty/crypto_conn.h linux-2.6/drivers/acrypto/crypto_conn.h
--- /tmp/empty/crypto_conn.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_conn.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,44 @@
+/*
+ * crypto_conn.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CRYPTO_CONN_H
+#define __CRYPTO_CONN_H
+
+#include "acrypto.h"
+
+#define CRYPTO_CONN_READ_SESSIONS 0
+#define CRYPTO_CONN_DUMP_QUEUE 1
+#define CRYPTO_GET_STAT 2
+
+struct crypto_conn_data {
+ char name[SCACHE_NAMELEN];
+ __u16 cmd;
+ __u16 len;
+ __u8 data[0];
+};
+
+#ifdef __KERNEL__
+
+int crypto_conn_init(void);
+void crypto_conn_fini(void);
+
+#endif /* __KERNEL__ */
+#endif /* __CRYPTO_CONN_H */
diff -Nru /tmp/empty/crypto_def.h linux-2.6/drivers/acrypto/crypto_def.h
--- /tmp/empty/crypto_def.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_def.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,38 @@
+/*
+ * crypto_def.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CRYPTO_DEF_H
+#define __CRYPTO_DEF_H
+
+#define CRYPTO_OP_DECRYPT 0
+#define CRYPTO_OP_ENCRYPT 1
+#define CRYPTO_OP_HMAC 2
+
+#define CRYPTO_MODE_ECB 0
+#define CRYPTO_MODE_CBC 1
+#define CRYPTO_MODE_CFB 2
+#define CRYPTO_MODE_OFB 3
+
+#define CRYPTO_TYPE_AES_128 0
+#define CRYPTO_TYPE_AES_192 1
+#define CRYPTO_TYPE_AES_256 2
+
+#endif /* __CRYPTO_DEF_H */
diff -Nru /tmp/empty/crypto_dev.c linux-2.6/drivers/acrypto/crypto_dev.c
--- /tmp/empty/crypto_dev.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_dev.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,371 @@
+/*
+ * crypto_dev.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include "acrypto.h"
+
+static LIST_HEAD(cdev_list);
+static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED;
+static u32 cdev_ids;
+
+struct list_head *crypto_device_list = &cdev_list;
+spinlock_t *crypto_device_lock = &cdev_lock;
+
+static int crypto_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static int crypto_probe(struct device *dev)
+{
+ return -ENODEV;
+}
+
+static int crypto_remove(struct device *dev)
+{
+ return 0;
+}
+
+static void crypto_release(struct device *dev)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, device);
+
+ complete(&d->dev_released);
+}
+
+static void crypto_class_release(struct class *class)
+{
+}
+
+static void crypto_class_release_device(struct class_device *class_dev)
+{
+}
+
+struct class crypto_class = {
+ .name = "acrypto",
+ .class_release = crypto_class_release,
+ .release = crypto_class_release_device
+};
+
+struct bus_type crypto_bus_type = {
+ .name = "acrypto",
+ .match = crypto_match
+};
+
+struct device_driver crypto_driver = {
+ .name = "crypto_driver",
+ .bus = &crypto_bus_type,
+ .probe = crypto_probe,
+ .remove = crypto_remove,
+};
+
+struct device crypto_dev = {
+ .parent = NULL,
+ .bus = &crypto_bus_type,
+ .bus_id = "Asynchronous crypto",
+ .driver = &crypto_driver,
+ .release = &crypto_release
+};
+
+static ssize_t sessions_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%d\n", atomic_read(&d->refcnt));
+}
+static ssize_t name_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%s\n", d->name);
+}
+static ssize_t devices_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d;
+ int off = 0;
+
+ spin_lock_irq(&cdev_lock);
+ list_for_each_entry(d, &cdev_list, cdev_entry) {
+ off += sprintf(buf + off, "%s ", d->name);
+ }
+ spin_unlock_irq(&cdev_lock);
+
+ if (!off)
+ off = sprintf(buf, "No devices registered yet.");
+
+ off += sprintf(buf + off, "\n");
+
+ return off;
+}
+
+static ssize_t kmem_failed_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%llu\n", d->stat.kmem_failed);
+}
+static ssize_t sstarted_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%llu\n", d->stat.sstarted);
+}
+static ssize_t sfinished_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%llu\n", d->stat.sfinished);
+}
+static ssize_t scompleted_show(struct class_device *dev, char *buf)
+{
+ struct crypto_device *d = container_of(dev, struct crypto_device, class_device);
+
+ return sprintf(buf, "%llu\n", d->stat.scompleted);
+}
+
+static CLASS_DEVICE_ATTR(sessions, 0444, sessions_show, NULL);
+static CLASS_DEVICE_ATTR(name, 0444, name_show, NULL);
+CLASS_DEVICE_ATTR(devices, 0444, devices_show, NULL);
+static CLASS_DEVICE_ATTR(scompleted, 0444, scompleted_show, NULL);
+static CLASS_DEVICE_ATTR(sstarted, 0444, sstarted_show, NULL);
+static CLASS_DEVICE_ATTR(sfinished, 0444, sfinished_show, NULL);
+static CLASS_DEVICE_ATTR(kmem_failed, 0444, kmem_failed_show, NULL);
+
+static int compare_device(struct crypto_device *d1, struct crypto_device *d2)
+{
+ if (!strncmp(d1->name, d2->name, sizeof(d1->name)))
+ return 1;
+
+ return 0;
+}
+
+static void create_device_attributes(struct crypto_device *dev)
+{
+ class_device_create_file(&dev->class_device, &class_device_attr_sessions);
+ class_device_create_file(&dev->class_device, &class_device_attr_name);
+ class_device_create_file(&dev->class_device, &class_device_attr_scompleted);
+ class_device_create_file(&dev->class_device, &class_device_attr_sstarted);
+ class_device_create_file(&dev->class_device, &class_device_attr_sfinished);
+ class_device_create_file(&dev->class_device, &class_device_attr_kmem_failed);
+}
+
+static void remove_device_attributes(struct crypto_device *dev)
+{
+ class_device_remove_file(&dev->class_device, &class_device_attr_sessions);
+ class_device_remove_file(&dev->class_device, &class_device_attr_name);
+ class_device_remove_file(&dev->class_device, &class_device_attr_scompleted);
+ class_device_remove_file(&dev->class_device, &class_device_attr_sstarted);
+ class_device_remove_file(&dev->class_device, &class_device_attr_sfinished);
+ class_device_remove_file(&dev->class_device, &class_device_attr_kmem_failed);
+}
+
+int __match_initializer(struct crypto_capability *cap, struct crypto_session_initializer *ci)
+{
+ if (cap->operation == ci->operation && cap->type == ci->type &&
+ cap->mode == (ci->mode & 0x1fff))
+ return 1;
+
+ return 0;
+}
+
+int match_initializer(struct crypto_device *dev, struct crypto_session_initializer *ci)
+{
+ int i;
+
+ for (i = 0; i < dev->cap_number; ++i) {
+ struct crypto_capability *cap = &dev->cap[i];
+
+ if (__match_initializer(cap, ci)) {
+ if (cap->qlen >= atomic_read(&dev->refcnt) + 1) {
+ dprintk("cap->len=%u, req=%u.\n",
+ cap->qlen, atomic_read(&dev->refcnt) + 1);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void crypto_device_get(struct crypto_device *dev)
+{
+ atomic_inc(&dev->refcnt);
+}
+
+struct crypto_device *crypto_device_get_name(char *name)
+{
+ struct crypto_device *dev;
+ int found = 0;
+
+ spin_lock_irq(&cdev_lock);
+ list_for_each_entry(dev, &cdev_list, cdev_entry) {
+ if (!strcmp(dev->name, name)) {
+ found = 1;
+ crypto_device_get(dev);
+ break;
+ }
+ }
+ spin_unlock_irq(&cdev_lock);
+
+ if (!found)
+ return NULL;
+
+ return dev;
+}
+
+void crypto_device_put(struct crypto_device *dev)
+{
+ atomic_dec(&dev->refcnt);
+}
+
+int __crypto_device_add(struct crypto_device *dev)
+{
+ int err;
+
+ memset(&dev->stat, 0, sizeof(dev->stat));
+ spin_lock_init(&dev->stat_lock);
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->session_lock);
+ INIT_LIST_HEAD(&dev->session_list);
+ atomic_set(&dev->refcnt, 0);
+ dev->sid = 0;
+ dev->flags = 0;
+ init_completion(&dev->dev_released);
+ memcpy(&dev->device, &crypto_dev, sizeof(struct device));
+ dev->driver = &crypto_driver;
+
+ snprintf(dev->device.bus_id, sizeof(dev->device.bus_id), "%s", dev->name);
+ err = device_register(&dev->device);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto device %s: err=%d.\n",
+ dev->name, err);
+ return err;
+ }
+
+ snprintf(dev->class_device.class_id, sizeof(dev->class_device.class_id), "%s", dev->name);
+ dev->class_device.dev = &dev->device;
+ dev->class_device.class = &crypto_class;
+
+ err = class_device_register(&dev->class_device);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto class device %s: err=%d.\n",
+ dev->name, err);
+ device_unregister(&dev->device);
+ return err;
+ }
+
+ create_device_attributes(dev);
+
+ return 0;
+}
+
+void __crypto_device_remove(struct crypto_device *dev)
+{
+ remove_device_attributes(dev);
+ class_device_unregister(&dev->class_device);
+ device_unregister(&dev->device);
+}
+
+int crypto_device_add(struct crypto_device *dev)
+{
+ int err;
+
+ err = __crypto_device_add(dev);
+ if (err)
+ return err;
+
+ spin_lock_irq(&cdev_lock);
+ list_add(&dev->cdev_entry, &cdev_list);
+ dev->id = ++cdev_ids;
+ spin_unlock_irq(&cdev_lock);
+
+ dprintk(KERN_INFO "Crypto device %s was registered with ID=%x.\n",
+ dev->name, dev->id);
+
+ return 0;
+}
+
+void crypto_device_remove(struct crypto_device *dev)
+{
+ struct crypto_device *__dev, *n;
+
+ __crypto_device_remove(dev);
+
+ spin_lock_irq(&cdev_lock);
+ list_for_each_entry_safe(__dev, n, &cdev_list, cdev_entry) {
+ if (compare_device(__dev, dev)) {
+ list_del_init(&__dev->cdev_entry);
+ spin_unlock_irq(&cdev_lock);
+
+ /*
+ * In test cases or when crypto device driver is not written correctly,
+ * it's ->data_ready() method will not be callen anymore
+ * after device is removed from crypto device list.
+ *
+ * For such cases we either should provide ->flush() call
+ * or properly write ->data_ready() method.
+ */
+
+ while (atomic_read(&__dev->refcnt)) {
+
+ dprintk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
+ __dev->name, atomic_read(&dev->refcnt));
+
+ /*
+ * Hack zone: you need to write good ->data_ready()
+ * and crypto device driver itself.
+ *
+ * Driver shoud not buzz if it has pending sessions
+ * in it's queue and it was removed from global device list.
+ *
+ * Although I can workaround it here, for example by
+ * flushing the whole queue and drop all pending sessions.
+ */
+
+ __dev->data_ready(__dev);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+
+ dprintk(KERN_ERR "Crypto device %s was unregistered.\n",
+ dev->name);
+ return;
+ }
+ }
+ spin_unlock_irq(&cdev_lock);
+
+ dprintk(KERN_ERR "Crypto device %s was not registered.\n", dev->name);
+}
+
+EXPORT_SYMBOL_GPL(crypto_device_add);
+EXPORT_SYMBOL_GPL(crypto_device_remove);
+EXPORT_SYMBOL_GPL(crypto_device_get);
+EXPORT_SYMBOL_GPL(crypto_device_get_name);
+EXPORT_SYMBOL_GPL(crypto_device_put);
diff -Nru /tmp/empty/crypto_lb.c linux-2.6/drivers/acrypto/crypto_lb.c
--- /tmp/empty/crypto_lb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_lb.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,631 @@
+/*
+ * crypto_lb.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+
+#include "acrypto.h"
+#include "crypto_lb.h"
+#include "crypto_stat.h"
+#include "crypto_route.h"
+
+static LIST_HEAD(crypto_lb_list);
+static spinlock_t crypto_lb_lock = SPIN_LOCK_UNLOCKED;
+static int lb_num = 0;
+static struct crypto_lb *current_lb, *default_lb;
+static struct completion thread_exited;
+static int need_exit;
+static struct workqueue_struct *crypto_lb_queue;
+static DECLARE_WAIT_QUEUE_HEAD(crypto_lb_wait_queue);
+
+extern struct list_head *crypto_device_list;
+extern spinlock_t *crypto_device_lock;
+
+extern int force_lb_remove;
+extern struct crypto_device main_crypto_device;
+
+static int lb_is_current(struct crypto_lb *l)
+{
+ return (l->crypto_device_list != NULL && l->crypto_device_lock != NULL);
+}
+
+static int lb_is_default(struct crypto_lb *l)
+{
+ return (l == default_lb);
+}
+
+static void __lb_set_current(struct crypto_lb *l)
+{
+ struct crypto_lb *c = current_lb;
+
+ if (c) {
+ l->crypto_device_list = crypto_device_list;
+ l->crypto_device_lock = crypto_device_lock;
+ current_lb = l;
+ c->crypto_device_list = NULL;
+ c->crypto_device_lock = NULL;
+ } else {
+ l->crypto_device_list = crypto_device_list;
+ l->crypto_device_lock = crypto_device_lock;
+ current_lb = l;
+ }
+}
+
+static void lb_set_current(struct crypto_lb *l)
+{
+ struct crypto_lb *c = current_lb;
+
+ if (c) {
+ spin_lock_irq(&c->lock);
+ __lb_set_current(l);
+ spin_unlock_irq(&c->lock);
+ } else
+ __lb_set_current(l);
+}
+
+static void __lb_set_default(struct crypto_lb *l)
+{
+ default_lb = l;
+}
+
+static void lb_set_default(struct crypto_lb *l)
+{
+ struct crypto_lb *c = default_lb;
+
+ if (c) {
+ spin_lock_irq(&c->lock);
+ __lb_set_default(l);
+ spin_unlock_irq(&c->lock);
+ } else
+ __lb_set_default(l);
+}
+
+static int crypto_lb_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static int crypto_lb_probe(struct device *dev)
+{
+ return -ENODEV;
+}
+
+static int crypto_lb_remove(struct device *dev)
+{
+ return 0;
+}
+
+static void crypto_lb_release(struct device *dev)
+{
+ struct crypto_lb *d = container_of(dev, struct crypto_lb, device);
+
+ complete(&d->dev_released);
+}
+
+static void crypto_lb_class_release(struct class *class)
+{
+}
+
+static void crypto_lb_class_release_device(struct class_device *class_dev)
+{
+}
+
+struct class crypto_lb_class = {
+ .name = "crypto_lb",
+ .class_release = crypto_lb_class_release,
+ .release = crypto_lb_class_release_device
+};
+
+struct bus_type crypto_lb_bus_type = {
+ .name = "crypto_lb",
+ .match = crypto_lb_match
+};
+
+struct device_driver crypto_lb_driver = {
+ .name = "crypto_lb_driver",
+ .bus = &crypto_lb_bus_type,
+ .probe = crypto_lb_probe,
+ .remove = crypto_lb_remove,
+};
+
+struct device crypto_lb_dev = {
+ .parent = NULL,
+ .bus = &crypto_lb_bus_type,
+ .bus_id = "crypto load balancer",
+ .driver = &crypto_lb_driver,
+ .release = &crypto_lb_release
+};
+
+static ssize_t name_show(struct class_device *dev, char *buf)
+{
+ struct crypto_lb *lb = container_of(dev, struct crypto_lb, class_device);
+
+ return sprintf(buf, "%s\n", lb->name);
+}
+
+static ssize_t current_show(struct class_device *dev, char *buf)
+{
+ struct crypto_lb *lb;
+ int off = 0;
+
+ spin_lock_irq(&crypto_lb_lock);
+
+ list_for_each_entry(lb, &crypto_lb_list, lb_entry) {
+ if (lb_is_current(lb))
+ off += sprintf(buf + off, "[");
+ if (lb_is_default(lb))
+ off += sprintf(buf + off, "(");
+ off += sprintf(buf + off, "%s", lb->name);
+ if (lb_is_default(lb))
+ off += sprintf(buf + off, ")");
+ if (lb_is_current(lb))
+ off += sprintf(buf + off, "]");
+ }
+
+ spin_unlock_irq(&crypto_lb_lock);
+
+ if (!off)
+ off = sprintf(buf, "No load balancers regitered yet.");
+
+ off += sprintf(buf + off, "\n");
+
+ return off;
+}
+static ssize_t current_store(struct class_device *dev, const char *buf, size_t count)
+{
+ struct crypto_lb *lb;
+
+ spin_lock_irq(&crypto_lb_lock);
+
+ list_for_each_entry(lb, &crypto_lb_list, lb_entry) {
+ if (count == strlen(lb->name) && !strcmp(buf, lb->name)) {
+ lb_set_current(lb);
+ lb_set_default(lb);
+
+ dprintk(KERN_INFO "Load balancer %s is set as current and default.\n",
+ lb->name);
+
+ break;
+ }
+ }
+ spin_unlock_irq(&crypto_lb_lock);
+
+ return count;
+}
+
+static CLASS_DEVICE_ATTR(name, 0444, name_show, NULL);
+CLASS_DEVICE_ATTR(lbs, 0644, current_show, current_store);
+
+static void create_device_attributes(struct crypto_lb *lb)
+{
+ class_device_create_file(&lb->class_device, &class_device_attr_name);
+}
+
+static void remove_device_attributes(struct crypto_lb *lb)
+{
+ class_device_remove_file(&lb->class_device, &class_device_attr_name);
+}
+
+static int compare_lb(struct crypto_lb *l1, struct crypto_lb *l2)
+{
+ if (!strncmp(l1->name, l2->name, sizeof(l1->name)))
+ return 1;
+
+ return 0;
+}
+
+void crypto_lb_rehash(void)
+{
+ if (!current_lb)
+ return;
+
+ spin_lock_irq(¤t_lb->lock);
+
+ current_lb->rehash(current_lb);
+
+ spin_unlock_irq(¤t_lb->lock);
+
+ wake_up_interruptible(&crypto_lb_wait_queue);
+}
+
+struct crypto_device *crypto_lb_find_device(struct crypto_session_initializer *ci, struct crypto_data *data)
+{
+ struct crypto_device *dev;
+
+ if (!current_lb)
+ return NULL;
+
+ if (sci_binded(ci)) {
+ int found = 0;
+
+ spin_lock_irq(crypto_device_lock);
+
+ list_for_each_entry(dev, crypto_device_list, cdev_entry) {
+ if (dev->id == ci->bdev) {
+ found = 1;
+ break;
+ }
+ }
+
+ spin_unlock_irq(crypto_device_lock);
+
+ return (found) ? dev : NULL;
+ }
+
+ spin_lock_irq(¤t_lb->lock);
+
+ current_lb->rehash(current_lb);
+
+ spin_lock(crypto_device_lock);
+
+ dev = current_lb->find_device(current_lb, ci, data);
+ if (dev)
+ crypto_device_get(dev);
+
+ spin_unlock(crypto_device_lock);
+
+ spin_unlock_irq(¤t_lb->lock);
+
+ wake_up_interruptible(&crypto_lb_wait_queue);
+
+ return dev;
+}
+
+static int __crypto_lb_register(struct crypto_lb *lb)
+{
+ int err;
+
+ spin_lock_init(&lb->lock);
+
+ init_completion(&lb->dev_released);
+ memcpy(&lb->device, &crypto_lb_dev, sizeof(struct device));
+ lb->driver = &crypto_lb_driver;
+
+ snprintf(lb->device.bus_id, sizeof(lb->device.bus_id), "%s", lb->name);
+ err = device_register(&lb->device);
+ if (err) {
+ dprintk(KERN_ERR
+ "Failed to register crypto load balancer device %s: err=%d.\n",
+ lb->name, err);
+ return err;
+ }
+
+ snprintf(lb->class_device.class_id, sizeof(lb->class_device.class_id), "%s", lb->name);
+ lb->class_device.dev = &lb->device;
+ lb->class_device.class = &crypto_lb_class;
+
+ err = class_device_register(&lb->class_device);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto load balancer class device %s: err=%d.\n",
+ lb->name, err);
+ device_unregister(&lb->device);
+ return err;
+ }
+
+ create_device_attributes(lb);
+ wake_up_interruptible(&crypto_lb_wait_queue);
+
+ return 0;
+
+}
+
+static void __crypto_lb_unregister(struct crypto_lb *lb)
+{
+ wake_up_interruptible(&crypto_lb_wait_queue);
+ remove_device_attributes(lb);
+ class_device_unregister(&lb->class_device);
+ device_unregister(&lb->device);
+}
+
+int crypto_lb_register(struct crypto_lb *lb, int set_current, int set_default)
+{
+ struct crypto_lb *__lb;
+ int err;
+
+ spin_lock_irq(&crypto_lb_lock);
+
+ list_for_each_entry(__lb, &crypto_lb_list, lb_entry) {
+ if (unlikely(compare_lb(__lb, lb))) {
+ spin_unlock_irq(&crypto_lb_lock);
+
+ dprintk(KERN_ERR "Crypto load balancer %s is already registered.\n",
+ lb->name);
+ return -EINVAL;
+ }
+ }
+
+ list_add(&lb->lb_entry, &crypto_lb_list);
+
+ spin_unlock_irq(&crypto_lb_lock);
+
+ err = __crypto_lb_register(lb);
+ if (err) {
+ spin_lock_irq(&crypto_lb_lock);
+ list_del_init(&lb->lb_entry);
+ spin_unlock_irq(&crypto_lb_lock);
+
+ return err;
+ }
+
+ if (!default_lb || set_default)
+ lb_set_default(lb);
+
+ if (!current_lb || set_current)
+ lb_set_current(lb);
+
+ dprintk(KERN_INFO "Crypto load balancer %s was registered and set to be [%s.%s].\n",
+ lb->name, (lb_is_current(lb)) ? "current" : "not current",
+ (lb_is_default(lb)) ? "default" : "not default");
+
+ lb_num++;
+
+ return 0;
+}
+
+void crypto_lb_unregister(struct crypto_lb *lb)
+{
+ struct crypto_lb *__lb, *n;
+
+ if (lb_num == 1) {
+ dprintk(KERN_INFO "You are removing crypto load balancer %s which is current and default.\n"
+ "There is no other crypto load balancers. "
+ "Removing %s delayed untill new load balancer is registered.\n",
+ lb->name, (force_lb_remove) ? "is not" : "is");
+ while (lb_num == 1 && !force_lb_remove) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ }
+ }
+
+ __crypto_lb_unregister(lb);
+
+ spin_lock_irq(&crypto_lb_lock);
+
+ list_for_each_entry_safe(__lb, n, &crypto_lb_list, lb_entry) {
+ if (compare_lb(__lb, lb)) {
+ lb_num--;
+ list_del_init(&__lb->lb_entry);
+
+ dprintk(KERN_ERR "Crypto load balancer %s was unregistered.\n",
+ lb->name);
+ } else if (lb_num) {
+ if (lb_is_default(lb))
+ lb_set_default(__lb);
+ if (lb_is_current(lb))
+ lb_set_current(default_lb);
+ }
+ }
+
+ spin_unlock_irq(&crypto_lb_lock);
+}
+
+static void crypto_lb_queue_wrapper(void *data)
+{
+ struct crypto_device *dev = &main_crypto_device;
+ struct crypto_session *s = (struct crypto_session *)data;
+
+ dprintk(KERN_INFO "%s: Calling callback for session %llu [%llu] flags=%x, "
+ "op=%04u, type=%04x, mode=%04x, priority=%04x\n", __func__,
+ s->ci.id, s->ci.dev_id, s->ci.flags, s->ci.operation,
+ s->ci.type, s->ci.mode, s->ci.priority);
+
+ spin_lock_irq(&s->lock);
+ crypto_stat_finish_inc(s);
+
+ finish_session(s);
+ unstart_session(s);
+ spin_unlock_irq(&s->lock);
+
+ s->ci.callback(&s->ci, &s->data);
+
+ if (session_finished(s)) {
+ kfree(s);
+ return;
+ } else {
+ /*
+ * Special case: crypto consumer marks session as "not finished"
+ * in it's callback - it means that crypto consumer wants
+ * this session to be processed further,
+ * for example crypto consumer can add new route and then
+ * mark session as "not finished".
+ */
+
+ uncomplete_session(s);
+ unstart_session(s);
+ crypto_session_insert_main(dev, s);
+ }
+ spin_unlock_irq(&s->lock);
+}
+
+static void crypto_lb_process_next_route(struct crypto_session *s)
+{
+ struct crypto_route *rt;
+ struct crypto_device *dev;
+
+ rt = crypto_route_dequeue(s);
+ if (rt) {
+ dev = rt->dev;
+
+ spin_lock_irq(&dev->session_lock);
+ list_del_init(&s->dev_queue_entry);
+ spin_unlock_irq(&dev->session_lock);
+
+ crypto_route_free(rt);
+
+ dev = crypto_route_get_current_device(s);
+ if (dev) {
+ dprintk(KERN_INFO "%s: processing new route to %s.\n",
+ __func__, dev->name);
+
+ memcpy(&s->ci, &rt->ci, sizeof(s->ci));
+ crypto_session_insert(dev, s);
+
+ /*
+ * Reference to this device was already hold when
+ * new routing was added.
+ */
+ crypto_device_put(dev);
+ }
+ }
+}
+
+void crypto_wake_lb(void)
+{
+ wake_up_interruptible(&crypto_lb_wait_queue);
+}
+
+int crypto_lb_thread(void *data)
+{
+ struct crypto_session *s, *n;
+ struct crypto_device *dev = (struct crypto_device *)data;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+
+ while (!need_exit) {
+ list_for_each_entry_safe(s, n, &dev->session_list,
+ main_queue_entry) {
+ dprintk("session %llu [%llu]: flags=%x, route_num=%d, %s,%s,%s,%s.\n",
+ s->ci.id, s->ci.dev_id, s->ci.flags,
+ crypto_route_queue_len(s),
+ (session_completed(s)) ? "completed" : "not completed",
+ (session_finished(s)) ? "finished" : "not finished",
+ (session_started(s)) ? "started" : "not started",
+ (session_is_processed(s)) ? "is being processed" : "is not being processed");
+
+ if (!spin_trylock(&s->lock))
+ continue;
+
+ if (session_is_processed(s))
+ goto unlock;
+ if (session_started(s))
+ goto unlock;
+
+ if (session_completed(s)) {
+ crypto_stat_ptime_inc(s);
+
+ if (crypto_route_queue_len(s) > 1) {
+ crypto_lb_process_next_route(s);
+ } else {
+ start_session(s);
+ crypto_stat_start_inc(s);
+
+ dprintk("%s: going to remove session %llu [%llu].\n",
+ __func__, s->ci.id, s->ci.dev_id);
+
+ crypto_session_dequeue_main(s);
+ spin_unlock(&s->lock);
+
+ INIT_WORK(&s->work, &crypto_lb_queue_wrapper, s);
+ queue_work(crypto_lb_queue, &s->work);
+ continue;
+ }
+ }
+ unlock:
+ spin_unlock(&s->lock);
+ }
+
+ interruptible_sleep_on_timeout(&crypto_lb_wait_queue, 100);
+ }
+
+ flush_workqueue(crypto_lb_queue);
+ complete_and_exit(&thread_exited, 0);
+}
+
+int crypto_lb_init(void)
+{
+ int err;
+ long pid;
+
+ err = bus_register(&crypto_lb_bus_type);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto load balancer bus: err=%d.\n", err);
+ goto err_out_exit;
+ }
+
+ err = driver_register(&crypto_lb_driver);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto load balancer driver: err=%d.\n", err);
+ goto err_out_bus_unregister;
+ }
+
+ crypto_lb_class.class_dev_attrs = &class_device_attr_lbs;
+
+ err = class_register(&crypto_lb_class);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto load balancer class: err=%d.\n", err);
+ goto err_out_driver_unregister;
+ }
+
+ crypto_lb_queue = create_workqueue("clbq");
+ if (!crypto_lb_queue) {
+ dprintk(KERN_ERR "Failed to create crypto load balaner work queue.\n");
+ goto err_out_class_unregister;
+ }
+
+ init_completion(&thread_exited);
+ pid = kernel_thread(crypto_lb_thread, &main_crypto_device, CLONE_FS | CLONE_FILES);
+ if (IS_ERR((void *)pid)) {
+ dprintk(KERN_ERR "Failed to create kernel load balancing thread.\n");
+ goto err_out_destroy_workqueue;
+ }
+
+ return 0;
+
+err_out_destroy_workqueue:
+ destroy_workqueue(crypto_lb_queue);
+err_out_class_unregister:
+ class_unregister(&crypto_lb_class);
+err_out_driver_unregister:
+ driver_unregister(&crypto_lb_driver);
+err_out_bus_unregister:
+ bus_unregister(&crypto_lb_bus_type);
+err_out_exit:
+ return err;
+}
+
+void crypto_lb_fini(void)
+{
+ need_exit = 1;
+ wait_for_completion(&thread_exited);
+ flush_workqueue(crypto_lb_queue);
+ destroy_workqueue(crypto_lb_queue);
+ class_unregister(&crypto_lb_class);
+ driver_unregister(&crypto_lb_driver);
+ bus_unregister(&crypto_lb_bus_type);
+}
+
+EXPORT_SYMBOL_GPL(crypto_lb_register);
+EXPORT_SYMBOL_GPL(crypto_lb_unregister);
+EXPORT_SYMBOL_GPL(crypto_lb_rehash);
+EXPORT_SYMBOL_GPL(crypto_lb_find_device);
+EXPORT_SYMBOL_GPL(crypto_wake_lb);
Binary files /tmp/empty/.crypto_lb.c.swn and linux-2.6/drivers/acrypto/.crypto_lb.c.swn differ
Binary files /tmp/empty/.crypto_lb.c.swo and linux-2.6/drivers/acrypto/.crypto_lb.c.swo differ
Binary files /tmp/empty/.crypto_lb.c.swp and linux-2.6/drivers/acrypto/.crypto_lb.c.swp differ
diff -Nru /tmp/empty/crypto_lb.h linux-2.6/drivers/acrypto/crypto_lb.h
--- /tmp/empty/crypto_lb.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_lb.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,62 @@
+/*
+ * crypto_lb.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CRYPTO_LB_H
+#define __CRYPTO_LB_H
+
+#include "acrypto.h"
+
+#define CRYPTO_LB_NAMELEN 32
+
+struct crypto_lb {
+ struct list_head lb_entry;
+
+ char name[CRYPTO_LB_NAMELEN];
+
+ void (*rehash)(struct crypto_lb *);
+ struct crypto_device * (*find_device) (struct crypto_lb *,
+ struct crypto_session_initializer *,
+ struct crypto_data *);
+
+ spinlock_t lock;
+
+ spinlock_t *crypto_device_lock;
+ struct list_head *crypto_device_list;
+
+ struct device_driver *driver;
+ struct device device;
+ struct class_device class_device;
+ struct completion dev_released;
+
+};
+
+int crypto_lb_register(struct crypto_lb *lb, int set_current, int set_default);
+void crypto_lb_unregister(struct crypto_lb *);
+
+inline void crypto_lb_rehash(void);
+struct crypto_device *crypto_lb_find_device(struct crypto_session_initializer *, struct crypto_data *);
+
+void crypto_wake_lb(void);
+
+int crypto_lb_init(void);
+void crypto_lb_fini(void);
+
+#endif /* __CRYPTO_LB_H */
diff -Nru /tmp/empty/crypto_main.c linux-2.6/drivers/acrypto/crypto_main.c
--- /tmp/empty/crypto_main.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_main.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,340 @@
+/*
+ * crypto_main.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "acrypto.h"
+#include "crypto_lb.h"
+#include "crypto_conn.h"
+#include "crypto_route.h"
+
+int force_lb_remove;
+module_param(force_lb_remove, int, 0);
+
+struct crypto_device main_crypto_device;
+
+extern struct bus_type crypto_bus_type;
+extern struct device_driver crypto_driver;
+extern struct class crypto_class;
+extern struct device crypto_dev;
+
+extern struct class_device_attribute class_device_attr_devices;
+extern struct class_device_attribute class_device_attr_lbs;
+
+static void dump_ci(struct crypto_session_initializer *ci)
+{
+ dprintk("%llu [%llu] op=%04u, type=%04x, mode=%04x, priority=%04x",
+ ci->id, ci->dev_id,
+ ci->operation, ci->type, ci->mode, ci->priority);
+}
+
+static void __crypto_session_insert(struct crypto_device *dev, struct crypto_session *s)
+{
+ struct crypto_session *__s;
+
+ if (unlikely(list_empty(&dev->session_list))) {
+ list_add(&s->dev_queue_entry, &dev->session_list);
+ } else {
+ int inserted = 0;
+
+ list_for_each_entry(__s, &dev->session_list, dev_queue_entry) {
+ if (__s->ci.priority < s->ci.priority) {
+ list_add_tail(&s->dev_queue_entry, &__s->dev_queue_entry);
+ inserted = 1;
+ break;
+ }
+ }
+
+ if (!inserted)
+ list_add_tail(&s->dev_queue_entry, &dev->session_list);
+ }
+
+ dump_ci(&s->ci);
+ dprintk(" added to crypto device %s [%d].\n", dev->name, atomic_read(&dev->refcnt));
+}
+
+void crypto_session_insert_main(struct crypto_device *dev, struct crypto_session *s)
+{
+ struct crypto_session *__s;
+
+ spin_lock_irq(&dev->session_lock);
+
+ crypto_device_get(dev);
+ if (unlikely(list_empty(&dev->session_list))) {
+ list_add(&s->main_queue_entry, &dev->session_list);
+ } else {
+ int inserted = 0;
+
+ list_for_each_entry(__s, &dev->session_list, main_queue_entry) {
+ if (__s->ci.priority < s->ci.priority) {
+ list_add_tail(&s->main_queue_entry,
+ &__s->main_queue_entry);
+ inserted = 1;
+ break;
+ }
+ }
+
+ if (!inserted)
+ list_add_tail(&s->main_queue_entry, &dev->session_list);
+ }
+
+ spin_unlock_irq(&dev->session_lock);
+}
+
+void crypto_session_insert(struct crypto_device *dev, struct crypto_session *s)
+{
+ spin_lock_irq(&dev->session_lock);
+ __crypto_session_insert(dev, s);
+ spin_unlock_irq(&dev->session_lock);
+}
+
+struct crypto_session *crypto_session_create(struct crypto_session_initializer *ci, struct crypto_data *d)
+{
+ struct crypto_device *dev = &main_crypto_device;
+ struct crypto_device *ldev;
+ struct crypto_session *s;
+ int err;
+
+ if (d->priv_size > CRYPTO_MAX_PRIV_SIZE) {
+ dprintk("priv_size %u is too big, maximum allowed %u.\n",
+ d->priv_size, CRYPTO_MAX_PRIV_SIZE);
+ return NULL;
+ }
+
+ ldev = crypto_lb_find_device(ci, d);
+ if (!ldev) {
+ dprintk("Cannot find suitable device.\n");
+ return NULL;
+ }
+
+ s = kmalloc(sizeof(*s) + d->priv_size, GFP_ATOMIC);
+ if (!s) {
+ ldev->stat.kmem_failed++;
+ goto err_out_device_put;
+ }
+
+ memset(s, 0xAB, sizeof(*s));
+
+ crypto_route_head_init(&s->route_list);
+ INIT_LIST_HEAD(&s->dev_queue_entry);
+ INIT_LIST_HEAD(&s->main_queue_entry);
+
+ spin_lock_init(&s->lock);
+
+ memcpy(&s->ci, ci, sizeof(s->ci));
+ memcpy(&s->data, d, sizeof(s->data));
+ s->data.priv = s + 1;
+ if (d->priv_size) {
+ s->data.priv = s + 1;
+ if (d->priv)
+ memcpy(s->data.priv, d->priv, d->priv_size);
+ }
+
+ s->ci.id = dev->sid++;
+ s->ci.dev_id = ldev->sid++;
+ s->ci.flags = 0;
+
+ err = crypto_route_add_direct(ldev, s, ci);
+ if (err) {
+ dprintk("Can not add route to device %s.\n", ldev->name);
+ goto err_out_session_free;
+ }
+
+ return s;
+
+err_out_session_free:
+ kfree(s);
+err_out_device_put:
+ crypto_device_put(ldev);
+
+ return NULL;
+}
+
+void crypto_session_add(struct crypto_session *s)
+{
+ struct crypto_device *ldev;
+ struct crypto_device *dev = &main_crypto_device;
+
+ ldev = crypto_route_get_current_device(s);
+ BUG_ON(!ldev); /* This can not happen. */
+
+ spin_lock_irq(&s->lock);
+ crypto_session_insert(ldev, s);
+ crypto_device_put(ldev);
+ crypto_session_insert_main(dev, s);
+ spin_unlock_irq(&s->lock);
+
+ if (ldev->data_ready)
+ ldev->data_ready(ldev);
+}
+
+struct crypto_session *crypto_session_alloc(struct crypto_session_initializer *ci, struct crypto_data *d)
+{
+ struct crypto_session *s;
+
+ s = crypto_session_create(ci, d);
+ if (!s)
+ return NULL;
+
+ crypto_session_add(s);
+
+ return s;
+}
+
+void crypto_session_dequeue_route(struct crypto_session *s)
+{
+ struct crypto_route *rt;
+ struct crypto_device *dev;
+
+ BUG_ON(crypto_route_queue_len(s) > 1);
+
+ while ((rt = crypto_route_dequeue(s))) {
+ dev = rt->dev;
+
+ dprintk(KERN_INFO "Removing route entry for device %s.\n", dev->name);
+
+ spin_lock_irq(&dev->session_lock);
+
+ list_del_init(&s->dev_queue_entry);
+
+ spin_unlock_irq(&dev->session_lock);
+
+ crypto_route_free(rt);
+ }
+}
+
+void __crypto_session_dequeue_main(struct crypto_session *s)
+{
+ struct crypto_device *dev = &main_crypto_device;
+
+ list_del(&s->main_queue_entry);
+ crypto_device_put(dev);
+}
+
+void crypto_session_dequeue_main(struct crypto_session *s)
+{
+ struct crypto_device *dev = &main_crypto_device;
+
+ spin_lock_irq(&dev->session_lock);
+
+ __crypto_session_dequeue_main(s);
+
+ spin_unlock_irq(&dev->session_lock);
+}
+
+int __devinit cmain_init(void)
+{
+ struct crypto_device *dev = &main_crypto_device;
+ int err;
+
+ snprintf(dev->name, sizeof(dev->name), "crypto_sessions");
+
+ err = bus_register(&crypto_bus_type);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto bus: err=%d.\n",
+ err);
+ return err;
+ }
+
+ err = driver_register(&crypto_driver);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto driver: err=%d.\n",
+ err);
+ goto err_out_bus_unregister;
+ }
+
+ err = class_register(&crypto_class);
+ if (err) {
+ dprintk(KERN_ERR "Failed to register crypto class: err=%d.\n",
+ err);
+ goto err_out_driver_unregister;
+ }
+
+ err = crypto_lb_init();
+ if (err)
+ goto err_out_class_unregister;
+
+ err = crypto_conn_init();
+ if (err)
+ goto err_out_crypto_lb_fini;
+
+ err = __crypto_device_add(dev);
+ if (err)
+ goto err_out_crypto_conn_fini;
+
+ err = class_device_create_file(&dev->class_device, &class_device_attr_devices);
+ if (err)
+ dprintk("Failed to create \"devices\" attribute: err=%d.\n",
+ err);
+ err = class_device_create_file(&dev->class_device, &class_device_attr_lbs);
+ if (err)
+ dprintk("Failed to create \"lbs\" attribute: err=%d.\n", err);
+
+ return 0;
+
+err_out_crypto_conn_fini:
+ crypto_conn_fini();
+err_out_crypto_lb_fini:
+ crypto_lb_fini();
+err_out_class_unregister:
+ class_unregister(&crypto_class);
+err_out_driver_unregister:
+ driver_unregister(&crypto_driver);
+err_out_bus_unregister:
+ bus_unregister(&crypto_bus_type);
+
+ return err;
+}
+
+void __devexit cmain_fini(void)
+{
+ struct crypto_device *dev = &main_crypto_device;
+
+ class_device_remove_file(&dev->class_device, &class_device_attr_devices);
+ class_device_remove_file(&dev->class_device, &class_device_attr_lbs);
+ __crypto_device_remove(dev);
+
+ crypto_conn_fini();
+ crypto_lb_fini();
+
+ class_unregister(&crypto_class);
+ driver_unregister(&crypto_driver);
+ bus_unregister(&crypto_bus_type);
+}
+
+module_init(cmain_init);
+module_exit(cmain_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Asynchronous crypto layer.");
+
+EXPORT_SYMBOL(crypto_session_alloc);
+EXPORT_SYMBOL_GPL(crypto_session_create);
+EXPORT_SYMBOL_GPL(crypto_session_add);
+EXPORT_SYMBOL_GPL(crypto_session_dequeue_route);
diff -Nru /tmp/empty/crypto_provider.c linux-2.6/drivers/acrypto/crypto_provider.c
--- /tmp/empty/crypto_provider.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_provider.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,183 @@
+/*
+ * crypto_provider.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/crypto.h>
+#include <linux/mm.h>
+
+#define DEBUG
+#include "acrypto.h"
+#include "crypto_stat.h"
+#include "crypto_def.h"
+
+static void prov_data_ready(struct crypto_device *dev);
+
+static struct crypto_capability prov_caps[] = {
+ {CRYPTO_OP_ENCRYPT, CRYPTO_TYPE_AES_128, CRYPTO_MODE_ECB, 1000},
+ {CRYPTO_OP_DECRYPT, CRYPTO_TYPE_AES_128, CRYPTO_MODE_ECB, 1000},
+};
+static int prov_cap_number = sizeof(prov_caps) / sizeof(prov_caps[0]);
+
+static struct completion thread_exited;
+static DECLARE_WAIT_QUEUE_HEAD(crypto_wait_queue);
+static int need_exit;
+static struct crypto_tfm *tfm;
+
+static struct crypto_device pdev = {
+ .name = "crypto_provider",
+ .data_ready = prov_data_ready,
+ .cap = &prov_caps[0],
+};
+
+static void prov_data_ready(struct crypto_device *dev)
+{
+ wake_up(&crypto_wait_queue);
+}
+
+static int crypto_thread(void *data)
+{
+ struct crypto_device *dev = (struct crypto_device *)data;
+ u8 *key;
+ unsigned int keylen;
+ int err;
+ struct crypto_session *s, *n;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+
+ while (!need_exit) {
+ interruptible_sleep_on_timeout(&crypto_wait_queue, 100);
+
+ if (need_exit)
+ break;
+
+ list_for_each_entry_safe(s, n, &dev->session_list, dev_queue_entry) {
+ if (!spin_trylock(&s->lock))
+ continue;
+
+ if (session_completed(s)) {
+ goto unlock;
+ }
+ start_process_session(s);
+
+ dprintk("Begin to process session %llu [%llu] in %s.\n",
+ s->ci.id, s->ci.dev_id, pdev.name);
+
+ key = ((u8 *) page_address(s->data.sg_key.page)) + s->data.sg_key.offset;
+ keylen = s->data.sg_key.length;
+
+ err = crypto_cipher_setkey(tfm, key, keylen);
+ if (err) {
+ dprintk(KERN_ERR "Failed to set key [keylen=%d]: err=%d.\n",
+ keylen, err);
+ broke_session(s);
+ goto out;
+ }
+
+ if (s->ci.operation == CRYPTO_OP_ENCRYPT)
+ crypto_cipher_encrypt(tfm, &s->data.sg_dst, &s->data.sg_src, s->data.sg_src.length);
+ else
+ crypto_cipher_decrypt(tfm, &s->data.sg_dst, &s->data.sg_src, s->data.sg_src.length);
+
+ s->data.sg_dst.length = s->data.sg_src.length;
+ s->data.sg_dst.offset = 0;
+
+ dprintk(KERN_INFO "Completing session %llu [%llu] in %s.\n",
+ s->ci.id, s->ci.dev_id, pdev.name);
+
+out:
+ crypto_stat_complete_inc(s);
+ crypto_session_dequeue_route(s);
+ complete_session(s);
+ stop_process_session(s);
+unlock:
+ spin_unlock(&s->lock);
+ }
+ }
+
+ complete_and_exit(&thread_exited, 0);
+}
+
+int prov_init(void)
+{
+ int err, pid;
+
+ tfm = crypto_alloc_tfm("aes", 0);
+ if (!tfm) {
+ printk(KERN_ERR "Failed to allocate SHA1 tfm.\n");
+ return -EINVAL;
+ }
+
+ init_completion(&thread_exited);
+ pid = kernel_thread(crypto_thread, &pdev, CLONE_FS | CLONE_FILES);
+ if (IS_ERR((void *)pid)) {
+ err = -EINVAL;
+ dprintk(KERN_ERR "Failed to create kernel load balancing thread.\n");
+ goto err_out_free_tfm;
+ }
+
+ pdev.cap_number = prov_cap_number;
+
+ err = crypto_device_add(&pdev);
+ if (err)
+ goto err_out_remove_thread;
+
+ dprintk(KERN_INFO "Test crypto provider module %s is loaded.\n", pdev.name);
+
+ return 0;
+
+err_out_remove_thread:
+ need_exit = 1;
+ wake_up(&crypto_wait_queue);
+ wait_for_completion(&thread_exited);
+err_out_free_tfm:
+ crypto_free_tfm(tfm);
+
+ return err;
+}
+
+void prov_fini(void)
+{
+ need_exit = 1;
+ wake_up(&crypto_wait_queue);
+ wait_for_completion(&thread_exited);
+
+ crypto_device_remove(&pdev);
+ crypto_free_tfm(tfm);
+
+ dprintk(KERN_INFO "Test crypto provider module %s is unloaded.\n", pdev.name);
+}
+
+module_init(prov_init);
+module_exit(prov_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Test crypto module provider.");
Binary files /tmp/empty/.crypto_provider.c.swp and linux-2.6/drivers/acrypto/.crypto_provider.c.swp differ
diff -Nru /tmp/empty/crypto_route.h linux-2.6/drivers/acrypto/crypto_route.h
--- /tmp/empty/crypto_route.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_route.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,242 @@
+/*
+ * crypto_route.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CRYPTO_ROUTE_H
+#define __CRYPTO_ROUTE_H
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "acrypto.h"
+
+static inline struct crypto_route *crypto_route_alloc_direct(struct crypto_device *dev,
+ struct crypto_session_initializer *ci)
+{
+ struct crypto_route *rt;
+
+ rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
+ if (!rt) {
+ crypto_device_put(dev);
+ return NULL;
+ }
+
+ memset(rt, 0, sizeof(*rt));
+ memcpy(&rt->ci, ci, sizeof(*ci));
+
+ rt->dev = dev;
+
+ return rt;
+}
+
+static inline struct crypto_route *crypto_route_alloc(struct crypto_device *dev,
+ struct crypto_session_initializer *ci)
+{
+ struct crypto_route *rt;
+
+ if (!match_initializer(dev, ci))
+ return NULL;
+
+ rt = crypto_route_alloc_direct(dev, ci);
+
+ return rt;
+}
+
+static inline void crypto_route_free(struct crypto_route *rt)
+{
+ crypto_device_put(rt->dev);
+ rt->dev = NULL;
+ kfree(rt);
+}
+
+static inline void __crypto_route_del(struct crypto_route *rt, struct crypto_route_head *list)
+{
+ struct crypto_route *next, *prev;
+
+ list->qlen--;
+ next = rt->next;
+ prev = rt->prev;
+ rt->next = rt->prev = NULL;
+ rt->list = NULL;
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void crypto_route_del(struct crypto_route *rt)
+{
+ struct crypto_route_head *list = rt->list;
+
+ if (list) {
+ spin_lock_irq(&list->lock);
+ if (list == rt->list)
+ __crypto_route_del(rt, rt->list);
+ spin_unlock_irq(&list->lock);
+
+ crypto_route_free(rt);
+ }
+}
+
+static inline struct crypto_route *__crypto_route_dequeue(struct crypto_route_head *list)
+{
+ struct crypto_route *next, *prev, *result;
+
+ prev = (struct crypto_route *)list;
+ next = prev->next;
+ result = NULL;
+ if (next != prev) {
+ result = next;
+ next = next->next;
+ list->qlen--;
+ next->prev = prev;
+ prev->next = next;
+ result->next = result->prev = NULL;
+ result->list = NULL;
+ }
+ return result;
+}
+
+static inline struct crypto_route *crypto_route_dequeue(struct crypto_session *s)
+{
+ struct crypto_route *rt;
+
+ spin_lock_irq(&s->route_list.lock);
+
+ rt = __crypto_route_dequeue(&s->route_list);
+
+ spin_unlock_irq(&s->route_list.lock);
+
+ return rt;
+}
+
+static inline void __crypto_route_queue(struct crypto_route *rt, struct crypto_route_head *list)
+{
+ struct crypto_route *prev, *next;
+
+ rt->list = list;
+ list->qlen++;
+ next = (struct crypto_route *)list;
+ prev = next->prev;
+ rt->next = next;
+ rt->prev = prev;
+ next->prev = prev->next = rt;
+}
+
+static inline void crypto_route_queue(struct crypto_route *rt, struct crypto_session *s)
+{
+
+ spin_lock_irq(&s->route_list.lock);
+
+ __crypto_route_queue(rt, &s->route_list);
+
+ spin_unlock_irq(&s->route_list.lock);
+}
+
+static inline int crypto_route_add(struct crypto_device *dev, struct crypto_session *s,
+ struct crypto_session_initializer *ci)
+{
+ struct crypto_route *rt;
+
+ rt = crypto_route_alloc(dev, ci);
+ if (!rt)
+ return -ENOMEM;
+
+ crypto_route_queue(rt, s);
+
+ return 0;
+}
+
+static inline int crypto_route_add_direct(struct crypto_device *dev, struct crypto_session *s,
+ struct crypto_session_initializer *ci)
+{
+ struct crypto_route *rt;
+
+ rt = crypto_route_alloc_direct(dev, ci);
+ if (!rt)
+ return -ENOMEM;
+
+ crypto_route_queue(rt, s);
+
+ return 0;
+}
+
+static inline int crypto_route_queue_len(struct crypto_session *s)
+{
+ return s->route_list.qlen;
+}
+
+static inline void crypto_route_head_init(struct crypto_route_head *list)
+{
+ spin_lock_init(&list->lock);
+ list->prev = list->next = (struct crypto_route *)list;
+ list->qlen = 0;
+}
+
+static inline struct crypto_route *__crypto_route_current(struct crypto_route_head *list)
+{
+ struct crypto_route *next, *prev, *result;
+
+ prev = (struct crypto_route *)list;
+ next = prev->next;
+ result = NULL;
+ if (next != prev)
+ result = next;
+
+ return result;
+}
+
+static inline struct crypto_route *crypto_route_current(struct crypto_session *s)
+{
+ struct crypto_route_head *list;
+ struct crypto_route *rt = NULL;
+
+ list = &s->route_list;
+
+ if (list) {
+ spin_lock_irq(&list->lock);
+
+ rt = __crypto_route_current(list);
+
+ spin_unlock_irq(&list->lock);
+ }
+
+ return rt;
+}
+
+static inline struct crypto_device *crypto_route_get_current_device(struct crypto_session *s)
+{
+ struct crypto_route *rt = NULL;
+ struct crypto_device *dev = NULL;
+ struct crypto_route_head *list = &s->route_list;
+
+ spin_lock_irq(&list->lock);
+
+ rt = __crypto_route_current(list);
+ if (rt) {
+ dev = rt->dev;
+ crypto_device_get(dev);
+ }
+
+ spin_unlock_irq(&list->lock);
+
+ return dev;
+}
+
+#endif /* __CRYPTO_ROUTE_H */
diff -Nru /tmp/empty/crypto_stat.c linux-2.6/drivers/acrypto/crypto_stat.c
--- /tmp/empty/crypto_stat.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_stat.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,100 @@
+/*
+ * crypto_stat.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "acrypto.h"
+#include "crypto_route.h"
+
+void crypto_stat_start_inc(struct crypto_session *s)
+{
+ struct crypto_device *dev;
+
+ dev = crypto_route_get_current_device(s);
+ if (dev) {
+ spin_lock_irq(&dev->stat_lock);
+ dev->stat.sstarted++;
+ spin_unlock_irq(&dev->stat_lock);
+
+ crypto_device_put(dev);
+ }
+}
+
+void crypto_stat_finish_inc(struct crypto_session *s)
+{
+ struct crypto_device *dev;
+
+ dev = crypto_route_get_current_device(s);
+ if (dev) {
+ spin_lock_irq(&dev->stat_lock);
+ dev->stat.sfinished++;
+ spin_unlock_irq(&dev->stat_lock);
+
+ crypto_device_put(dev);
+ }
+}
+
+void crypto_stat_complete_inc(struct crypto_session *s)
+{
+ struct crypto_device *dev;
+
+ dev = crypto_route_get_current_device(s);
+ if (dev) {
+ spin_lock_irq(&dev->stat_lock);
+ dev->stat.scompleted++;
+ spin_unlock_irq(&dev->stat_lock);
+
+ crypto_device_put(dev);
+ }
+}
+
+void crypto_stat_ptime_inc(struct crypto_session *s)
+{
+ struct crypto_device *dev;
+
+ dev = crypto_route_get_current_device(s);
+ if (dev) {
+ int i;
+
+ spin_lock_irq(&dev->stat_lock);
+ for (i = 0; i < dev->cap_number; ++i) {
+ if (__match_initializer(&dev->cap[i], &s->ci)) {
+ dev->cap[i].ptime += s->ci.ptime;
+ dev->cap[i].scomp++;
+ break;
+ }
+ }
+ spin_unlock_irq(&dev->stat_lock);
+
+ crypto_device_put(dev);
+ }
+}
+
+EXPORT_SYMBOL(crypto_stat_start_inc);
+EXPORT_SYMBOL(crypto_stat_finish_inc);
+EXPORT_SYMBOL(crypto_stat_complete_inc);
diff -Nru /tmp/empty/crypto_stat.h linux-2.6/drivers/acrypto/crypto_stat.h
--- /tmp/empty/crypto_stat.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/crypto_stat.h 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,32 @@
+/*
+ * crypto_stat.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CRYPTO_STAT_H
+#define __CRYPTO_STAT_H
+
+#include "acrypto.h"
+
+void crypto_stat_start_inc(struct crypto_session *s);
+void crypto_stat_finish_inc(struct crypto_session *s);
+void crypto_stat_complete_inc(struct crypto_session *s);
+void crypto_stat_ptime_inc(struct crypto_session *s);
+
+#endif /* __CRYPTO_STAT_H */
diff -Nru /tmp/empty/Kconfig linux-2.6/drivers/acrypto/Kconfig
--- /tmp/empty/Kconfig 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/Kconfig 2004-12-14 18:04:35.000000000 +0300
@@ -0,0 +1,40 @@
+menu "Asynchronous crypto layer"
+
+config ACRYPTO
+ tristate "Asyncronous crypto layer"
+ depends on CONNECTOR
+ help
+ Say Y here if you want to use new Linux asynchronous crypto layer.
+ This support is also available as a module. If so, the module
+ will be called acrypto.ko.
+
+config SIMPLE_LB
+ tristate "Simple asyncronous crypto load balancer"
+ depends on ACRYPTO
+ help
+ Say Y here if you want to compile simple load balancer which takes into
+ account only crypto device's queue length.
+ This support is also available as a module. If so, the module
+ will be called simpl_lb.ko.
+
+config TEST_CRYPTO_PROVIDER
+ tristate "Simple crypto provider"
+ depends on CRYPTO && ACRYPTO
+ help
+ Say Y here if you want to compile test bridge between
+ synchronous crypto layer and asyncronous one.
+ It supports only AES-128-ECB.
+ This support is also available as a module. If so, the module
+ will be called crypto_provider.ko.
+
+config TEST_CRYPTO_CONSUMER
+ tristate "Simple crypto consumer"
+ depends on ACRYPTO
+ help
+ Say Y here if you want to compile test crypto consumer
+ which injects crypto session one time per jiffie.
+ It supports only AES-128-ECB.
+ This support is also available as a module. If so, the module
+ will be called consumer.ko.
+
+endmenu
diff -Nru /tmp/empty/Makefile linux-2.6/drivers/acrypto/Makefile
--- /tmp/empty/Makefile 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/Makefile 2004-12-14 17:52:25.000000000 +0300
@@ -0,0 +1,6 @@
+obj-$(CONFIG_ACRYPTO) += acrypto.o
+obj-$(CONFIG_SIMPLE_LB) += simple_lb.o
+obj-$(CONFIG_TEST_CRYPTO_PROVIDER) += crypto_provider.o
+obj-$(CONFIG_TEST_CRYPTO_CONSUMER) += consumer.o
+
+acrypto-objs := crypto_main.o crypto_lb.o crypto_dev.o crypto_conn.o crypto_stat.o
diff -Nru /tmp/empty/simple_lb.c linux-2.6/drivers/acrypto/simple_lb.c
--- /tmp/empty/simple_lb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/acrypto/simple_lb.c 2004-12-14 18:53:11.000000000 +0300
@@ -0,0 +1,91 @@
+/*
+ * simple_lb.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "crypto_lb.h"
+
+static void simple_lb_rehash(struct crypto_lb *);
+static struct crypto_device *simple_lb_find_device(struct crypto_lb *,
+ struct
+ crypto_session_initializer *,
+ struct crypto_data *);
+
+struct crypto_lb simple_lb = {
+ .name = "simple_lb",
+ .rehash = simple_lb_rehash,
+ .find_device = simple_lb_find_device
+};
+
+static void simple_lb_rehash(struct crypto_lb *lb)
+{
+}
+
+static struct crypto_device *simple_lb_find_device(struct crypto_lb *lb,
+ struct
+ crypto_session_initializer
+ *ci,
+ struct crypto_data *data)
+{
+ struct crypto_device *dev, *__dev;
+ int min = 0x7ffffff;
+
+ __dev = NULL;
+ list_for_each_entry(dev, lb->crypto_device_list, cdev_entry) {
+ if (device_broken(dev))
+ continue;
+ if (!match_initializer(dev, ci))
+ continue;
+
+ if (atomic_read(&dev->refcnt) < min) {
+ min = atomic_read(&dev->refcnt);
+ __dev = dev;
+ }
+ }
+
+ return __dev;
+}
+
+int __devinit simple_lb_init(void)
+{
+ dprintk(KERN_INFO "Registering simple crypto load balancer.\n");
+
+ return crypto_lb_register(&simple_lb, 1, 1);
+}
+
+void __devexit simple_lb_fini(void)
+{
+ dprintk(KERN_INFO "Unregistering simple crypto load balancer.\n");
+
+ crypto_lb_unregister(&simple_lb);
+}
+
+module_init(simple_lb_init);
+module_exit(simple_lb_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Simple crypto load balancer.");
[-- Attachment #1.3: connector.patch --]
[-- Type: text/x-patch, Size: 24546 bytes --]
diff -Nru /tmp/empty/Kconfig linux-2.6/drivers/connector/Kconfig
--- /tmp/empty/Kconfig 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/Kconfig 2004-09-26 00:12:57.000000000 +0400
@@ -0,0 +1,13 @@
+menu "Connector - unified userspace <-> kernelspace linker"
+
+config CONNECTOR
+ tristate "Connector - unified userspace <-> kernelspace linker"
+ depends on NET
+ ---help---
+ This is unified userspace <-> kernelspace connector working on top
+ of the netlink socket protocol.
+
+ Connector support can also be built as a module. If so, the module
+ will be called connector.ko.
+
+endmenu
diff -Nru /tmp/empty/Makefile linux-2.6/drivers/connector/Makefile
--- /tmp/empty/Makefile 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/Makefile 2004-09-26 00:12:57.000000000 +0400
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CONNECTOR) += cn.o
+cn-objs := cn_queue.o connector.o
diff -Nru /tmp/empty/cn_queue.c linux-2.6/drivers/connector/cn_queue.c
--- /tmp/empty/cn_queue.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/cn_queue.c 2004-09-26 21:50:12.000000000 +0400
@@ -0,0 +1,219 @@
+/*
+ * cn_queue.c
+ *
+ * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/suspend.h>
+
+#include "cn_queue.h"
+
+static void cn_queue_wrapper(void *data)
+{
+ struct cn_callback_entry *cbq = (struct cn_callback_entry *)data;
+
+ atomic_inc(&cbq->cb->refcnt);
+ cbq->cb->callback(cbq->cb->priv);
+ atomic_dec(&cbq->cb->refcnt);
+
+ cbq->destruct_data(cbq->ddata);
+}
+
+static struct cn_callback_entry *cn_queue_alloc_callback_entry(struct
+ cn_callback *cb)
+{
+ struct cn_callback_entry *cbq;
+
+ cbq = kmalloc(sizeof(*cbq), GFP_KERNEL);
+ if (!cbq) {
+ printk(KERN_ERR "Failed to create new callback queue.\n");
+ return NULL;
+ }
+
+ memset(cbq, 0, sizeof(*cbq));
+
+ cbq->cb = cb;
+
+ INIT_WORK(&cbq->work, &cn_queue_wrapper, cbq);
+
+ return cbq;
+}
+
+static void cn_queue_free_callback(struct cn_callback_entry *cbq)
+{
+ cancel_delayed_work(&cbq->work);
+
+ while (atomic_read(&cbq->cb->refcnt)) {
+ printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
+ cbq->pdev->name, atomic_read(&cbq->cb->refcnt));
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ }
+
+ kfree(cbq);
+}
+
+int cn_cb_equal(struct cb_id *i1, struct cb_id *i2)
+{
+ return ((i1->idx == i2->idx) && (i1->val == i2->val));
+}
+
+int cn_queue_add_callback(struct cn_queue_dev *dev, struct cn_callback *cb)
+{
+ struct cn_callback_entry *cbq, *n, *__cbq;
+ int found = 0;
+
+ cbq = cn_queue_alloc_callback_entry(cb);
+ if (!cbq)
+ return -ENOMEM;
+
+ atomic_inc(&dev->refcnt);
+ cbq->pdev = dev;
+
+ spin_lock(&dev->queue_lock);
+ list_for_each_entry_safe(__cbq, n, &dev->queue_list, callback_entry) {
+ if (cn_cb_equal(&__cbq->cb->id, &cb->id)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ atomic_set(&cbq->cb->refcnt, 1);
+ list_add_tail(&cbq->callback_entry, &dev->queue_list);
+ }
+ spin_unlock(&dev->queue_lock);
+
+ if (found) {
+ atomic_dec(&dev->refcnt);
+ atomic_set(&cbq->cb->refcnt, 0);
+ cn_queue_free_callback(cbq);
+ return -EINVAL;
+ }
+
+ cbq->nls = dev->nls;
+ cbq->seq = 0;
+ //cbq->group = ++dev->netlink_groups;
+ cbq->group = cbq->cb->id.idx;
+
+ return 0;
+}
+
+void cn_queue_del_callback(struct cn_queue_dev *dev, struct cn_callback *cb)
+{
+ struct cn_callback_entry *cbq = NULL, *n;
+ int found = 0;
+
+ spin_lock(&dev->queue_lock);
+ list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) {
+ if (cn_cb_equal(&cbq->cb->id, &cb->id)) {
+ list_del(&cbq->callback_entry);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock(&dev->queue_lock);
+
+ if (found) {
+ atomic_dec(&cbq->cb->refcnt);
+ cn_queue_free_callback(cbq);
+ atomic_dec(&dev->refcnt);
+ }
+}
+
+struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls)
+{
+ struct cn_queue_dev *dev;
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_ERR "%s: Failed to allocte new struct cn_queue_dev.\n",
+ name);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ snprintf(dev->name, sizeof(dev->name), "%s", name);
+
+ atomic_set(&dev->refcnt, 0);
+ INIT_LIST_HEAD(&dev->queue_list);
+ spin_lock_init(&dev->queue_lock);
+
+ dev->nls = nls;
+ dev->netlink_groups = 0;
+
+ dev->cn_queue = create_workqueue(dev->name);
+ if (!dev->cn_queue) {
+ printk(KERN_ERR "Failed to create %s queue.\n", dev->name);
+ kfree(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+void cn_queue_free_dev(struct cn_queue_dev *dev)
+{
+ struct cn_callback_entry *cbq, *n;
+
+ flush_workqueue(dev->cn_queue);
+ destroy_workqueue(dev->cn_queue);
+
+ spin_lock(&dev->queue_lock);
+ list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) {
+ list_del(&cbq->callback_entry);
+ atomic_dec(&cbq->cb->refcnt);
+ }
+ spin_unlock(&dev->queue_lock);
+
+ while (atomic_read(&dev->refcnt)) {
+ printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
+ dev->name, atomic_read(&dev->refcnt));
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ }
+
+ memset(dev, 0, sizeof(*dev));
+ kfree(dev);
+ dev = NULL;
+}
+
+EXPORT_SYMBOL(cn_queue_add_callback);
+EXPORT_SYMBOL(cn_queue_del_callback);
+EXPORT_SYMBOL(cn_queue_alloc_dev);
+EXPORT_SYMBOL(cn_queue_free_dev);
diff -Nru /tmp/empty/cn_queue.h linux-2.6/drivers/connector/cn_queue.h
--- /tmp/empty/cn_queue.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/cn_queue.h 2004-09-26 00:14:16.000000000 +0400
@@ -0,0 +1,90 @@
+/*
+ * cn_queue.h
+ *
+ * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CN_QUEUE_H
+#define __CN_QUEUE_H
+
+#include <asm/types.h>
+
+struct cb_id
+{
+ __u32 idx;
+ __u32 val;
+};
+
+#ifdef __KERNEL__
+
+#include <asm/atomic.h>
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+
+#define CN_CBQ_NAMELEN 32
+
+struct cn_queue_dev
+{
+ atomic_t refcnt;
+ unsigned char name[CN_CBQ_NAMELEN];
+
+ struct workqueue_struct *cn_queue;
+
+ struct list_head queue_list;
+ spinlock_t queue_lock;
+
+ int netlink_groups;
+ struct sock *nls;
+};
+
+struct cn_callback
+{
+ unsigned char name[CN_CBQ_NAMELEN];
+
+ struct cb_id id;
+ void (* callback)(void *);
+ void *priv;
+
+ atomic_t refcnt;
+};
+
+struct cn_callback_entry
+{
+ struct list_head callback_entry;
+ struct cn_callback *cb;
+ struct work_struct work;
+ struct cn_queue_dev *pdev;
+
+ void (* destruct_data)(void *);
+ void *ddata;
+
+ int seq, group;
+ struct sock *nls;
+};
+
+int cn_queue_add_callback(struct cn_queue_dev *dev, struct cn_callback *cb);
+void cn_queue_del_callback(struct cn_queue_dev *dev, struct cn_callback *cb);
+
+struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *);
+void cn_queue_free_dev(struct cn_queue_dev *dev);
+
+int cn_cb_equal(struct cb_id *, struct cb_id *);
+
+#endif /* __KERNEL__ */
+#endif /* __CN_QUEUE_H */
diff -Nru /tmp/empty/connector.c linux-2.6/drivers/connector/connector.c
--- /tmp/empty/connector.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/connector.c 2004-09-26 21:52:02.000000000 +0400
@@ -0,0 +1,498 @@
+/*
+ * connector.c
+ *
+ * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/moduleparam.h>
+
+#include <net/sock.h>
+
+#include "../connector/connector.h"
+#include "../connector/cn_queue.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
+
+static int unit = NETLINK_NFLOG;
+static u32 cn_idx = -1;
+static u32 cn_val = -1;
+
+module_param(unit, int, 0);
+module_param(cn_idx, uint, 0);
+module_param(cn_val, uint, 0);
+
+spinlock_t notify_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(notify_list);
+
+static struct cn_dev cdev;
+
+/*
+ * msg->seq and msg->ack are used to determine message genealogy.
+ * When someone sends message it puts there locally unique sequence
+ * and random acknowledge numbers.
+ * Sequence number may be copied into nlmsghdr->nlmsg_seq too.
+ *
+ * Sequence number is incremented with each message to be sent.
+ *
+ * If we expect reply to our message,
+ * then sequence number in received message MUST be the same as in original message,
+ * and acknowledge number MUST be the same + 1.
+ *
+ * If we receive message and it's sequence number is not equal to one we are expecting,
+ * then it is new message.
+ * If we receive message and it's sequence number is the same as one we are expecting,
+ * but it's acknowledge is not equal acknowledge number in original message + 1,
+ * then it is new message.
+ *
+ */
+void cn_netlink_send(struct cn_msg *msg, u32 __groups)
+{
+ struct cn_callback_entry *n, *__cbq;
+ unsigned int size;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ struct cn_msg *data;
+ struct cn_dev *dev = &cdev;
+ u32 groups = 0;
+ int found = 0;
+
+ if (!__groups)
+ {
+ spin_lock(&dev->cbdev->queue_lock);
+ list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) {
+ if (cn_cb_equal(&__cbq->cb->id, &msg->id)) {
+ found = 1;
+ groups = __cbq->group;
+ }
+ }
+ spin_unlock(&dev->cbdev->queue_lock);
+
+ if (!found) {
+ printk(KERN_ERR "Failed to find multicast netlink group for callback[0x%x.0x%x]. seq=%u\n",
+ msg->id.idx, msg->id.val, msg->seq);
+ return;
+ }
+ }
+ else
+ groups = __groups;
+
+ size = NLMSG_SPACE(sizeof(*msg) + msg->len);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "Failed to allocate new skb with size=%u.\n", size);
+ return;
+ }
+
+ nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh));
+
+ data = (struct cn_msg *)NLMSG_DATA(nlh);
+
+ memcpy(data, msg, sizeof(*data) + msg->len);
+#if 0
+ printk("%s: len=%u, seq=%u, ack=%u, group=%u.\n",
+ __func__, msg->len, msg->seq, msg->ack, groups);
+#endif
+ NETLINK_CB(skb).dst_groups = groups;
+ netlink_broadcast(dev->nls, skb, 0, groups, GFP_ATOMIC);
+
+ return;
+
+ nlmsg_failure:
+ printk(KERN_ERR "Failed to send %u.%u\n", msg->seq, msg->ack);
+ kfree_skb(skb);
+ return;
+}
+
+static int cn_call_callback(struct cn_msg *msg, void (*destruct_data) (void *), void *data)
+{
+ struct cn_callback_entry *n, *__cbq;
+ struct cn_dev *dev = &cdev;
+ int found = 0;
+
+ spin_lock(&dev->cbdev->queue_lock);
+ list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) {
+ if (cn_cb_equal(&__cbq->cb->id, &msg->id)) {
+ __cbq->cb->priv = msg;
+
+ __cbq->ddata = data;
+ __cbq->destruct_data = destruct_data;
+
+ queue_work(dev->cbdev->cn_queue, &__cbq->work);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock(&dev->cbdev->queue_lock);
+
+ return found;
+}
+
+static int __cn_rx_skb(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ u32 pid, uid, seq, group;
+ struct cn_msg *msg;
+
+ pid = NETLINK_CREDS(skb)->pid;
+ uid = NETLINK_CREDS(skb)->uid;
+ seq = nlh->nlmsg_seq;
+ group = NETLINK_CB((skb)).groups;
+ msg = (struct cn_msg *)NLMSG_DATA(nlh);
+
+ if (msg->len != nlh->nlmsg_len - sizeof(*msg) - sizeof(*nlh)) {
+ printk(KERN_ERR "skb does not have enough length: "
+ "requested msg->len=%u[%u], nlh->nlmsg_len=%u[%u], skb->len=%u[must be %u].\n",
+ msg->len, NLMSG_SPACE(msg->len),
+ nlh->nlmsg_len, nlh->nlmsg_len - sizeof(*nlh),
+ skb->len, msg->len + sizeof(*msg));
+ return -EINVAL;
+ }
+#if 0
+ printk(KERN_INFO "pid=%u, uid=%u, seq=%u, group=%u.\n",
+ pid, uid, seq, group);
+#endif
+ return cn_call_callback(msg, (void (*)(void *))kfree_skb, skb);
+}
+
+static void cn_rx_skb(struct sk_buff *__skb)
+{
+ struct nlmsghdr *nlh;
+ u32 len;
+ int err;
+ struct sk_buff *skb;
+
+ skb = skb_get(__skb);
+ if (!skb) {
+ printk(KERN_ERR "Failed to reference an skb.\n");
+ return;
+ }
+#if 0
+ printk(KERN_INFO
+ "skb: len=%u, data_len=%u, truesize=%u, proto=%u, cloned=%d, shared=%d.\n",
+ skb->len, skb->data_len, skb->truesize, skb->protocol,
+ skb_cloned(skb), skb_shared(skb));
+#endif
+ while (skb->len >= NLMSG_SPACE(0)) {
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
+ skb->len < nlh->nlmsg_len ||
+ nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
+ printk(KERN_INFO "nlmsg_len=%u, sizeof(*nlh)=%u\n",
+ nlh->nlmsg_len, sizeof(*nlh));
+ break;
+ }
+
+ len = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (len > skb->len)
+ len = skb->len;
+
+ err = __cn_rx_skb(skb, nlh);
+ if (err) {
+#if 0
+ if (err < 0 && (nlh->nlmsg_flags & NLM_F_ACK))
+ netlink_ack(skb, nlh, -err);
+#endif
+ kfree_skb(skb);
+ break;
+ } else {
+#if 0
+ if (nlh->nlmsg_flags & NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+#endif
+ kfree_skb(skb);
+ break;
+ }
+ skb_pull(skb, len);
+ }
+}
+
+static void cn_input(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL)
+ cn_rx_skb(skb);
+}
+
+static void cn_notify(struct cb_id *id, u32 notify_event)
+{
+ struct cn_ctl_entry *ent;
+
+ spin_lock(¬ify_lock);
+ list_for_each_entry(ent, ¬ify_list, notify_entry) {
+ int i;
+ struct cn_notify_req *req;
+ struct cn_ctl_msg *ctl = ent->msg;
+ int a, b;
+
+ a = b = 0;
+
+ req = (struct cn_notify_req *)ctl->data;
+ for (i=0; i<ctl->idx_notify_num; ++i, ++req) {
+ if (id->idx >= req->first && id->idx < req->first + req->range) {
+ a = 1;
+ break;
+ }
+ }
+
+ for (i=0; i<ctl->val_notify_num; ++i, ++req) {
+ if (id->val >= req->first && id->val < req->first + req->range) {
+ b = 1;
+ break;
+ }
+ }
+
+ if (a && b) {
+ struct cn_msg m;
+
+ printk(KERN_INFO "Notifying group %x with event %u about %x.%x.\n",
+ ctl->group, notify_event,
+ id->idx, id->val);
+
+ memset(&m, 0, sizeof(m));
+ m.ack = notify_event;
+
+ memcpy(&m.id, id, sizeof(m.id));
+ cn_netlink_send(&m, ctl->group);
+ }
+ }
+ spin_unlock(¬ify_lock);
+}
+
+int cn_add_callback(struct cb_id *id, char *name, void (*callback) (void *))
+{
+ int err;
+ struct cn_dev *dev = &cdev;
+ struct cn_callback *cb;
+
+ cb = kmalloc(sizeof(*cb), GFP_KERNEL);
+ if (!cb) {
+ printk(KERN_INFO "%s: Failed to allocate new struct cn_callback.\n",
+ dev->cbdev->name);
+ return -ENOMEM;
+ }
+
+ memset(cb, 0, sizeof(*cb));
+
+ snprintf(cb->name, sizeof(cb->name), "%s", name);
+
+ memcpy(&cb->id, id, sizeof(cb->id));
+ cb->callback = callback;
+
+ atomic_set(&cb->refcnt, 0);
+
+ err = cn_queue_add_callback(dev->cbdev, cb);
+ if (err) {
+ kfree(cb);
+ return err;
+ }
+
+ cn_notify(id, 0);
+
+ return 0;
+}
+
+void cn_del_callback(struct cb_id *id)
+{
+ struct cn_dev *dev = &cdev;
+ struct cn_callback_entry *n, *__cbq;
+
+ list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) {
+ if (cn_cb_equal(&__cbq->cb->id, id)) {
+ cn_queue_del_callback(dev->cbdev, __cbq->cb);
+ cn_notify(id, 1);
+ break;
+ }
+ }
+}
+
+static int cn_ctl_msg_equals(struct cn_ctl_msg *m1, struct cn_ctl_msg *m2)
+{
+ int i;
+ struct cn_notify_req *req1, *req2;
+
+ if (m1->idx_notify_num != m2->idx_notify_num)
+ return 0;
+
+ if (m1->val_notify_num != m2->val_notify_num)
+ return 0;
+
+ if (m1->len != m2->len)
+ return 0;
+
+ if ((m1->idx_notify_num + m1->val_notify_num)*sizeof(*req1) != m1->len) {
+ printk(KERN_ERR "Notify entry[idx_num=%x, val_num=%x, len=%u] contains garbage. Removing.\n",
+ m1->idx_notify_num, m1->val_notify_num, m1->len);
+ return 1;
+ }
+
+ req1 = (struct cn_notify_req *)m1->data;
+ req2 = (struct cn_notify_req *)m2->data;
+
+ for (i=0; i<m1->idx_notify_num; ++i) {
+ if (memcmp(req1, req2, sizeof(*req1)))
+ return 0;
+
+ req1++;
+ req2++;
+ }
+
+ for (i=0; i<m1->val_notify_num; ++i) {
+ if (memcmp(req1, req2, sizeof(*req1)))
+ return 0;
+
+ req1++;
+ req2++;
+ }
+
+ return 1;
+}
+
+static void cn_callback(void * data)
+{
+ struct cn_msg *msg = (struct cn_msg *)data;
+ struct cn_ctl_msg *ctl;
+ struct cn_ctl_entry *ent;
+ u32 size;
+
+ if (msg->len < sizeof(*ctl)) {
+ printk(KERN_ERR "Wrong connector request size %u, must be >= %u.\n",
+ msg->len, sizeof(*ctl));
+ return;
+ }
+
+ ctl = (struct cn_ctl_msg *)msg->data;
+
+ size = sizeof(*ctl) + (ctl->idx_notify_num + ctl->val_notify_num)*sizeof(struct cn_notify_req);
+
+ if (msg->len != size) {
+ printk(KERN_ERR "Wrong connector request size %u, must be == %u.\n",
+ msg->len, size);
+ return;
+ }
+
+ if (ctl->len + sizeof(*ctl) != msg->len) {
+ printk(KERN_ERR "Wrong message: msg->len=%u must be equal to inner_len=%u [+%u].\n",
+ msg->len, ctl->len, sizeof(*ctl));
+ return;
+ }
+
+ /*
+ * Remove notification.
+ */
+ if (ctl->group == 0) {
+ struct cn_ctl_entry *n;
+
+ spin_lock(¬ify_lock);
+ list_for_each_entry_safe(ent, n, ¬ify_list, notify_entry) {
+ if (cn_ctl_msg_equals(ent->msg, ctl)) {
+ list_del(&ent->notify_entry);
+ kfree(ent);
+ }
+ }
+ spin_unlock(¬ify_lock);
+
+ return;
+ }
+
+ size += sizeof(*ent);
+
+ ent = kmalloc(size, GFP_ATOMIC);
+ if (!ent) {
+ printk(KERN_ERR "Failed to allocate %d bytes for new notify entry.\n", size);
+ return;
+ }
+
+ memset(ent, 0, size);
+
+ ent->msg = (struct cn_ctl_msg *)(ent + 1);
+
+ memcpy(ent->msg, ctl, size - sizeof(*ent));
+
+ spin_lock(¬ify_lock);
+ list_add(&ent->notify_entry, ¬ify_list);
+ spin_unlock(¬ify_lock);
+
+ {
+ int i;
+ struct cn_notify_req *req;
+
+ printk("Notify group %x for idx: ", ctl->group);
+
+ req = (struct cn_notify_req *)ctl->data;
+ for (i=0; i<ctl->idx_notify_num; ++i, ++req) {
+ printk("%u-%u ", req->first, req->first+req->range-1);
+ }
+
+ printk("\nNotify group %x for val: ", ctl->group);
+
+ for (i=0; i<ctl->val_notify_num; ++i, ++req) {
+ printk("%u-%u ", req->first, req->first+req->range-1);
+ }
+ printk("\n");
+ }
+}
+
+static int cn_init(void)
+{
+ struct cn_dev *dev = &cdev;
+
+ dev->input = cn_input;
+ dev->id.idx = cn_idx;
+ dev->id.val = cn_val;
+
+ dev->nls = netlink_kernel_create(unit, dev->input);
+ if (!dev->nls) {
+ printk(KERN_ERR "Failed to create new netlink socket(%u).\n",
+ unit);
+ return -EIO;
+ }
+
+ dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
+ if (!dev->cbdev) {
+ if (dev->nls->sk_socket)
+ sock_release(dev->nls->sk_socket);
+ return -EINVAL;
+ }
+
+ return cn_add_callback(&dev->id, "connector", &cn_callback);
+}
+
+static void cn_fini(void)
+{
+ struct cn_dev *dev = &cdev;
+
+ cn_del_callback(&dev->id);
+ cn_queue_free_dev(dev->cbdev);
+ if (dev->nls->sk_socket)
+ sock_release(dev->nls->sk_socket);
+}
+
+module_init(cn_init);
+module_exit(cn_fini);
+
+EXPORT_SYMBOL(cn_add_callback);
+EXPORT_SYMBOL(cn_del_callback);
+EXPORT_SYMBOL(cn_netlink_send);
diff -Nru /tmp/empty/connector.h linux-2.6/drivers/connector/connector.h
--- /tmp/empty/connector.h 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/connector/connector.h 2004-09-26 00:14:16.000000000 +0400
@@ -0,0 +1,81 @@
+/*
+ * connector.h
+ *
+ * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CONNECTOR_H
+#define __CONNECTOR_H
+
+#include "../connector/cn_queue.h"
+
+#define CONNECTOR_MAX_MSG_SIZE 1024
+
+struct cn_msg
+{
+ struct cb_id id;
+
+ __u32 seq;
+ __u32 ack;
+
+ __u32 len; /* Length of the following data */
+ __u8 data[0];
+};
+
+struct cn_notify_req
+{
+ __u32 first;
+ __u32 range;
+};
+
+struct cn_ctl_msg
+{
+ __u32 idx_notify_num;
+ __u32 val_notify_num;
+ __u32 group;
+ __u32 len;
+ __u8 data[0];
+};
+
+#ifdef __KERNEL__
+
+#include <net/sock.h>
+
+struct cn_ctl_entry
+{
+ struct list_head notify_entry;
+ struct cn_ctl_msg *msg;
+};
+
+struct cn_dev
+{
+ struct cb_id id;
+
+ u32 seq, groups;
+ struct sock *nls;
+ void (*input)(struct sock *sk, int len);
+
+ struct cn_queue_dev *cbdev;
+};
+
+int cn_add_callback(struct cb_id *, char *, void (* callback)(void *));
+void cn_del_callback(struct cb_id *);
+void cn_netlink_send(struct cn_msg *, u32);
+
+#endif /* __KERNEL__ */
+#endif /* __CONNECTOR_H */
[-- Attachment #1.4: Kconfig.connector.patch --]
[-- Type: text/x-patch, Size: 304 bytes --]
--- linux-2.6/drivers/Kconfig.orig 2004-09-26 13:34:48.000000000 +0400
+++ linux-2.6/drivers/Kconfig 2004-09-26 13:34:57.000000000 +0400
@@ -44,6 +44,8 @@
source "drivers/w1/Kconfig"
+source "drivers/connector/Kconfig"
+
source "drivers/misc/Kconfig"
source "drivers/media/Kconfig"
[-- Attachment #1.5: Kconfig.crypto.patch --]
[-- Type: text/x-patch, Size: 307 bytes --]
--- linux-2.6/drivers/Kconfig.nocrypto 2004-10-30 09:05:52.000000000 +0400
+++ linux-2.6/drivers/Kconfig 2004-10-30 09:06:11.000000000 +0400
@@ -42,6 +42,8 @@
source "drivers/i2c/Kconfig"
+source "drivers/acrypto/Kconfig"
+
source "drivers/w1/Kconfig"
source "drivers/connector/Kconfig"
[-- Attachment #1.6: Makefile.connector.patch --]
[-- Type: text/x-patch, Size: 389 bytes --]
--- linux-2.6/drivers/Makefile.orig 2004-09-25 23:47:08.000000000 +0400
+++ linux-2.6/drivers/Makefile 2004-09-26 13:34:25.000000000 +0400
@@ -44,6 +44,7 @@
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_W1) += w1/
+obj-$(CONFIG_CONNECTOR) += connector/
obj-$(CONFIG_PHONE) += telephony/
obj-$(CONFIG_MD) += md/
obj-$(CONFIG_BT) += bluetooth/
[-- Attachment #1.7: Makefile.crypto.patch --]
[-- Type: text/x-patch, Size: 376 bytes --]
--- linux-2.6/drivers/Makefile.nocrypto 2004-10-30 09:05:02.000000000 +0400
+++ linux-2.6/drivers/Makefile 2004-10-30 09:05:31.000000000 +0400
@@ -49,5 +49,6 @@
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_I2C) += i2c/
+obj-$(CONFIG_ACRYPTO) += acrypto/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_CONNECTOR) += connector/
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
reply other threads:[~2004-12-14 16:03 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1103040236.3430.54.camel@uganda \
--to=johnpol@2ka.mipt.ru \
--cc=cryptoapi@lists.logix.cz \
--cc=hadi@cyberus.ca \
--cc=jmorris@redhat.com \
--cc=michal@logix.cz \
--cc=netdev@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).