/* * crypto_main.c * * Copyright (c) 2004 Evgeniy Polyakov * * * 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 #include #include #include #include #include #include #include #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 inline 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)); } inline void crypto_session_insert_main(struct crypto_device *dev, struct crypto_session *s) { struct crypto_session *__s; spin_lock_bh(&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_bh(&dev->session_lock); dump_ci(&s->ci); dprintk(" added to main crypto device %s [%d].\n", dev->name, atomic_read(&dev->refcnt)); } inline void crypto_session_insert(struct crypto_device *dev, struct crypto_session *s) { spin_lock_bh(&dev->session_lock); __crypto_session_insert(dev, s); spin_unlock_bh(&dev->session_lock); if (dev->data_ready) dev->data_ready(dev); } struct crypto_session *crypto_session_alloc(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); 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); spin_lock_init(&s->lock); memcpy(&s->ci, ci, sizeof(s->ci)); memcpy(&s->data, d, sizeof(s->data)); if (d->priv_size) { s->data.priv = s+1; if (d->priv) memcpy(s->data.priv, d->priv, d->priv_size); } else s->data.priv = NULL; s->ci.id = dev->sid++; s->ci.dev_id = ldev->sid++; s->ci.flags = 0; #if 1 err = crypto_route_add(ldev, s, ci); if (err) { dprintk("Can not add route to device %s.\n", ldev->name); goto err_out_session_free; } crypto_session_insert(ldev, s); crypto_device_put(ldev); #endif crypto_session_insert_main(dev, s); return s; err_out_session_free: crypto_device_put(ldev); err_out_device_put: kfree(s); return NULL; } 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_bh(&dev->session_lock); list_del(&s->dev_queue_entry); spin_unlock_bh(&dev->session_lock); crypto_route_free(rt); } } inline 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); } inline void crypto_session_dequeue_main(struct crypto_session *s) { struct crypto_device *dev = &main_crypto_device; spin_lock_bh(&dev->session_lock); __crypto_session_dequeue_main(s); spin_unlock_bh(&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 "); MODULE_DESCRIPTION("Asynchronous crypto layer."); EXPORT_SYMBOL(crypto_session_alloc);