Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 141292
b: refs/heads/master
c: a0fde6e
h: refs/heads/master
v: v3
  • Loading branch information
Evgeniy Polyakov authored and Greg Kroah-Hartman committed Apr 3, 2009
1 parent 09f06a5 commit 5ef9d20
Show file tree
Hide file tree
Showing 2 changed files with 336 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6694b31ac12fd914ae5ecf937a8f55945f46940d
refs/heads/master: a0fde6e06ee91582675ef885841ae911037f4a4b
335 changes: 335 additions & 0 deletions trunk/drivers/staging/dst/trans.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
/*
* 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/bio.h>
#include <linux/dst.h>
#include <linux/slab.h>
#include <linux/mempool.h>

/*
* Transaction memory pool size.
*/
static int dst_mempool_num = 32;
module_param(dst_mempool_num, int, 0644);

/*
* Transaction tree management.
*/
static inline int dst_trans_cmp(dst_gen_t gen, dst_gen_t new)
{
if (gen < new)
return 1;
if (gen > new)
return -1;
return 0;
}

struct dst_trans *dst_trans_search(struct dst_node *node, dst_gen_t gen)
{
struct rb_root *root = &node->trans_root;
struct rb_node *n = root->rb_node;
struct dst_trans *t, *ret = NULL;
int cmp;

while (n) {
t = rb_entry(n, struct dst_trans, trans_entry);

cmp = dst_trans_cmp(t->gen, gen);
if (cmp < 0)
n = n->rb_left;
else if (cmp > 0)
n = n->rb_right;
else {
ret = t;
break;
}
}

dprintk("%s: %s transaction: id: %llu.\n", __func__,
(ret)?"found":"not found", gen);

return ret;
}

static int dst_trans_insert(struct dst_trans *new)
{
struct rb_root *root = &new->n->trans_root;
struct rb_node **n = &root->rb_node, *parent = NULL;
struct dst_trans *ret = NULL, *t;
int cmp;

while (*n) {
parent = *n;

t = rb_entry(parent, struct dst_trans, trans_entry);

cmp = dst_trans_cmp(t->gen, new->gen);
if (cmp < 0)
n = &parent->rb_left;
else if (cmp > 0)
n = &parent->rb_right;
else {
ret = t;
break;
}
}

new->send_time = jiffies;
if (ret) {
printk("%s: exist: old: gen: %llu, bio: %llu/%u, send_time: %lu, "
"new: gen: %llu, bio: %llu/%u, send_time: %lu.\n",
__func__,
ret->gen, (u64)ret->bio->bi_sector,
ret->bio->bi_size, ret->send_time,
new->gen, (u64)new->bio->bi_sector,
new->bio->bi_size, new->send_time);
return -EEXIST;
}

rb_link_node(&new->trans_entry, parent, n);
rb_insert_color(&new->trans_entry, root);

dprintk("%s: inserted: gen: %llu, bio: %llu/%u, send_time: %lu.\n",
__func__, new->gen, (u64)new->bio->bi_sector,
new->bio->bi_size, new->send_time);

return 0;
}

int dst_trans_remove_nolock(struct dst_trans *t)
{
struct dst_node *n = t->n;

if (t->trans_entry.rb_parent_color) {
rb_erase(&t->trans_entry, &n->trans_root);
t->trans_entry.rb_parent_color = 0;
}
return 0;
}

int dst_trans_remove(struct dst_trans *t)
{
int ret;
struct dst_node *n = t->n;

mutex_lock(&n->trans_lock);
ret = dst_trans_remove_nolock(t);
mutex_unlock(&n->trans_lock);

return ret;
}

/*
* When transaction is completed and there are no more users,
* we complete appriate block IO request with given error status.
*/
void dst_trans_put(struct dst_trans *t)
{
if (atomic_dec_and_test(&t->refcnt)) {
struct bio *bio = t->bio;

dprintk("%s: completed t: %p, gen: %llu, bio: %p.\n",
__func__, t, t->gen, bio);

bio_endio(bio, t->error);
bio_put(bio);

dst_node_put(t->n);
mempool_free(t, t->n->trans_pool);
}
}

/*
* Process given block IO request: allocate transaction, insert it into the tree
* and send/schedule crypto processing.
*/
int dst_process_bio(struct dst_node *n, struct bio *bio)
{
struct dst_trans *t;
int err = -ENOMEM;

t = mempool_alloc(n->trans_pool, GFP_NOFS);
if (!t)
goto err_out_exit;

t->n = dst_node_get(n);
t->bio = bio;
t->error = 0;
t->retries = 0;
atomic_set(&t->refcnt, 1);
t->gen = atomic_long_inc_return(&n->gen);

t->enc = bio_data_dir(bio);
dst_bio_to_cmd(bio, &t->cmd, DST_IO, t->gen);

mutex_lock(&n->trans_lock);
err = dst_trans_insert(t);
mutex_unlock(&n->trans_lock);
if (err)
goto err_out_free;

dprintk("%s: gen: %llu, bio: %llu/%u, dir/enc: %d, need_crypto: %d.\n",
__func__, t->gen, (u64)bio->bi_sector,
bio->bi_size, t->enc, dst_need_crypto(n));

if (dst_need_crypto(n) && t->enc)
dst_trans_crypto(t);
else
dst_trans_send(t);

return 0;

err_out_free:
dst_node_put(n);
mempool_free(t, n->trans_pool);
err_out_exit:
bio_endio(bio, err);
bio_put(bio);
return err;
}

/*
* Scan for timeout/stale transactions.
* Each transaction is being resent multiple times before error completion.
*/
static void dst_trans_scan(struct work_struct *work)
{
struct dst_node *n = container_of(work, struct dst_node, trans_work.work);
struct rb_node *rb_node;
struct dst_trans *t;
unsigned long timeout = n->trans_scan_timeout;
int num = 10 * n->trans_max_retries;

mutex_lock(&n->trans_lock);

for (rb_node = rb_first(&n->trans_root); rb_node; ) {
t = rb_entry(rb_node, struct dst_trans, trans_entry);

if (timeout && time_after(t->send_time + timeout, jiffies)
&& t->retries == 0)
break;
#if 0
dprintk("%s: t: %p, gen: %llu, n: %s, retries: %u, max: %u.\n",
__func__, t, t->gen, n->name,
t->retries, n->trans_max_retries);
#endif
if (--num == 0)
break;

dst_trans_get(t);

rb_node = rb_next(rb_node);

if (timeout && (++t->retries < n->trans_max_retries)) {
dst_trans_send(t);
} else {
t->error = -ETIMEDOUT;
dst_trans_remove_nolock(t);
dst_trans_put(t);
}

dst_trans_put(t);
}

mutex_unlock(&n->trans_lock);

/*
* If no timeout specified then system is in the middle of exiting process,
* so no need to reschedule scanning process again.
*/
if (timeout) {
if (!num)
timeout = HZ;
schedule_delayed_work(&n->trans_work, timeout);
}
}

/*
* Flush all transactions and mark them as timed out.
* Destroy transaction pools.
*/
void dst_node_trans_exit(struct dst_node *n)
{
struct dst_trans *t;
struct rb_node *rb_node;

if (!n->trans_cache)
return;

dprintk("%s: n: %p, cancelling the work.\n", __func__, n);
cancel_delayed_work_sync(&n->trans_work);
flush_scheduled_work();
dprintk("%s: n: %p, work has been cancelled.\n", __func__, n);

for (rb_node = rb_first(&n->trans_root); rb_node; ) {
t = rb_entry(rb_node, struct dst_trans, trans_entry);

dprintk("%s: t: %p, gen: %llu, n: %s.\n",
__func__, t, t->gen, n->name);

rb_node = rb_next(rb_node);

t->error = -ETIMEDOUT;
dst_trans_remove_nolock(t);
dst_trans_put(t);
}

mempool_destroy(n->trans_pool);
kmem_cache_destroy(n->trans_cache);
}

/*
* Initialize transaction storage for given node.
* Transaction stores not only control information,
* but also network command and crypto data (if needed)
* to reduce number of allocations. Thus transaction size
* differs from node to node.
*/
int dst_node_trans_init(struct dst_node *n, unsigned int size)
{
/*
* We need this, since node with given name can be dropped from the
* hash table, but be still alive, so subsequent creation of the node
* with the same name may collide with existing cache name.
*/

snprintf(n->cache_name, sizeof(n->cache_name), "%s-%p", n->name, n);

n->trans_cache = kmem_cache_create(n->cache_name,
size + n->crypto.crypto_attached_size,
0, 0, NULL);
if (!n->trans_cache)
goto err_out_exit;

n->trans_pool = mempool_create_slab_pool(dst_mempool_num, n->trans_cache);
if (!n->trans_pool)
goto err_out_cache_destroy;

mutex_init(&n->trans_lock);
n->trans_root = RB_ROOT;

INIT_DELAYED_WORK(&n->trans_work, dst_trans_scan);
schedule_delayed_work(&n->trans_work, n->trans_scan_timeout);

dprintk("%s: n: %p, size: %u, crypto: %u.\n",
__func__, n, size, n->crypto.crypto_attached_size);

return 0;

err_out_cache_destroy:
kmem_cache_destroy(n->trans_cache);
err_out_exit:
return -ENOMEM;
}

0 comments on commit 5ef9d20

Please sign in to comment.