New POHMELFS release.
Subsystems (6)
cover New POHMELFS release.
Hello.
This is a new release of the POHMEL filesystem.
POHMELFS stands for Parallel Optimized Host Message Exchange Layered File System.
This is a high performance network filesystem with local coherent cache of data and metadata. Its main goal is distributed parallel processing of data. Network filesystem is a client transport.
POHMELFS protocol was proven to be superior to NFS in lots (if not all, then it is in a roadmap) operations.
Basic POHMELFS features:
* Local coherent cache for data and metadata. (Byte-range) locking. Locks were prepared to be byte-range, but since all Linux filesystems lock the whole inode, it was decided to lock the whole object during writing. Actual messages are being sent for locking/cache coherency protocol are byte-range, but because the whole inode is locked, lock is cached, so range actually is equal to the inode size. One can simultaneously write into the same page via different offsets from different client, and every time file will be coherent on all clients which do it and on the server itself.
* Completely async processing of all events (hard and symlinks are the only exceptions) including object creation and data reading and writing.
* Flexible object architecture optimized for network processing. Ability to create long pathes to object and remove arbitrary huge directories in single network command.
* High performance is one of the main design goals.
* Very fast and scalable multithreaded userspace server. Being in userspace it works with any underlying filesystem and still is much faster than async in-kernel NFS one.
* Transactions support. Full failover for all operations. Resending transactions to different servers on timeout or error.
* Client is able to switch between different servers
(if one goes down, client automatically reconnects to second and so on).
* Client parallel extensions: ability to write to multiple servers and balance reading between them.
* Client dynamic server reconfiguration: ability to add/remove servers from working set in run-time.
* Strong authentification and possible data encryption in network channel.
* Extended attributes support.
This release brings following features:
* combine locking with inode attribute changes
* add get/set attrbibute commands into cache coherency protocol (attributes are cached and flushed to the server at a writeback time or on demand because of cache coherency protocol)
* crypto threads pool's cleanup
* 2.6.27 rebase
* optimize read/write locking and number of messages
needed for cache coherency management
* debug cleanups
* bug fixes
1. POHMELFS homepage
http://www.ioremap.net/projects/pohmelfs
2. GIT trees.
http://www.ioremap.net/cgi-bin/gitweb.cgi
PATCH 1/5 POHMELFS: documentation.
Documentation/filesystems/pohmelfs/design_notes.txt new file +70 -0
@@ -0,0 +1,70 @@ +POHMELFS: Parallel Optimized Host Message Exchange Layered File System. + + Evgeniy Polyakov <[email protected]> + +Homepage: http://www.ioremap.net/projects/pohmelfs + +POHMELFS first began as a network filesystem with coherent local data and +metadata caches but is now evolving into a parallel distributed filesystem. + +Main features of this FS include: + * Locally coherent cache for data and metadata with (potentially) byte-range locks. + Since all Linux filesystems lock the whole inode during writing, algorithm + is very simple and does not use byte-ranges, although they are sent in + locking messages. + * Completely async processing of all events except creation of hard and symbolic + links, and rename events. + Object creation and data reading and writing are processed asynchronously. + * Flexible object architecture optimized for network processing. + Ability to create long paths to objects and remove arbitrarily huge + directories with a single network command. + (like removing the whole kernel tree via a single network command). + * Very high performance. + * Fast and scalable multithreaded userspace server. Being in userspace it works + with any underlying filesystem and still is much faster than async in-kernel NFS one. + * Client is able to switch between different servers (if one goes down, client + automatically reconnects to second and so on). + * Transactions support. Full failover for all operations. + Resending transactions to different servers on timeout or error. + * Read request (data read, directory listing, lookup requests) balancing between multiple servers. + * Write requests are replicated to multiple servers and completed only when all of them are acked. + * Ability to add and/or remove servers from the working set at run-time. + * Strong authentification and possible data encryption in network channel. + * Extended attributes support. + +POHMELFS is based on transactions, which are potentially long-standing objects that live +in the client's memory. Each transaction contains all the information needed to process a given +command (or set of commands, which is frequently used during data writing: single transactions +can contain creation and data writing commands). Transactions are committed by all the servers +to which they are sent and, in case of failures, are eventually resent or dropped with an error. +For example, reading will return an error if no servers are available. + +POHMELFS uses a asynchronous approach to data processing. Courtesy of transactions, it is +possible to detach replies from requests and, if the command requires data to be received, the +caller sleeps waiting for it. Thus, it is possible to issue multiple read commands to different +servers and async threads will pick up replies in parallel, find appropriate transactions in the +system and put the data where it belongs (like the page or inode cache). + +The main feature of POHMELFS is writeback data and the metadata cache. +Only a few non-performance critical operations use the write-through cache and +are synchronous: hard and symbolic link creation, and object rename. Creation, +removal of objects and data writing are asynchronous and are sent to +the server during system writeback. Only one writer at a time is allowed for any +given inode, which is guarded by an appropriate locking protocol. +Because of this feature, POHMELFS is extremely fast at metadata intensive +workloads and can fully utilize the bandwidth to the servers when doing bulk +data transfers. + +POHMELFS clients operate with a working set of servers and are capable of balancing read-only +operations (like lookups or directory listings) between them. +Administrators can add or remove servers from the set at run-time via special commands (described +in Documentation/pohmelfs/info.txt file). Writes are replicated to all servers. + +POHMELFS is capable of full data channel encryption and/or strong crypto hashing. +One can select any kernel supported cipher, encryption mode, hash type and operation mode +(hmac or digest). It is also possible to use both or neither (default). Crypto configuration +is checked during mount time and, if the server does not support it, appropriate capabilities +will be disabled or mount will fail (if 'crypto_fail_unsupported' mount option is specified). +Crypto performance heavily depends on the number of crypto threads, which asynchronously perform +crypto operations and send the resulting data to server or submit it up the stack. This number +can be controlled via a mount option.
Documentation/filesystems/pohmelfs/info.txt new file +86 -0
86 lines not on this page · raw message
Documentation/filesystems/pohmelfs/network_protocol.txt new file +227 -0
227 lines not on this page · raw message
PATCH 2/5 POHMELFS: core functionality.
fs/pohmelfs/config.c new file +471 -0
@@ -0,0 +1,471 @@ +/* +* 2007+ Copyright (c) Evgeniy Polyakov <[email protected]> +* 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. +*/ + +#include <linux/kernel.h> +#include <linux/connector.h> +#include <linux/crypto.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/in.h> + +#include "netfs.h" + +/* +* Global configuration list. +* Each client can be asked to get one of them. +* +* Allows to provide remote server address (ipv4/v6/whatever), port +* and so on via kernel connector. +*/ + +static struct cb_id pohmelfs_cn_id = {.idx =POHMELFS_CN_IDX , .val =POHMELFS_CN_VAL }; +static LIST_HEAD (pohmelfs_config_list); +static DEFINE_MUTEX (pohmelfs_config_lock); + +static inline int pohmelfs_config_eql (struct pohmelfs_ctl *sc,struct pohmelfs_ctl *ctl) +{ +if (sc->idx == ctl->idx && sc->type == ctl->type && + sc->proto == ctl->proto && + sc->addrlen == ctl->addrlen && + !memcmp (&sc->addr, &ctl->addr, ctl->addrlen)) +return 1 ; + +return 0 ; +} + +static struct pohmelfs_config_group *pohmelfs_find_config_group (unsigned int idx) +{ +struct pohmelfs_config_group *g, *group =NULL ; + +list_for_each_entry (g, &pohmelfs_config_list, group_entry) { +if (g->idx == idx) { + group = g; +break ; + } + } + +return group; +} + +static struct pohmelfs_config_group *pohmelfs_find_create_config_group (unsigned int idx) +{ +struct pohmelfs_config_group *g; + + g =pohmelfs_find_config_group (idx); +if (g) +return g; + + g =kzalloc (sizeof (struct pohmelfs_config_group ),GFP_KERNEL ); +if (!g) +return NULL ; + +INIT_LIST_HEAD (&g->config_list); + g->idx = idx; + g->num_entry =0 ; + +list_add_tail (&g->group_entry, &pohmelfs_config_list); + +return g; +} + +int pohmelfs_copy_config (struct pohmelfs_sb *psb) +{ +struct pohmelfs_config_group *g; +struct pohmelfs_config *c, *dst; +int err = -ENODEV ; + +mutex_lock (&pohmelfs_config_lock); + + g =pohmelfs_find_config_group (psb->idx); +if (!g) + goto out_unlock; + +/* +* Run over all entries in given config group and try to crate and +* initialize those, which do not exist in superblock list. +* Skip all existing entries. +*/ + +list_for_each_entry (c, &g->config_list, config_entry) { + err =0 ; +list_for_each_entry (dst, &psb->state_list, config_entry) { +if (pohmelfs_config_eql (&dst->state.ctl, &c->state.ctl)) { + err = -EEXIST ; +break ; + } + } + +if (err) +continue ; + + dst =kzalloc (sizeof (struct pohmelfs_config ),GFP_KERNEL ); +if (!dst) { + err = -ENOMEM ; +break ; + } + +memcpy (&dst->state.ctl, &c->state.ctl,sizeof (struct pohmelfs_ctl )); + +list_add_tail (&dst->config_entry, &psb->state_list); + + err =pohmelfs_state_init_one (psb, dst); +if (err) { +list_del (&dst->config_entry); +kfree (dst); + } + + err =0 ; + } + +out_unlock: +mutex_unlock (&pohmelfs_config_lock); + +return err; +} + +int pohmelfs_copy_crypto (struct pohmelfs_sb *psb) +{ +struct pohmelfs_config_group *g; +int err = -ENOENT ; + +mutex_lock (&pohmelfs_config_lock); + g =pohmelfs_find_config_group (psb->idx); +if (!g) + goto err_out_exit; + + err = -ENOMEM ; + psb->hash_string =kstrdup (g->hash_string,GFP_KERNEL ); +if (!psb->hash_string) + goto err_out_exit; + + psb->hash_strlen = g->hash_strlen; + + psb->cipher_string =kstrdup (g->cipher_string,GFP_KERNEL ); +if (!psb->cipher_string) + goto err_out_free_hash_string; + psb->cipher_strlen = g->cipher_strlen; + + psb->hash_key =kmalloc (g->hash_keysize,GFP_KERNEL ); +if (!psb->hash_key) + goto err_out_free_cipher_string; +memcpy (psb->hash_key, g->hash_key, g->hash_keysize); + psb->hash_keysize = g->hash_keysize; + + psb->cipher_key =kmalloc (g->cipher_keysize,GFP_KERNEL ); +if (!psb->cipher_key) + goto err_out_free_hash; +memcpy (psb->cipher_key, g->cipher_key, g->cipher_keysize); + psb->cipher_keysize = g->cipher_keysize; + +mutex_unlock (&pohmelfs_config_lock); + +return 0 ; + +err_out_free_hash: +kfree (psb->hash_key); +err_out_free_cipher_string: +kfree (psb->cipher_string); +err_out_free_hash_string: +kfree (psb->hash_string); +err_out_exit: +mutex_unlock (&pohmelfs_config_lock); +return err; +} + +static int pohmelfs_send_reply (int err,int msg_num,int action,struct cn_msg *msg,struct pohmelfs_ctl *ctl) +{ +struct pohmelfs_cn_ack *ack; + + ack =kmalloc (sizeof (struct pohmelfs_cn_ack ),GFP_KERNEL ); +if (!ack) +return -ENOMEM ; + +memset (ack,0 ,sizeof (struct pohmelfs_cn_ack )); +memcpy (&ack->msg, msg,sizeof (struct cn_msg )); + +if (action ==POHMELFS_CTLINFO_ACK ) +memcpy (&ack->ctl, ctl,sizeof (struct pohmelfs_ctl )); + + ack->msg.len =sizeof (struct pohmelfs_cn_ack ) -sizeof (struct cn_msg ); + ack->msg.ack = msg->ack +1 ; + ack->error = err; + ack->msg_num = msg_num; + +cn_netlink_send (&ack->msg,0 ,GFP_KERNEL ); +kfree (ack); +return 0 ; +} + +static int pohmelfs_cn_disp (struct cn_msg *msg) +{ +struct pohmelfs_config_group *g; +struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; +struct pohmelfs_config *c, *tmp; +int err =0 , i =1 ; + +if (msg->len !=sizeof (struct pohmelfs_ctl )) +return -EBADMSG ; + +mutex_lock (&pohmelfs_config_lock); + + g =pohmelfs_find_config_group (ctl->idx); +if (!g) { +pohmelfs_send_reply (err,0 ,POHMELFS_NOINFO_ACK , msg,NULL ); + goto out_unlock; + } + +list_for_each_entry_safe (c, tmp, &g->config_list, config_entry) { +struct pohmelfs_ctl *sc = &c->state.ctl; +if (pohmelfs_send_reply (err, g->num_entry - i,POHMELFS_CTLINFO_ACK , msg, sc)) { + err = -ENOMEM ; + goto out_unlock; + } + i +=1 ; + } + +out_unlock: +mutex_unlock (&pohmelfs_config_lock); +return err; +} + +static int pohmelfs_cn_ctl (struct cn_msg *msg,int action) +{ +struct pohmelfs_config_group *g; +struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; +struct pohmelfs_config *c, *tmp; +int err =0 ; + +if (msg->len !=sizeof (struct pohmelfs_ctl )) +return -EBADMSG ; + +mutex_lock (&pohmelfs_config_lock); + + g =pohmelfs_find_create_config_group (ctl->idx); +if (!g) { + err = -ENOMEM ; + goto out_unlock; + } + +list_for_each_entry_safe (c, tmp, &g->config_list, config_entry) { +struct pohmelfs_ctl *sc = &c->state.ctl; + +if (pohmelfs_config_eql (sc, ctl)) { +if (action ==POHMELFS_FLAGS_ADD ) { + err = -EEXIST ; + goto out_unlock; + }else if (action ==POHMELFS_FLAGS_DEL ) { +list_del (&c->config_entry); + g->num_entry--; +kfree (c); + goto out_unlock; + }else { + err = -EEXIST ; + goto out_unlock; + } + } + } +if (action ==POHMELFS_FLAGS_DEL ) { + err = -EBADMSG ; + goto out_unlock; + } + + c =kzalloc (sizeof (struct pohmelfs_config ),GFP_KERNEL ); +if (!c) { + err = -ENOMEM ; + goto out_unlock; + } +memcpy (&c->state.ctl, ctl,sizeof (struct pohmelfs_ctl )); + g->num_entry++; +list_add_tail (&c->config_entry, &g->config_list); + +out_unlock: +mutex_unlock (&pohmelfs_config_lock); +if (pohmelfs_send_reply (err,0 ,POHMELFS_NOINFO_ACK , msg,NULL )) + err = -ENOMEM ; + +return err; +} + +static int pohmelfs_crypto_hash_init (struct pohmelfs_config_group *g,struct pohmelfs_crypto *c) +{ +char *algo = (char *)c->data; +u8 *key = (u8 *)(algo + c->strlen); + +if (g->hash_string) +return -EEXIST ; + + g->hash_string =kstrdup (algo,GFP_KERNEL ); +if (!g->hash_string) +return -ENOMEM ; + g->hash_strlen = c->strlen; + g->hash_keysize = c->keysize; + + g->hash_key =kmalloc (c->keysize,GFP_KERNEL ); +if (!g->hash_key) { +kfree (g->hash_string); +return -ENOMEM ; + } + +memcpy (g->hash_key, key, c->keysize); + +return 0 ; +} + +static int pohmelfs_crypto_cipher_init (struct pohmelfs_config_group *g,struct pohmelfs_crypto *c) +{ +char *algo = (char *)c->data; +u8 *key = (u8 *)(algo + c->strlen); + +if (g->cipher_string) +return -EEXIST ; + + g->cipher_string =kstrdup (algo,GFP_KERNEL ); +if (!g->cipher_string) +return -ENOMEM ; + g->cipher_strlen = c->strlen; + g->cipher_keysize = c->keysize; + + g->cipher_key =kmalloc (c->keysize,GFP_KERNEL ); +if (!g->cipher_key) { +kfree (g->cipher_string); +return -ENOMEM ; + } + +memcpy (g->cipher_key, key, c->keysize); + +return 0 ; +} + + +static int pohmelfs_cn_crypto (struct cn_msg *msg) +{ +struct pohmelfs_crypto *crypto = (struct pohmelfs_crypto *)msg->data; +struct pohmelfs_config_group *g; +int err =0 ; + +dprintk ("%s: idx: %u, strlen: %u, type: %u, keysize: %u, algo: %s.\n" , + __func__, crypto->idx, crypto->strlen, crypto->type, + crypto->keysize, (char *)crypto->data); + +mutex_lock (&pohmelfs_config_lock); + g =pohmelfs_find_create_config_group (crypto->idx); +if (!g) { + err = -ENOMEM ; + goto out_unlock; + } + +switch (crypto->type) { +case POHMELFS_CRYPTO_HASH : + err =pohmelfs_crypto_hash_init (g, crypto); +break ; +case POHMELFS_CRYPTO_CIPHER : + err =pohmelfs_crypto_cipher_init (g, crypto); +break ; +default : + err = -ENOTSUPP ; +break ; + } + +out_unlock: +mutex_unlock (&pohmelfs_config_lock); +if (pohmelfs_send_reply (err,0 ,POHMELFS_NOINFO_ACK , msg,NULL )) + err = -ENOMEM ; + +return err; +} + +static void pohmelfs_cn_callback (void *data) +{ +struct cn_msg *msg = data; +int err; + +switch (msg->flags) { +case POHMELFS_FLAGS_ADD : + err =pohmelfs_cn_ctl (msg,POHMELFS_FLAGS_ADD ); +break ; +case POHMELFS_FLAGS_DEL : + err =pohmelfs_cn_ctl (msg,POHMELFS_FLAGS_DEL ); +break ; +case POHMELFS_FLAGS_SHOW : + err =pohmelfs_cn_disp (msg); +break ; +case POHMELFS_FLAGS_CRYPTO : + err =pohmelfs_cn_crypto (msg); +break ; +default : + err = -ENOSYS ; +break ; + } +} + +int pohmelfs_config_check (struct pohmelfs_config *config,int idx) +{ +struct pohmelfs_ctl *ctl = &config->state.ctl; +struct pohmelfs_config *tmp; +int err = -ENOENT ; +struct pohmelfs_ctl *sc; +struct pohmelfs_config_group *g; + +mutex_lock (&pohmelfs_config_lock); + + g =pohmelfs_find_config_group (ctl->idx); +if (g) { +list_for_each_entry (tmp, &g->config_list, config_entry) { + sc = &tmp->state.ctl; + +if (pohmelfs_config_eql (sc, ctl)) { + err =0 ; +break ; + } + } + } + +mutex_unlock (&pohmelfs_config_lock); + +return err; +} + +int __init pohmelfs_config_init (void ) +{ +return cn_add_callback (&pohmelfs_cn_id,"pohmelfs" , pohmelfs_cn_callback); +} + +void pohmelfs_config_exit (void ) +{ +struct pohmelfs_config *c, *tmp; +struct pohmelfs_config_group *g, *gtmp; + +cn_del_callback (&pohmelfs_cn_id); + +mutex_lock (&pohmelfs_config_lock); +list_for_each_entry_safe (g, gtmp, &pohmelfs_config_list, group_entry) { +list_for_each_entry_safe (c, tmp, &g->config_list, config_entry) { +list_del (&c->config_entry); +kfree (c); + } + +list_del (&g->group_entry); + +if (g->hash_string) +kfree (g->hash_string); + +if (g->cipher_string) +kfree (g->cipher_string); + +kfree (g); + } +mutex_unlock (&pohmelfs_config_lock); +}
fs/pohmelfs/inode.c new file +1993 -0
1,993 lines not on this page · raw message
fs/pohmelfs/dir.c new file +1136 -0
536 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
PATCH 3/5 POHMELFS: transactions and network.
fs/pohmelfs/trans.c new file +703 -0
@@ -0,0 +1,703 @@ +/* +* 2007+ Copyright (c) Evgeniy Polyakov <[email protected]> +* 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. +*/ + +#include <linux/module.h> +#include <linux/crypto.h> +#include <linux/fs.h> +#include <linux/jhash.h> +#include <linux/hash.h> +#include <linux/ktime.h> +#include <linux/mempool.h> +#include <linux/mm.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/parser.h> +#include <linux/poll.h> +#include <linux/swap.h> +#include <linux/slab.h> +#include <linux/statfs.h> +#include <linux/writeback.h> + +#include "netfs.h" + +static struct kmem_cache *netfs_trans_dst; +static mempool_t *netfs_trans_dst_pool; + +static void netfs_trans_init_static (struct netfs_trans *t,int num,int size) +{ + t->page_num = num; + t->total_size = size; +atomic_set (&t->refcnt,1 ); + +mutex_init (&t->dst_lock); +INIT_LIST_HEAD (&t->dst_list); +} + +static int netfs_trans_send_pages (struct netfs_trans *t,struct netfs_state *st) +{ +int err =0 ; +unsigned int i, attached_pages = t->attached_pages, ci; +struct msghdr msg; +struct page **pages = (t->eng)?t->eng->pages:t->pages; +struct page *p; +unsigned int size; + + msg.msg_name =NULL ; + msg.msg_namelen =0 ; + msg.msg_control =NULL ; + msg.msg_controllen =0 ; + msg.msg_flags =MSG_WAITALL |MSG_MORE ; + + ci =0 ; +for (i=0 ; i<t->page_num; ++i) { +struct page *page = pages[ci]; +struct netfs_cmd cmd; +struct iovec io; + + p = t->pages[i]; + +if (!p) +continue ; + + size =page_private (p); + + io.iov_base = &cmd; + io.iov_len =sizeof (struct netfs_cmd ); + + cmd.cmd =NETFS_WRITE_PAGE ; + cmd.ext =0 ; + cmd.id =0 ; + cmd.size = size; + cmd.start = p->index; + cmd.start <<=PAGE_CACHE_SHIFT ; + cmd.csize =0 ; + cmd.cpad =0 ; + cmd.iv =pohmelfs_gen_iv (t); + +netfs_convert_cmd (&cmd); + + msg.msg_iov = &io; + msg.msg_iovlen =1 ; + msg.msg_flags =MSG_WAITALL |MSG_MORE ; + + err =kernel_sendmsg (st->socket, &msg, (struct kvec *)msg.msg_iov,1 ,sizeof (struct netfs_cmd )); +if (err <=0 ) { +printk ("%s: %d/%d failed to send transaction header: t: %p, gen: %u, err: %d.\n" , + __func__, i, t->page_num, t, t->gen, err); +if (err ==0 ) + err = -ECONNRESET ; + goto err_out; + } + + msg.msg_flags =MSG_WAITALL |(attached_pages ==1 )?0 :MSG_MORE ; + + err =kernel_sendpage (st->socket, page,0 , size, msg.msg_flags); +if (err <=0 ) { +printk ("%s: %d/%d failed to send transaction page: t: %p, gen: %u, size: %u, err: %d.\n" , + __func__, i, t->page_num, t, t->gen, size, err); +if (err ==0 ) + err = -ECONNRESET ; + goto err_out; + } + +dprintk ("%s: %d/%d sent t: %p, gen: %u, page: %p/%p, size: %u.\n" , + __func__, i, t->page_num, t, t->gen, page, p, size); + + err =0 ; + attached_pages--; +if (!attached_pages) +break ; + ci++; + +continue ; + +err_out: +printk ("%s: t: %p, gen: %u, err: %d.\n" , __func__, t, t->gen, err); +netfs_state_exit (st); +break ; + } + +return err; +} + +int netfs_trans_send (struct netfs_trans *t,struct netfs_state *st) +{ +int err; +struct msghdr msg; + +netfs_state_lock (st); +if (!st->socket) { + err =netfs_state_init (st); +if (err) + goto err_out_unlock_return; + } + + msg.msg_iov = &t->iovec; + msg.msg_iovlen =1 ; + msg.msg_name =NULL ; + msg.msg_namelen =0 ; + msg.msg_control =NULL ; + msg.msg_controllen =0 ; + msg.msg_flags =MSG_WAITALL ; + +if (t->attached_pages) + msg.msg_flags |=MSG_MORE ; + + err =kernel_sendmsg (st->socket, &msg, (struct kvec *)msg.msg_iov,1 , t->iovec.iov_len); +if (err <=0 ) { +printk ("%s: failed to send contig transaction: t: %p, gen: %u, size: %u, err: %d.\n" , + __func__, t, t->gen, t->iovec.iov_len, err); +if (err ==0 ) + err = -ECONNRESET ; + goto err_out_unlock_return; + } + +dprintk ("%s: sent %s transaction: t: %p, gen: %u, size: %u, page_num: %u.\n" , + __func__, (t->page_num)?"partial" :"full" , + t, t->gen, t->iovec.iov_len, t->page_num); + + err =0 ; +if (t->attached_pages) + err =netfs_trans_send_pages (t, st); + +err_out_unlock_return: +netfs_state_unlock (st); + +dprintk ("%s: t: %p, gen: %u, err: %d.\n" , + __func__, t, t->gen, err); + + t->result = err; +return err; +} + +static inline int netfs_trans_cmp (unsigned int gen,unsigned int new) +{ +if (gen < new) +return 1 ; +if (gen > new) +return -1 ; +return 0 ; +} + +struct netfs_trans_dst *netfs_trans_search (struct netfs_state *st,unsigned int gen) +{ +struct rb_root *root = &st->trans_root; +struct rb_node *n = root->rb_node; +struct netfs_trans_dst *tmp, *ret =NULL ; +struct netfs_trans *t; +int cmp; + +while (n) { + tmp = rb_entry(n, struct netfs_trans_dst, state_entry); + t = tmp->trans; + + cmp =netfs_trans_cmp (t->gen, gen); +if (cmp <0 ) + n = n->rb_left; +else if (cmp >0 ) + n = n->rb_right; +else { + ret = tmp; +break ; + } + } + +return ret; +} + +static int netfs_trans_insert (struct netfs_trans_dst *ndst,struct netfs_state *st) +{ +struct rb_root *root = &st->trans_root; +struct rb_node **n = &root->rb_node, *parent =NULL ; +struct netfs_trans_dst *ret =NULL , *tmp; +struct netfs_trans *t =NULL , *new = ndst->trans; +int cmp; + +while (*n) { + parent = *n; + + tmp = rb_entry(parent, struct netfs_trans_dst, state_entry); + t = tmp->trans; + + cmp =netfs_trans_cmp (t->gen, new->gen); +if (cmp <0 ) + n = &parent->rb_left; +else if (cmp >0 ) + n = &parent->rb_right; +else { + ret = tmp; +break ; + } + } + +if (ret) { +printk ("%s: exist: old: gen: %u, flags: %x, send_time: %lu, " +"new: gen: %u, flags: %x, send_time: %lu.\n" , + __func__, t->gen, t->flags, ret->send_time, + new->gen, new->flags, ndst->send_time); +return -EEXIST ; + } + +rb_link_node (&ndst->state_entry, parent, n); +rb_insert_color (&ndst->state_entry, root); + ndst->send_time = jiffies; + +return 0 ; +} + +int netfs_trans_remove_nolock (struct netfs_trans_dst *dst,struct netfs_state *st) +{ +if (dst && dst->state_entry.rb_parent_color) { +rb_erase (&dst->state_entry, &st->trans_root); + dst->state_entry.rb_parent_color =0 ; +return 1 ; + } +return 0 ; +} + +static int netfs_trans_remove_state (struct netfs_trans_dst *dst) +{ +int ret; +struct netfs_state *st = dst->state; + +mutex_lock (&st->trans_lock); + ret =netfs_trans_remove_nolock (dst, st); +mutex_unlock (&st->trans_lock); + +return ret; +} + +/* +* Create new destination for given transaction associated with given network state. +* Transaction's reference counter is bumped and will be dropped when either +* reply is received or when async timeout detection task will fail resending +* and drop transaction. +*/ +static int netfs_trans_push_dst (struct netfs_trans *t,struct netfs_state *st) +{ +struct netfs_trans_dst *dst; +int err; + + dst =mempool_alloc (netfs_trans_dst_pool,GFP_KERNEL ); +if (!dst) +return -ENOMEM ; + + dst->retries =0 ; + dst->send_time =0 ; + dst->state = st; + dst->trans = t; +netfs_trans_get (t); + +mutex_lock (&st->trans_lock); + err =netfs_trans_insert (dst, st); +mutex_unlock (&st->trans_lock); + +if (err) + goto err_out_free; + +mutex_lock (&t->dst_lock); +list_add_tail (&dst->trans_entry, &t->dst_list); +mutex_unlock (&t->dst_lock); + +return 0 ; + +err_out_free: + t->result = err; +netfs_trans_put (t); +mempool_free (dst, netfs_trans_dst_pool); +return err; +} + +static void netfs_trans_free_dst (struct netfs_trans_dst *dst) +{ +netfs_trans_put (dst->trans); +mempool_free (dst, netfs_trans_dst_pool); +} + +static void netfs_trans_remove_dst (struct netfs_trans_dst *dst) +{ +netfs_trans_remove_state (dst); +netfs_trans_free_dst (dst); +} + +/* +* Drop destination transaction entry when we know it. +*/ +void netfs_trans_drop_dst (struct netfs_trans_dst *dst) +{ +struct netfs_trans *t = dst->trans; + +mutex_lock (&t->dst_lock); +list_del_init (&dst->trans_entry); +mutex_unlock (&t->dst_lock); + +netfs_trans_remove_dst (dst); +} + +/* +* Drop destination transaction entry when we know it and when we +* already removed dst from state tree. +*/ +void netfs_trans_drop_dst_nostate (struct netfs_trans_dst *dst) +{ +struct netfs_trans *t = dst->trans; + +mutex_lock (&t->dst_lock); +list_del_init (&dst->trans_entry); +mutex_unlock (&t->dst_lock); + +netfs_trans_free_dst (dst); +} + +/* +* This drops destination transaction entry from appropriate network state +* tree and drops related reference counter. It is possible that transaction +* will be freed here if its reference counter hits zero. +* Destination transaction entry will be freed. +*/ +void netfs_trans_drop_trans (struct netfs_trans *t,struct netfs_state *st) +{ +struct netfs_trans_dst *dst, *tmp, *ret =NULL ; + +mutex_lock (&t->dst_lock); +list_for_each_entry_safe (dst, tmp, &t->dst_list, trans_entry) { +if (dst->state == st) { + ret = dst; +list_del (&dst->trans_entry); +break ; + } + } +mutex_unlock (&t->dst_lock); + +if (ret) +netfs_trans_remove_dst (ret); +} + +/* +* This drops destination transaction entry from appropriate network state +* tree and drops related reference counter. It is possible that transaction +* will be freed here if its reference counter hits zero. +* Destination transaction entry will be freed. +*/ +void netfs_trans_drop_last (struct netfs_trans *t,struct netfs_state *st) +{ +struct netfs_trans_dst *dst, *tmp, *ret; + +mutex_lock (&t->dst_lock); + ret = list_entry(t->dst_list.prev, struct netfs_trans_dst, trans_entry); +if (ret->state != st) { + ret =NULL ; +list_for_each_entry_safe (dst, tmp, &t->dst_list, trans_entry) { +if (dst->state == st) { + ret = dst; +list_del_init (&dst->trans_entry); +break ; + } + } + }else { +list_del (&ret->trans_entry); + } +mutex_unlock (&t->dst_lock); + +if (ret) +netfs_trans_remove_dst (ret); +} + +static int netfs_trans_push (struct netfs_trans *t,struct netfs_state *st) +{ +int err; + + err =netfs_trans_push_dst (t, st); +if (err) +return err; + + err =netfs_trans_send (t, st); +if (err) + goto err_out_free; + +if (t->flags &NETFS_TRANS_SINGLE_DST ) +pohmelfs_switch_active (st->psb); + +return 0 ; + +err_out_free: + t->result = err; +netfs_trans_drop_last (t, st); + +return err; +} + +int netfs_trans_finish_send (struct netfs_trans *t,struct pohmelfs_sb *psb) +{ +struct pohmelfs_config *c; +int err = -ENODEV ; +struct netfs_state *st; +#if 0 +dprintk ("%s: t: %p, gen: %u, size: %u, page_num: %u, active: %p.\n" , + __func__, t, t->gen, t->iovec.iov_len, t->page_num, psb->active_state); +#endif +mutex_lock (&psb->state_lock); +if ((t->flags &NETFS_TRANS_SINGLE_DST ) && psb->active_state) { + st = &psb->active_state->state; + + err = -EPIPE ; +if (netfs_state_poll (st) &POLLOUT ) { + err =netfs_trans_push_dst (t, st); +if (!err) { + err =netfs_trans_send (t, st); +if (err) { +netfs_trans_drop_last (t, st); + }else { +pohmelfs_switch_active (psb); + goto out; + } + } + } +pohmelfs_switch_active (psb); + } + +list_for_each_entry (c, &psb->state_list, config_entry) { + st = &c->state; + + err =netfs_trans_push (t, st); +if (!err && (t->flags &NETFS_TRANS_SINGLE_DST )) +break ; + } +out: +mutex_unlock (&psb->state_lock); +#if 0 +dprintk ("%s: fully sent t: %p, gen: %u, size: %u, page_num: %u, err: %d.\n" , + __func__, t, t->gen, t->iovec.iov_len, t->page_num, err); +#endif +if (err) + t->result = err; +return err; +} + +int netfs_trans_finish (struct netfs_trans *t,struct pohmelfs_sb *psb) +{ +int err; +struct netfs_cmd *cmd = t->iovec.iov_base; + + t->gen =atomic_inc_return (&psb->trans_gen); + + cmd->size = t->iovec.iov_len -sizeof (struct netfs_cmd ) + + t->attached_size + t->attached_pages *sizeof (struct netfs_cmd ); + cmd->cmd =NETFS_TRANS ; + cmd->start = t->gen; + cmd->id =0 ; + +if (psb->perform_crypto) { + cmd->ext = psb->crypto_attached_size; + cmd->csize = psb->crypto_attached_size; + } +#if 0 +dprintk ("%s: trans: %llu, crypto_attached_size: %u, attached_size: %u, attached_pages: %d, trans_size: %u.\n" , + __func__, cmd->start, psb->crypto_attached_size, t->attached_size, t->attached_pages, cmd->size); +#endif + err =pohmelfs_trans_crypt (t, psb); +if (err) + t->result = err; +netfs_trans_put (t); +return err; +} + +/* +* Resend transaction to remote server(s). +* If new servers were added into superblock, we can try to send data +* to them too. +* +* It is called under superblock's state_lock, so we can safely +* dereference psb->state_list. Also, transaction's reference counter is +* bumped, so it can not go away under us, thus we can safely access all +* its members. State is locked. +* +* This function returns 0 if transaction was successfully sent to at +* least one destination target. +*/ +int netfs_trans_resend (struct netfs_trans *t,struct pohmelfs_sb *psb) +{ +struct netfs_trans_dst *dst; +struct netfs_state *st; +struct pohmelfs_config *c; +int err, exist, error = -ENODEV ; + +list_for_each_entry (c, &psb->state_list, config_entry) { + st = &c->state; + + exist =0 ; +mutex_lock (&t->dst_lock); +list_for_each_entry (dst, &t->dst_list, trans_entry) { +if (st == dst->state) { + exist =1 ; +break ; + } + } +mutex_unlock (&t->dst_lock); + +if (exist) { +if (!(t->flags &NETFS_TRANS_SINGLE_DST )) { +dprintk ("%s: resending st: %p, t: %p, gen: %u.\n" , + __func__, st, t, t->gen); + err =netfs_trans_send (t, st); +if (!err) + error =0 ; + } +continue ; + } + +dprintk ("%s: pushing/resending st: %p, t: %p, gen: %u.\n" , + __func__, st, t, t->gen); + err =netfs_trans_push (t, st); +if (err) +continue ; + error =0 ; +if (t->flags &NETFS_TRANS_SINGLE_DST ) +break ; + } + + t->result = error; +return error; +} + +void *netfs_trans_add (struct netfs_trans *t,unsigned int size) +{ +struct iovec *io = &t->iovec; +void *ptr; + +if (size > t->total_size) { + ptr =ERR_PTR (-EINVAL ); + goto out; + } + +if (io->iov_len + size > t->total_size) { +dprintk ("%s: too big size t: %p, gen: %u, iov_len: %u, size: %u, total: %u.\n" , + __func__, t, t->gen, io->iov_len, size, t->total_size); + ptr =ERR_PTR (-E2BIG ); + goto out; + } + + ptr = io->iov_base + io->iov_len; + io->iov_len += size; + +out: +dprintk ("%s: t: %p, gen: %u, size: %u, total: %u.\n" , + __func__, t, t->gen, size, io->iov_len); +return ptr; +} + +void netfs_trans_free (struct netfs_trans *t) +{ +if (t->eng) +pohmelfs_crypto_thread_make_ready (t->eng->thread); +kfree (t); +} + +struct netfs_trans *netfs_trans_alloc (struct pohmelfs_sb *psb,unsigned int size, +unsigned int flags,unsigned int nr) +{ +struct netfs_trans *t; +unsigned int num, cont, pad, size_no_trans; +unsigned int crypto_added =0 ; +struct netfs_cmd *cmd; + +if (psb->perform_crypto) + crypto_added = psb->crypto_attached_size; + +/* +* |sizeof(struct netfs_trans)| +* |sizeof(struct netfs_cmd)| - transaction header +* |size| - buffer with requested size +* |padding| - crypto padding, zero bytes +* |nr * sizeof(struct page *)| - array of page pointers +* +* Overall size should be less than PAGE_SIZE for guaranteed allocation. +*/ + + cont = size; + size =ALIGN (size, psb->crypto_align_size); + pad = size - cont; + + size_no_trans = size +sizeof (struct netfs_cmd ) *2 + crypto_added; + + cont =sizeof (struct netfs_trans ) + size_no_trans; + + num = (PAGE_SIZE - cont)/sizeof (struct page *); + +if (nr > num) + nr = num; + + t =kzalloc (cont + nr*sizeof (struct page *),GFP_NOIO ); +if (!t) + goto err_out_exit; + +memset (t,0 ,sizeof (struct netfs_trans )); + + t->iovec.iov_base = (void *)(t +1 ); + t->pages = (struct page **)(t->iovec.iov_base + size_no_trans); + +/* +* Reserving space for transaction header. +*/ + t->iovec.iov_len =sizeof (struct netfs_cmd ) + crypto_added; + +netfs_trans_init_static (t, nr, size_no_trans); + + t->flags = flags; + t->psb = psb; + + cmd = (struct netfs_cmd *)t->iovec.iov_base; + + cmd->size = size; + cmd->cpad = pad; + cmd->csize = crypto_added; + +dprintk ("%s: t: %p, gen: %u, size: %u, padding: %u, align_size: %u, flags: %x, " +"page_num: %u, base: %p, pages: %p.\n" , + __func__, t, t->gen, size, pad, psb->crypto_align_size, flags, nr, + t->iovec.iov_base, t->pages); + +return t; + +err_out_exit: +return NULL ; +} + +int netfs_trans_init (void ) +{ +int err = -ENOMEM ; + + netfs_trans_dst =kmem_cache_create ("netfs_trans_dst" ,sizeof (struct netfs_trans_dst ), +0 ,0 ,NULL ); +if (!netfs_trans_dst) + goto err_out_exit; + + netfs_trans_dst_pool =mempool_create_slab_pool (256 , netfs_trans_dst); +if (!netfs_trans_dst_pool) + goto err_out_free; + +return 0 ; + +err_out_free: +kmem_cache_destroy (netfs_trans_dst); +err_out_exit: +return err; +} + +void netfs_trans_exit (void ) +{ +mempool_destroy (netfs_trans_dst_pool); +kmem_cache_destroy (netfs_trans_dst); +}
fs/pohmelfs/net.c new file +1070 -0
1,070 lines not on this page · raw message
fs/pohmelfs/netfs.h new file +877 -0
877 lines not on this page · raw message
PATCH 4/5 POHMELFS: crypto, locking, path and cmd caches.
fs/pohmelfs/crypto.c new file +880 -0
@@ -0,0 +1,880 @@ +/* +* 2007+ Copyright (c) Evgeniy Polyakov <[email protected]> +* 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. +*/ + +#include <linux/crypto.h> +#include <linux/highmem.h> +#include <linux/kthread.h> +#include <linux/pagemap.h> +#include <linux/slab.h> + +#include "netfs.h" + +static struct crypto_hash *pohmelfs_init_hash (struct pohmelfs_sb *psb) +{ +int err; +struct crypto_hash *hash; + + hash =crypto_alloc_hash (psb->hash_string,0 ,CRYPTO_ALG_ASYNC ); +if (IS_ERR (hash)) { + err =PTR_ERR (hash); +dprintk ("%s: idx: %u: failed to allocate hash '%s', err: %d.\n" , + __func__, psb->idx, psb->hash_string, err); + goto err_out_exit; + } + + psb->crypto_attached_size =crypto_hash_digestsize (hash); + +if (!psb->hash_keysize) +return hash; + + err =crypto_hash_setkey (hash, psb->hash_key, psb->hash_keysize); +if (err) { +dprintk ("%s: idx: %u: failed to set key for hash '%s', err: %d.\n" , + __func__, psb->idx, psb->hash_string, err); + goto err_out_free; + } + +return hash; + +err_out_free: +crypto_free_hash (hash); +err_out_exit: +return ERR_PTR (err); +} + +static struct crypto_ablkcipher *pohmelfs_init_cipher (struct pohmelfs_sb *psb) +{ +int err = -EINVAL ; +struct crypto_ablkcipher *cipher; + +if (!psb->cipher_keysize) + goto err_out_exit; + + cipher =crypto_alloc_ablkcipher (psb->cipher_string,0 ,0 ); +if (IS_ERR (cipher)) { + err =PTR_ERR (cipher); +dprintk ("%s: idx: %u: failed to allocate cipher '%s', err: %d.\n" , + __func__, psb->idx, psb->cipher_string, err); + goto err_out_exit; + } + +crypto_ablkcipher_clear_flags (cipher, ~0 ); + + err =crypto_ablkcipher_setkey (cipher, psb->cipher_key, psb->cipher_keysize); +if (err) { +dprintk ("%s: idx: %u: failed to set key for cipher '%s', err: %d.\n" , + __func__, psb->idx, psb->cipher_string, err); + goto err_out_free; + } + +return cipher; + +err_out_free: +crypto_free_ablkcipher (cipher); +err_out_exit: +return ERR_PTR (err); +} + +int pohmelfs_crypto_engine_init (struct pohmelfs_crypto_engine *e,struct pohmelfs_sb *psb) +{ +int err; + + e->page_num =0 ; + + e->size =PAGE_SIZE ; + e->data =kmalloc (e->size,GFP_KERNEL ); +if (!e->data) { + err = -ENOMEM ; + goto err_out_exit; + } + +if (psb->hash_string) { + e->hash =pohmelfs_init_hash (psb); +if (IS_ERR (e->hash)) { + err =PTR_ERR (e->hash); + e->hash =NULL ; + goto err_out_free; + } + } + +if (psb->cipher_string) { + e->cipher =pohmelfs_init_cipher (psb); +if (IS_ERR (e->cipher)) { + err =PTR_ERR (e->cipher); + e->cipher =NULL ; + goto err_out_free_hash; + } + } + +return 0 ; + +err_out_free_hash: +crypto_free_hash (e->hash); +err_out_free: +kfree (e->data); +err_out_exit: +return err; +} + +void pohmelfs_crypto_engine_exit (struct pohmelfs_crypto_engine *e) +{ +if (e->hash) +crypto_free_hash (e->hash); +if (e->cipher) +crypto_free_ablkcipher (e->cipher); +kfree (e->data); +} + +static void pohmelfs_crypto_complete (struct crypto_async_request *req,int err) +{ +struct pohmelfs_crypto_completion *c = req->data; + +if (err == -EINPROGRESS ) +return ; + +dprintk ("%s: req: %p, err: %d.\n" , __func__, req, err); + c->error = err; +complete (&c->complete); +} + +static int pohmelfs_crypto_process (struct ablkcipher_request *req, +struct scatterlist *sg_dst,struct scatterlist *sg_src, +void *iv,int enc,unsigned long timeout) +{ +struct pohmelfs_crypto_completion complete; +int err; + +init_completion (&complete.complete); + complete.error = -EINPROGRESS ; + +ablkcipher_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + pohmelfs_crypto_complete, &complete); + +ablkcipher_request_set_crypt (req, sg_src, sg_dst, sg_src->length, iv); + +if (enc) + err =crypto_ablkcipher_encrypt (req); +else + err =crypto_ablkcipher_decrypt (req); + +switch (err) { +case -EINPROGRESS : +case -EBUSY : + err =wait_for_completion_interruptible_timeout (&complete.complete, + timeout); +if (!err) + err = -ETIMEDOUT ; +else + err = complete.error; +break ; +default : +break ; + } + +return err; +} + +int pohmelfs_crypto_process_input_data (struct pohmelfs_crypto_engine *e,u64 cmd_iv, +void *data,struct page *page,unsigned int size) +{ +int err; +struct scatterlist sg; + +if (!e->cipher && !e->hash) +return 0 ; + +dprintk ("%s: eng: %p, iv: %llx, data: %p, page: %p/%lu, size: %u.\n" , + __func__, e, cmd_iv, data, page, (page)?page->index:0 , size); + +if (data) { +sg_init_one (&sg, data, size); + }else { +sg_init_table (&sg,1 ); +sg_set_page (&sg, page, size,0 ); + } + +if (e->cipher) { +struct ablkcipher_request *req = e->data +crypto_hash_digestsize (e->hash); +u8 iv[32 ]; + +memset (iv,0 ,sizeof (iv)); +memcpy (iv, &cmd_iv,sizeof (cmd_iv)); + +ablkcipher_request_set_tfm (req, e->cipher); + + err =pohmelfs_crypto_process (req, &sg, &sg, iv,0 , e->timeout); +if (err) + goto err_out_exit; + } + +if (e->hash) { +struct hash_desc desc; +void *dst = e->data + e->size/2 ; + + desc.tfm = e->hash; + desc.flags =0 ; + + err =crypto_hash_init (&desc); +if (err) + goto err_out_exit; + + err =crypto_hash_update (&desc, &sg, size); +if (err) + goto err_out_exit; + + err =crypto_hash_final (&desc, dst); +if (err) + goto err_out_exit; + + err = !!memcmp (dst, e->data,crypto_hash_digestsize (e->hash)); + +if (err) { +#ifdef CONFIG_POHMELFS_DEBUG +unsigned int i; +unsigned char *recv = e->data, *calc = dst; + +dprintk ("%s: eng: %p, hash: %p, cipher: %p: iv : %llx, hash mismatch (recv/calc): " , + __func__, e, e->hash, e->cipher, cmd_iv); +for (i=0 ; i<crypto_hash_digestsize (e->hash); ++i) { +#if 0 +dprintka ("%02x " , recv[i]); +if (recv[i] != calc[i]) { +dprintka ("| calc byte: %02x.\n" , calc[i]); +break ; + } +#else +dprintka ("%02x/%02x " , recv[i], calc[i]); +#endif + } +dprintk ("\n" ); +#endif + goto err_out_exit; + }else { +dprintk ("%s: eng: %p, hash: %p, cipher: %p: hashes matched.\n" , + __func__, e, e->hash, e->cipher); + } + } + +dprintk ("%s: eng: %p, size: %u, hash: %p, cipher: %p: completed.\n" , + __func__, e, e->size, e->hash, e->cipher); + +return 0 ; + +err_out_exit: +dprintk ("%s: eng: %p, hash: %p, cipher: %p: err: %d.\n" , + __func__, e, e->hash, e->cipher, err); +return err; +} + +static int pohmelfs_trans_iter (struct netfs_trans *t,struct pohmelfs_crypto_engine *e, +int (* iterator) (struct pohmelfs_crypto_engine *e, +struct scatterlist *dst, +struct scatterlist *src)) +{ +void *data = t->iovec.iov_base +sizeof (struct netfs_cmd ) + t->psb->crypto_attached_size; +unsigned int size = t->iovec.iov_len -sizeof (struct netfs_cmd ) - t->psb->crypto_attached_size; +struct netfs_cmd *cmd = data; +unsigned int sz, pages = t->attached_pages, i, csize, cmd_cmd, dpage_idx; +struct scatterlist sg_src, sg_dst; +int err; + +while (size) { + cmd = data; + cmd_cmd =__be16_to_cpu (cmd->cmd); + csize =__be32_to_cpu (cmd->size); + cmd->iv =__cpu_to_be64 (e->iv); + +if (cmd_cmd ==NETFS_READ_PAGES || cmd_cmd ==NETFS_READ_PAGE ) + csize =__be16_to_cpu (cmd->ext); + + sz = csize +__be16_to_cpu (cmd->cpad) +sizeof (struct netfs_cmd ); + +dprintk ("%s: size: %u, sz: %u, cmd_size: %u, cmd_cpad: %u.\n" , + __func__, size, sz,__be32_to_cpu (cmd->size),__be16_to_cpu (cmd->cpad)); + + data += sz; + size -= sz; + +sg_init_one (&sg_src, cmd->data, sz -sizeof (struct netfs_cmd )); +sg_init_one (&sg_dst, cmd->data, sz -sizeof (struct netfs_cmd )); + + err =iterator (e, &sg_dst, &sg_src); +if (err) +return err; + } + +if (!pages) +return 0 ; + + dpage_idx =0 ; +for (i=0 ; i<t->page_num; ++i) { +struct page *page = t->pages[i]; +struct page *dpage = e->pages[dpage_idx]; + +if (!page) +continue ; + +sg_init_table (&sg_src,1 ); +sg_init_table (&sg_dst,1 ); +sg_set_page (&sg_src, page,page_private (page),0 ); +sg_set_page (&sg_dst, dpage,page_private (page),0 ); + + err =iterator (e, &sg_dst, &sg_src); +if (err) +return err; + + pages--; +if (!pages) +break ; + dpage_idx++; + } + +return 0 ; +} + +static int pohmelfs_encrypt_iterator (struct pohmelfs_crypto_engine *e, +struct scatterlist *sg_dst,struct scatterlist *sg_src) +{ +struct ablkcipher_request *req = e->data; +u8 iv[32 ]; + +memset (iv,0 ,sizeof (iv)); + +memcpy (iv, &e->iv,sizeof (e->iv)); + +return pohmelfs_crypto_process (req, sg_dst, sg_src, iv,1 , e->timeout); +} + +static int pohmelfs_encrypt (struct pohmelfs_crypto_thread *tc) +{ +struct netfs_trans *t = tc->trans; +struct pohmelfs_crypto_engine *e = &tc->eng; +struct ablkcipher_request *req = e->data; + +memset (req,0 ,sizeof (struct ablkcipher_request )); +ablkcipher_request_set_tfm (req, e->cipher); + + e->iv =pohmelfs_gen_iv (t); + +return pohmelfs_trans_iter (t, e, pohmelfs_encrypt_iterator); +} + +static int pohmelfs_hash_iterator (struct pohmelfs_crypto_engine *e, +struct scatterlist *sg_dst,struct scatterlist *sg_src) +{ +return crypto_hash_update (e->data, sg_src, sg_src->length); +} + +static int pohmelfs_hash (struct pohmelfs_crypto_thread *tc) +{ +struct pohmelfs_crypto_engine *e = &tc->eng; +struct hash_desc *desc = e->data; +unsigned char *dst = tc->trans->iovec.iov_base +sizeof (struct netfs_cmd ); +int err; + + desc->tfm = e->hash; + desc->flags =0 ; + + err =crypto_hash_init (desc); +if (err) +return err; + + err =pohmelfs_trans_iter (tc->trans, e, pohmelfs_hash_iterator); +if (err) +return err; + + err =crypto_hash_final (desc, dst); +if (err) +return err; + + { +unsigned int i; +dprintk ("%s: " , __func__); +for (i=0 ; i<tc->psb->crypto_attached_size; ++i) +dprintka ("%02x " , dst[i]); +dprintka ("\n" ); + } + +return 0 ; +} + +static void pohmelfs_crypto_pages_free (struct pohmelfs_crypto_engine *e) +{ +unsigned int i; + +for (i=0 ; i<e->page_num; ++i) +__free_page (e->pages[i]); +kfree (e->pages); +} + +static int pohmelfs_crypto_pages_alloc (struct pohmelfs_crypto_engine *e,struct pohmelfs_sb *psb) +{ +unsigned int i; + + e->pages =kmalloc (psb->trans_max_pages *sizeof (struct page *),GFP_KERNEL ); +if (!e->pages) +return -ENOMEM ; + +for (i=0 ; i<psb->trans_max_pages; ++i) { + e->pages[i] =alloc_page (GFP_KERNEL ); +if (!e->pages[i]) +break ; + } + + e->page_num = i; +if (!e->page_num) + goto err_out_free; + +return 0 ; + +err_out_free: +kfree (e->pages); +return -ENOMEM ; +} + +static void pohmelfs_sys_crypto_exit_one (struct pohmelfs_crypto_thread *t) +{ +struct pohmelfs_sb *psb = t->psb; + +if (t->thread) +kthread_stop (t->thread); + +mutex_lock (&psb->crypto_thread_lock); +list_del (&t->thread_entry); + psb->crypto_thread_num--; +mutex_unlock (&psb->crypto_thread_lock); + +pohmelfs_crypto_engine_exit (&t->eng); +pohmelfs_crypto_pages_free (&t->eng); +kfree (t); +} + +static int pohmelfs_crypto_finish (struct netfs_trans *t,struct pohmelfs_sb *psb,int err) +{ +if (likely (!err)) { +struct netfs_cmd *cmd = t->iovec.iov_base; +netfs_convert_cmd (cmd); + + err =netfs_trans_finish_send (t, psb); + } + t->result = err; +netfs_trans_put (t); + +return err; +} + +void pohmelfs_crypto_thread_make_ready (struct pohmelfs_crypto_thread *th) +{ +struct pohmelfs_sb *psb = th->psb; + + th->page =NULL ; + th->trans =NULL ; + +mutex_lock (&psb->crypto_thread_lock); +list_move_tail (&th->thread_entry, &psb->crypto_ready_list); +mutex_unlock (&psb->crypto_thread_lock); +wake_up (&psb->wait); +} + +static int pohmelfs_crypto_thread_trans (struct pohmelfs_crypto_thread *t) +{ +struct netfs_trans *trans; +int err =0 ; + + trans = t->trans; + trans->eng =NULL ; + +if (t->eng.hash) { + err =pohmelfs_hash (t); +if (err) + goto out_complete; + } + +if (t->eng.cipher) { + err =pohmelfs_encrypt (t); +if (err) + goto out_complete; + trans->eng = &t->eng; + } + +out_complete: + t->page =NULL ; + t->trans =NULL ; + +if (!trans->eng) +pohmelfs_crypto_thread_make_ready (t); + +pohmelfs_crypto_finish (trans, t->psb, err); +return err; +} + +static int pohmelfs_crypto_thread_page (struct pohmelfs_crypto_thread *t) +{ +struct pohmelfs_crypto_engine *e = &t->eng; +struct page *page = t->page; +int err; + +WARN_ON (!PageChecked (page)); + + err =pohmelfs_crypto_process_input_data (e, e->iv,NULL , page, t->size); +if (!err) +SetPageUptodate (page); +else +SetPageError (page); +unlock_page (page); +page_cache_release (page); + +pohmelfs_crypto_thread_make_ready (t); + +return err; +} + +static int pohmelfs_crypto_thread_func (void *data) +{ +struct pohmelfs_crypto_thread *t = data; + +while (!kthread_should_stop ()) { +wait_event_interruptible (t->wait,kthread_should_stop () || + t->trans || t->page); + +if (kthread_should_stop ()) +break ; + +if (!t->trans && !t->page) +continue ; + +dprintk ("%s: thread: %p, trans: %p, page: %p.\n" , + __func__, t, t->trans, t->page); + +if (t->trans) +pohmelfs_crypto_thread_trans (t); +else if (t->page) +pohmelfs_crypto_thread_page (t); + } + +return 0 ; +} + +static void pohmelfs_crypto_flush (struct pohmelfs_sb *psb,struct list_head *head) +{ +while (!list_empty (head)) { +struct pohmelfs_crypto_thread *t =NULL ; + +mutex_lock (&psb->crypto_thread_lock); +if (!list_empty (head)) { + t = list_first_entry(head, struct pohmelfs_crypto_thread, thread_entry); +list_del_init (&t->thread_entry); + } +mutex_unlock (&psb->crypto_thread_lock); + +if (t) +pohmelfs_sys_crypto_exit_one (t); + } +} + +static void pohmelfs_sys_crypto_exit (struct pohmelfs_sb *psb) +{ +while (!list_empty (&psb->crypto_active_list) || !list_empty (&psb->crypto_ready_list)) { +dprintk ("%s: crypto_thread_num: %u.\n" , __func__, psb->crypto_thread_num); +pohmelfs_crypto_flush (psb, &psb->crypto_active_list); +pohmelfs_crypto_flush (psb, &psb->crypto_ready_list); + } +} + +static int pohmelfs_sys_crypto_init (struct pohmelfs_sb *psb) +{ +unsigned int i; +struct pohmelfs_crypto_thread *t; +struct pohmelfs_config *c; +struct netfs_state *st; +int err; + +list_for_each_entry (c, &psb->state_list, config_entry) { + st = &c->state; + + err =pohmelfs_crypto_engine_init (&st->eng, psb); +if (err) + goto err_out_exit; + +dprintk ("%s: st: %p, eng: %p, hash: %p, cipher: %p.\n" , + __func__, st, &st->eng, &st->eng.hash, &st->eng.cipher); + } + +for (i=0 ; i<psb->crypto_thread_num; ++i) { + err = -ENOMEM ; + t =kzalloc (sizeof (struct pohmelfs_crypto_thread ),GFP_KERNEL ); +if (!t) + goto err_out_free_state_engines; + +init_waitqueue_head (&t->wait); + + t->psb = psb; + t->trans =NULL ; + t->eng.thread = t; + + err =pohmelfs_crypto_engine_init (&t->eng, psb); +if (err) + goto err_out_free_state_engines; + + err =pohmelfs_crypto_pages_alloc (&t->eng, psb); +if (err) + goto err_out_free; + + t->thread =kthread_run (pohmelfs_crypto_thread_func, t, +"pohmelfs-crypto-%d-%d" , psb->idx, i); +if (IS_ERR (t->thread)) { + err =PTR_ERR (t->thread); + t->thread =NULL ; + goto err_out_free; + } + +if (t->eng.cipher) + psb->crypto_align_size =crypto_ablkcipher_blocksize (t->eng.cipher); + +mutex_lock (&psb->crypto_thread_lock); +list_add_tail (&t->thread_entry, &psb->crypto_ready_list); +mutex_unlock (&psb->crypto_thread_lock); + } + + psb->crypto_thread_num = i; +return 0 ; + +err_out_free: +pohmelfs_sys_crypto_exit_one (t); +err_out_free_state_engines: +list_for_each_entry (c, &psb->state_list, config_entry) { + st = &c->state; +pohmelfs_crypto_engine_exit (&st->eng); + } +err_out_exit: +pohmelfs_sys_crypto_exit (psb); +return err; +} + +void pohmelfs_crypto_exit (struct pohmelfs_sb *psb) +{ +pohmelfs_sys_crypto_exit (psb); + +kfree (psb->hash_string); +kfree (psb->cipher_string); +} + +static int pohmelfs_crypt_init_complete (struct page **pages,unsigned int page_num, +void *private,int err) +{ +struct pohmelfs_sb *psb = private; + + psb->flags = -err; +dprintk ("%s: err: %d, flags: %lx.\n" , __func__, err, psb->flags); + +wake_up (&psb->wait); + +return err; +} + +static int pohmelfs_crypto_init_handshake (struct pohmelfs_sb *psb) +{ +struct netfs_trans *t; +struct netfs_capabilities *cap; +struct netfs_cmd *cmd; +char *str; +int err = -ENOMEM , size; + + size =sizeof (struct netfs_capabilities ) + + psb->cipher_strlen + psb->hash_strlen +2 ;/* 0 bytes */ + + t =netfs_trans_alloc (psb, size,0 ,0 ); +if (!t) + goto err_out_exit; + + t->complete = pohmelfs_crypt_init_complete; + t->private = psb; + + cmd =netfs_trans_current (t); + cap = (struct netfs_capabilities *)(cmd +1 ); + str = (char *)(cap +1 ); + + cmd->cmd =NETFS_CAPABILITIES ; + cmd->id = psb->idx; + cmd->size = size; + cmd->start =0 ; + cmd->ext =0 ; + cmd->csize =0 ; + +netfs_convert_cmd (cmd); +netfs_trans_update (cmd, t, size); + + cap->hash_strlen = psb->hash_strlen; +if (cap->hash_strlen) { +sprintf (str,"%s" , psb->hash_string); + str += cap->hash_strlen; + } + + cap->cipher_strlen = psb->cipher_strlen; + cap->cipher_keysize = psb->cipher_keysize; +if (cap->cipher_strlen) +sprintf (str,"%s" , psb->cipher_string); + +netfs_convert_capabilities (cap); + + psb->flags = ~0 ; + err =netfs_trans_finish (t, psb); +if (err) + goto err_out_exit; + + err =wait_event_interruptible_timeout (psb->wait, (psb->flags != ~0 ), + psb->wait_on_page_timeout); +if (!err) + err = -ETIMEDOUT ; +else + err = -psb->flags; + +if (!err) + psb->perform_crypto =1 ; + psb->flags =0 ; + +/* +* At this point NETFS_CAPABILITIES response command +* should setup superblock in a way, which is acceptible +* for both client and server, so if server refuses connection, +* it will send error in transaction response. +*/ + +if (err) + goto err_out_exit; + +return 0 ; + +err_out_exit: +return err; +} + +int pohmelfs_crypto_init (struct pohmelfs_sb *psb) +{ +int err; + +if (!psb->cipher_string && !psb->hash_string) +return 0 ; + + err =pohmelfs_crypto_init_handshake (psb); +if (err) +return err; + + err =pohmelfs_sys_crypto_init (psb); +if (err) +return err; + +return 0 ; +} + +static int pohmelfs_crypto_thread_get (struct pohmelfs_sb *psb, +int (* action)(struct pohmelfs_crypto_thread *t,void *data),void *data) +{ +struct pohmelfs_crypto_thread *t =NULL ; +int err; + +while (!t) { + err =wait_event_interruptible_timeout (psb->wait, + !list_empty (&psb->crypto_ready_list), + psb->wait_on_page_timeout); + + t =NULL ; + err =0 ; +mutex_lock (&psb->crypto_thread_lock); +if (!list_empty (&psb->crypto_ready_list)) { + t =list_entry (psb->crypto_ready_list.prev, + struct pohmelfs_crypto_thread, + thread_entry); + +list_move_tail (&t->thread_entry, + &psb->crypto_active_list); + +action (t, data); +wake_up (&t->wait); + + } +mutex_unlock (&psb->crypto_thread_lock); + } + +return err; +} + +static int pohmelfs_trans_crypt_action (struct pohmelfs_crypto_thread *t,void *data) +{ +struct netfs_trans *trans = data; + +netfs_trans_get (trans); + t->trans = trans; + +dprintk ("%s: t: %p, gen: %u, thread: %p.\n" , __func__, trans, trans->gen, t); +return 0 ; +} + +int pohmelfs_trans_crypt (struct netfs_trans *trans,struct pohmelfs_sb *psb) +{ +if ((!psb->hash_string && !psb->cipher_string) || !psb->perform_crypto) { +netfs_trans_get (trans); +return pohmelfs_crypto_finish (trans, psb,0 ); + } + +return pohmelfs_crypto_thread_get (psb, pohmelfs_trans_crypt_action, trans); +} + +struct pohmelfs_crypto_input_action_data +{ +struct page *page; +struct pohmelfs_crypto_engine *e; +u64 iv; +unsigned int size; +}; + +static int pohmelfs_crypt_input_page_action (struct pohmelfs_crypto_thread *t,void *data) +{ +struct pohmelfs_crypto_input_action_data *act = data; + +memcpy (t->eng.data, act->e->data, t->psb->crypto_attached_size); + + t->size = act->size; + t->eng.iv = act->iv; + + t->page = act->page; +return 0 ; +} + +int pohmelfs_crypto_process_input_page (struct pohmelfs_crypto_engine *e, +struct page *page,unsigned int size,u64 iv) +{ +struct inode *inode = page->mapping->host; +struct pohmelfs_crypto_input_action_data act; +int err = -ENOENT ; + + act.page = page; + act.e = e; + act.size = size; + act.iv = iv; + + err =pohmelfs_crypto_thread_get (POHMELFS_SB (inode->i_sb), + pohmelfs_crypt_input_page_action, &act); +if (err) + goto err_out_exit; + +return 0 ; + +err_out_exit: +SetPageUptodate (page); +page_cache_release (page); + +return err; +}
fs/pohmelfs/lock.c new file +168 -0
168 lines not on this page · raw message
fs/pohmelfs/mcache.c new file +178 -0
178 lines not on this page · raw message
fs/pohmelfs/path_entry.c new file +356 -0
356 lines not on this page · raw message
PATCH 5/5 POHMELFS: VFS changes and make/config files.
fs/Kconfig +2 -0
@@ -1557,6 +1557,8 @@ menuconfig NETWORK_FILESYSTEMS if NETWORK_FILESYSTEMS +source "fs/pohmelfs/Kconfig" + config NFS_FS tristate "NFS client support" depends on INET
fs/Makefile +1 -0
@@ -122,3 +122,4 @@ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_GFS2_FS) += gfs2/ +obj-$(CONFIG_POHMELFS) += pohmelfs/
fs/pohmelfs/Kconfig new file +23 -0
@@ -0,0 +1,23 @@ +config POHMELFS + tristate "POHMELFS filesystem support" + select CONNECTOR + help + POHMELFS stands for Parallel Optimized Host Message Exchange Layered File System. + This is a network filesystem which supports coherent caching of data and metadata + on clients. + +config POHMELFS_DEBUG + bool "POHMELFS debugging" + depends on POHMELFS + default n + help + Turns on excessive POHMELFS debugging facilities. + You usually do not want to slow things down noticebly and get really lots of kernel + messages in syslog. + +config POHMELFS_CRYPTO + bool "POHMELFS crypto support" + depends on CONFIG_CRYPTO_BLKCIPHER && CONFIG_CRYPTO_HASH + help + This option allows to encrypt and/or protect with strong cryptographic hash all dataflow + between server and clients. Each config group can have own keys.
fs/pohmelfs/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +obj-$(CONFIG_POHMELFS) += pohmelfs.o + +pohmelfs-y := inode.o config.o dir.o net.o path_entry.o trans.o crypto.o lock.o mcache.o
mm/filemap.c +2 -0
@@ -497,6 +497,7 @@ int add_to_page_cache_lru(struct page *page, struct address_space *mapping,lru_cache_add (page);return ret; } +EXPORT_SYMBOL_GPL (add_to_page_cache_lru);#ifdef CONFIG_NUMA struct page *__page_cache_alloc (gfp_t gfp)
@@ -614,6 +615,7 @@ int __lock_page_killable(struct page *page)return __wait_on_bit_lock (page_waitqueue (page), &wait, sync_page_killable,TASK_KILLABLE ); } +EXPORT_SYMBOL_GPL (__lock_page_killable);/** * __lock_page_nosync - get a lock on the page, without calling sync_page()