123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
- * 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);
- spin_lock_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;
- BUG_ON(!t->iovec.iov_len);
- BUG_ON(t->iovec.iov_len > 1024*1024*1024);
- netfs_state_lock_send(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: %zu, 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: %zu, 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:
- if (st->need_reset)
- netfs_state_exit(st);
- netfs_state_unlock_send(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;
- spin_lock(&t->dst_lock);
- list_add_tail(&dst->trans_entry, &t->dst_list);
- spin_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)
- {
- if (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;
- spin_lock(&t->dst_lock);
- list_del_init(&dst->trans_entry);
- spin_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;
- spin_lock(&t->dst_lock);
- list_del_init(&dst->trans_entry);
- spin_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;
- spin_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;
- }
- }
- spin_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;
- spin_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);
- }
- spin_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);
- list_for_each_entry(c, &psb->state_list, config_entry) {
- st = &c->state;
- if (t->flags & NETFS_TRANS_SINGLE_DST) {
- if (!(st->ctl.perm & POHMELFS_IO_PERM_READ))
- continue;
- } else {
- if (!(st->ctl.perm & POHMELFS_IO_PERM_WRITE))
- continue;
- }
- if (psb->active_state && (psb->active_state->state.ctl.prio >= st->ctl.prio) &&
- (t->flags & NETFS_TRANS_SINGLE_DST))
- st = &psb->active_state->state;
- err = netfs_trans_push(t, st);
- if (!err && (t->flags & NETFS_TRANS_SINGLE_DST))
- break;
- }
- 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;
- }
- dprintk("%s: t: %u, size: %u, iov_len: %zu, attached_size: %u, attached_pages: %u.\n",
- __func__, t->gen, cmd->size, t->iovec.iov_len, t->attached_size, t->attached_pages);
- err = pohmelfs_trans_crypt(t, psb);
- if (err) {
- t->result = err;
- netfs_convert_cmd(cmd);
- dprintk("%s: trans: %llu, crypto_attached_size: %u, attached_size: %u, attached_pages: %d, trans_size: %u, err: %d.\n",
- __func__, cmd->start, psb->crypto_attached_size, t->attached_size, t->attached_pages, cmd->size, 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;
- spin_lock(&t->dst_lock);
- list_for_each_entry(dst, &t->dst_list, trans_entry) {
- if (st == dst->state) {
- exist = 1;
- break;
- }
- }
- spin_unlock(&t->dst_lock);
- if (exist) {
- if (!(t->flags & NETFS_TRANS_SINGLE_DST) ||
- (c->config_entry.next == &psb->state_list)) {
- 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: %zu, 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: %zu.\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;
- 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);
- }
|