* [3/4] Distributed storage. Algorithms.
From: Evgeniy Polyakov @ 2007-11-05 18:42 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, linux-fsdevel
Mirror and linear data stripping algorithms for DST.
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
diff --git a/drivers/block/dst/alg_linear.c b/drivers/block/dst/alg_linear.c
new file mode 100644
index 0000000..cb77b57
--- /dev/null
+++ b/drivers/block/dst/alg_linear.c
@@ -0,0 +1,104 @@
+/*
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dst.h>
+
+static struct dst_alg *alg_linear;
+
+/*
+ * This callback is invoked when node is removed from storage.
+ */
+static void dst_linear_del_node(struct dst_node *n)
+{
+}
+
+/*
+ * This callback is invoked when node is added to storage.
+ */
+static int dst_linear_add_node(struct dst_node *n)
+{
+ struct dst_storage *st = n->st;
+
+ dprintk("%s: disk_size: %llu, node_size: %llu.\n",
+ __func__, st->disk_size, n->size);
+
+ mutex_lock(&st->tree_lock);
+ n->start = st->disk_size;
+ st->disk_size += n->size;
+ mutex_unlock(&st->tree_lock);
+
+ return 0;
+}
+
+static int dst_linear_remap(struct dst_request *req)
+{
+ int err;
+
+ if (req->node->bdev) {
+ generic_make_request(req->bio);
+ return 0;
+ }
+
+ err = kst_check_permissions(req->state, req->bio);
+ if (err)
+ return err;
+
+ return req->state->ops->push(req);
+}
+
+/*
+ * Failover callback - it is invoked each time error happens during
+ * request processing.
+ */
+static int dst_linear_error(struct kst_state *st, int err)
+{
+ if (err)
+ set_bit(DST_NODE_FROZEN, &st->node->flags);
+ else
+ clear_bit(DST_NODE_FROZEN, &st->node->flags);
+ return 0;
+}
+
+static struct dst_alg_ops alg_linear_ops = {
+ .remap = dst_linear_remap,
+ .add_node = dst_linear_add_node,
+ .del_node = dst_linear_del_node,
+ .error = dst_linear_error,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit alg_linear_init(void)
+{
+ alg_linear = dst_alloc_alg("alg_linear", &alg_linear_ops);
+ if (!alg_linear)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __devexit alg_linear_exit(void)
+{
+ dst_remove_alg(alg_linear);
+}
+
+module_init(alg_linear_init);
+module_exit(alg_linear_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Linear distributed algorithm.");
diff --git a/drivers/block/dst/alg_mirror.c b/drivers/block/dst/alg_mirror.c
new file mode 100644
index 0000000..1b55f4d
--- /dev/null
+++ b/drivers/block/dst/alg_mirror.c
@@ -0,0 +1,1113 @@
+/*
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/dst.h>
+
+struct dst_mirror_node_data
+{
+ u64 age;
+};
+
+struct dst_mirror_priv
+{
+ unsigned int chunk_num;
+
+ u64 last_start;
+
+ spinlock_t backlog_lock;
+ struct list_head backlog_list;
+
+ struct dst_mirror_node_data old_data, new_data;
+
+ unsigned long *chunk;
+};
+
+static struct dst_alg *alg_mirror;
+static struct bio_set *dst_mirror_bio_set;
+
+static int dst_mirror_resync(struct dst_node *n, int ndp);
+
+static void dst_mirror_mark_sync(struct dst_node *n)
+{
+ if (test_bit(DST_NODE_NOTSYNC, &n->flags)) {
+ struct dst_mirror_priv *priv = n->priv;
+
+ clear_bit(DST_NODE_NOTSYNC, &n->flags);
+ dprintk("%s: node: %p, %llu:%llu synchronization "
+ "has been completed.\n",
+ __func__, n, n->start, n->size);
+ priv->old_data.age = 0;
+ }
+}
+
+static void dst_mirror_mark_notsync(struct dst_node *n)
+{
+ if (!test_bit(DST_NODE_NOTSYNC, &n->flags)) {
+ set_bit(DST_NODE_NOTSYNC, &n->flags);
+ dprintk("%s: not synced node n: %p.\n", __func__, n);
+ }
+}
+
+static void dst_mirror_mark_node_notsync(struct dst_node *n)
+{
+ struct dst_mirror_priv *p = n->priv;
+
+ memset(p->chunk, 0xff, DIV_ROUND_UP(p->chunk_num, BITS_PER_LONG)*sizeof(long));
+ dst_mirror_mark_notsync(n);
+ dst_mirror_resync(n, 0);
+}
+
+static void dst_mirror_mark_node_sync(struct dst_node *n)
+{
+ struct dst_mirror_priv *p = n->priv;
+
+ memset(p->chunk, 0x0, DIV_ROUND_UP(p->chunk_num, BITS_PER_LONG)*sizeof(long));
+ dst_mirror_mark_sync(n);
+}
+
+static ssize_t dst_mirror_mark_dirty(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dst_node *n = container_of(dev, struct dst_node, device);
+
+ dst_mirror_mark_node_notsync(n);
+ return count;
+}
+
+static ssize_t dst_mirror_mark_clean(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dst_node *n = container_of(dev, struct dst_node, device);
+
+ dst_mirror_mark_node_sync(n);
+ return count;
+}
+
+static ssize_t dst_mirror_chunk_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dst_node *n = container_of(dev, struct dst_node, device);
+ struct dst_mirror_priv *priv = n->priv;
+ unsigned int i;
+ int rest = PAGE_SIZE, rest_bits = priv->chunk_num;
+
+ for (i = 0; i < DIV_ROUND_UP(priv->chunk_num, BITS_PER_LONG); ++i) {
+ int bit, j;
+
+ for (j = 0; j < min(BITS_PER_LONG, rest_bits); ++j) {
+ bit = (priv->chunk[i] >> j) & 1;
+ sprintf(buf, "%c", (bit)?'+':'-');
+ buf++;
+ }
+
+ rest_bits -= j;
+ rest -= j;
+
+ if (rest < BITS_PER_LONG || rest_bits <= 0)
+ break;
+ }
+
+ return PAGE_SIZE - rest;
+}
+
+static struct device_attribute dst_mirror_attrs[] = {
+ __ATTR(chunks, S_IRUGO, dst_mirror_chunk_mask_show, NULL),
+ __ATTR(dirty, S_IWUSR, NULL, dst_mirror_mark_dirty),
+ __ATTR(clean, S_IWUSR, NULL, dst_mirror_mark_clean),
+};
+
+/*
+ * This callback is invoked when node is removed from storage.
+ */
+static void dst_mirror_del_node(struct dst_node *n)
+{
+ struct dst_mirror_priv *priv = n->priv;
+ struct dst_request *req, *tmp;
+
+ list_for_each_entry_safe(req, tmp, &priv->backlog_list, request_list_entry) {
+ kst_del_req(req);
+ kst_complete_req(req, -ENODEV);
+ }
+
+ if (priv) {
+ vfree(priv->chunk);
+ kfree(priv);
+ n->priv = NULL;
+ }
+
+ if (n->device.parent == &n->st->device) {
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(dst_mirror_attrs); ++i)
+ device_remove_file(&n->device, &dst_mirror_attrs[i]);
+ }
+}
+
+static void dst_mirror_handle_priv(struct dst_node *n)
+{
+ if (n->priv) {
+ int err, i;
+
+ for (i=0; i<ARRAY_SIZE(dst_mirror_attrs); ++i)
+ err = device_create_file(&n->device,
+ &dst_mirror_attrs[i]);
+ }
+}
+
+static void dst_mirror_destructor(struct bio *bio)
+{
+ dprintk("%s: bio: %p.\n", __func__, bio);
+ bio_free(bio, dst_mirror_bio_set);
+}
+
+/*
+ * This function copies node's private on-disk data from first node
+ * to the new one.
+ */
+static int dst_mirror_get_node_data(struct dst_node *n,
+ struct dst_mirror_node_data *ndata, int old)
+{
+ struct dst_node *first;
+ struct dst_mirror_priv *p;
+
+ mutex_lock(&n->st->tree_lock);
+ first = dst_storage_tree_search(n->st, n->start);
+ mutex_unlock(&n->st->tree_lock);
+ if (!first) {
+ dprintk("%s: there are no nodes in the storage.\n", __func__);
+ return -ENODEV;
+ }
+
+ p = first->priv;
+ memcpy(ndata, (old)?&p->old_data:&p->new_data, sizeof(struct dst_mirror_node_data));
+
+ dst_node_put(first);
+ return 0;
+}
+
+struct dst_mirror_ndp
+{
+ int err;
+ struct page *page;
+ struct completion complete;
+};
+
+static void dst_mirror_ndp_bio_endio(struct dst_request *req, int err)
+{
+ struct dst_mirror_ndp *cmp = req->bio->bi_private;
+
+ cmp->err = err;
+ dprintk("%s: completing request: bio: %p, cmp: %p.\n",
+ __func__, req->bio, cmp);
+ complete(&cmp->complete);
+}
+
+static int dst_mirror_ndp_end_io(struct bio *bio, unsigned int size, int err)
+{
+ struct dst_mirror_ndp *cmp = bio->bi_private;
+
+ if (bio->bi_size)
+ return 0;
+
+ dprintk("%s: completing request: bio: %p, cmp: %p.\n", __func__, bio, cmp);
+ complete(&cmp->complete);
+ return 0;
+}
+
+/*
+ * This function reads or writes node's private data from underlying media.
+ */
+static int dst_mirror_process_node_data(struct dst_node *n,
+ struct dst_mirror_node_data *ndata, int op)
+{
+ struct bio *bio;
+ int err = -ENOMEM;
+ struct dst_mirror_ndp *cmp;
+ void *addr;
+
+ cmp = kzalloc(sizeof(struct dst_mirror_ndp), GFP_KERNEL);
+ if (!cmp)
+ goto err_out_exit;
+
+ cmp->page = alloc_page(GFP_NOIO);
+ if (!cmp->page)
+ goto err_out_free_cmp;
+
+ addr = kmap(cmp->page);
+
+ init_completion(&cmp->complete);
+
+ if (op == WRITE)
+ memcpy(addr, ndata, sizeof(struct dst_mirror_node_data));
+
+ bio = bio_alloc_bioset(GFP_NOIO, 1, dst_mirror_bio_set);
+ if (!bio)
+ goto err_out_free_page;
+
+ bio->bi_rw = op;
+ bio->bi_private = cmp;
+ bio->bi_sector = n->size;
+ bio->bi_bdev = n->bdev;
+ bio->bi_destructor = dst_mirror_destructor;
+ bio->bi_end_io = dst_mirror_ndp_end_io;
+
+ err = bio_add_pc_page(n->st->queue, bio, cmp->page, 512, 0);
+ if (err <= 0)
+ goto err_out_free_bio;
+
+ if (n->bdev) {
+ generic_make_request(bio);
+ } else {
+ struct dst_request req;
+
+ memset(&req, 0, sizeof(struct dst_request));
+
+ req.node = n;
+ req.state = n->state;
+ req.start = bio->bi_sector;
+ req.size = req.orig_size = bio->bi_size;
+ req.bio = bio;
+ req.idx = bio->bi_idx;
+ req.num = bio->bi_vcnt;
+ req.flags = 0;
+ req.offset = 0;
+ req.bio_endio = &dst_mirror_ndp_bio_endio;
+ req.callback = &kst_data_callback;
+
+ err = req.state->ops->push(&req);
+ if (err)
+ req.bio_endio(&req, err);
+ }
+
+ dprintk("%s: waiting for completion: bio: %p, cmp: %p, err: %d.\n",
+ __func__, bio, cmp);
+
+ wait_for_completion(&cmp->complete);
+
+ err = cmp->err;
+
+ if (!err && (op != WRITE))
+ memcpy(ndata, addr, sizeof(struct dst_mirror_node_data));
+
+ kunmap(cmp->page);
+
+ dprintk("%s: freeing bio: %p, err: %d.\n", __func__, bio, err);
+
+err_out_free_bio:
+ bio_put(bio);
+err_out_free_page:
+ __free_page(cmp->page);
+err_out_free_cmp:
+ kfree(cmp);
+err_out_exit:
+ return err;
+}
+
+/*
+ * This function reads node's private data from underlying media.
+ */
+static int dst_mirror_read_node_data(struct dst_node *n,
+ struct dst_mirror_node_data *ndata)
+{
+ return dst_mirror_process_node_data(n, ndata, READ);
+}
+
+/*
+ * This function writes node's private data from underlying media.
+ */
+static int dst_mirror_write_node_data(struct dst_node *n,
+ struct dst_mirror_node_data *ndata)
+{
+ dprintk("%s: writing new age: %llx, node: %p %llu-%llu.\n",
+ __func__, ndata->age, n, n->start, n->size);
+ return dst_mirror_process_node_data(n, ndata, WRITE);
+}
+
+static int dst_mirror_ndp_setup(struct dst_node *n, int first_node, int clean_on_sync)
+{
+ struct dst_mirror_priv *p = n->priv;
+ int sync = 1, err;
+
+ err = dst_mirror_read_node_data(n, &p->old_data);
+ if (err)
+ return err;
+
+ if (first_node) {
+ p->new_data.age = *(u64 *)&n->st;
+
+ dprintk("%s: first age: %llx -> %llx.\n",
+ __func__, p->old_data.age, p->new_data.age);
+
+ err = dst_mirror_write_node_data(n, &p->new_data);
+ if (err)
+ return err;
+ } else {
+ err = dst_mirror_get_node_data(n, &p->new_data, 1);
+ if (err)
+ return err;
+
+ if (p->new_data.age != p->old_data.age) {
+ sync = 0;
+ dprintk("%s: node %llu:%llu is not synced with the first "
+ "node (old != new): %llx != %llx.\n",
+ __func__, n->start, n->start+n->size,
+ p->old_data.age, p->new_data.age);
+ err = dst_mirror_get_node_data(n, &p->new_data, 0);
+ if (err)
+ return err;
+ } else {
+ err = dst_mirror_get_node_data(n, &p->new_data, 0);
+ if (err)
+ return err;
+
+ err = dst_mirror_write_node_data(n, &p->new_data);
+ if (err)
+ return err;
+
+ dprintk("%s: node %llu:%llu is in sync with the first node.\n",
+ __func__, n->start, n->start+n->size);
+ }
+ }
+
+ if (!sync)
+ dst_mirror_mark_node_notsync(n);
+ else if (clean_on_sync)
+ dst_mirror_mark_node_sync(n);
+
+ dprintk("%s: age: old: %llx, new: %llx.\n", __func__, p->old_data.age, p->new_data.age);
+
+ return 0;
+}
+
+/*
+ * This callback is invoked when node is added to storage.
+ */
+static int dst_mirror_add_node(struct dst_node *n)
+{
+ struct dst_storage *st = n->st;
+ struct dst_mirror_priv *priv;
+ int err = -ENOMEM, first_node = 0;
+ u64 disk_size;
+
+ n->size--; /* A sector size actually. */
+
+ mutex_lock(&st->tree_lock);
+ disk_size = st->disk_size;
+ if (st->disk_size) {
+ st->disk_size = min(n->size, st->disk_size);
+ } else {
+ st->disk_size = n->size;
+ first_node = 1;
+ }
+ mutex_unlock(&st->tree_lock);
+
+ priv = kzalloc(sizeof(struct dst_mirror_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->chunk_num = st->disk_size;
+
+ priv->chunk = vmalloc(DIV_ROUND_UP(priv->chunk_num, BITS_PER_LONG) * sizeof(long));
+ if (!priv->chunk)
+ goto err_out_free;
+
+ spin_lock_init(&priv->backlog_lock);
+ INIT_LIST_HEAD(&priv->backlog_list);
+
+ dprintk("%s: %llu:%llu, chunk_num: %u, disk_size: %llu.\n\n",
+ __func__, n->start, n->size,
+ priv->chunk_num, st->disk_size);
+
+ n->priv_callback = &dst_mirror_handle_priv;
+ n->priv = priv;
+
+ err = dst_mirror_ndp_setup(n, first_node, 1);
+ if (err)
+ goto err_out_free_chunk;
+
+ return 0;
+
+err_out_free_chunk:
+ vfree(priv->chunk);
+err_out_free:
+ kfree(priv);
+ n->priv = NULL;
+
+ mutex_lock(&st->tree_lock);
+ st->disk_size = disk_size;
+ mutex_unlock(&st->tree_lock);
+ return err;
+}
+
+static void dst_mirror_sync_destructor(struct bio *bio)
+{
+ struct bio_vec *bv;
+ int i;
+
+ bio_for_each_segment(bv, bio, i)
+ __free_page(bv->bv_page);
+ bio_free(bio, dst_mirror_bio_set);
+}
+
+/*
+ * Without errors it is always called under node's request lock,
+ * so it is safe to requeue them.
+ */
+static void dst_mirror_bio_error(struct dst_request *req, int err)
+{
+ int i;
+ struct dst_mirror_priv *priv = req->node->priv;
+ unsigned int num, idx;
+ u64 start = req->start - to_sector(req->orig_size - req->size);
+
+ if (err)
+ dst_mirror_mark_notsync(req->node);
+
+ priv->last_start = req->start;
+
+ idx = start;
+ num = to_sector(req->orig_size);
+
+ dprintk("%s: %llu:%llu start: %llu, size: %llu, "
+ "chunk_num: %u, idx: %d, num: %d, err: %d, node: %p.\n",
+ __func__, req->node->start, req->node->size,
+ start, req->orig_size, priv->chunk_num,
+ idx, num, err, req->node);
+
+ if (unlikely(idx >= priv->chunk_num || idx + num > priv->chunk_num)) {
+ dprintk("%s: %llu:%llu req: %p, start: %llu, orig_size: %llu, "
+ "req_start: %llu, req_size: %llu, "
+ "chunk_num: %u, idx: %d, num: %d, err: %d.\n",
+ __func__, req->node->start, req->node->size, req,
+ start, req->orig_size,
+ req->start, req->size,
+ priv->chunk_num, idx, num, err);
+ return;
+ }
+
+ if (err) {
+ for (i=0; i<num; ++i)
+ __set_bit(idx+i, priv->chunk);
+ } else {
+ for (i=0; i<num; ++i)
+ __clear_bit(idx+i, priv->chunk);
+ }
+}
+
+static void dst_mirror_sync_req_endio(struct dst_request *req, int err)
+{
+ struct dst_node *n = req->node;
+ struct dst_mirror_priv *p = req->node->priv;
+ int i;
+ unsigned long notsync = 0;
+
+ dst_mirror_bio_error(req, err);
+
+ dprintk("%s: freeing bio: %p, bi_size: %u, "
+ "orig_size: %llu, req: %p, node: %p.\n",
+ __func__, req->bio, req->bio->bi_size, req->orig_size, req,
+ req->node);
+
+ bio_put(req->bio);
+
+ if (!test_bit(DST_NODE_NOTSYNC, &n->flags))
+ return;
+
+ for (i = 0; i < DIV_ROUND_UP(p->chunk_num, BITS_PER_LONG); ++i) {
+ notsync = p->chunk[i];
+ if (notsync)
+ break;
+ }
+
+ if (notsync) {
+ dprintk("%s: %d/%d sync: %lx, ffs: %lu, chunk_num (modulo %u): %d.\n",
+ __func__, i, DIV_ROUND_UP(p->chunk_num, BITS_PER_LONG),
+ notsync, __ffs(notsync), BITS_PER_LONG,
+ p->chunk_num%BITS_PER_LONG);
+ if (i != DIV_ROUND_UP(p->chunk_num, BITS_PER_LONG) - 1)
+ return;
+
+ if (__ffs(notsync) != (p->chunk_num%BITS_PER_LONG))
+ return;
+ }
+
+ dst_mirror_mark_sync(n);
+}
+
+static int dst_mirror_sync_endio(struct bio *bio, unsigned int size, int err)
+{
+ struct dst_request *req = bio->bi_private;
+ struct dst_node *n = req->node;
+ struct dst_mirror_priv *priv = n->priv;
+ unsigned long flags;
+
+ dprintk("%s: bio: %p, err: %d, size: %u, req: %p.\n",
+ __func__, bio, err, bio->bi_size, req);
+
+ if (bio->bi_size)
+ return 1;
+
+ bio->bi_rw = WRITE;
+ bio->bi_size = req->orig_size;
+ bio->bi_sector = req->start;
+
+ if (!err) {
+ spin_lock_irqsave(&priv->backlog_lock, flags);
+ list_add_tail(&req->request_list_entry, &priv->backlog_list);
+ spin_unlock_irqrestore(&priv->backlog_lock, flags);
+ kst_wake(req->state);
+ } else {
+ req->bio_endio(req, err);
+ dst_free_request(req);
+ }
+ return 0;
+}
+
+static int dst_mirror_sync_block(struct dst_node *n,
+ int bit_start, int bit_num)
+{
+ u64 start = to_bytes(bit_start);
+ u32 size = to_bytes(bit_num);
+ struct bio *bio;
+ unsigned int nr_pages = DIV_ROUND_UP(size, PAGE_SIZE), i;
+ struct page *page;
+ int err = -ENOMEM;
+ struct dst_request *req;
+
+ dprintk("%s: bit_start: %d, bit_num: %d, start: %llu, nr_pages: %u, "
+ "disk_size: %llu.\n",
+ __func__, bit_start, bit_num, start, nr_pages,
+ n->st->disk_size);
+
+ while (nr_pages) {
+ req = dst_clone_request(NULL, n->w->req_pool);
+ if (!req)
+ return -ENOMEM;
+
+ bio = bio_alloc_bioset(GFP_NOIO, nr_pages, dst_mirror_bio_set);
+ if (!bio)
+ goto err_out_free_req;
+
+ bio->bi_rw = READ;
+ bio->bi_private = req;
+ bio->bi_sector = to_sector(start);
+ bio->bi_bdev = NULL;
+ bio->bi_destructor = dst_mirror_sync_destructor;
+ bio->bi_end_io = dst_mirror_sync_endio;
+
+ for (i = 0; i < nr_pages; ++i) {
+ err = -ENOMEM;
+
+ page = alloc_page(GFP_NOIO);
+ if (!page)
+ break;
+
+ err = bio_add_pc_page(n->st->queue, bio, page,
+ min_t(u32, PAGE_SIZE, size), 0);
+ if (err <= 0)
+ break;
+ size -= err;
+ err = 0;
+ }
+
+ if (err && !bio->bi_vcnt)
+ goto err_out_put_bio;
+
+ req->node = n;
+ req->state = n->state;
+ req->start = bio->bi_sector;
+ req->size = req->orig_size = bio->bi_size;
+ req->bio = bio;
+ req->idx = bio->bi_idx;
+ req->num = bio->bi_vcnt;
+ req->flags = DST_REQ_CHECK_QUEUE;
+ req->offset = 0;
+ req->bio_endio = &dst_mirror_sync_req_endio;
+ req->callback = &kst_data_callback;
+
+ dprintk("%s: start: %llu, size: %llu/%u, bio: %p, req: %p, "
+ "node: %p.\n",
+ __func__, req->start, req->size, nr_pages, bio,
+ req, req->node);
+
+ err = n->st->queue->make_request_fn(n->st->queue, bio);
+ if (err)
+ goto err_out_put_bio;
+
+ nr_pages -= bio->bi_vcnt;
+ start += bio->bi_size;
+ }
+
+ return 0;
+
+err_out_put_bio:
+ bio_put(bio);
+err_out_free_req:
+ dst_free_request(req);
+ return err;
+}
+
+/*
+ * Resync logic.
+ *
+ * System allocates and queues requests for number of regions.
+ * Each request initially is reading from the one of the nodes.
+ * When it is completed, system checks if given region was already
+ * written to, and in such case just drops read request, otherwise
+ * it writes it to the node being updated. Any write clears not-uptodate
+ * bit, which is used as a flag that region must be synchronized or not.
+ * Reading is never performed from the node under resync.
+ */
+static int dst_mirror_resync(struct dst_node *n, int ndp)
+{
+ struct dst_mirror_priv *priv = n->priv;
+ int err = 0, sync = 1, total = priv->chunk_num;
+ unsigned int i;
+
+ dprintk("%s: node: %p, %llu:%llu synchronization has been started.\n",
+ __func__, n, n->start, n->size);
+
+ if (ndp) {
+ err = dst_mirror_ndp_setup(n, 0, 0);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(priv->chunk_num, BITS_PER_LONG); ++i) {
+ int bit, num, start;
+ unsigned long word = priv->chunk[i];
+
+ if (!word)
+ continue;
+
+ num = 0;
+ start = -1;
+ while (word && num < BITS_PER_LONG) {
+ bit = __ffs(word);
+ if (start == -1)
+ start = bit;
+ num++;
+ word >>= (bit+1);
+
+ if (--total == 0)
+ break;
+ }
+
+ if (start != -1) {
+ err = dst_mirror_sync_block(n, start + i*BITS_PER_LONG,
+ num);
+ if (err)
+ break;
+ sync = 0;
+ }
+
+ if (total == 0)
+ break;
+ }
+
+ return err;
+}
+
+static int dst_mirror_end_io(struct bio *bio, unsigned int size, int err)
+{
+ struct dst_request *req = bio->bi_private;
+
+ if (bio->bi_size)
+ return 0;
+
+ dprintk("%s: req: %p, bio: %p, req->bio: %p, err: %d.\n",
+ __func__, req, bio, req->bio, err);
+ req->bio_endio(req, err);
+ bio_put(bio);
+ return 0;
+}
+
+static void dst_mirror_read_endio(struct dst_request *req, int err)
+{
+ dst_mirror_bio_error(req, err);
+
+ if (!err)
+ kst_bio_endio(req, 0);
+}
+
+static void dst_mirror_write_endio(struct dst_request *req, int err)
+{
+ dst_mirror_bio_error(req, err);
+
+ req = req->priv;
+
+ dprintk("%s: req: %p, priv: %p err: %d, bio: %p, "
+ "cnt: %d, orig_size: %llu.\n",
+ __func__, req, req->priv, err, req->bio,
+ atomic_read(&req->refcnt), req->orig_size);
+
+ if (atomic_dec_and_test(&req->refcnt)) {
+ bio_endio(req->bio, req->orig_size, 0);
+ dst_free_request(req);
+ }
+}
+
+static int dst_mirror_process_request_nosync(struct dst_request *req,
+ struct dst_node *n)
+{
+ int err = 0;
+
+ /*
+ * Block layer requires to clone a bio.
+ */
+ if (n->bdev) {
+ struct bio *clone = bio_alloc_bioset(GFP_NOIO,
+ req->bio->bi_max_vecs, dst_mirror_bio_set);
+
+ __bio_clone(clone, req->bio);
+
+ clone->bi_bdev = n->bdev;
+ clone->bi_destructor = dst_mirror_destructor;
+ clone->bi_private = req;
+ clone->bi_end_io = &dst_mirror_end_io;
+
+ dprintk("%s: clone: %p, bio: %p, req: %p.\n",
+ __func__, clone, req->bio, req);
+
+ generic_make_request(clone);
+ } else {
+ struct dst_request nr;
+ /*
+ * Network state processing engine will clone request
+ * by itself if needed. We can not use the same structure
+ * here, since number of its fields will be modified.
+ */
+ memcpy(&nr, req, sizeof(struct dst_request));
+
+ nr.node = n;
+ nr.state = n->state;
+ nr.priv = req;
+
+ err = kst_check_permissions(n->state, req->bio);
+ if (!err)
+ err = n->state->ops->push(&nr);
+ }
+
+ dprintk("%s: req: %p, n: %p, bdev: %p, err: %d.\n",
+ __func__, req, n, n->bdev, err);
+
+ return err;
+}
+
+static void dst_mirror_sync_requeue(struct dst_node *n)
+{
+ struct dst_mirror_priv *p = n->priv;
+ struct dst_request *req;
+ unsigned int num, idx, i;
+ u64 start;
+ unsigned long flags;
+ int err;
+
+ while (!list_empty(&p->backlog_list)) {
+ req = NULL;
+ spin_lock_irqsave(&p->backlog_lock, flags);
+ if (!list_empty(&p->backlog_list)) {
+ req = list_entry(p->backlog_list.next,
+ struct dst_request,
+ request_list_entry);
+ list_del_init(&req->request_list_entry);
+ }
+ spin_unlock_irqrestore(&p->backlog_lock, flags);
+
+ if (!req)
+ break;
+
+ start = req->start - to_sector(req->orig_size - req->size);
+
+ idx = start;
+ num = to_sector(req->orig_size);
+
+ for (i=0; i<num; ++i)
+ if (test_bit(idx+i, p->chunk))
+ break;
+
+ dprintk("%s: idx: %u, num: %u, i: %u, req: %p, "
+ "start: %llu, size: %llu.\n",
+ __func__, idx, num, i, req,
+ req->start, req->orig_size);
+
+ err = -1;
+ if (i != num) {
+ err = dst_mirror_process_request_nosync(req, n);
+ if (!err)
+ dst_free_request(req);
+ }
+
+ if (err)
+ kst_complete_req(req, err);
+ }
+
+ if (p->old_data.age == 0)
+ dst_mirror_write_node_data(n, &p->new_data);
+}
+
+static int dst_mirror_process_request(struct dst_request *req,
+ struct dst_node *n)
+{
+ dst_mirror_sync_requeue(n);
+ return dst_mirror_process_request_nosync(req, n);
+}
+
+static int dst_mirror_write(struct dst_request *oreq)
+{
+ struct dst_node *n, *node = oreq->node;
+ struct dst_request *req;
+ int num, err = 0, err_num = 0, orig_num;
+
+ req = dst_clone_request(oreq, oreq->node->w->req_pool);
+ if (!req) {
+ err = -ENOMEM;
+ goto err_out_exit;
+ }
+
+ req->priv = req;
+
+ /*
+ * This logic is pretty simple - req->bio_endio will not
+ * call bio_endio() until all mirror devices completed
+ * processing of the request (no matter with or without error).
+ * Mirror's req->bio_endio callback will take care of that.
+ */
+ orig_num = num = atomic_read(&req->node->shared_num) + 1;
+ atomic_set(&req->refcnt, num);
+
+ req->bio_endio = &dst_mirror_write_endio;
+
+ dprintk("\n%s: req: %p, mirror to %d nodes.\n",
+ __func__, req, num);
+
+ err = dst_mirror_process_request(req, node);
+ if (err)
+ err_num++;
+
+ if (--num) {
+ list_for_each_entry(n, &node->shared, shared) {
+ dprintk("\n%s: req: %p, start: %llu, size: %llu, "
+ "num: %d, n: %p, state: %p.\n",
+ __func__, req, req->start,
+ req->size, num, n, n->state);
+
+ err = dst_mirror_process_request(req, n);
+ if (err)
+ err_num++;
+
+ if (--num <= 0)
+ break;
+ }
+ }
+
+ if (err_num == orig_num) {
+ dprintk("%s: req: %p, num: %d, err: %d.\n",
+ __func__, req, num, err);
+ err = -ENODEV;
+ goto err_out_exit;
+ }
+
+ return 0;
+
+err_out_exit:
+ return err;
+}
+
+static int dst_mirror_read(struct dst_request *req)
+{
+ struct dst_node *node = req->node, *n, *min_dist_node;
+ struct dst_mirror_priv *priv = node->priv;
+ u64 dist, d;
+ int err;
+
+ req->bio_endio = &dst_mirror_read_endio;
+
+ do {
+ err = -ENODEV;
+ min_dist_node = NULL;
+ dist = -1ULL;
+
+ /*
+ * Reading is never performed from the node under resync.
+ * If this will cause any troubles (like all nodes must be
+ * resynced between each other), this check can be removed
+ * and per-chunk dirty bit can be tested instead.
+ */
+
+ if (!test_bit(DST_NODE_NOTSYNC, &node->flags)) {
+ priv = node->priv;
+ if (req->start > priv->last_start)
+ dist = req->start - priv->last_start;
+ else
+ dist = priv->last_start - req->start;
+ min_dist_node = req->node;
+ }
+
+ list_for_each_entry(n, &node->shared, shared) {
+ if (test_bit(DST_NODE_NOTSYNC, &n->flags))
+ continue;
+
+ priv = n->priv;
+
+ if (req->start > priv->last_start)
+ d = req->start - priv->last_start;
+ else
+ d = priv->last_start - req->start;
+
+ if (d < dist)
+ min_dist_node = n;
+ }
+
+ if (!min_dist_node)
+ break;
+
+ req->node = min_dist_node;
+ req->state = req->node->state;
+
+ if (req->node->bdev) {
+ req->bio->bi_bdev = req->node->bdev;
+ generic_make_request(req->bio);
+ err = 0;
+ break;
+ }
+
+ err = req->state->ops->push(req);
+ if (err) {
+ dprintk("%s: req: %p, bio: %p, node: %p, err: %d.\n",
+ __func__, req, req->bio, min_dist_node, err);
+ dst_mirror_mark_notsync(req->node);
+ }
+ } while (err && min_dist_node);
+
+ if (err || !min_dist_node) {
+ dprintk("%s: req: %p, bio: %p, node: %p, err: %d.\n",
+ __func__, req, req->bio, min_dist_node, err);
+ if (!err)
+ err = -ENODEV;
+ }
+ dprintk("%s: req: %p, err: %d.\n", __func__, req, err);
+ return err;
+}
+
+/*
+ * This callback is invoked from block layer request processing function,
+ * its task is to remap block request to different nodes.
+ */
+static int dst_mirror_remap(struct dst_request *req)
+{
+ int (*remap[])(struct dst_request *) =
+ {&dst_mirror_read, &dst_mirror_write};
+
+ return remap[bio_rw(req->bio) == WRITE](req);
+}
+
+static int dst_mirror_error(struct kst_state *st, int err)
+{
+ struct dst_request *req, *tmp;
+ unsigned int revents = st->socket->ops->poll(NULL, st->socket, NULL);
+
+ dprintk("%s: err: %d, revents: %x, notsync: %d.\n",
+ __func__, err, revents,
+ test_bit(DST_NODE_NOTSYNC, &st->node->flags));
+
+ if (err == -EEXIST)
+ return err;
+
+ if (!(revents & (POLLERR | POLLHUP)) &&
+ (err == -EPIPE || err == -ECONNRESET)) {
+ if (test_bit(DST_NODE_NOTSYNC, &st->node->flags)) {
+ return dst_mirror_resync(st->node, 1);
+ }
+ return 0;
+ }
+
+ if (atomic_read(&st->node->shared_num) == 0 &&
+ !st->node->shared_head) {
+ dprintk("%s: this node is the only one in the mirror, "
+ "can not mark it notsync.\n", __func__);
+ return err;
+ }
+
+ dst_mirror_mark_notsync(st->node);
+
+ mutex_lock(&st->request_lock);
+ list_for_each_entry_safe(req, tmp, &st->request_list,
+ request_list_entry) {
+ kst_del_req(req);
+ dprintk("%s: requeue [%c], start: %llu, idx: %d,"
+ " num: %d, size: %llu, offset: %u, err: %d.\n",
+ __func__, (bio_rw(req->bio) == WRITE)?'W':'R',
+ req->start, req->idx, req->num, req->size,
+ req->offset, err);
+
+ if (bio_rw(req->bio) != WRITE) {
+ req->start -= to_sector(req->orig_size - req->size);
+ req->size = req->orig_size;
+ req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
+ req->idx = 0;
+ if (dst_mirror_read(req))
+ dst_free_request(req);
+ } else {
+ kst_complete_req(req, err);
+ }
+ }
+ mutex_unlock(&st->request_lock);
+ return err;
+}
+
+static struct dst_alg_ops alg_mirror_ops = {
+ .remap = dst_mirror_remap,
+ .add_node = dst_mirror_add_node,
+ .del_node = dst_mirror_del_node,
+ .error = dst_mirror_error,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit alg_mirror_init(void)
+{
+ int err = -ENOMEM;
+
+ dst_mirror_bio_set = bioset_create(256, 256);
+ if (!dst_mirror_bio_set)
+ return -ENOMEM;
+
+ alg_mirror = dst_alloc_alg("alg_mirror", &alg_mirror_ops);
+ if (!alg_mirror)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ bioset_free(dst_mirror_bio_set);
+ return err;
+}
+
+static void __devexit alg_mirror_exit(void)
+{
+ dst_remove_alg(alg_mirror);
+ bioset_free(dst_mirror_bio_set);
+}
+
+module_init(alg_mirror_init);
+module_exit(alg_mirror_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Mirror distributed algorithm.");
--
Evgeniy Polyakov
^ permalink raw reply related
* [2/4] Distributed storage. Network processing.
From: Evgeniy Polyakov @ 2007-11-05 18:42 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, linux-fsdevel
This file contains all bits needed for async non-blocking network
processing of the block requests directed to DST.
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
diff --git a/drivers/block/dst/kst.c b/drivers/block/dst/kst.c
new file mode 100644
index 0000000..ba5e5ef
--- /dev/null
+++ b/drivers/block/dst/kst.c
@@ -0,0 +1,1475 @@
+/*
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/kthread.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/poll.h>
+#include <linux/bio.h>
+#include <linux/dst.h>
+
+#include <net/sock.h>
+
+struct kst_poll_helper
+{
+ poll_table pt;
+ struct kst_state *st;
+};
+
+static LIST_HEAD(kst_worker_list);
+static DEFINE_MUTEX(kst_worker_mutex);
+
+/*
+ * This function creates bound socket for local export node.
+ */
+static int kst_sock_create(struct kst_state *st, struct saddr *addr,
+ int type, int proto, int backlog)
+{
+ int err;
+
+ err = sock_create(addr->sa_family, type, proto, &st->socket);
+ if (err)
+ goto err_out_exit;
+
+ err = st->socket->ops->bind(st->socket, (struct sockaddr *)addr,
+ addr->sa_data_len);
+
+ err = st->socket->ops->listen(st->socket, backlog);
+ if (err)
+ goto err_out_release;
+
+ st->socket->sk->sk_allocation = GFP_NOIO;
+
+ return 0;
+
+err_out_release:
+ sock_release(st->socket);
+err_out_exit:
+ return err;
+}
+
+static void kst_sock_release(struct kst_state *st)
+{
+ if (st->socket) {
+ sock_release(st->socket);
+ st->socket = NULL;
+ }
+}
+
+void kst_wake(struct kst_state *st)
+{
+ if (st) {
+ struct kst_worker *w = st->node->w;
+ unsigned long flags;
+
+ spin_lock_irqsave(&w->ready_lock, flags);
+ if (list_empty(&st->ready_entry))
+ list_add_tail(&st->ready_entry, &w->ready_list);
+ spin_unlock_irqrestore(&w->ready_lock, flags);
+
+ wake_up(&w->wait);
+ }
+}
+EXPORT_SYMBOL_GPL(kst_wake);
+
+/*
+ * Polling machinery.
+ */
+static int kst_state_wake_callback(wait_queue_t *wait, unsigned mode,
+ int sync, void *key)
+{
+ struct kst_state *st = container_of(wait, struct kst_state, wait);
+ kst_wake(st);
+ return 1;
+}
+
+static void kst_queue_func(struct file *file, wait_queue_head_t *whead,
+ poll_table *pt)
+{
+ struct kst_state *st = container_of(pt, struct kst_poll_helper, pt)->st;
+
+ st->whead = whead;
+ init_waitqueue_func_entry(&st->wait, kst_state_wake_callback);
+ add_wait_queue(whead, &st->wait);
+}
+
+static void kst_poll_exit(struct kst_state *st)
+{
+ if (st->whead) {
+ remove_wait_queue(st->whead, &st->wait);
+ st->whead = NULL;
+ }
+}
+
+/*
+ * This function removes request from state tree and ordering list.
+ */
+void kst_del_req(struct dst_request *req)
+{
+ list_del_init(&req->request_list_entry);
+}
+EXPORT_SYMBOL_GPL(kst_del_req);
+
+static struct dst_request *kst_req_first(struct kst_state *st)
+{
+ struct dst_request *req = NULL;
+
+ if (!list_empty(&st->request_list))
+ req = list_entry(st->request_list.next, struct dst_request,
+ request_list_entry);
+ return req;
+}
+
+/*
+ * This function dequeues first request from the queue and tree.
+ */
+static struct dst_request *kst_dequeue_req(struct kst_state *st)
+{
+ struct dst_request *req;
+
+ mutex_lock(&st->request_lock);
+ req = kst_req_first(st);
+ if (req)
+ kst_del_req(req);
+ mutex_unlock(&st->request_lock);
+ return req;
+}
+
+/*
+ * This function enqueues request into tree, indexed by start of the request,
+ * and also puts request into ordered queue.
+ */
+int kst_enqueue_req(struct kst_state *st, struct dst_request *req)
+{
+ if (unlikely(req->flags & DST_REQ_CHECK_QUEUE)) {
+ struct dst_request *r;
+
+ list_for_each_entry(r, &st->request_list, request_list_entry) {
+ if (bio_rw(r->bio) != bio_rw(req->bio))
+ continue;
+
+ if (r->start >= req->start + req->size)
+ continue;
+
+ if (r->start + r->size <= req->start)
+ continue;
+
+ return -EEXIST;
+ }
+ }
+
+ list_add_tail(&req->request_list_entry, &st->request_list);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kst_enqueue_req);
+
+/*
+ * BIOs for local exporting node are freed via this function.
+ */
+static void kst_export_put_bio(struct bio *bio)
+{
+ int i;
+ struct bio_vec *bv;
+
+ dprintk("%s: bio: %p, size: %u, idx: %d, num: %d, req: %p.\n",
+ __func__, bio, bio->bi_size, bio->bi_idx,
+ bio->bi_vcnt, bio->bi_private);
+
+ bio_for_each_segment(bv, bio, i)
+ __free_page(bv->bv_page);
+ bio_put(bio);
+}
+
+/*
+ * This is a generic request completion function for requests,
+ * queued for async processing.
+ * If it is local export node, state machine is different,
+ * see details below.
+ */
+void kst_complete_req(struct dst_request *req, int err)
+{
+ dprintk("%s: bio: %p, req: %p, size: %llu, orig_size: %llu, "
+ "bi_size: %u, err: %d, flags: %u.\n",
+ __func__, req->bio, req, req->size, req->orig_size,
+ req->bio->bi_size, err, req->flags);
+
+ if (req->flags & DST_REQ_EXPORT) {
+ if (err || !(req->flags & DST_REQ_EXPORT_WRITE)) {
+ req->bio_endio(req, err);
+ goto out;
+ }
+
+ req->bio->bi_rw = WRITE;
+ generic_make_request(req->bio);
+ } else {
+ req->bio_endio(req, err);
+ }
+out:
+ dst_free_request(req);
+}
+EXPORT_SYMBOL_GPL(kst_complete_req);
+
+static void kst_flush_requests(struct kst_state *st)
+{
+ struct dst_request *req;
+
+ while ((req = kst_dequeue_req(st)) != NULL)
+ kst_complete_req(req, -EIO);
+}
+
+static int kst_poll_init(struct kst_state *st)
+{
+ struct kst_poll_helper ph;
+
+ ph.st = st;
+ init_poll_funcptr(&ph.pt, &kst_queue_func);
+
+ st->socket->ops->poll(NULL, st->socket, &ph.pt);
+ return 0;
+}
+
+/*
+ * Main state creation function.
+ * It creates new state according to given operations
+ * and links it into worker structure and node.
+ */
+static struct kst_state *kst_state_init(struct dst_node *node,
+ unsigned int permissions,
+ struct kst_state_ops *ops, void *data)
+{
+ struct kst_state *st;
+ int err;
+
+ st = kzalloc(sizeof(struct kst_state), GFP_KERNEL);
+ if (!st)
+ return ERR_PTR(-ENOMEM);
+
+ st->permissions = permissions;
+ st->node = node;
+ st->ops = ops;
+ INIT_LIST_HEAD(&st->ready_entry);
+ INIT_LIST_HEAD(&st->entry);
+ INIT_LIST_HEAD(&st->request_list);
+ mutex_init(&st->request_lock);
+
+ err = st->ops->init(st, data);
+ if (err)
+ goto err_out_free;
+ mutex_lock(&node->w->state_mutex);
+ list_add_tail(&st->entry, &node->w->state_list);
+ mutex_unlock(&node->w->state_mutex);
+
+ kst_wake(st);
+
+ return st;
+
+err_out_free:
+ kfree(st);
+ return ERR_PTR(err);
+}
+
+/*
+ * This function is called when node is removed,
+ * or when state is destroyed for connected to local exporting
+ * node client.
+ */
+void kst_state_exit(struct kst_state *st)
+{
+ struct kst_worker *w = st->node->w;
+
+ mutex_lock(&w->state_mutex);
+ list_del_init(&st->entry);
+ mutex_unlock(&w->state_mutex);
+
+ st->ops->exit(st);
+
+ if (st == st->node->state)
+ st->node->state = NULL;
+
+ kfree(st);
+}
+
+static int kst_error(struct kst_state *st, int err)
+{
+ if ((err == -ECONNRESET || err == -EPIPE) && st->ops->recovery)
+ err = st->ops->recovery(st, err);
+
+ return st->node->st->alg->ops->error(st, err);
+}
+
+/*
+ * This is main state processing function.
+ * It tries to complete request and invoke appropriate
+ * callbacks in case of errors or successfull operation finish.
+ */
+static int kst_thread_process_state(struct kst_state *st)
+{
+ int err, empty;
+ unsigned int revents;
+ struct dst_request *req, *tmp;
+
+ mutex_lock(&st->request_lock);
+ if (st->ops->ready) {
+ err = st->ops->ready(st);
+ if (err) {
+ mutex_unlock(&st->request_lock);
+ if (err < 0)
+ kst_state_exit(st);
+ return err;
+ }
+ }
+
+ err = 0;
+ empty = 1;
+ req = NULL;
+ list_for_each_entry_safe(req, tmp, &st->request_list, request_list_entry) {
+ empty = 0;
+ revents = st->socket->ops->poll(st->socket->file,
+ st->socket, NULL);
+ if (!revents)
+ break;
+ err = req->callback(req, revents);
+ if (req->size && !err)
+ err = 1;
+
+ if (err < 0 || !req->size) {
+ if (!req->size)
+ err = 0;
+ kst_del_req(req);
+ kst_complete_req(req, err);
+ }
+
+ if (err)
+ break;
+ }
+
+ dprintk("%s: broke the loop: err: %d, list_empty: %d.\n",
+ __func__, err, list_empty(&st->request_list));
+ mutex_unlock(&st->request_lock);
+
+ if (err < 0) {
+ dprintk("%s: req: %p, err: %d, st: %p, node->state: %p.\n",
+ __func__, req, err, st, st->node->state);
+
+ if (st != st->node->state) {
+ /*
+ * Accepted client has state not related to storage
+ * node, so it must be freed explicitely.
+ * We do not try to fix clients connections to local
+ * export nodes, just drop the client.
+ */
+
+ kst_state_exit(st);
+ return err;
+ }
+
+ err = kst_error(st, err);
+ if (err)
+ return err;
+
+ kst_wake(st);
+ }
+
+ if (list_empty(&st->request_list) && !empty)
+ kst_wake(st);
+
+ return err;
+}
+
+/*
+ * Main worker thread - one per storage.
+ */
+static int kst_thread_func(void *data)
+{
+ struct kst_worker *w = data;
+ struct kst_state *st;
+ unsigned long flags;
+ int err = 0;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible_timeout(w->wait,
+ !list_empty(&w->ready_list) ||
+ kthread_should_stop(),
+ HZ);
+
+ st = NULL;
+ spin_lock_irqsave(&w->ready_lock, flags);
+ if (!list_empty(&w->ready_list)) {
+ st = list_entry(w->ready_list.next, struct kst_state,
+ ready_entry);
+ list_del_init(&st->ready_entry);
+ }
+ spin_unlock_irqrestore(&w->ready_lock, flags);
+
+ if (!st)
+ continue;
+
+ err = kst_thread_process_state(st);
+ }
+
+ return err;
+}
+
+/*
+ * Worker initialization - this object will host andprocess all states,
+ * which in turn host requests for remote targets.
+ */
+struct kst_worker *kst_worker_init(int id)
+{
+ struct kst_worker *w;
+ int err;
+
+ w = kzalloc(sizeof(struct kst_worker), GFP_KERNEL);
+ if (!w)
+ return ERR_PTR(-ENOMEM);
+
+ w->id = id;
+ init_waitqueue_head(&w->wait);
+ spin_lock_init(&w->ready_lock);
+ mutex_init(&w->state_mutex);
+
+ INIT_LIST_HEAD(&w->ready_list);
+ INIT_LIST_HEAD(&w->state_list);
+
+ w->req_pool = mempool_create_slab_pool(256, dst_request_cache);
+ if (!w->req_pool) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+
+ w->thread = kthread_run(&kst_thread_func, w, "kst%d", w->id);
+ if (IS_ERR(w->thread)) {
+ err = PTR_ERR(w->thread);
+ goto err_out_destroy;
+ }
+
+ mutex_lock(&kst_worker_mutex);
+ list_add_tail(&w->entry, &kst_worker_list);
+ mutex_unlock(&kst_worker_mutex);
+
+ return w;
+
+err_out_destroy:
+ mempool_destroy(w->req_pool);
+err_out_free:
+ kfree(w);
+ return ERR_PTR(err);
+}
+
+void kst_worker_exit(struct kst_worker *w)
+{
+ struct kst_state *st, *n;
+
+ mutex_lock(&kst_worker_mutex);
+ list_del(&w->entry);
+ mutex_unlock(&kst_worker_mutex);
+
+ kthread_stop(w->thread);
+
+ list_for_each_entry_safe(st, n, &w->state_list, entry) {
+ kst_state_exit(st);
+ }
+
+ mempool_destroy(w->req_pool);
+ kfree(w);
+}
+
+/*
+ * Common state exit callback.
+ * Removes itself from worker's list of states,
+ * releases socket and flushes all requests.
+ */
+static void kst_common_exit(struct kst_state *st)
+{
+ unsigned long flags;
+
+ kst_poll_exit(st);
+
+ spin_lock_irqsave(&st->node->w->ready_lock, flags);
+ list_del_init(&st->ready_entry);
+ spin_unlock_irqrestore(&st->node->w->ready_lock, flags);
+
+ kst_flush_requests(st);
+ kst_sock_release(st);
+}
+
+/*
+ * Listen socket contains security attributes in request_list,
+ * so it can not be flushed via usual way.
+ */
+static void kst_listen_flush(struct kst_state *st)
+{
+ struct dst_secure *s, *tmp;
+
+ list_for_each_entry_safe(s, tmp, &st->request_list, sec_entry) {
+ list_del(&s->sec_entry);
+ kfree(s);
+ }
+}
+
+static void kst_listen_exit(struct kst_state *st)
+{
+ kst_listen_flush(st);
+ kst_common_exit(st);
+}
+
+/*
+ * BIO vector receiving function - does not block, but may sleep because
+ * of scheduling policy.
+ */
+static int kst_data_recv_bio_vec(struct kst_state *st, struct bio_vec *bv,
+ unsigned int offset, unsigned int size)
+{
+ struct msghdr msg;
+ struct kvec iov;
+ void *kaddr;
+ int err;
+
+ kaddr = kmap(bv->bv_page);
+
+ iov.iov_base = kaddr + bv->bv_offset + offset;
+ iov.iov_len = size;
+
+ msg.msg_iov = (struct iovec *)&iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL;
+
+ err = kernel_recvmsg(st->socket, &msg, &iov, 1, iov.iov_len,
+ msg.msg_flags);
+ kunmap(bv->bv_page);
+
+ return err;
+}
+
+/*
+ * BIO vector sending function - does not block, but may sleep because
+ * of scheduling policy.
+ */
+static int kst_data_send_bio_vec(struct kst_state *st, struct bio_vec *bv,
+ unsigned int offset, unsigned int size)
+{
+ return kernel_sendpage(st->socket, bv->bv_page,
+ bv->bv_offset + offset, size,
+ MSG_DONTWAIT | MSG_NOSIGNAL);
+}
+
+static u32 dst_csum_bvec(struct bio_vec *bv, unsigned int offset, unsigned int size)
+{
+ void *addr;
+ u32 csum;
+
+ addr = kmap_atomic(bv->bv_page, KM_USER0);
+ csum = dst_csum_data(addr + bv->bv_offset + offset, size);
+ kunmap_atomic(addr, KM_USER0);
+
+ return csum;
+}
+
+typedef int (*kst_data_process_bio_vec_t)(struct kst_state *st,
+ struct bio_vec *bv, unsigned int offset, unsigned int size);
+
+/*
+ * @req: processing request.
+ * Contains BIO and all related to its processing info.
+ *
+ * This function sends or receives requested number of pages from given BIO.
+ *
+ * In case of errors negative value is returned and @size,
+ * @index and @off are set to the:
+ * - number of bytes not yet processed (i.e. the rest of the bytes to be
+ * processed).
+ * - index of the last bio_vec started to be processed (header sent).
+ * - offset of the first byte to be processed in the bio_vec.
+ *
+ * If there are no errors, zero is returned.
+ * -EAGAIN is not an error and is transformed into zero return value,
+ * called must check if @size is zero, in that case whole BIO is processed
+ * and thus req->bio_endio() can be called, othervise new request must be allocated
+ * to be processed later.
+ */
+static int kst_data_process_bio(struct dst_request *req)
+{
+ int err = -ENOSPC;
+ struct dst_remote_request r;
+ kst_data_process_bio_vec_t func;
+ unsigned int cur_size;
+ int use_csum = test_bit(DST_NODE_USE_CSUM, &req->node->flags);
+
+ if (bio_rw(req->bio) == WRITE) {
+ r.cmd = cpu_to_be32(DST_WRITE);
+ func = kst_data_send_bio_vec;
+ } else {
+ r.cmd = cpu_to_be32(DST_READ);
+ func = kst_data_recv_bio_vec;
+ }
+
+ dprintk("%s: start: [%c], start: %llu, idx: %d, num: %d, "
+ "size: %llu, offset: %u, flags: %x, use_csum: %d.\n",
+ __func__, (bio_rw(req->bio) == WRITE)?'W':'R',
+ req->start, req->idx, req->num, req->size, req->offset,
+ req->flags, use_csum);
+
+ while (req->idx < req->num) {
+ struct bio_vec *bv = bio_iovec_idx(req->bio, req->idx);
+
+ cur_size = min_t(u64, bv->bv_len - req->offset, req->size);
+
+ dprintk("%s: page: %p, off: %u, len: %u, req->offset: %u, "
+ "req->size: %llu, cur_size: %u, flags: %x, "
+ "use_csum: %d, req->csum: %x.\n",
+ __func__, bv->bv_page, bv->bv_offset, bv->bv_len,
+ req->offset, req->size, cur_size,
+ req->flags, use_csum, req->tmp_csum);
+
+ if (cur_size == 0) {
+ printk(KERN_ERR "%s: %d/%d: start: %llu, "
+ "bv_offset: %u, bv_len: %u, "
+ "req_offset: %u, req_size: %llu, "
+ "req: %p, bio: %p, err: %d.\n",
+ __func__, req->idx, req->num, req->start,
+ bv->bv_offset, bv->bv_len,
+ req->offset, req->size,
+ req, req->bio, err);
+ BUG();
+ }
+
+ if (!(req->flags & DST_REQ_HEADER_SENT)) {
+ r.sector = cpu_to_be64(req->start);
+ r.offset = cpu_to_be32(bv->bv_offset + req->offset);
+ r.size = cpu_to_be32(cur_size);
+ r.csum = 0;
+
+ if (use_csum && bio_rw(req->bio) == WRITE &&
+ !req->tmp_offset) {
+ req->tmp_offset = req->offset;
+ r.csum = cpu_to_be32(dst_csum_bvec(bv,
+ req->offset, cur_size));
+ }
+
+ err = dst_data_send_header(req->state->socket, &r);
+ dprintk("%s: %d/%d: sending header: cmd: %u, start: %llu, "
+ "bv_offset: %u, bv_len: %u, "
+ "a offset: %u, offset: %u, "
+ "cur_size: %u, err: %d.\n",
+ __func__, req->idx, req->num, be32_to_cpu(r.cmd),
+ req->start, bv->bv_offset, bv->bv_len,
+ bv->bv_offset + req->offset,
+ req->offset, cur_size, err);
+
+ if (err != sizeof(struct dst_remote_request)) {
+ if (err >= 0)
+ err = -EINVAL;
+ break;
+ }
+
+ req->flags |= DST_REQ_HEADER_SENT;
+ }
+
+ if (use_csum && (bio_rw(req->bio) != WRITE) &&
+ !(req->flags & DST_REQ_CHEKSUM_RECV)) {
+ struct dst_remote_request tmp_req;
+
+ err = dst_data_recv_header(req->state->socket, &tmp_req, 0);
+ dprintk("%s: %d/%d: receiving header: start: %llu, "
+ "bv_offset: %u, bv_len: %u, "
+ "a offset: %u, offset: %u, "
+ "cur_size: %u, err: %d.\n",
+ __func__, req->idx, req->num,
+ req->start, bv->bv_offset, bv->bv_len,
+ bv->bv_offset + req->offset,
+ req->offset, cur_size, err);
+
+ if (err != sizeof(struct dst_remote_request)) {
+ if (err >= 0)
+ err = -EINVAL;
+ break;
+ }
+
+ if (req->tmp_csum) {
+ printk("%s: req: %p, old csum: %x, new: %x.\n",
+ __func__, req, req->tmp_csum,
+ be32_to_cpu(tmp_req.csum));
+ BUG_ON(1);
+ }
+
+ dprintk("%s: req: %p, old csum: %x, new: %x.\n",
+ __func__, req, req->tmp_csum,
+ be32_to_cpu(tmp_req.csum));
+ req->tmp_csum = be32_to_cpu(tmp_req.csum);
+
+ req->flags |= DST_REQ_CHEKSUM_RECV;
+ }
+
+ err = func(req->state, bv, req->offset, cur_size);
+ if (err <= 0)
+ break;
+
+ req->offset += err;
+ req->size -= err;
+
+ if (req->offset != bv->bv_len) {
+ dprintk("%s: %d/%d: this: start: %llu, bv_offset: %u, "
+ "bv_len: %u, offset: %u, "
+ "cur_size: %u, err: %d.\n",
+ __func__, req->idx, req->num, req->start,
+ bv->bv_offset, bv->bv_len,
+ req->offset, cur_size, err);
+ err = -EAGAIN;
+ break;
+ }
+
+ if (use_csum && bio_rw(req->bio) != WRITE) {
+ u32 csum = dst_csum_bvec(bv, req->tmp_offset,
+ bv->bv_len - req->tmp_offset);
+
+ dprintk("%s: req: %p, csum: %x, received csum: %x.\n",
+ __func__, req, csum, req->tmp_csum);
+
+ if (csum != req->tmp_csum) {
+ printk("%s: %d/%d: broken checksum: start: %llu, "
+ "bv_offset: %u, bv_len: %u, "
+ "a offset: %u, offset: %u, "
+ "cur_size: %u, orig_size: %llu.\n",
+ __func__, req->idx, req->num,
+ req->start, bv->bv_offset, bv->bv_len,
+ bv->bv_offset + req->offset,
+ req->offset, cur_size, req->orig_size);
+ printk("%s: broken checksum: req: %p, csum: %x, "
+ "should be: %x, flags: %x, "
+ "req->tmp_offset: %u, rw: %lu.\n",
+ __func__, req, csum, req->tmp_csum,
+ req->flags, req->tmp_offset, bio_rw(req->bio));
+
+ req->offset -= err;
+ req->size += err;
+
+ err = -EREMOTEIO;
+ break;
+ }
+ }
+
+ req->offset = 0;
+ req->idx++;
+ req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
+ req->tmp_csum = 0;
+ req->start += to_sector(bv->bv_len);
+ }
+
+ if (err <= 0 && err != -EAGAIN) {
+ if (err == 0)
+ err = -ECONNRESET;
+ } else
+ err = 0;
+
+ if (err < 0 || (req->idx == req->num && req->size)) {
+ dprintk("%s: return: idx: %d, num: %d, offset: %u, "
+ "size: %llu, err: %d.\n",
+ __func__, req->idx, req->num, req->offset,
+ req->size, err);
+ }
+ dprintk("%s: end: start: %llu, idx: %d, num: %d, "
+ "size: %llu, offset: %u.\n",
+ __func__, req->start, req->idx, req->num,
+ req->size, req->offset);
+
+ return err;
+}
+
+void kst_bio_endio(struct dst_request *req, int err)
+{
+ if (err && printk_ratelimit())
+ printk("%s: freeing bio: %p, bi_size: %u, "
+ "orig_size: %llu, req: %p, err: %d.\n",
+ __func__, req->bio, req->bio->bi_size, req->orig_size,
+ req, err);
+ bio_endio(req->bio, req->orig_size, err);
+}
+EXPORT_SYMBOL_GPL(kst_bio_endio);
+
+/*
+ * This callback is invoked by worker thread to process given request.
+ */
+int kst_data_callback(struct dst_request *req, unsigned int revents)
+{
+ int err;
+
+ dprintk("%s: req: %p, num: %d, idx: %d, bio: %p, "
+ "revents: %x, flags: %x.\n",
+ __func__, req, req->num, req->idx, req->bio,
+ revents, req->flags);
+
+ if (req->flags & DST_REQ_EXPORT_READ)
+ return 1;
+
+ err = kst_data_process_bio(req);
+
+ if (revents & (POLLERR | POLLHUP | POLLRDHUP))
+ err = -EPIPE;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(kst_data_callback);
+
+struct dst_request *dst_clone_request(struct dst_request *req, mempool_t *pool)
+{
+ struct dst_request *new_req;
+
+ new_req = mempool_alloc(pool, GFP_NOIO);
+ if (!new_req)
+ return NULL;
+
+ memset(new_req, 0, sizeof(struct dst_request));
+
+ dprintk("%s: req: %p, new_req: %p.\n", __func__, req, new_req);
+
+ if (req) {
+ new_req->bio = req->bio;
+ new_req->state = req->state;
+ new_req->node = req->node;
+ new_req->idx = req->idx;
+ new_req->num = req->num;
+ new_req->size = req->size;
+ new_req->orig_size = req->orig_size;
+ new_req->offset = req->offset;
+ new_req->tmp_offset = req->tmp_offset;
+ new_req->tmp_csum = req->tmp_csum;
+ new_req->start = req->start;
+ new_req->flags = req->flags;
+ new_req->bio_endio = req->bio_endio;
+ new_req->priv = req->priv;
+ }
+
+ return new_req;
+}
+EXPORT_SYMBOL_GPL(dst_clone_request);
+
+void dst_free_request(struct dst_request *req)
+{
+ dprintk("%s: free req: %p, pool: %p, bio: %p, state: %p, node: %p.\n",
+ __func__, req, req->node->w->req_pool,
+ req->bio, req->state, req->node);
+ mempool_free(req, req->node->w->req_pool);
+}
+EXPORT_SYMBOL_GPL(dst_free_request);
+
+/*
+ * This is main data processing function, eventually invoked from block layer.
+ * It tries to complte request, but if it is about to block, it allocates
+ * new request and queues it to main worker to be processed when events allow.
+ */
+static int kst_data_push(struct dst_request *req)
+{
+ struct kst_state *st = req->state;
+ struct dst_request *new_req;
+ unsigned int revents;
+ int err, locked = 0;
+
+ dprintk("%s: start: %llu, size: %llu, bio: %p.\n",
+ __func__, req->start, req->size, req->bio);
+
+ if (!list_empty(&st->request_list) || (req->flags & DST_REQ_ALWAYS_QUEUE))
+ goto alloc_new_req;
+
+ if (mutex_trylock(&st->request_lock)) {
+ locked = 1;
+
+ if (!list_empty(&st->request_list))
+ goto alloc_new_req;
+
+ revents = st->socket->ops->poll(NULL, st->socket, NULL);
+ if (revents & POLLOUT) {
+ err = kst_data_process_bio(req);
+ if (err < 0)
+ goto out_unlock;
+
+ if (!req->size)
+ goto out_bio_endio;
+ }
+ }
+
+alloc_new_req:
+ err = -ENOMEM;
+ new_req = dst_clone_request(req, req->node->w->req_pool);
+ if (!new_req)
+ goto out_unlock;
+
+ new_req->callback = &kst_data_callback;
+
+ if (!locked)
+ mutex_lock(&st->request_lock);
+
+ locked = 1;
+
+ err = kst_enqueue_req(st, new_req);
+ if (err)
+ goto out_unlock;
+ mutex_unlock(&st->request_lock);
+
+ err = 0;
+ goto out;
+
+out_bio_endio:
+ req->bio_endio(req, err);
+out_unlock:
+ if (locked)
+ mutex_unlock(&st->request_lock);
+ locked = 0;
+
+ if (err) {
+ err = kst_error(st, err);
+ if (!err)
+ goto alloc_new_req;
+ }
+
+ if (err && printk_ratelimit()) {
+ printk("%s: error [%c], start: %llu, idx: %d, num: %d, "
+ "size: %llu, offset: %u, err: %d.\n",
+ __func__, (bio_rw(req->bio) == WRITE)?'W':'R',
+ req->start, req->idx, req->num, req->size,
+ req->offset, err);
+ }
+
+out:
+
+ kst_wake(st);
+ return err;
+}
+
+/*
+ * Remote node initialization callback.
+ */
+static int kst_data_init(struct kst_state *st, void *data)
+{
+ int err;
+
+ st->socket = data;
+ st->socket->sk->sk_allocation = GFP_NOIO;
+ /*
+ * Why not?
+ */
+ st->socket->sk->sk_sndbuf = st->socket->sk->sk_sndbuf = 1024*1024*10;
+
+ err = kst_poll_init(st);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/*
+ * Remote node recovery function - tries to reconnect to given target.
+ */
+static int kst_data_recovery(struct kst_state *st, int err)
+{
+ struct socket *sock;
+ struct sockaddr addr;
+ int addrlen;
+ struct dst_request *req;
+
+ if (err != -ECONNRESET && err != -EPIPE) {
+ dprintk("%s: state %p does not know how "
+ "to recover from error %d.\n",
+ __func__, st, err);
+ return err;
+ }
+
+ err = sock_create(st->socket->ops->family, st->socket->type,
+ st->socket->sk->sk_protocol, &sock);
+ if (err < 0)
+ goto err_out_exit;
+
+ sock->sk->sk_sndtimeo = sock->sk->sk_rcvtimeo =
+ msecs_to_jiffies(DST_DEFAULT_TIMEO);
+
+ err = sock->ops->getname(st->socket, &addr, &addrlen, 2);
+ if (err)
+ goto err_out_destroy;
+
+ err = sock->ops->connect(sock, &addr, addrlen, 0);
+ if (err)
+ goto err_out_destroy;
+
+ kst_poll_exit(st);
+ kst_sock_release(st);
+
+ mutex_lock(&st->request_lock);
+ err = st->ops->init(st, sock);
+ if (!err) {
+ /*
+ * After reconnection is completed all requests
+ * must be resent from the state they were finished previously,
+ * but with new headers.
+ */
+ list_for_each_entry(req, &st->request_list, request_list_entry)
+ req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
+ }
+ mutex_unlock(&st->request_lock);
+ if (err < 0)
+ goto err_out_destroy;
+
+ kst_wake(st);
+ dprintk("%s: reconnected.\n", __func__);
+
+ return 0;
+
+err_out_destroy:
+ sock_release(sock);
+err_out_exit:
+ dprintk("%s: revovery failed: st: %p, err: %d.\n", __func__, st, err);
+ return err;
+}
+
+/*
+ * Local exporting node end IO callbacks.
+ */
+static int kst_export_write_end_io(struct bio *bio, unsigned int size, int err)
+{
+ dprintk("%s: bio: %p, size: %u, idx: %d, num: %d, err: %d.\n",
+ __func__, bio, bio->bi_size, bio->bi_idx, bio->bi_vcnt, err);
+
+ if (bio->bi_size)
+ return 1;
+
+ kst_export_put_bio(bio);
+ return 0;
+}
+
+static int kst_export_read_end_io(struct bio *bio, unsigned int size, int err)
+{
+ struct dst_request *req = bio->bi_private;
+ struct kst_state *st = req->state;
+ int use_csum = test_bit(DST_NODE_USE_CSUM, &req->node->flags);
+
+ dprintk("%s: bio: %p, req: %p, size: %u, idx: %d, num: %d, err: %d.\n",
+ __func__, bio, req, bio->bi_size, bio->bi_idx,
+ bio->bi_vcnt, err);
+
+ if (bio->bi_size)
+ return 1;
+
+ if (err) {
+ kst_export_put_bio(bio);
+ return 0;
+ }
+
+ bio->bi_size = req->size = req->orig_size;
+ bio->bi_rw = WRITE;
+ if (use_csum)
+ req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
+
+ /*
+ * This is a race with kst_data_callback(), which checks
+ * this bit to determine if it can or can not process given
+ * request. This does not harm actually, since subsequent
+ * state wakeup will call it again and thus will pick
+ * given request in time.
+ */
+ req->flags &= ~DST_REQ_EXPORT_READ;
+ kst_wake(st);
+ return 0;
+}
+
+/*
+ * This callback is invoked each time new request from remote
+ * node to given local export node is received.
+ * It allocates new block IO request and queues it for processing.
+ */
+static int kst_export_ready(struct kst_state *st)
+{
+ struct dst_remote_request r;
+ struct bio *bio;
+ int err, nr, i;
+ struct dst_request *req;
+ unsigned int revents = st->socket->ops->poll(NULL, st->socket, NULL);
+
+ if (revents & (POLLERR | POLLHUP)) {
+ err = -EPIPE;
+ goto err_out_exit;
+ }
+
+ if (!(revents & POLLIN) || !list_empty(&st->request_list))
+ return 0;
+
+ err = dst_data_recv_header(st->socket, &r, 1);
+ if (err != sizeof(struct dst_remote_request)) {
+ err = -ECONNRESET;
+ goto err_out_exit;
+ }
+
+ kst_convert_header(&r);
+
+ dprintk("\n%s: st: %p, cmd: %u, sector: %llu, size: %u, "
+ "csum: %x, offset: %u.\n",
+ __func__, st, r.cmd, r.sector,
+ r.size, r.csum, r.offset);
+
+ err = -EINVAL;
+ if (r.cmd != DST_READ && r.cmd != DST_WRITE && r.cmd != DST_REMOTE_CFG)
+ goto err_out_exit;
+
+ if ((s64)(r.sector + to_sector(r.size)) < 0 ||
+ (r.sector + to_sector(r.size)) > st->node->size ||
+ r.offset >= PAGE_SIZE)
+ goto err_out_exit;
+
+ if (r.cmd == DST_REMOTE_CFG) {
+ r.sector = st->node->size;
+
+ if (test_bit(DST_NODE_USE_CSUM, &st->node->flags))
+ r.csum = 1;
+
+ kst_convert_header(&r);
+
+ err = dst_data_send_header(st->socket, &r);
+ if (err != sizeof(struct dst_remote_request)) {
+ err = -EINVAL;
+ goto err_out_exit;
+ }
+ kst_wake(st);
+ return 0;
+ }
+
+ nr = DIV_ROUND_UP(r.size, PAGE_SIZE);
+
+ while (r.size) {
+ int nr_pages = min(BIO_MAX_PAGES, nr);
+ unsigned int size;
+ struct page *page;
+
+ err = -ENOMEM;
+ req = dst_clone_request(NULL, st->node->w->req_pool);
+ if (!req)
+ goto err_out_exit;
+
+ bio = bio_alloc(GFP_NOIO, nr_pages);
+ if (!bio)
+ goto err_out_free_req;
+
+ req->flags = DST_REQ_EXPORT | DST_REQ_HEADER_SENT |
+ DST_REQ_CHEKSUM_RECV;
+ req->bio = bio;
+ req->state = st;
+ req->node = st->node;
+ req->callback = &kst_data_callback;
+ req->bio_endio = &kst_bio_endio;
+
+ req->tmp_offset = 0;
+ req->tmp_csum = r.csum;
+
+ /*
+ * Yes, looks a bit weird.
+ * Logic is simple - for local exporting node all operations
+ * are reversed compared to usual nodes, since usual nodes
+ * process remote data and local export node process remote
+ * requests, so that writing data means sending data to
+ * remote node and receiving on the local export one.
+ *
+ * So, to process writing to the exported node we need first
+ * to receive data from the net (i.e. to perform READ
+ * operationin terms of usual node), and then put it to the
+ * storage (WRITE command, so it will be changed before
+ * calling generic_make_request()).
+ *
+ * To process read request from the exported node we need
+ * first to read it from storage (READ command for BIO)
+ * and then send it over the net (perform WRITE operation
+ * in terms of network).
+ */
+ if (r.cmd == DST_WRITE) {
+ req->flags |= DST_REQ_EXPORT_WRITE;
+ bio->bi_end_io = kst_export_write_end_io;
+ } else {
+ req->flags |= DST_REQ_EXPORT_READ;
+ bio->bi_end_io = kst_export_read_end_io;
+ }
+ bio->bi_rw = READ;
+ bio->bi_private = req;
+ bio->bi_sector = r.sector;
+ bio->bi_bdev = st->node->bdev;
+
+ for (i = 0; i < nr_pages; ++i) {
+ page = alloc_page(GFP_NOIO);
+ if (!page)
+ break;
+
+ size = min_t(u32, PAGE_SIZE - r.offset, r.size);
+
+ err = bio_add_page(bio, page, size, 0);
+ dprintk("%s: %d/%d: page: %p, size: %u, "
+ "offset: %u (used zero), err: %d.\n",
+ __func__, i, nr_pages, page, size,
+ r.offset, err);
+ if (err <= 0)
+ break;
+
+ if (err == size)
+ nr--;
+
+ r.size -= err;
+ r.sector += to_sector(err);
+
+ if (!r.size)
+ break;
+ }
+
+ if (!bio->bi_vcnt) {
+ err = -ENOMEM;
+ goto err_out_put;
+ }
+
+ req->size = req->orig_size = bio->bi_size;
+ req->start = bio->bi_sector;
+ req->idx = 0;
+ req->num = bio->bi_vcnt;
+
+ dprintk("%s: submitting: bio: %p, req: %p, start: %llu, "
+ "size: %llu, idx: %d, num: %d, offset: %u, csum: %x.\n",
+ __func__, bio, req, req->start, req->size,
+ req->idx, req->num, req->offset, req->tmp_csum);
+
+ err = kst_enqueue_req(st, req);
+ if (err)
+ goto err_out_put;
+
+ if (r.cmd == DST_READ) {
+ generic_make_request(bio);
+ }
+ }
+
+ kst_wake(st);
+ return 0;
+
+err_out_put:
+ bio_put(bio);
+err_out_free_req:
+ dst_free_request(req);
+err_out_exit:
+ return err;
+}
+
+static void kst_export_exit(struct kst_state *st)
+{
+ struct dst_node *n = st->node;
+
+ kst_common_exit(st);
+ dst_node_put(n);
+}
+
+static struct kst_state_ops kst_data_export_ops = {
+ .init = &kst_data_init,
+ .push = &kst_data_push,
+ .exit = &kst_export_exit,
+ .ready = &kst_export_ready,
+};
+
+/*
+ * This callback is invoked each time listening socket for
+ * given local export node becomes ready.
+ * It creates new state for connected client and queues for processing.
+ */
+static int kst_listen_ready(struct kst_state *st)
+{
+ struct socket *newsock;
+ struct saddr addr;
+ struct kst_state *newst;
+ int err;
+ unsigned int revents, permissions = 0;
+ struct dst_secure *s;
+
+ revents = st->socket->ops->poll(NULL, st->socket, NULL);
+ if (!(revents & POLLIN))
+ return 1;
+
+ err = sock_create(st->socket->ops->family, st->socket->type,
+ st->socket->sk->sk_protocol, &newsock);
+ if (err)
+ goto err_out_exit;
+
+ err = st->socket->ops->accept(st->socket, newsock, 0);
+ if (err)
+ goto err_out_put;
+
+ if (newsock->ops->getname(newsock, (struct sockaddr *)&addr,
+ (int *)&addr.sa_data_len, 2) < 0) {
+ err = -ECONNABORTED;
+ goto err_out_put;
+ }
+
+ list_for_each_entry(s, &st->request_list, sec_entry) {
+ void *sec_addr, *new_addr;
+
+ sec_addr = ((void *)&s->sec.addr) + s->sec.check_offset;
+ new_addr = ((void *)&addr) + s->sec.check_offset;
+
+ if (!memcmp(sec_addr, new_addr,
+ addr.sa_data_len - s->sec.check_offset)) {
+ permissions = s->sec.permissions;
+ break;
+ }
+ }
+
+ /*
+ * So far only reading and writing are supported.
+ * Block device does not know about anything else,
+ * but as far as I recall, there was a prognosis,
+ * that computer will never require more than 640kb of RAM.
+ */
+ if (permissions == 0) {
+ err = -EPERM;
+ goto err_out_put;
+ }
+
+ if (st->socket->ops->family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+ printk(KERN_INFO "%s: Client: %u.%u.%u.%u:%d.\n", __func__,
+ NIPQUAD(sin->sin_addr.s_addr), ntohs(sin->sin_port));
+ } else if (st->socket->ops->family == AF_INET6) {
+ struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&addr;
+ printk(KERN_INFO "%s: Client: "
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%d",
+ __func__,
+ NIP6(sin->sin6_addr), ntohs(sin->sin6_port));
+ }
+
+ dst_node_get(st->node);
+ newst = kst_state_init(st->node, permissions,
+ &kst_data_export_ops, newsock);
+ if (IS_ERR(newst)) {
+ err = PTR_ERR(newst);
+ goto err_out_put;
+ }
+
+ /*
+ * Negative return value means error, positive - stop this state
+ * processing. Zero allows to check state for pending requests.
+ * Listening socket contains security objects in request list,
+ * since it does not have any requests.
+ */
+ return 1;
+
+err_out_put:
+ sock_release(newsock);
+err_out_exit:
+ return 1;
+}
+
+static int kst_listen_init(struct kst_state *st, void *data)
+{
+ int err = -ENOMEM, i;
+ struct dst_le_template *tmp = data;
+ struct dst_secure *s;
+
+ for (i=0; i<tmp->le->secure_attr_num; ++i) {
+ s = kmalloc(sizeof(struct dst_secure), GFP_KERNEL);
+ if (!s)
+ goto err_out_exit;
+
+ memcpy(&s->sec, tmp->data, sizeof(struct dst_secure_user));
+
+ list_add_tail(&s->sec_entry, &st->request_list);
+ tmp->data += sizeof(struct dst_secure_user);
+
+ if (s->sec.addr.sa_family == AF_INET) {
+ struct sockaddr_in *sin =
+ (struct sockaddr_in *)&s->sec.addr;
+ printk(KERN_INFO "%s: Client: %u.%u.%u.%u:%d, "
+ "permissions: %x.\n",
+ __func__, NIPQUAD(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port), s->sec.permissions);
+ } else if (s->sec.addr.sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin =
+ (struct sockaddr_in6 *)&s->sec.addr;
+ printk(KERN_INFO "%s: Client: "
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%d, "
+ "permissions: %x.\n",
+ __func__, NIP6(sin->sin6_addr),
+ ntohs(sin->sin6_port), s->sec.permissions);
+ }
+ }
+
+ err = kst_sock_create(st, &tmp->le->rctl.addr, tmp->le->rctl.type,
+ tmp->le->rctl.proto, tmp->le->backlog);
+ if (err)
+ goto err_out_exit;
+
+ err = kst_poll_init(st);
+ if (err)
+ goto err_out_release;
+
+ return 0;
+
+err_out_release:
+ kst_sock_release(st);
+err_out_exit:
+ kst_listen_flush(st);
+ return err;
+}
+
+/*
+ * Operations for different types of states.
+ * There are three:
+ * data state - created for remote node, when distributed storage connects
+ * to remote node, which contain data.
+ * listen state - created for local export node, when remote distributed
+ * storage's node connects to given node to get/put data.
+ * data export state - created for each client connected to above listen
+ * state.
+ */
+static struct kst_state_ops kst_listen_ops = {
+ .init = &kst_listen_init,
+ .exit = &kst_listen_exit,
+ .ready = &kst_listen_ready,
+};
+static struct kst_state_ops kst_data_ops = {
+ .init = &kst_data_init,
+ .push = &kst_data_push,
+ .exit = &kst_common_exit,
+ .recovery = &kst_data_recovery,
+};
+
+struct kst_state *kst_listener_state_init(struct dst_node *node,
+ struct dst_le_template *tmp)
+{
+ return kst_state_init(node, DST_PERM_READ | DST_PERM_WRITE,
+ &kst_listen_ops, tmp);
+}
+
+struct kst_state *kst_data_state_init(struct dst_node *node,
+ struct socket *newsock)
+{
+ return kst_state_init(node, DST_PERM_READ | DST_PERM_WRITE,
+ &kst_data_ops, newsock);
+}
+
+/*
+ * Remove all workers and associated states.
+ */
+void kst_exit_all(void)
+{
+ struct kst_worker *w, *n;
+
+ list_for_each_entry_safe(w, n, &kst_worker_list, entry) {
+ kst_worker_exit(w);
+ }
+}
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 10eb56b..9e67d58 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -36,9 +36,11 @@
#define CN_VAL_CIFS 0x1
#define CN_W1_IDX 0x3 /* w1 communication */
#define CN_W1_VAL 0x1
+#define CN_DST_IDX 0x4 /* Distributed storage */
+#define CN_DST_VAL 0x1
-#define CN_NETLINK_USERS 4
+#define CN_NETLINK_USERS 5
/*
* Maximum connector's message size.
diff --git a/include/linux/dst.h b/include/linux/dst.h
new file mode 100644
index 0000000..52575c2
--- /dev/null
+++ b/include/linux/dst.h
@@ -0,0 +1,377 @@
+/*
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DST_H
+#define __DST_H
+
+#include <linux/types.h>
+
+#define DST_NAMELEN 32
+#define DST_NAME "dst"
+#define DST_IOCTL 0xba
+
+enum {
+ DST_DEL_NODE = 0, /* Remove node with given id from storage */
+ DST_ADD_REMOTE, /* Add remote node with given id to the storage */
+ DST_ADD_LOCAL, /* Add local node with given id to the storage */
+ DST_ADD_LOCAL_EXPORT, /* Add local node with given id to the storage to be exported and used by remote peers */
+ DST_START_STORAGE, /* Array is ready and storage can be started, if there will be new nodes
+ * added to the storage, they will be checked against existing size and
+ * probably be dropped (for example in mirror format when new node has smaller
+ * size than array created) or inserted.
+ */
+ DST_STOP_STORAGE, /* Remove array and all nodes. */
+ DST_CMD_MAX
+};
+
+#define DST_CTL_FLAGS_REMOTE (1<<0)
+#define DST_CTL_FLAGS_EXPORT (1<<1)
+#define DST_CTL_USE_CSUM (1<<2)
+
+struct dst_ctl
+{
+ char st[DST_NAMELEN];
+ char alg[DST_NAMELEN];
+ __u32 flags, cmd;
+ __u64 start, size;
+};
+
+struct dst_local_ctl
+{
+ char name[DST_NAMELEN];
+};
+
+#define SADDR_MAX_DATA 128
+
+struct saddr {
+ unsigned short sa_family; /* address family, AF_xxx */
+ char sa_data[SADDR_MAX_DATA]; /* 14 bytes of protocol address */
+ unsigned short sa_data_len; /* Number of bytes used in sa_data */
+};
+
+struct dst_remote_ctl
+{
+ __u16 type;
+ __u16 proto;
+ struct saddr addr;
+};
+
+#define DST_PERM_READ (1<<0)
+#define DST_PERM_WRITE (1<<1)
+
+/*
+ * Right now it is simple model, where each remote address
+ * is assigned to set of permissions it is allowed to perform.
+ * In real world block device does not know anything but
+ * reading and writing, so it should be more than enough.
+ */
+struct dst_secure_user
+{
+ unsigned int permissions;
+ unsigned short check_offset;
+ struct saddr addr;
+};
+
+struct dst_local_export_ctl
+{
+ __u32 backlog;
+ int secure_attr_num;
+ struct dst_local_ctl lctl;
+ struct dst_remote_ctl rctl;
+};
+
+enum {
+ DST_REMOTE_CFG = 1, /* Request remote configuration */
+ DST_WRITE, /* Writing */
+ DST_READ, /* Reading */
+ DST_NCMD_MAX,
+};
+
+struct dst_remote_request
+{
+ __u32 cmd;
+ __u32 csum;
+ __u32 size;
+ __u32 offset;
+ __u64 sector;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/rbtree.h>
+#include <linux/net.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/mempool.h>
+#include <linux/device.h>
+#include <linux/crc32c.h>
+
+//#define DST_DEBUG
+
+#ifdef DST_DEBUG
+#define dprintk(f, a...) printk(KERN_NOTICE f, ##a)
+#else
+#define dprintk(f, a...) do {} while (0)
+#endif
+
+struct kst_worker
+{
+ struct list_head entry;
+
+ struct list_head state_list;
+ struct mutex state_mutex;
+
+ struct list_head ready_list;
+ spinlock_t ready_lock;
+
+ mempool_t *req_pool;
+
+ struct task_struct *thread;
+
+ wait_queue_head_t wait;
+
+ int id;
+};
+
+struct kst_state;
+struct dst_node;
+
+#define DST_REQ_HEADER_SENT (1<<0)
+#define DST_REQ_EXPORT (1<<1)
+#define DST_REQ_EXPORT_WRITE (1<<2)
+#define DST_REQ_EXPORT_READ (1<<3)
+#define DST_REQ_ALWAYS_QUEUE (1<<4)
+#define DST_REQ_CHEKSUM_RECV (1<<5)
+#define DST_REQ_CHECK_QUEUE (1<<6)
+
+struct dst_request
+{
+ struct list_head request_list_entry;
+ struct bio *bio;
+ struct kst_state *state;
+ struct dst_node *node;
+
+ u32 tmp_csum, tmp_offset;
+
+ u32 flags;
+
+ u32 offset;
+ int idx, num;
+
+ int (*callback)(struct dst_request *dst,
+ unsigned int revents);
+ void (*bio_endio)(struct dst_request *dst,
+ int err);
+
+ atomic_t refcnt;
+ void *priv;
+
+ u64 size, orig_size, start;
+};
+
+struct kst_state_ops
+{
+ int (*init)(struct kst_state *, void *);
+ int (*push)(struct dst_request *req);
+ int (*ready)(struct kst_state *);
+ int (*recovery)(struct kst_state *, int err);
+ void (*exit)(struct kst_state *);
+};
+
+struct kst_state
+{
+ struct list_head entry;
+ struct list_head ready_entry;
+
+ wait_queue_t wait;
+ wait_queue_head_t *whead;
+
+ struct dst_node *node;
+ struct socket *socket;
+
+ u32 permissions;
+
+ struct mutex request_lock;
+ struct list_head request_list;
+
+ struct kst_state_ops *ops;
+};
+
+#define DST_DEFAULT_TIMEO 2000
+
+struct dst_storage;
+
+struct dst_alg_ops
+{
+ int (*add_node)(struct dst_node *n);
+ void (*del_node)(struct dst_node *n);
+ int (*remap)(struct dst_request *req);
+ int (*error)(struct kst_state *state, int err);
+ struct module *owner;
+};
+
+struct dst_alg
+{
+ struct list_head entry;
+ char name[DST_NAMELEN];
+ atomic_t refcnt;
+ struct dst_alg_ops *ops;
+};
+
+#define DST_ST_STARTED (1<<0)
+
+struct dst_storage
+{
+ struct list_head entry;
+ char name[DST_NAMELEN];
+ struct dst_alg *alg;
+ atomic_t refcnt;
+ struct mutex tree_lock;
+ struct rb_root tree_root;
+
+ request_queue_t *queue;
+ struct gendisk *disk;
+
+ long flags;
+ u64 disk_size;
+
+ struct device device;
+};
+
+#define DST_NODE_FROZEN 0
+#define DST_NODE_NOTSYNC 1
+#define DST_NODE_USE_CSUM 2
+
+struct dst_node
+{
+ struct rb_node tree_node;
+
+ struct list_head shared;
+ struct dst_node *shared_head;
+
+ struct block_device *bdev;
+ struct dst_storage *st;
+ struct kst_state *state;
+ struct kst_worker *w;
+
+ atomic_t refcnt;
+ atomic_t shared_num;
+
+ void (*cleanup)(struct dst_node *);
+
+ long flags;
+
+ u64 start, size;
+
+ void (*priv_callback)(struct dst_node *);
+ void *priv;
+
+ struct device device;
+};
+
+struct dst_le_template
+{
+ struct dst_local_export_ctl *le;
+ void *data;
+};
+
+struct dst_secure
+{
+ struct list_head sec_entry;
+ struct dst_secure_user sec;
+};
+
+void kst_state_exit(struct kst_state *st);
+
+struct kst_worker *kst_worker_init(int id);
+void kst_worker_exit(struct kst_worker *w);
+
+struct kst_state *kst_listener_state_init(struct dst_node *node,
+ struct dst_le_template *tmp);
+struct kst_state *kst_data_state_init(struct dst_node *node,
+ struct socket *newsock);
+
+void kst_wake(struct kst_state *st);
+
+void kst_exit_all(void);
+
+struct dst_alg *dst_alloc_alg(char *name, struct dst_alg_ops *ops);
+void dst_remove_alg(struct dst_alg *alg);
+
+struct dst_node *dst_storage_tree_search(struct dst_storage *st, u64 start);
+
+void dst_node_put(struct dst_node *n);
+
+static inline struct dst_node *dst_node_get(struct dst_node *n)
+{
+ atomic_inc(&n->refcnt);
+ return n;
+}
+
+struct dst_request *dst_clone_request(struct dst_request *req, mempool_t *pool);
+void dst_free_request(struct dst_request *req);
+
+void kst_complete_req(struct dst_request *req, int err);
+void kst_bio_endio(struct dst_request *req, int err);
+void kst_del_req(struct dst_request *req);
+int kst_enqueue_req(struct kst_state *st, struct dst_request *req);
+
+int kst_data_callback(struct dst_request *req, unsigned int revents);
+
+extern struct kmem_cache *dst_request_cache;
+
+static inline sector_t to_sector(unsigned long long n)
+{
+ return (n >> 9);
+}
+
+static inline unsigned long to_bytes(sector_t n)
+{
+ return (n << 9);
+}
+
+/*
+ * Checks state's permissions.
+ * Returns -EPERM if check failed.
+ */
+static inline int kst_check_permissions(struct kst_state *st, struct bio *bio)
+{
+ if ((bio_rw(bio) == WRITE) && !(st->permissions & DST_PERM_WRITE))
+ return -EPERM;
+
+ return 0;
+}
+
+static inline __u32 dst_csum_data(unsigned char *d, unsigned int size)
+{
+ return crc32c_le(0, d, size);
+}
+
+static inline void kst_convert_header(struct dst_remote_request *r)
+{
+ r->cmd = be32_to_cpu(r->cmd);
+ r->sector = be64_to_cpu(r->sector);
+ r->offset = be32_to_cpu(r->offset);
+ r->size = be32_to_cpu(r->size);
+ r->csum = be32_to_cpu(r->csum);
+}
+
+extern int dst_data_send_header(struct socket *sock,
+ struct dst_remote_request *r);
+extern int dst_data_recv_header(struct socket *sock,
+ struct dst_remote_request *r, int block);
+
+#endif /* __KERNEL__ */
+#endif /* __DST_H */
--
Evgeniy Polyakov
^ permalink raw reply related
* [1/4] Distributed storage. Documentation.
From: Evgeniy Polyakov @ 2007-11-05 18:42 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, linux-fsdevel
DST documentation.
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
diff --git a/Documentation/dst/algorithms.txt b/Documentation/dst/algorithms.txt
new file mode 100644
index 0000000..1437a6a
--- /dev/null
+++ b/Documentation/dst/algorithms.txt
@@ -0,0 +1,115 @@
+Each storage by itself is just a set of contiguous logical blocks, with
+allowed number of operations. Nodes, each of which has own start and size,
+are placed into storage by appropriate algorithm, which remaps
+logical sector number into real node's sector. One can create
+own algorithms, since DST has pluggable interface for that.
+Currently mirrored and linear algorithms are supported.
+
+Let's briefly describe how they work.
+
+Linear algorithm.
+Simple approach of concatenating storages into single device with
+increased size is used in this algorithm. Essentially new device
+has size equal to sum of sizes of underlying nodes and nodes are
+placed one after another.
+
+ /----- Node 1 ---\ /------ Node 3 ----\
+start end start end
+ |==================|========================|==================|
+ | start end |
+ | \------- Node 2 ---------/ |
+ | |
+start end
+ \-------------------------- DST storage ----------------------/
+
+ /\
+ ||
+ ||
+
+ IO operations
+
+ Figure 1.
+ 3 nodes combined into single storage using linear algorithm.
+
+Mirror algorithm.
+In this algorithms nodes are placed under each other, so when
+operation comes to the first one, it can be mirrored to all
+underlying nodes. In case of reading, actual data is obtained from
+the nearest node - algoritm keeps track of previous operation
+and knows where it was stopped, so that subsequent seek to the
+start of the new request will take the shortest time.
+Writing is always mirrored to all underlying nodes.
+
+ IO operations
+ ||
+ ||
+ \/
+
+|---------------- DST storage -------------------|
+| prev position |
+|-------|------------ Node 1 --------------------|
+| prev pos |
+|-------------------- Node 2 -----|--------------|
+|prev pos |
+|---|---------------- Node 3 --------------------|
+
+ Figure 2.
+ 3 nodes combined into single storage using mirror algorithm.
+
+Each algorithm must implement number of callbacks,
+which must be registered during initialization time.
+
+struct dst_alg_ops
+{
+ int (*add_node)(struct dst_node *n);
+ void (*del_node)(struct dst_node *n);
+ int (*remap)(struct dst_request *req);
+ int (*error)(struct kst_state *state, int err);
+ struct module *owner;
+};
+
+@add_node.
+This callback is invoked when new node is being added into the storage,
+but before node is actually added into the storage, so that it could
+be accessed from it. When it is called, all appropriate initialization
+of the underlying device is already completed (system has been connected
+to remote node or got a reference to the local block device). At this
+stage algorithm can add node into private map.
+It must return zero on success or negative value otherwise.
+
+@del_node.
+This callback is invoked when node is being deleted from the storage,
+i.e. when its reference counter hits zero. It is called before
+any cleaning is performed.
+It must return zero on success or negative value otherwise.
+
+@remap.
+This callback is invoked each time new bio hits the storage.
+Request structure contains BIO itself, pointer to the node, which originally
+stores the whole region under given IO request, and various parameters
+used by storage core to process this block request.
+It must return zero on success or negative value otherwise. It is upto
+this method to call all cleaning if remapping failed, for example it must
+call kst_bio_endio() for given callback in case of error, which in turn
+will call bio_endio(). Note, that dst_request structure provided in this
+callback is allocated on stack, so if there is a need to use it outside
+of the given function, it must be cloned (it will happen automatically
+in state's push callback, but that copy will not be shared by any other
+user).
+
+@error.
+This callback is invoked for each error, which happend when processed
+requests for remote nodes or when talking to remote size
+of the local export node (state contains data related to data
+transfers over the network).
+If this function has fixed given error, it must return 0 or negative
+error value otherwise.
+
+@owner.
+This is module reference counter updated automatically by DST core.
+
+Algorithm must provide its name and above structure to the
+dst_alloc_alg() function, which will return a reference to the newly
+created algorithm.
+To remove it, one needs to call dst_remove_alg() with given algorithm
+pointer.
diff --git a/Documentation/dst/dst.txt b/Documentation/dst/dst.txt
new file mode 100644
index 0000000..a6ea126
--- /dev/null
+++ b/Documentation/dst/dst.txt
@@ -0,0 +1,69 @@
+Distributed storage. Design and implementation.
+http://tservice.net.ru/~s0mbre/old/?section=projects&item=dst
+
+ Evgeniy Polyakov
+
+This document is intended to briefly describe design and
+implementation details of the distributed storage project,
+aimed to create ability to group physically and/or logically
+distributed storages into single device.
+
+Main operational unit in the storage is node. Node can represent
+either remote storage, connected to local machine, or local
+device, or storage exported to the outside of the system.
+Here goes small explaination of basic therms.
+
+Local node.
+This node is just a logical link between block device (with given
+major and minor numbers) and structure in the DST hierarchy,
+which represents number of sectors on the area, corresponding to given
+block device. it can be a disk, a device mapper node or stacked
+block device on top of another underlying DST nodes.
+
+Local export node.
+Essentially the same as local node, but it allows to access
+to its data via network. Remote clients can connect to given local
+export node and read or write blocks according to its size.
+Blocks are then forwarded to underlying local node and processed
+there accordingly to the nature of the local node.
+
+Remote node.
+This type of nodes contain remotely accessible devices. One can think
+about remote nodes as remote disks, which can be connected to
+local system and combined into single storage. Remote nodes
+are presented as number of sectors accessed over the network
+by the local machine, where distributed storage is being formed.
+Remote node allows autoconfiguration - size of the storage and
+checksumming will be requested during node initialization (if remote
+node supports checksumming it will be turned on).
+
+
+Each node or set of them can be formed into single array, which
+in turn becomes a local node, which can be exported further by stacking
+a local export node on top of it.
+
+Each storage by itself is just a set of contiguous logical blocks, with
+allowed number of operations. Nodes, each of which has own start and size,
+are placed into storage by appropriate algorithm, which remaps
+logical sector number into real node's sector. One can create
+own algorithms, since DST has pluggable interface for that.
+Currently mirrored and linear algorithms are supported.
+One can find more details in Documentation/dst/algorithms.txt file.
+
+Main goal of the distributed storage is to combine remote nodes into
+single device, so each block IO request is being sent over the network
+(contrary requests for local nodes are handled by the gneric block
+layer features). Each network connection has number of variables which
+describe it (socket, list of requests, error handling and so on),
+which form kst_state structure. This network state is added into per-socket
+polling state machine, and can be processed by dedicated thread when
+becomes ready. This system forms asynchronous IO for given block
+requests. If block request can be processed without blocking, then
+no new structures are allocated and async part of the state is not used.
+
+When connection to the remote peer breaks, DST core tries to reconnect
+to failed node and no requests are marked as errorneous, instead
+they live in the queue until reconnectin is established.
+
+Userspace code, setup documentation and examples can be found on project's
+homepage above.
diff --git a/Documentation/dst/sysfs.txt b/Documentation/dst/sysfs.txt
new file mode 100644
index 0000000..79d79dc
--- /dev/null
+++ b/Documentation/dst/sysfs.txt
@@ -0,0 +1,30 @@
+This file describes sysfs files created for each storage.
+
+1. Per-storage files.
+Each storage has its own dir /sysfs/devices/$storage_name,
+which contains following files:
+
+alg - contains name of the algorithm used to created given storage
+name - name of the storage
+nodes - map of the storage (list of nodes and their sizes and starts)
+remove_all_nodes - writable file which allows to remove all nodes from given
+ storage
+n-$start-$cookie - per node directory, where
+ $start - start of the given node in sectors,
+ $cookie - unique node's id used by DST
+
+2. Per-node files.
+Node's files are located in /sysfs/devices/$storage_name/n-$start-$cookie
+directory, described above.
+
+chunks - private file for mirroring algorithm, contains map of update/dirty
+ sectors of the node, '-' means update, '+' is dirty and has to be
+ resynced sector
+clean - writable file, writing leads to marking node as clean (in sync)
+dirty - writable file, writing leads to marking node as dirty (not in sync)
+size - size of the given node in sectors
+start - start of the given node in the storage in sectors
+type - contains type of the node in the following format: $type: $dev
+ where $type is either 'L' or 'R' - local or remote acordingly,
+ and $dev is device name for local node (/dev/sda1 for example)
+ or address of the remote node (192.168.4.81:1025 for example)
--
Evgeniy Polyakov
^ permalink raw reply related
* [0/4] Distributed storage. Squizzed black-out of the dancing back-aching hippo.
From: Evgeniy Polyakov @ 2007-11-05 18:41 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, linux-fsdevel
Hi.
I'm pleased to announce 7'th and the final release of the distributed
storage subsystem (DST). It allows to form a storage on top of local and
remote nodes and combine them in linear or mirroring setup, which in
turn can be exported to remote nodes.
Short changelog:
* added strong checksum support (Castagnoli crc)
* extended autoconfiguration (added ability to request if remote
side supports strong checksum and turn it on if needed)
* documentation addon - sysfs files
* added clean/dirty sysfs files which allows to mark
node as clean (sinc) or dirty (not sync)
* fair number of bug fixes (including really tricky
bastards, which are unlikely to be found in real
setups, but which were still bugs)
* and the main one - added release name (it clearly shows my condition)
Overall list of features of the DST can be found on project's homepage:
http://tservice.net.ru/~s0mbre/old/?section=projects&item=dst
Thank you.
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
--
Evgeniy Polyakov
^ permalink raw reply
* [PATCH 5/5] powerpc: handle mpc8360 rev. 2.1 RGMII timing erratum
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
if on a rev. 2.1, adjust UCC clock and data timing characteristics
as specified in the rev.2.1 erratum #2.
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
---
arch/powerpc/platforms/83xx/mpc836x_mds.c | 31 ++++++++++++++++++++++++++--
1 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c
index 0f3855c..0a72260 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c
@@ -96,14 +96,39 @@ static void __init mpc836x_mds_setup_arch(void)
if ((np = of_find_compatible_node(NULL, "network", "ucc_geth"))
!= NULL){
+ uint svid;
+
/* Reset the Ethernet PHY */
- bcsr_regs[9] &= ~0x20;
+#define BCSR9_GETHRST 0x20
+ clrbits8(&bcsr_regs[9], BCSR9_GETHRST);
udelay(1000);
- bcsr_regs[9] |= 0x20;
+ setbits8(&bcsr_regs[9], BCSR9_GETHRST);
+
+ /* handle mpc8360ea rev.2.1 erratum 2: RGMII Timing */
+ svid = mfspr(SPRN_SVR);
+ if (svid == 0x80480021) {
+ void __iomem *immap;
+
+ immap = ioremap(get_immrbase() + 0x14a8, 8);
+
+ /*
+ * IMMR + 0x14A8[4:5] = 11 (clk delay for UCC 2)
+ * IMMR + 0x14A8[18:19] = 11 (clk delay for UCC 1)
+ */
+ setbits32(immap, 0x0c003000);
+
+ /*
+ * IMMR + 0x14AC[20:27] = 10101010
+ * (data delay for both UCC's)
+ */
+ clrsetbits_be32(immap + 4, 0xff0, 0xaa0);
+
+ iounmap(immap);
+ }
+
iounmap(bcsr_regs);
of_node_put(np);
}
-
#endif /* CONFIG_QUICC_ENGINE */
}
--
1.5.2.2
^ permalink raw reply related
* [PATCH 3/5] phylib: marvell: add support for TX-only and RX-only Internal Delay
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
Previously, Internal Delay specification implied the delay be
applied to both TX and RX. This patch allows for separate TX/RX-only
internal delay specification.
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
---
drivers/net/phy/marvell.c | 26 +++++++++++++++++---------
1 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index d2ede5f..55eb5c1 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -143,21 +143,29 @@ static int m88e1111_config_init(struct phy_device *phydev)
int err;
if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
- (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) {
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
int temp;
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
- temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
- if (temp < 0)
- return temp;
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
+ if (temp < 0)
+ return temp;
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
-
- err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
- if (err < 0)
- return err;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ temp &= ~MII_M1111_TX_DELAY;
+ temp |= MII_M1111_RX_DELAY;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ temp &= ~MII_M1111_RX_DELAY;
+ temp |= MII_M1111_TX_DELAY;
}
+ err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
+
temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
if (temp < 0)
return temp;
--
1.5.2.2
^ permalink raw reply related
* [PATCH 1/5] powerpc: document rgmii-rxid and rgmii-txid phy-connection-types
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
A h/w bug requires we program the PHY in RGMII mode for internal delay
on the receive or transmit side only; document the new property values.
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
---
Documentation/powerpc/booting-without-of.txt | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index a96e853..b97721c 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1634,8 +1634,9 @@ platforms are moved over to use the flattened-device-tree model.
MAC addresses passed by the firmware when no information other
than indices is available to associate an address with a device.
- phy-connection-type : a string naming the controller/PHY interface type,
- i.e., "mii" (default), "rmii", "gmii", "rgmii", "rgmii-id", "tbi",
- or "rtbi".
+ i.e., "mii" (default), "rmii", "gmii", "rgmii", "rgmii-id" (Internal
+ Delay), "rgmii-txid" (delay on TX only), "rgmii-rxid" (delay on RX only),
+ "tbi", or "rtbi".
Example:
ucc@2000 {
--
1.5.2.2
^ permalink raw reply related
* [PATCH 4/5] ucc_geth: handle passing of RX-only and TX-only internal delay PHY connection type parameters
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
Extend the RGMII-Internal Delay specification case to include
TX-only and RX-only variants.
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
---
drivers/net/ucc_geth.c | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index a3ff270..7f68990 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -1460,6 +1460,8 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+ (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+ (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
upsmr |= UPSMR_RPM;
switch (ugeth->max_speed) {
@@ -1557,6 +1559,8 @@ static void adjust_link(struct net_device *dev)
if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+ (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+ (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
if (phydev->speed == SPEED_10)
upsmr |= UPSMR_R10M;
@@ -3795,6 +3799,10 @@ static phy_interface_t to_phy_interface(const char *phy_connection_type)
return PHY_INTERFACE_MODE_RGMII;
if (strcasecmp(phy_connection_type, "rgmii-id") == 0)
return PHY_INTERFACE_MODE_RGMII_ID;
+ if (strcasecmp(phy_connection_type, "rgmii-txid") == 0)
+ return PHY_INTERFACE_MODE_RGMII_TXID;
+ if (strcasecmp(phy_connection_type, "rgmii-rxid") == 0)
+ return PHY_INTERFACE_MODE_RGMII_RXID;
if (strcasecmp(phy_connection_type, "rtbi") == 0)
return PHY_INTERFACE_MODE_RTBI;
@@ -3889,6 +3897,8 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
case PHY_INTERFACE_MODE_GMII:
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_RTBI:
max_speed = SPEED_1000;
--
1.5.2.2
^ permalink raw reply related
* [PATCH 2/5] phylib: add PHY interface modes for internal delay for tx and rx only
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
Allow phylib specification of cases where hardware needs to configure
PHYs for Internal Delay only on either RX or TX (not both).
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
---
include/linux/phy.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/include/linux/phy.h b/include/linux/phy.h
index f0742b6..e10763d 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -58,6 +58,8 @@ typedef enum {
PHY_INTERFACE_MODE_RMII,
PHY_INTERFACE_MODE_RGMII,
PHY_INTERFACE_MODE_RGMII_ID,
+ PHY_INTERFACE_MODE_RGMII_RXID,
+ PHY_INTERFACE_MODE_RGMII_TXID,
PHY_INTERFACE_MODE_RTBI
} phy_interface_t;
--
1.5.2.2
^ permalink raw reply related
* [PATCH 0/5] fixups for mpc8360 rev. 2.1 erratum #2 (RGMII Timing)
From: Kim Phillips @ 2007-11-05 18:15 UTC (permalink / raw)
To: Li Yang, Kumar Gala, netdev, linuxppc-dev; +Cc: jgarzik, paulus
Hello all,
the following patches fix RGMII timing for rev. 2.1 of the mpc8360,
according to erratum #2 (erratum text included below). Basically the
most intrusive part is the addition of two new RGMII Internal Delay
modes; one for TX delay only, and the other for RX delay only (i.e, not
both at the same time).
Please review, and since this affects both netdev and powerpc trees,
one maintainer should ack them for the other to push upstream (i.e,
Kumar acks them, and Leo picks them up to go through netdev or the
other way around; either way is fine with me). I'm hoping they're
trivial enough to go in 2.6.24.
Depending on how the review goes, a follow-on patch to u-boot will be
sent out that fixes up the phy-connection-type in the device tree (from
"rgmii-id" to "rgmii-rxid" iff on mpc8360rev2.1).
Thanks,
Kim
mpc8360 rev 2.1 erratum #2:
-----------
Recommended AC timings for chip 8360Rev2.1 UCC ETH RGMII when working
with Rev Pilot MDS for proper RGMII operation:
IMMR_BASE + 0x14A8[4:5] = 11 (clk delay for UCC 2)
IMMR_BASE + 0x14A8[18:19] = 11 (clk delay for UCC 1)
IMMR_BASE + 0x14AC[20:27] = 10101010 (data delay for both UCC's)
The Phy (Marvell 88e1111) should be configured NOT to work with RGMII
delay for TxD.
^ permalink raw reply
* [PATCH 4/4] NET : makes sctp use the {DEFINE|REF}_PROTO_INUSE infrastructure
From: Eric Dumazet @ 2007-11-05 18:03 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 259 bytes --]
Trivial patch to make "sctcp,sctpv6" protocols uses the fast "inuse
sockets" infrastructure
Each protocol use then a static percpu var, instead of a dynamic one.
This saves some ram and some cpu cycles
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
[-- Attachment #2: prot_inuse_sctp.patch --]
[-- Type: text/plain, Size: 1053 bytes --]
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index bd6f42a..a7ecf31 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -6455,6 +6455,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
}
+DEFINE_PROTO_INUSE(sctp)
+
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",
@@ -6483,9 +6485,12 @@ struct proto sctp_prot = {
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
+ REF_PROTO_INUSE(sctp)
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+DEFINE_PROTO_INUSE(sctpv6)
+
struct proto sctpv6_prot = {
.name = "SCTPv6",
.owner = THIS_MODULE,
@@ -6513,5 +6518,6 @@ struct proto sctpv6_prot = {
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
+ REF_PROTO_INUSE(sctpv6)
};
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
^ permalink raw reply related
* [PATCH 3/4] NET : makes ipv6 use the {DEFINE|REF}_PROTO_INUSE infrastructure
From: Eric Dumazet @ 2007-11-05 18:02 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 437 bytes --]
Trivial patch to make "tcpv6,udpv6,udplitev6,rawv6" protocols uses the
fast "inuse sockets" infrastructure
Each protocol use then a static percpu var, instead of a dynamic one.
This saves some ram and some cpu cycles
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
net/ipv6/raw.c | 3 +++
net/ipv6/tcp_ipv6.c | 3 +++
net/ipv6/udp.c | 3 +++
net/ipv6/udplite.c | 3 +++
4 files changed, 12 insertions(+)
[-- Attachment #2: prot_inuse_ipv6.patch --]
[-- Type: text/plain, Size: 2279 bytes --]
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ca24ef1..807260d 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1144,6 +1144,8 @@ static int rawv6_init_sk(struct sock *sk)
return(0);
}
+DEFINE_PROTO_INUSE(rawv6)
+
struct proto rawv6_prot = {
.name = "RAWv6",
.owner = THIS_MODULE,
@@ -1166,6 +1168,7 @@ struct proto rawv6_prot = {
.compat_setsockopt = compat_rawv6_setsockopt,
.compat_getsockopt = compat_rawv6_getsockopt,
#endif
+ REF_PROTO_INUSE(rawv6)
};
#ifdef CONFIG_PROC_FS
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 4b90328..c7b8921 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -2107,6 +2107,8 @@ void tcp6_proc_exit(void)
}
#endif
+DEFINE_PROTO_INUSE(tcpv6)
+
struct proto tcpv6_prot = {
.name = "TCPv6",
.owner = THIS_MODULE,
@@ -2141,6 +2143,7 @@ struct proto tcpv6_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
+ REF_PROTO_INUSE(tcpv6)
};
static struct inet6_protocol tcpv6_protocol = {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index caebad6..d198ed4 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -971,6 +971,8 @@ void udp6_proc_exit(void) {
/* ------------------------------------------------------------------------ */
+DEFINE_PROTO_INUSE(udpv6)
+
struct proto udpv6_prot = {
.name = "UDPv6",
.owner = THIS_MODULE,
@@ -992,6 +994,7 @@ struct proto udpv6_prot = {
.compat_setsockopt = compat_udpv6_setsockopt,
.compat_getsockopt = compat_udpv6_getsockopt,
#endif
+ REF_PROTO_INUSE(udpv6)
};
static struct inet_protosw udpv6_protosw = {
diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c
index 766566f..5a0379f 100644
--- a/net/ipv6/udplite.c
+++ b/net/ipv6/udplite.c
@@ -40,6 +40,8 @@ static int udplite_v6_get_port(struct sock *sk, unsigned short snum)
return udplite_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
+DEFINE_PROTO_INUSE(udplitev6)
+
struct proto udplitev6_prot = {
.name = "UDPLITEv6",
.owner = THIS_MODULE,
@@ -62,6 +64,7 @@ struct proto udplitev6_prot = {
.compat_setsockopt = compat_udpv6_setsockopt,
.compat_getsockopt = compat_udpv6_getsockopt,
#endif
+ REF_PROTO_INUSE(udplitev6)
};
static struct inet_protosw udplite6_protosw = {
^ permalink raw reply related
* Re: [PATCH 1/2] NET: Re-add VLAN tag for devices incapable of keeping it
From: Patrick McHardy @ 2007-11-05 18:00 UTC (permalink / raw)
To: Dave Johnson
Cc: David Miller, jes, mchan, ram.vepa, linux-kernel, netdev, bguo
In-Reply-To: <18223.22291.622615.129374@zeus.sw.starentnetworks.com>
Dave Johnson wrote:
> +/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */
> +static inline int __vlan_hwaccel_rx(struct sk_buff *skb,
> + struct vlan_group *grp,
> + unsigned short vlan_tag, int polling)
> +{
> ...
> + /* Driver removed vlan tag from the packet, but we have no
> + * vlan device for this VID.
> + *
> + * This can occur for 2 reasons:
> + * 1) Have a vlan group, we want some VIDs, just not this VID.
> + * 2) Have no vlan group, but hardware limitation requires it
> + * to remove vlan tags anyway.
> + *
> + * Normally if no vlan group is configured, the underlying
> + * driver will configure the hardware to NOT strip out the
> + * vlan tag. It can then call netif_receive_skb/netif_rx
> + * with the vlan tag still intact. However some devices
> + * cannot be configured to keep the vlan tag and must not
> + * call netif_receive_skb/netif_rx with the vlan tag
> + * missing. This would allow the normal protocol handlers
> + * to process this tagged packet as if it arived without a
> + * vlan tag.
> + *
> + * We need to re-add the TAG and hand the packet off to the
> + * base device with the TAG present so any protocol handlers
> + * (PF_PACKET, etc...) will get a chance to see it.
> + */
This looks like a rather expensive operation for the unlikely case
that packets will be received by a packet socket. IMO it should only
be reconstructed if actually needed, by af_packet itself.
As we discussed some time back storing the VLAN tag in the CB on
TX clashes with other users of the CB like qdiscs, so we need a
new field in the skb for this anyway.
^ permalink raw reply
* [PATCH 2/4] NET : makes ipv4 use the {DEFINE|REF}_PROTO_INUSE infrastructure
From: Eric Dumazet @ 2007-11-05 17:59 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 429 bytes --]
Trivial patch to make "tcp,udp,udplite,raw" protocols uses the fast
"inuse sockets" infrastructure
Each protocol use then a static percpu var, instead of a dynamic one.
This saves some ram and some cpu cycles
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
net/ipv4/raw.c | 3 +++
net/ipv4/tcp_ipv4.c | 3 +++
net/ipv4/udp.c | 3 +++
net/ipv4/udplite.c | 3 +++
4 files changed, 12 insertions(+)
[-- Attachment #2: prot_inuse_ipv4.patch --]
[-- Type: text/plain, Size: 2220 bytes --]
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 3916fac..66b42f5 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -760,6 +760,8 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
}
}
+DEFINE_PROTO_INUSE(raw)
+
struct proto raw_prot = {
.name = "RAW",
.owner = THIS_MODULE,
@@ -781,6 +783,7 @@ struct proto raw_prot = {
.compat_setsockopt = compat_raw_setsockopt,
.compat_getsockopt = compat_raw_getsockopt,
#endif
+ REF_PROTO_INUSE(raw)
};
#ifdef CONFIG_PROC_FS
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index eec02b2..a5c8e4b 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2417,6 +2417,8 @@ void tcp4_proc_exit(void)
}
#endif /* CONFIG_PROC_FS */
+DEFINE_PROTO_INUSE(tcp)
+
struct proto tcp_prot = {
.name = "TCP",
.owner = THIS_MODULE,
@@ -2451,6 +2453,7 @@ struct proto tcp_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
+ REF_PROTO_INUSE(tcp)
};
void __init tcp_v4_init(struct net_proto_family *ops)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 4bc25b4..03c400c 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1430,6 +1430,8 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
}
+DEFINE_PROTO_INUSE(udp)
+
struct proto udp_prot = {
.name = "UDP",
.owner = THIS_MODULE,
@@ -1452,6 +1454,7 @@ struct proto udp_prot = {
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
+ REF_PROTO_INUSE(udp)
};
/* ------------------------------------------------------------------------ */
diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c
index 9497720..f5baeb3 100644
--- a/net/ipv4/udplite.c
+++ b/net/ipv4/udplite.c
@@ -44,6 +44,8 @@ static struct net_protocol udplite_protocol = {
.no_policy = 1,
};
+DEFINE_PROTO_INUSE(udplite)
+
struct proto udplite_prot = {
.name = "UDP-Lite",
.owner = THIS_MODULE,
@@ -67,6 +69,7 @@ struct proto udplite_prot = {
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
+ REF_PROTO_INUSE(udplite)
};
static struct inet_protosw udplite4_protosw = {
^ permalink raw reply related
* [PATCH 1/4] NET : defines an infrastructure to keep 'inuse' changes in an efficent SMP/NUMA way.
From: Eric Dumazet @ 2007-11-05 17:56 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 1510 bytes --]
"struct proto" currently uses an array stats[NR_CPUS] to track change on
'inuse' sockets per protocol.
If NR_CPUS is big, this means we use a big memory area for this.
Moreover, all this memory area is located on a single node on NUMA
machines, increasing memory pressure on the boot node.
In this patch, I tried to :
- Keep a fast !CONFIG_SMP implementation
- Keep a fast CONFIG_SMP implementation for often used protocols
(tcp,udp,raw,...)
- Introduce a NUMA efficient implementation
Some helper macros are defined in include/net/sock.h
These macros take into account CONFIG_SMP
If a "struct proto" is declared without using DEFINE_PROTO_INUSE /
REF_PROTO_INUSE
macros, it will automatically use a default implementation, using a
dynamically allocated percpu zone.
This default implementation will be NUMA efficient, but might use 32/64
bytes per possible cpu
because of current alloc_percpu() implementation.
However it still should be better than previous implementation based on
stats[NR_CPUS] field.
When a "struct proto" is changed to use the new macros, we use a single
static "int" percpu variable,
lowering the memory and cpu costs, still preserving NUMA efficiency.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
include/net/sock.h | 63 ++++++++++++++++++++++++++++++++++++++-----
net/core/sock.c | 48 ++++++++++++++++++++++++++++++++
net/ipv4/proc.c | 19 ++----------
net/ipv6/proc.c | 19 ++----------
4 files changed, 112 insertions(+), 37 deletions(-)
[-- Attachment #2: prot_inuse_infra.patch --]
[-- Type: text/plain, Size: 7186 bytes --]
diff --git a/include/net/sock.h b/include/net/sock.h
index 20de3fa..5504fb9 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -560,6 +560,14 @@ struct proto {
void (*unhash)(struct sock *sk);
int (*get_port)(struct sock *sk, unsigned short snum);
+#ifdef CONFIG_SMP
+ /* Keeping track of sockets in use */
+ void (*inuse_add)(struct proto *prot, int inc);
+ int (*inuse_getval)(const struct proto *prot);
+ int *inuse_ptr;
+#else
+ int inuse;
+#endif
/* Memory pressure */
void (*enter_memory_pressure)(void);
atomic_t *memory_allocated; /* Current allocated memory. */
@@ -592,12 +600,38 @@ struct proto {
#ifdef SOCK_REFCNT_DEBUG
atomic_t socks;
#endif
- struct {
- int inuse;
- u8 __pad[SMP_CACHE_BYTES - sizeof(int)];
- } stats[NR_CPUS];
};
+/*
+ * Special macros to let protos use a fast version of inuse{get|add}
+ * using a static percpu variable per proto instead of an allocated one,
+ * saving one dereference.
+ * This might be changed if/when dynamic percpu vars become fast.
+ */
+#ifdef CONFIG_SMP
+# define DEFINE_PROTO_INUSE(NAME) \
+static DEFINE_PER_CPU(int, NAME##_inuse); \
+static void NAME##_inuse_add(struct proto *prot, int inc) \
+{ \
+ __get_cpu_var(NAME##_inuse) += inc; \
+} \
+ \
+static int NAME##_inuse_getval(const struct proto *prot)\
+{ \
+ int res = 0, cpu; \
+ \
+ for_each_possible_cpu(cpu) \
+ res += per_cpu(NAME##_inuse, cpu); \
+ return res; \
+}
+# define REF_PROTO_INUSE(NAME) \
+ .inuse_add = NAME##_inuse_add, \
+ .inuse_getval = NAME##_inuse_getval,
+#else
+# define DEFINE_PROTO_INUSE(NAME)
+# define REF_PROTO_INUSE(NAME)
+#endif
+
extern int proto_register(struct proto *prot, int alloc_slab);
extern void proto_unregister(struct proto *prot);
@@ -629,12 +663,29 @@ static inline void sk_refcnt_debug_release(const struct sock *sk)
/* Called with local bh disabled */
static __inline__ void sock_prot_inc_use(struct proto *prot)
{
- prot->stats[smp_processor_id()].inuse++;
+#ifdef CONFIG_SMP
+ prot->inuse_add(prot, 1);
+#else
+ prot->inuse++;
+#endif
}
static __inline__ void sock_prot_dec_use(struct proto *prot)
{
- prot->stats[smp_processor_id()].inuse--;
+#ifdef CONFIG_SMP
+ prot->inuse_add(prot, -1);
+#else
+ prot->inuse--;
+#endif
+}
+
+static __inline__ int sock_prot_inuse(struct proto *proto)
+{
+#ifdef CONFIG_SMP
+ return proto->inuse_getval(proto);
+#else
+ return proto->inuse;
+#endif
}
/* With per-bucket locks this operation is not-atomic, so that
diff --git a/net/core/sock.c b/net/core/sock.c
index 12ad206..e077f26 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1801,12 +1801,41 @@ EXPORT_SYMBOL(sk_common_release);
static DEFINE_RWLOCK(proto_list_lock);
static LIST_HEAD(proto_list);
+#ifdef CONFIG_SMP
+/*
+ * Define default functions to keep track of inuse sockets per protocol
+ * Note that often used protocols use dedicated functions to get a speed increase.
+ * (see DEFINE_PROTO_INUSE/REF_PROTO_INUSE)
+ */
+static void inuse_add(struct proto *prot, int inc)
+{
+ per_cpu_ptr(prot->inuse_ptr, smp_processor_id())[0] += inc;
+}
+
+static int inuse_get(const struct proto *prot)
+{
+ int res = 0, cpu;
+ for_each_possible_cpu(cpu)
+ res += per_cpu_ptr(prot->inuse_ptr, cpu)[0];
+ return res;
+}
+#endif
+
int proto_register(struct proto *prot, int alloc_slab)
{
char *request_sock_slab_name = NULL;
char *timewait_sock_slab_name;
int rc = -ENOBUFS;
+#ifdef CONFIG_SMP
+ if (!prot->inuse_getval || !prot->inuse_add) {
+ prot->inuse_ptr = alloc_percpu(int);
+ if (prot->inuse_ptr == NULL)
+ goto out;
+ prot->inuse_getval = inuse_get;
+ prot->inuse_add = inuse_add;
+ }
+#endif
if (alloc_slab) {
prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
SLAB_HWCACHE_ALIGN, NULL);
@@ -1814,7 +1843,7 @@ int proto_register(struct proto *prot, int alloc_slab)
if (prot->slab == NULL) {
printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
prot->name);
- goto out;
+ goto out_free_inuse;
}
if (prot->rsk_prot != NULL) {
@@ -1873,6 +1902,15 @@ out_free_request_sock_slab_name:
out_free_sock_slab:
kmem_cache_destroy(prot->slab);
prot->slab = NULL;
+out_free_inuse:
+#ifdef CONFIG_SMP
+ if (prot->inuse_ptr != NULL) {
+ free_percpu(prot->inuse_ptr);
+ prot->inuse_ptr = NULL;
+ prot->inuse_getval = NULL;
+ prot->inuse_add = NULL;
+ }
+#endif
goto out;
}
@@ -1884,6 +1922,14 @@ void proto_unregister(struct proto *prot)
list_del(&prot->node);
write_unlock(&proto_list_lock);
+#ifdef CONFIG_SMP
+ if (prot->inuse_ptr != NULL) {
+ free_percpu(prot->inuse_ptr);
+ prot->inuse_ptr = NULL;
+ prot->inuse_getval = NULL;
+ prot->inuse_add = NULL;
+ }
+#endif
if (prot->slab != NULL) {
kmem_cache_destroy(prot->slab);
prot->slab = NULL;
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index ffdccc0..ce34b28 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -46,17 +46,6 @@
#include <net/sock.h>
#include <net/raw.h>
-static int fold_prot_inuse(struct proto *proto)
-{
- int res = 0;
- int cpu;
-
- for_each_possible_cpu(cpu)
- res += proto->stats[cpu].inuse;
-
- return res;
-}
-
/*
* Report socket allocation statistics [mea@utu.fi]
*/
@@ -64,12 +53,12 @@ static int sockstat_seq_show(struct seq_file *seq, void *v)
{
socket_seq_show(seq);
seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
- fold_prot_inuse(&tcp_prot), atomic_read(&tcp_orphan_count),
+ sock_prot_inuse(&tcp_prot), atomic_read(&tcp_orphan_count),
tcp_death_row.tw_count, atomic_read(&tcp_sockets_allocated),
atomic_read(&tcp_memory_allocated));
- seq_printf(seq, "UDP: inuse %d\n", fold_prot_inuse(&udp_prot));
- seq_printf(seq, "UDPLITE: inuse %d\n", fold_prot_inuse(&udplite_prot));
- seq_printf(seq, "RAW: inuse %d\n", fold_prot_inuse(&raw_prot));
+ seq_printf(seq, "UDP: inuse %d\n", sock_prot_inuse(&udp_prot));
+ seq_printf(seq, "UDPLITE: inuse %d\n", sock_prot_inuse(&udplite_prot));
+ seq_printf(seq, "RAW: inuse %d\n", sock_prot_inuse(&raw_prot));
seq_printf(seq, "FRAG: inuse %d memory %d\n",
ip_frag_nqueues(), ip_frag_mem());
return 0;
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index be526ad..8631ed7 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -32,27 +32,16 @@
static struct proc_dir_entry *proc_net_devsnmp6;
-static int fold_prot_inuse(struct proto *proto)
-{
- int res = 0;
- int cpu;
-
- for_each_possible_cpu(cpu)
- res += proto->stats[cpu].inuse;
-
- return res;
-}
-
static int sockstat6_seq_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "TCP6: inuse %d\n",
- fold_prot_inuse(&tcpv6_prot));
+ sock_prot_inuse(&tcpv6_prot));
seq_printf(seq, "UDP6: inuse %d\n",
- fold_prot_inuse(&udpv6_prot));
+ sock_prot_inuse(&udpv6_prot));
seq_printf(seq, "UDPLITE6: inuse %d\n",
- fold_prot_inuse(&udplitev6_prot));
+ sock_prot_inuse(&udplitev6_prot));
seq_printf(seq, "RAW6: inuse %d\n",
- fold_prot_inuse(&rawv6_prot));
+ sock_prot_inuse(&rawv6_prot));
seq_printf(seq, "FRAG6: inuse %d memory %d\n",
ip6_frag_nqueues(), ip6_frag_mem());
return 0;
^ permalink raw reply related
* [PATCH 0/4] NET : struct proto diet
From: Eric Dumazet @ 2007-11-05 17:52 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
Hi
After reading David's machines have NR_CPUS set to 4096, I realized how
fat was 'struct proto', since it uses a stats[NR_CPUS] field to keep
track of inuse sockets per protocol.
With NR_CPUS = 4096, this means we allocate 4096*SMP_CACHE_BYTES bytes
per 'struct proto'.
As these structures are static (data section of kernel), this grows data
kernel section by 256 KB per protocol.
This patch series does some cleanup and optimization.
[PATCH 1/4] NET : defines an infrastructure to keep 'inuse' changes in
an efficent SMP/NUMA way.
[PATCH 2/4] NET : makes ipv4 use the {DEFINE|REF}_PROTO_INUSE
infrastructure
[PATCH 3/4] NET : makes ipv6 use the {DEFINE|REF}_PROTO_INUSE
infrastructure
[PATCH 4/4] NET : makes sctp use the {DEFINE|REF}_PROTO_INUSE
infrastructure
Thank you
Eric
^ permalink raw reply
* [PATCH 2/2] NET: Re-add VLAN tag for devices incapable of keeping it
From: Dave Johnson @ 2007-11-05 17:47 UTC (permalink / raw)
To: David Miller, jes, mchan, ram.vepa, linux-kernel, netdev, bguo
In-Reply-To: <472A6089.7020104@hp.com>
This patch changes the following drivers to use vlan_hwaccel_rx()
and/or vlan_hwaccel_receive_skb() with a NULL vlan group if needed as
they are not setup to dynamically enable/disable vlan removal in
their MAC based on the vlan group:
drivers/net/tg3.c Tested on BCM5704, looks good
drivers/net/bnx2.c Tested on BCM5708, looks good
drivers/net/acenic.c Not tested, I don't have one of these
drivers/net/s2io.c Not tested, I don't have one of these
In addition, these drivers might also need changes, but should
probably be done by the maintainer as it's not clear exactly what
change is needed:
drivers/net/amd8111e.c
drivers/net/cxgb3/*
Signed-off-by: Dave Johnson <djohnson@sw.starentnetworks.com>
===== drivers/net/acenic.c 1.77 vs edited =====
--- 1.77/drivers/net/acenic.c 2007-07-24 16:28:41 -04:00
+++ edited/drivers/net/acenic.c 2007-11-03 12:27:40 -04:00
@@ -2036,7 +2036,7 @@
/* send it up */
#if ACENIC_DO_VLAN
- if (ap->vlgrp && (bd_flags & BD_FLG_VLAN_TAG)) {
+ if (bd_flags & BD_FLG_VLAN_TAG) {
vlan_hwaccel_rx(skb, ap->vlgrp, retdesc->vlan);
} else
#endif
===== drivers/net/bnx2.c 1.135 vs edited =====
--- 1.135/drivers/net/bnx2.c 2007-09-20 15:14:21 -04:00
+++ edited/drivers/net/bnx2.c 2007-11-05 09:34:26 -05:00
@@ -2493,7 +2493,8 @@
}
#ifdef BCM_VLAN
- if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) && (bp->vlgrp != 0)) {
+ if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) &&
+ (bp->vlgrp || (bp->flags & ASF_ENABLE_FLAG))) {
vlan_hwaccel_receive_skb(skb, bp->vlgrp,
rx_hdr->l2_fhdr_vlan_tag);
}
===== drivers/net/s2io.c 1.124 vs edited =====
--- 1.124/drivers/net/s2io.c 2007-08-03 18:10:44 -04:00
+++ edited/drivers/net/s2io.c 2007-11-03 12:29:09 -04:00
@@ -6834,8 +6834,7 @@
sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize;
if (!sp->lro) {
skb->protocol = eth_type_trans(skb, dev);
- if ((sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2) &&
- vlan_strip_flag)) {
+ if (RXD_GET_VLAN_TAG(rxdp->Control_2) && vlan_strip_flag) {
/* Queueing the vlan frame to the upper layer */
if (napi)
vlan_hwaccel_receive_skb(skb, sp->vlgrp,
===== drivers/net/tg3.c 1.523 vs edited =====
--- 1.523/drivers/net/tg3.c 2007-09-11 04:28:44 -04:00
+++ edited/drivers/net/tg3.c 2007-11-05 09:38:33 -05:00
@@ -3417,7 +3417,7 @@
skb->protocol = eth_type_trans(skb, tp->dev);
#if TG3_VLAN_TAG_USED
- if (tp->vlgrp != NULL &&
+ if ((tp->vlgrp || (tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) &&
desc->type_flags & RXD_FLAG_VLAN) {
tg3_vlan_rx(tp, skb,
desc->err_vlan & RXD_VLAN_MASK);
^ permalink raw reply
* [PATCH 1/2] NET: Re-add VLAN tag for devices incapable of keeping it
From: Dave Johnson @ 2007-11-05 17:46 UTC (permalink / raw)
To: David Miller, jes, mchan, ram.vepa, linux-kernel, netdev, bguo
In-Reply-To: <472A6089.7020104@hp.com>
Some MACs are configured to remove VLAN tags on RX packets even if
the kernel isn't setup to use VLANs.
On these drivers it is bad to simply pass the packets with the VLAN
tag removed to the network stack. This gives the network stack the
impression these packets arrived from the network without a VLAN tag
even though they had one. This has 2 issues: 1, PF_PACKET sockets see
the packet without the VLAN tag when bound to the base device; and 2,
the L3 stacks will process these packets as if they had no tag to
begin with.
This patch changes vlan_hwaccel_rx() and vlan_hwaccel_receive_skb() to
accept a NULL vlan group. This simplifies the drivers as they can
call vlan_hwaccel_rx()/vlan_hwaccel_receive_skb() if the MAC
has removed the VLAN tag, or netif_rx()/netif_receive_skb() if the
MAC hasn't removed a VLAN tag (or the packet arrived untagged).
If the vlan group is NULL or no device for the received VID exists,
vlan_hwaccel_rx()/vlan_hwaccel_receive_skb() will now re-add a VLAN
tag and pass the packet to the base device with tag intact.
It is still preferable for the drivers to disable VLAN tag removal in
the hardware if no vlan group is configured. However for devices that
cannot do this the change will ensure tagged packets remain tagged in
the network stack.
Signed-off-by: Dave Johnson <djohnson@sw.starentnetworks.com>
---
Note, __vlan_hwaccel_rx() needed to move below __vlan_put_tag() so the
change really isn't as big as it may look below.
===== include/linux/if_vlan.h 1.25 vs edited =====
--- 1.25/include/linux/if_vlan.h 2007-07-14 21:53:28 -04:00
+++ edited/include/linux/if_vlan.h 2007-11-03 14:26:43 -04:00
@@ -164,71 +164,6 @@
(VLAN_TX_SKB_CB(__skb)->magic == VLAN_TX_COOKIE_MAGIC)
#define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag)
-/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */
-static inline int __vlan_hwaccel_rx(struct sk_buff *skb,
- struct vlan_group *grp,
- unsigned short vlan_tag, int polling)
-{
- struct net_device_stats *stats;
-
- if (skb_bond_should_drop(skb)) {
- dev_kfree_skb_any(skb);
- return NET_RX_DROP;
- }
-
- skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK);
- if (skb->dev == NULL) {
- dev_kfree_skb_any(skb);
-
- /* Not NET_RX_DROP, this is not being dropped
- * due to congestion.
- */
- return 0;
- }
-
- skb->dev->last_rx = jiffies;
-
- stats = vlan_dev_get_stats(skb->dev);
- stats->rx_packets++;
- stats->rx_bytes += skb->len;
-
- skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag);
- switch (skb->pkt_type) {
- case PACKET_BROADCAST:
- break;
-
- case PACKET_MULTICAST:
- stats->multicast++;
- break;
-
- case PACKET_OTHERHOST:
- /* Our lower layer thinks this is not local, let's make sure.
- * This allows the VLAN to have a different MAC than the underlying
- * device, and still route correctly.
- */
- if (!compare_ether_addr(eth_hdr(skb)->h_dest,
- skb->dev->dev_addr))
- skb->pkt_type = PACKET_HOST;
- break;
- };
-
- return (polling ? netif_receive_skb(skb) : netif_rx(skb));
-}
-
-static inline int vlan_hwaccel_rx(struct sk_buff *skb,
- struct vlan_group *grp,
- unsigned short vlan_tag)
-{
- return __vlan_hwaccel_rx(skb, grp, vlan_tag, 0);
-}
-
-static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb,
- struct vlan_group *grp,
- unsigned short vlan_tag)
-{
- return __vlan_hwaccel_rx(skb, grp, vlan_tag, 1);
-}
-
/**
* __vlan_put_tag - regular VLAN tag inserting
* @skb: skbuff to tag
@@ -243,10 +178,19 @@
static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short tag)
{
struct vlan_ethhdr *veth;
+ int new_mac_offset;
+
+ if (skb_mac_header_was_set(skb))
+ /* if the MAC header is not at skb->data we need to push to that
+ * place then pull back before returning to the caller.
+ */
+ new_mac_offset = VLAN_HLEN + skb->data - skb_mac_header(skb);
+ else
+ new_mac_offset = VLAN_HLEN;
- if (skb_headroom(skb) < VLAN_HLEN) {
+ if (skb_headroom(skb) < new_mac_offset) {
struct sk_buff *sk_tmp = skb;
- skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN);
+ skb = skb_realloc_headroom(sk_tmp, new_mac_offset);
kfree_skb(sk_tmp);
if (!skb) {
printk(KERN_ERR "vlan: failed to realloc headroom\n");
@@ -260,7 +204,7 @@
}
}
- veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);
+ veth = (struct vlan_ethhdr *)skb_push(skb, new_mac_offset);
/* Move the mac addresses to the beginning of the new header. */
memmove(skb->data, skb->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN);
@@ -275,6 +219,8 @@
skb->mac_header -= VLAN_HLEN;
skb->network_header -= VLAN_HLEN;
+ /* back to where we started, minus the new vlan header */
+ skb_pull(skb, new_mac_offset-VLAN_HLEN);
return skb;
}
@@ -372,6 +318,98 @@
} else {
return __vlan_get_tag(skb, tag);
}
+}
+
+/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */
+static inline int __vlan_hwaccel_rx(struct sk_buff *skb,
+ struct vlan_group *grp,
+ unsigned short vlan_tag, int polling)
+{
+ struct net_device_stats *stats;
+ struct net_device *new_dev;
+
+ if (skb_bond_should_drop(skb)) {
+ dev_kfree_skb_any(skb);
+ return NET_RX_DROP;
+ }
+
+ if (grp)
+ new_dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK);
+
+ if (!grp || !new_dev) {
+ /* Driver removed vlan tag from the packet, but we have no
+ * vlan device for this VID.
+ *
+ * This can occur for 2 reasons:
+ * 1) Have a vlan group, we want some VIDs, just not this VID.
+ * 2) Have no vlan group, but hardware limitation requires it
+ * to remove vlan tags anyway.
+ *
+ * Normally if no vlan group is configured, the underlying
+ * driver will configure the hardware to NOT strip out the
+ * vlan tag. It can then call netif_receive_skb/netif_rx
+ * with the vlan tag still intact. However some devices
+ * cannot be configured to keep the vlan tag and must not
+ * call netif_receive_skb/netif_rx with the vlan tag
+ * missing. This would allow the normal protocol handlers
+ * to process this tagged packet as if it arived without a
+ * vlan tag.
+ *
+ * We need to re-add the TAG and hand the packet off to the
+ * base device with the TAG present so any protocol handlers
+ * (PF_PACKET, etc...) will get a chance to see it.
+ */
+
+ skb = __vlan_put_tag(skb, vlan_tag);
+
+ if (likely(skb))
+ return (polling ? netif_receive_skb(skb) : netif_rx(skb));
+ else
+ return NET_RX_DROP;
+ }
+
+ skb->dev = new_dev;
+ skb->dev->last_rx = jiffies;
+
+ stats = vlan_dev_get_stats(skb->dev);
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
+ skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag);
+ switch (skb->pkt_type) {
+ case PACKET_BROADCAST:
+ break;
+
+ case PACKET_MULTICAST:
+ stats->multicast++;
+ break;
+
+ case PACKET_OTHERHOST:
+ /* Our lower layer thinks this is not local, let's make sure.
+ * This allows the VLAN to have a different MAC than the underlying
+ * device, and still route correctly.
+ */
+ if (!compare_ether_addr(eth_hdr(skb)->h_dest,
+ skb->dev->dev_addr))
+ skb->pkt_type = PACKET_HOST;
+ break;
+ };
+
+ return (polling ? netif_receive_skb(skb) : netif_rx(skb));
+}
+
+static inline int vlan_hwaccel_rx(struct sk_buff *skb,
+ struct vlan_group *grp,
+ unsigned short vlan_tag)
+{
+ return __vlan_hwaccel_rx(skb, grp, vlan_tag, 0);
+}
+
+static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb,
+ struct vlan_group *grp,
+ unsigned short vlan_tag)
+{
+ return __vlan_hwaccel_rx(skb, grp, vlan_tag, 1);
}
#endif /* __KERNEL__ */
^ permalink raw reply
* Re: Endianness problem with u32 classifier hash masks
From: Radu Rendec @ 2007-11-05 17:31 UTC (permalink / raw)
To: hadi; +Cc: Jarek Poplawski, netdev
In-Reply-To: <1194271589.4438.113.camel@localhost>
On Mon, 2007-11-05 at 09:06 -0500, jamal wrote:
> On Mon, 2007-05-11 at 14:52 +0100, Jarek Poplawski wrote:
>
...
> > If we manage to convince Jamal, IMHO a patch to something current like
> > 2.6.24-rc1-git14 (or maybe -rc2 soon), should suffice (plus some
> > options to diff to get function names etc. eg.: diff -Nurp). Try with
> > Documentation/SubmittingPatches. Git isn't necessary at all. And don't
> > forget about a changelog.
>
> That code hasnt changed at all in a few years, so even against
> 2.6.24-rc1 should be fine and can be applied cleanly to Daves net-2.6.
> Radu, please refer to my earlier email on things to try.
Ok, silly procmail rules + threaded view = bad idea. I just didn't see
your last messages earlier when I replied. Sorry!
But still, Jamal, I need more explanations on what you meant by "cutdown
on the conversion in u32_change()". And, before proceeding, I'd like to
see your reply to Jarek's last email (at 15:49 +0100) about not getting
0xff in the end.
Jarek, because I have to test anyway, I'll include ffs(mask) in my patch
and have it tested too.
I already have a clone of Dave's net-2.6 tree, so it shouldn't be such a
big effort to use git after all. I can also make the patch against
2.6.24-rc1, so please tell me which is more convinient for you.
Cheers,
Radu Rendec
^ permalink raw reply
* Re: [2.6 patch] remove comx driver docs
From: Alan Cox @ 2007-11-05 17:18 UTC (permalink / raw)
To: Adrian Bunk; +Cc: jgarzik, netdev, linux-kernel
In-Reply-To: <20071105170445.GQ12045@stusta.de>
On Mon, 5 Nov 2007 18:04:45 +0100
Adrian Bunk <bunk@kernel.org> wrote:
> The drivers have already been removed 3.5 years ago.
>
> Signed-off-by: Adrian Bunk <bunk@kernel.org>
Acked-by: Alan Cox <alan@redhat.com>
^ permalink raw reply
* Re: [2.6 patch] remove Documentation/networking/pt.txt
From: Alan Cox @ 2007-11-05 17:17 UTC (permalink / raw)
To: Adrian Bunk; +Cc: jgarzik, netdev, linux-kernel
In-Reply-To: <20071105170557.GT12045@stusta.de>
On Mon, 5 Nov 2007 18:05:57 +0100
Adrian Bunk <bunk@kernel.org> wrote:
> There's no no point in keeping documentation for a driver that was
> removed many years ago.
>
> Signed-off-by: Adrian Bunk <bunk@kernel.org>
Defintiely very dead
Acked-by: Alan Cox <alan@redhat.com>
^ permalink raw reply
* Re: dn_route.c momentarily exiting RCU read-side critical section
From: Paul E. McKenney @ 2007-11-05 16:12 UTC (permalink / raw)
To: Herbert Xu; +Cc: davem, linux-kernel, netdev, SteveW, dipankar
In-Reply-To: <E1Ip0Vs-0002zP-00@gondolin.me.apana.org.au>
On Mon, Nov 05, 2007 at 07:53:04PM +0800, Herbert Xu wrote:
> Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> >>
> >> > net/decnet/dn_route.c in dn_rt_cache_get_next() is as follows:
> >> >
> >> > static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_route *rt)
> >> > {
> >> > struct dn_rt_cache_iter_state *s = rcu_dereference(seq->private);
> >> >
> >> > rt = rt->u.dst.dn_next;
> >> > while(!rt) {
> >> > rcu_read_unlock_bh();
> >> > if (--s->bucket < 0)
> >> > break;
> >
> > OK, for my next stupid question: why is the rcu_dereference(seq->private)
> > required, as opposed to simply seq->private?
>
> It was put there by someone who went through the code converting
> all occurances of smp_read_barrier_depends to rcu_dereference.
> In this instance the rcu_dereference conversion doesn't make much
> sense so we should probably just revert it.
Thank you for the info! Stupid question #3: what sequence of events
would the smp_read_barrier_depends() be defending against?
Thanx, Paul
^ permalink raw reply
* [2.6 patch] drivers/net/netxen/: cleanups
From: Adrian Bunk @ 2007-11-05 17:07 UTC (permalink / raw)
To: amitkale, jgarzik; +Cc: netdev, linux-kernel
This patch contains the following cleanups:
- static functions in .c files shouldn't be marked inline
- make needlessly global code static
- #if 0 unused code
Signed-off-by: Adrian Bunk <bunk@kernel.org>
---
drivers/net/netxen/netxen_nic.h | 14 ----
drivers/net/netxen/netxen_nic_hw.c | 19 +++---
drivers/net/netxen/netxen_nic_hw.h | 8 --
drivers/net/netxen/netxen_nic_init.c | 70 ++++++++++++-----------
drivers/net/netxen/netxen_nic_isr.c | 7 +-
drivers/net/netxen/netxen_nic_main.c | 9 +-
drivers/net/netxen/netxen_nic_niu.c | 26 +++++---
drivers/net/netxen/netxen_nic_phan_reg.h | 7 --
8 files changed, 76 insertions(+), 84 deletions(-)
dbc7aeed37e41cd37a01cce259e5c0ab01f8dd88
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index fbc2553..ef9f986 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -1015,14 +1015,8 @@ int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter);
int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter);
int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter);
int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter);
-int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter);
-int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter);
void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter);
void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter);
-void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter, int port,
- long enable);
-void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter, int port,
- long enable);
int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg,
__u32 * readval);
int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter,
@@ -1045,7 +1039,6 @@ int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data,
int len);
void netxen_crb_writelit_adapter(struct netxen_adapter *adapter,
unsigned long off, int data);
-int netxen_nic_erase_pxe(struct netxen_adapter *adapter);
/* Functions from netxen_nic_init.c */
void netxen_free_adapter_offload(struct netxen_adapter *adapter);
@@ -1064,15 +1057,10 @@ int netxen_flash_erase_secondary(struct netxen_adapter *adapter);
int netxen_flash_erase_primary(struct netxen_adapter *adapter);
void netxen_halt_pegs(struct netxen_adapter *adapter);
-int netxen_rom_fast_write(struct netxen_adapter *adapter, int addr, int data);
int netxen_rom_se(struct netxen_adapter *adapter, int addr);
-int netxen_do_rom_se(struct netxen_adapter *adapter, int addr);
/* Functions from netxen_nic_isr.c */
int netxen_nic_link_ok(struct netxen_adapter *adapter);
-void netxen_nic_isr_other(struct netxen_adapter *adapter);
-void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link);
-void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable);
void netxen_initialize_adapter_sw(struct netxen_adapter *adapter);
void netxen_initialize_adapter_hw(struct netxen_adapter *adapter);
void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr,
@@ -1089,8 +1077,6 @@ int netxen_nic_tx_has_work(struct netxen_adapter *adapter);
void netxen_watchdog_task(struct work_struct *work);
void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx,
u32 ringid);
-void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, u32 ctx,
- u32 ringid);
int netxen_process_cmd_ring(unsigned long data);
u32 netxen_process_rcv_ring(struct netxen_adapter *adapter, int ctx, int max);
void netxen_nic_set_multi(struct net_device *netdev);
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 2c19b8d..b2c7861 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -33,7 +33,6 @@
#include "netxen_nic.h"
#include "netxen_nic_hw.h"
-#define DEFINE_GLOBAL_RECV_CRB
#include "netxen_nic_phan_reg.h"
@@ -244,12 +243,15 @@ struct netxen_recv_crb recv_crb_registers[] = {
},
};
-u64 ctx_addr_sig_regs[][3] = {
+static u64 ctx_addr_sig_regs[][3] = {
{NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)},
{NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)},
{NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)},
{NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)}
};
+#define CRB_CTX_ADDR_REG_LO(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][0])
+#define CRB_CTX_ADDR_REG_HI(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][2])
+#define CRB_CTX_SIGNATURE_REG(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][1])
/* PCI Windowing for DDR regions. */
@@ -279,8 +281,8 @@ u64 ctx_addr_sig_regs[][3] = {
#define NETXEN_NIC_WINDOW_MARGIN 0x100000
-unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter,
- unsigned long long addr);
+static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter,
+ unsigned long long addr);
void netxen_free_hw_resources(struct netxen_adapter *adapter);
int netxen_nic_set_mac(struct net_device *netdev, void *p)
@@ -886,11 +888,10 @@ void netxen_nic_read_w0(struct netxen_adapter *adapter, u32 index, u32 * value)
netxen_nic_pci_change_crbwindow(adapter, 1);
}
-int netxen_pci_set_window_warning_count = 0;
+static int netxen_pci_set_window_warning_count = 0;
-unsigned long
-netxen_nic_pci_set_window(struct netxen_adapter *adapter,
- unsigned long long addr)
+static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter,
+ unsigned long long addr)
{
static int ddr_mn_window = -1;
static int qdr_sn_window = -1;
@@ -952,6 +953,7 @@ netxen_nic_pci_set_window(struct netxen_adapter *adapter,
return addr;
}
+#if 0
int
netxen_nic_erase_pxe(struct netxen_adapter *adapter)
{
@@ -962,6 +964,7 @@ netxen_nic_erase_pxe(struct netxen_adapter *adapter)
}
return 0;
}
+#endif /* 0 */
int netxen_nic_get_board_info(struct netxen_adapter *adapter)
{
diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h
index 245bf13..197e1fc 100644
--- a/drivers/net/netxen/netxen_nic_hw.h
+++ b/drivers/net/netxen/netxen_nic_hw.h
@@ -520,15 +520,11 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter,
int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter,
netxen_niu_prom_mode_t mode);
-/* get/set the MAC address for a given MAC */
-int netxen_niu_macaddr_get(struct netxen_adapter *adapter,
- netxen_ethernet_macaddr_t * addr);
+/* set the MAC address for a given MAC */
int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
netxen_ethernet_macaddr_t addr);
-/* XG versons */
-int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter,
- netxen_ethernet_macaddr_t * addr);
+/* XG version */
int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter,
netxen_ethernet_macaddr_t addr);
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 3758926..8667e8f 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -54,13 +54,17 @@ static unsigned int crb_addr_xform[NETXEN_MAX_CRB_XFORM];
#define NETXEN_NIC_XDMA_RESET 0x8000ff
-static inline void
-netxen_nic_locked_write_reg(struct netxen_adapter *adapter,
- unsigned long off, int *data)
+static void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter,
+ uint32_t ctx, uint32_t ringid);
+
+#if 0
+static void netxen_nic_locked_write_reg(struct netxen_adapter *adapter,
+ unsigned long off, int *data)
{
void __iomem *addr = pci_base_offset(adapter, off);
writel(*data, addr);
}
+#endif /* 0 */
static void crb_addr_transform_setup(void)
{
@@ -255,7 +259,7 @@ void netxen_initialize_adapter_ops(struct netxen_adapter *adapter)
* netxen_decode_crb_addr(0 - utility to translate from internal Phantom CRB
* address to external PCI CRB address.
*/
-u32 netxen_decode_crb_addr(u32 addr)
+static u32 netxen_decode_crb_addr(u32 addr)
{
int i;
u32 base_addr, offset, pci_base;
@@ -282,7 +286,7 @@ static long rom_max_timeout = 100;
static long rom_lock_timeout = 10000;
static long rom_write_timeout = 700;
-static inline int rom_lock(struct netxen_adapter *adapter)
+static int rom_lock(struct netxen_adapter *adapter)
{
int iter;
u32 done = 0;
@@ -312,7 +316,7 @@ static inline int rom_lock(struct netxen_adapter *adapter)
return 0;
}
-int netxen_wait_rom_done(struct netxen_adapter *adapter)
+static int netxen_wait_rom_done(struct netxen_adapter *adapter)
{
long timeout = 0;
long done = 0;
@@ -329,7 +333,7 @@ int netxen_wait_rom_done(struct netxen_adapter *adapter)
return 0;
}
-static inline int netxen_rom_wren(struct netxen_adapter *adapter)
+static int netxen_rom_wren(struct netxen_adapter *adapter)
{
/* Set write enable latch in ROM status register */
netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_ABYTE_CNT, 0);
@@ -341,15 +345,15 @@ static inline int netxen_rom_wren(struct netxen_adapter *adapter)
return 0;
}
-static inline unsigned int netxen_rdcrbreg(struct netxen_adapter *adapter,
- unsigned int addr)
+static unsigned int netxen_rdcrbreg(struct netxen_adapter *adapter,
+ unsigned int addr)
{
unsigned int data = 0xdeaddead;
data = netxen_nic_reg_read(adapter, addr);
return data;
}
-static inline int netxen_do_rom_rdsr(struct netxen_adapter *adapter)
+static int netxen_do_rom_rdsr(struct netxen_adapter *adapter)
{
netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_INSTR_OPCODE,
M25P_INSTR_RDSR);
@@ -359,7 +363,7 @@ static inline int netxen_do_rom_rdsr(struct netxen_adapter *adapter)
return netxen_rdcrbreg(adapter, NETXEN_ROMUSB_ROM_RDATA);
}
-static inline void netxen_rom_unlock(struct netxen_adapter *adapter)
+static void netxen_rom_unlock(struct netxen_adapter *adapter)
{
u32 val;
@@ -368,7 +372,7 @@ static inline void netxen_rom_unlock(struct netxen_adapter *adapter)
}
-int netxen_rom_wip_poll(struct netxen_adapter *adapter)
+static int netxen_rom_wip_poll(struct netxen_adapter *adapter)
{
long timeout = 0;
long wip = 1;
@@ -385,8 +389,8 @@ int netxen_rom_wip_poll(struct netxen_adapter *adapter)
return 0;
}
-static inline int do_rom_fast_write(struct netxen_adapter *adapter, int addr,
- int data)
+static int do_rom_fast_write(struct netxen_adapter *adapter, int addr,
+ int data)
{
if (netxen_rom_wren(adapter)) {
return -1;
@@ -404,8 +408,8 @@ static inline int do_rom_fast_write(struct netxen_adapter *adapter, int addr,
return netxen_rom_wip_poll(adapter);
}
-static inline int
-do_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp)
+static int do_rom_fast_read(struct netxen_adapter *adapter,
+ int addr, int *valp)
{
cond_resched();
@@ -427,9 +431,8 @@ do_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp)
return 0;
}
-static inline int
-do_rom_fast_read_words(struct netxen_adapter *adapter, int addr,
- u8 *bytes, size_t size)
+static int do_rom_fast_read_words(struct netxen_adapter *adapter, int addr,
+ u8 *bytes, size_t size)
{
int addridx;
int ret = 0;
@@ -473,6 +476,7 @@ int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp)
return ret;
}
+#if 0
int netxen_rom_fast_write(struct netxen_adapter *adapter, int addr, int data)
{
int ret = 0;
@@ -484,9 +488,10 @@ int netxen_rom_fast_write(struct netxen_adapter *adapter, int addr, int data)
netxen_rom_unlock(adapter);
return ret;
}
+#endif /* 0 */
-static inline int do_rom_fast_write_words(struct netxen_adapter *adapter,
- int addr, u8 *bytes, size_t size)
+static int do_rom_fast_write_words(struct netxen_adapter *adapter,
+ int addr, u8 *bytes, size_t size)
{
int addridx = addr;
int ret = 0;
@@ -548,7 +553,7 @@ int netxen_rom_fast_write_words(struct netxen_adapter *adapter, int addr,
return ret;
}
-int netxen_rom_wrsr(struct netxen_adapter *adapter, int data)
+static int netxen_rom_wrsr(struct netxen_adapter *adapter, int data)
{
int ret;
@@ -567,7 +572,7 @@ int netxen_rom_wrsr(struct netxen_adapter *adapter, int data)
return netxen_rom_wip_poll(adapter);
}
-int netxen_rom_rdsr(struct netxen_adapter *adapter)
+static int netxen_rom_rdsr(struct netxen_adapter *adapter)
{
int ret;
@@ -632,7 +637,7 @@ out_kfree:
return ret;
}
-int netxen_do_rom_se(struct netxen_adapter *adapter, int addr)
+static int netxen_do_rom_se(struct netxen_adapter *adapter, int addr)
{
netxen_rom_wren(adapter);
netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_ADDRESS, addr);
@@ -646,7 +651,7 @@ int netxen_do_rom_se(struct netxen_adapter *adapter, int addr)
return netxen_rom_wip_poll(adapter);
}
-void check_erased_flash(struct netxen_adapter *adapter, int addr)
+static void check_erased_flash(struct netxen_adapter *adapter, int addr)
{
int i;
int val;
@@ -682,8 +687,8 @@ int netxen_rom_se(struct netxen_adapter *adapter, int addr)
return ret;
}
-int
-netxen_flash_erase_sections(struct netxen_adapter *adapter, int start, int end)
+static int netxen_flash_erase_sections(struct netxen_adapter *adapter,
+ int start, int end)
{
int ret = FLASH_SUCCESS;
int i;
@@ -990,7 +995,7 @@ int netxen_nic_rx_has_work(struct netxen_adapter *adapter)
return 0;
}
-static inline int netxen_nic_check_temp(struct netxen_adapter *adapter)
+static int netxen_nic_check_temp(struct netxen_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
uint32_t temp, temp_state, temp_val;
@@ -1064,9 +1069,8 @@ void netxen_watchdog_task(struct work_struct *work)
* and if the number of receives exceeds RX_BUFFERS_REFILL, then we
* invoke the routine to send more rx buffers to the Phantom...
*/
-void
-netxen_process_rcv(struct netxen_adapter *adapter, int ctxid,
- struct status_desc *desc)
+static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid,
+ struct status_desc *desc)
{
struct pci_dev *pdev = adapter->pdev;
struct net_device *netdev = adapter->netdev;
@@ -1460,8 +1464,8 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid)
}
}
-void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, uint32_t ctx,
- uint32_t ringid)
+static void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter,
+ uint32_t ctx, uint32_t ringid)
{
struct pci_dev *pdev = adapter->ahw.pdev;
struct sk_buff *skb;
diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c
index b2de6b6..0a261de 100644
--- a/drivers/net/netxen/netxen_nic_isr.c
+++ b/drivers/net/netxen/netxen_nic_isr.c
@@ -66,7 +66,8 @@ struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
return stats;
}
-void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link)
+static void netxen_indicate_link_status(struct netxen_adapter *adapter,
+ u32 link)
{
struct net_device *netdev = adapter->netdev;
@@ -76,6 +77,7 @@ void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link)
netif_carrier_off(netdev);
}
+#if 0
void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable)
{
__u32 int_src;
@@ -134,8 +136,9 @@ void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable)
if (adapter->enable_phy_interrupts)
adapter->enable_phy_interrupts(adapter);
}
+#endif /* 0 */
-void netxen_nic_isr_other(struct netxen_adapter *adapter)
+static void netxen_nic_isr_other(struct netxen_adapter *adapter)
{
int portno = adapter->portnum;
u32 val, linkup, qg_linksup;
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index a80f0cd..d0d1f05 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -89,8 +89,8 @@ MODULE_DEVICE_TABLE(pci, netxen_pci_tbl);
struct workqueue_struct *netxen_workq;
static void netxen_watchdog(unsigned long);
-static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter,
- uint32_t crb_producer)
+static void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter,
+ uint32_t crb_producer)
{
switch (adapter->portnum) {
case 0:
@@ -118,8 +118,8 @@ static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter
}
}
-static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter,
- u32 crb_consumer)
+static void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter,
+ u32 crb_consumer)
{
switch (adapter->portnum) {
case 0:
@@ -148,7 +148,6 @@ static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter
}
#define ADAPTER_LIST_SIZE 12
-int netxen_cards_found;
static void netxen_nic_disable_int(struct netxen_adapter *adapter)
{
diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c
index 5b9e1b3..94e096c 100644
--- a/drivers/net/netxen/netxen_nic_niu.c
+++ b/drivers/net/netxen/netxen_nic_niu.c
@@ -40,7 +40,7 @@
static long phy_lock_timeout = 100000000;
-static inline int phy_lock(struct netxen_adapter *adapter)
+static int phy_lock(struct netxen_adapter *adapter)
{
int i;
int done = 0, timeout = 0;
@@ -68,7 +68,7 @@ static inline int phy_lock(struct netxen_adapter *adapter)
return 0;
}
-static inline int phy_unlock(struct netxen_adapter *adapter)
+static int phy_unlock(struct netxen_adapter *adapter)
{
readl(pci_base_offset(adapter, NETXEN_PCIE_REG(PCIE_SEM3_UNLOCK)));
@@ -300,13 +300,15 @@ int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter)
return result;
}
+#if 0
int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter)
{
netxen_crb_writelit_adapter(adapter, NETXEN_NIU_ACTIVE_INT, -1);
return 0;
}
+#endif /* 0 */
-int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter)
+static int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter)
{
int result = 0;
if (0 !=
@@ -322,8 +324,8 @@ int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter)
* netxen_niu_gbe_set_mii_mode- Set 10/100 Mbit Mode for GbE MAC
*
*/
-void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter,
- int port, long enable)
+static void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter,
+ int port, long enable)
{
netxen_crb_writelit_adapter(adapter, NETXEN_NIU_MODE, 0x2);
netxen_crb_writelit_adapter(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port),
@@ -360,8 +362,8 @@ void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter,
/*
* netxen_niu_gbe_set_gmii_mode- Set GbE Mode for GbE MAC
*/
-void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter,
- int port, long enable)
+static void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter,
+ int port, long enable)
{
netxen_crb_writelit_adapter(adapter, NETXEN_NIU_MODE, 0x2);
netxen_crb_writelit_adapter(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port),
@@ -464,6 +466,7 @@ int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port)
return 0;
}
+#if 0
/*
* netxen_niu_gbe_handle_phy_interrupt - Handles GbE PHY interrupts
* @param enable 0 means don't enable the port
@@ -559,13 +562,14 @@ int netxen_niu_gbe_handle_phy_interrupt(struct netxen_adapter *adapter,
}
return result;
}
+#endif /* 0 */
/*
* Return the current station MAC address.
* Note that the passed-in value must already be in network byte order.
*/
-int netxen_niu_macaddr_get(struct netxen_adapter *adapter,
- netxen_ethernet_macaddr_t * addr)
+static int netxen_niu_macaddr_get(struct netxen_adapter *adapter,
+ netxen_ethernet_macaddr_t * addr)
{
u32 stationhigh;
u32 stationlow;
@@ -636,6 +640,7 @@ int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
return 0;
}
+#if 0
/* Enable a GbE interface */
int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter,
int port, netxen_niu_gbe_ifmode_t mode)
@@ -713,6 +718,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter,
return -EIO;
return 0;
}
+#endif /* 0 */
/* Disable a GbE interface */
int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter)
@@ -853,6 +859,7 @@ int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter,
return 0;
}
+#if 0
/*
* Return the current station MAC address.
* Note that the passed-in value must already be in network byte order.
@@ -883,6 +890,7 @@ int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter,
return 0;
}
+#endif /* 0 */
int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter,
netxen_niu_prom_mode_t mode)
diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h
index 10fe6fa..a6c28de 100644
--- a/drivers/net/netxen/netxen_nic_phan_reg.h
+++ b/drivers/net/netxen/netxen_nic_phan_reg.h
@@ -165,14 +165,7 @@ struct netxen_recv_crb {
u32 crb_status_ring_size;
};
-#if defined(DEFINE_GLOBAL_RECV_CRB)
-#else
extern struct netxen_recv_crb recv_crb_registers[];
-extern u64 ctx_addr_sig_regs[][3];
-#endif /* DEFINE_GLOBAL_RECEIVE_CRB */
-#define CRB_CTX_ADDR_REG_LO(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][0])
-#define CRB_CTX_ADDR_REG_HI(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][2])
-#define CRB_CTX_SIGNATURE_REG(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][1])
/*
* Temperature control.
^ permalink raw reply related
* [2.6 patch] remove Documentation/networking/routing.txt
From: Adrian Bunk @ 2007-11-05 17:06 UTC (permalink / raw)
To: kuznet; +Cc: netdev, linux-kernel
This file is so outdated that I can't see any value in keeping it.
Signed-off-by: Adrian Bunk <bunk@kernel.org>
---
Documentation/networking/00-INDEX | 2 -
Documentation/networking/routing.txt | 46 ---------------------------
2 files changed, 48 deletions(-)
54b248bbff6345016e060136cfb5c09d43b26b69
diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX
index 5e21f37..563e442 100644
--- a/Documentation/networking/00-INDEX
+++ b/Documentation/networking/00-INDEX
@@ -82,6 +82,4 @@ policy-routing.txt
ray_cs.txt
- Raylink Wireless LAN card driver info.
-routing.txt
- - the new routing mechanism
shaper.txt
- info on the module that can shape/limit transmitted traffic.
diff --git a/Documentation/networking/routing.txt b/Documentation/networking/routing.txt
deleted file mode 100644
index a26838b..0000000
--- a/Documentation/networking/routing.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-The directory ftp.inr.ac.ru:/ip-routing contains:
-
-- iproute.c - "professional" routing table maintenance utility.
-
-- rdisc.tar.gz - rdisc daemon, ported from Sun.
- STRONGLY RECOMMENDED FOR ALL HOSTS.
-
-- routing.tgz - original Mike McLagan's route by source patch.
- Currently it is obsolete.
-
-- gated.dif-ss<NEWEST>.gz - gated-R3_6Alpha_2 fixes.
- Look at README.gated
-
-- mrouted-3.8.dif.gz - mrouted-3.8 fixes.
-
-- rtmon.c - trivial debugging utility: reads and stores netlink.
-
-
-NEWS for user.
-
-- Policy based routing. Routing decisions are made on the basis
- not only of destination address, but also source address,
- TOS and incoming interface.
-- Complete set of IP level control messages.
- Now Linux is the only OS in the world complying to RFC requirements.
- Great win 8)
-- New interface addressing paradigm.
- Assignment of address ranges to interface,
- multiple prefixes etc. etc.
- Do not bother, it is compatible with the old one. Moreover:
-- You don't need to do "route add aaa.bbb.ccc... eth0" anymore,
- it is done automatically.
-- "Abstract" UNIX sockets and security enhancements.
- This is necessary to use TIRPC and TLI emulation library.
-
-NEWS for hacker.
-
-- New destination cache. Flexible, robust and just beautiful.
-- Network stack is reordered, simplified, optimized, a lot of bugs fixed.
- (well, and new bugs were introduced, but I haven't seen them yet 8))
- It is difficult to describe all the changes, look into source.
-
-If you see this file, then this patch works 8)
-
-Alexey Kuznetsov.
-kuznet@ms2.inr.ac.ru
^ permalink raw reply related
* [2.6 patch] remove Documentation/networking/ncsa-telnet
From: Adrian Bunk @ 2007-11-05 17:05 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, Pekka Pietikainen
Newsflash: There once was a version of NCSA telnet that had some bug.
Spotted by Pekka Pietikainen.
Signed-off-by: Adrian Bunk <bunk@kernel.org>
---
Documentation/networking/00-INDEX | 2 --
Documentation/networking/ncsa-telnet | 16 ----------------
2 files changed, 18 deletions(-)
b9ccc5424cc83b4c0ca9c8d380a4b7567916a463
diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX
index a9f4acc..9c64042 100644
--- a/Documentation/networking/00-INDEX
+++ b/Documentation/networking/00-INDEX
@@ -74,8 +74,6 @@ ltpc.txt
- the Apple or Farallon LocalTalk PC card driver
multicast.txt
- Behaviour of cards under Multicast
-ncsa-telnet
- - notes on how NCSA telnet (DOS) breaks with MTU discovery enabled.
netdevices.txt
- info on network device driver functions exported to the kernel.
olympic.txt
diff --git a/Documentation/networking/ncsa-telnet b/Documentation/networking/ncsa-telnet
deleted file mode 100644
index d77d28b..0000000
--- a/Documentation/networking/ncsa-telnet
+++ /dev/null
@@ -1,16 +0,0 @@
-NCSA telnet doesn't work with path MTU discovery enabled. This is due to a
-bug in NCSA that also stops it working with other modern networking code
-such as Solaris.
-
-The following information is courtesy of
-Marek <marekm@i17linuxb.ists.pwr.wroc.pl>
-
-There is a fixed version somewhere on ftp.upe.ac.za (sorry, I don't
-remember the exact pathname, and this site is very slow from here).
-It may or may not be faster for you to get it from
-ftp://ftp.ists.pwr.wroc.pl/pub/msdos/telnet/ncsa_upe/tel23074.zip
-(source is in v230704s.zip). I have tested it with 1.3.79 (with
-path mtu discovery enabled - ncsa 2.3.08 didn't work) and it seems
-to work. I don't know if anyone is working on this code - this
-version is over a year old. Too bad - it's faster and often more
-stable than these windoze telnets, and runs on almost anything...
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox