Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1299642 > unrolled thread

[RFC 00/31] Current state of MARS

Started byThomas Schoebel-Theuer <tst@schoebel-theuer.de>
First post2015-12-31 12:40 +0100
Last post2015-12-31 12:50 +0100
Articles 20 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [RFC 00/31] Current state of MARS Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:40 +0100
    [RFC 07/31] mars: add new module lib_pairing_heap Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:40 +0100
    [RFC 31/31] mars: activate build Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:40 +0100
    [RFC 04/31] mars: add new module brick_checking Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 29/31] mars: add new module Makefile Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 17/31] mars: add new module xio_bio Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 15/31] mars: add new module lib_mapfree Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 27/31] mars: add new module mars_proc Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 21/31] mars: add new module xio_copy Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 20/31] mars: add new module xio_if Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 13/31] mars: add new module xio Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 25/31] mars: add new module light_net Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 23/31] mars: add new module xio_server Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 01/31] mars: add new module lamport Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 19/31] mars: add new module xio_client Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 11/31] mars: add new module lib_timing Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 08/31] mars: add new module lib_queue Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 18/31] mars: add new module xio_sio Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 16/31] mars: add new module lib_log Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100
    [RFC 26/31] mars: add new module light_server_strategy Thomas Schoebel-Theuer <tst@schoebel-theuer.de> - 2015-12-31 12:50 +0100

#1299642 — [RFC 00/31] Current state of MARS

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:40 +0100
Subject[RFC 00/31] Current state of MARS
Message-ID<qLJKi-3PB-3@gated-at.bofh.it>
MARS Light is an asynchronous replication system for block storage over
long distances [1,2,3]. It is the base for HA over long distances (more than
50km) and over network bottlenecks, e.g. high / varying packet loss rates.

The out-of-tree (OOT) version of MARS (branch light0.1.y) is in production
at 1&1 Internet SE, currently at more than 1600 servers of various types,
more than 6 petabytes of total storage, and has collected more than 6 millions
of operating hours with less than 10 customer-visible incidents caused by
MARS (most of them in the early beta phase).

From my last LKML posting in 2014, I got the following major TODOs
from you:

1) no kernel prepatch anymore, no additional EXPORT_SYMBOL()s anywhere
in the rest of the kernel.

=> accomplished in the enclosed patchset (however not yet fully bug-free).
See also branches WIP-PORTABLE, WIP-BASE and WIP-PROPOSED-UPSTREAM at [1]
for some out-of-tree (OOT) versions of MARS.

=> this means there will be zero impact from MARS to the rest of the kernel
when MARS is not used. Additionally I placed it into drivers/staging/
(configurabale via migration script ./rework-mars-for-upstream.pl ).

2) replace the symlink tree by something else.

=> not yet done, due to lack of time. Sorry. I am planning to work on it
in the forthcoming year, and hope to get enough time for it.

I think the attached patchset is not yet ready for submission, but please
help me by giving early feedback. I would be very glad if some experienced
upstream hacker would mentor me and help me at various points.

Here are some of my thoughts about further development:

In future, I would like to store the old symlink information (list of
key => value pairs) in status files instead, one instance for each resource,
and one for global configuration data (notice that the information _needs_
to be persistent for coping with node failures, and that dynamic storage
is needed anyway for huge masses of transaction logfiles).

This should enhance scalability in the distributed system. Here is a
use case: almost our full 1&1 webhosting machine park has been migrated
from DRBD to MARS, retaining the conventional pair structure. Thus more
than 800 DRBD clusters have been turned into MARS clusters.

In future, the whole 1&1 machine park could collapse into a _single_ big
cluster consisting of thousands of machines. This has some advantages,
not limited to greater flexibility with dynamic join-resource /
leave-resource operations throughout that big cluster. For example,
userspace can more easily detect failures cluster-wide and datacenter-wide,
because the Lamport Clock algorithm used by MARS [2,3] is already a kind
of "heartbeat", even remembering the timestamps of last successful
information exchange. Thus it should be relatively easy to implement
quorum algorithms etc on top of it in userspace.

Note that I am currently paid by 1&1 to ensure a seamless upgrade path
for servers to any new data format, without downtime (other than
temporarily switching the primary roles).

If you don't want the old symlink format in the upstream kernel, I would
first implement the new format OOT (and strip out the old code for
submission). Otherwise, I would agree to do this intree, hopefully with
help and advise from some interested kernel hackers.

Cheers and a happy new year,

Thomas

[1] https://github.com/schoebel/mars
[2] https://github.com/schoebel/mars/blob/master/docu/MARS_Froscon2015.pdf
[3] https://github.com/schoebel/mars/blob/master/docu/mars-manual.pdf


Thomas Schoebel-Theuer (31):
  mars: add new module lamport
  mars: add new module brick_say
  mars: add new module brick_mem
  mars: add new module brick_checking
  mars: add new module meta
  mars: add new module brick
  mars: add new module lib_pairing_heap
  mars: add new module lib_queue
  mars: add new module lib_rank
  mars: add new module lib_limiter
  mars: add new module lib_timing
  mars: add new module vfs_compat
  mars: add new module xio
  mars: add new module xio_net
  mars: add new module lib_mapfree
  mars: add new module lib_log
  mars: add new module xio_bio
  mars: add new module xio_sio
  mars: add new module xio_client
  mars: add new module xio_if
  mars: add new module xio_copy
  mars: add new module xio_trans_logger
  mars: add new module xio_server
  mars: add new module light_strategy
  mars: add new module light_net
  mars: add new module light_server_strategy
  mars: add new module mars_proc
  mars: add new module mars_light
  mars: add new module Makefile
  mars: add new module Kconfig
  mars: activate build

 drivers/staging/Kconfig                            |    2 +
 drivers/staging/Makefile                           |    1 +
 drivers/staging/mars/Kconfig                       |  266 +
 drivers/staging/mars/Makefile                      |   61 +
 drivers/staging/mars/brick.c                       |  728 +++
 drivers/staging/mars/brick_mem.c                   | 1081 ++++
 drivers/staging/mars/brick_say.c                   |  916 +++
 drivers/staging/mars/lamport.c                     |   61 +
 drivers/staging/mars/lib/lib_limiter.c             |  129 +
 drivers/staging/mars/lib/lib_rank.c                |   87 +
 drivers/staging/mars/lib/lib_timing.c              |   71 +
 drivers/staging/mars/mars_light/light_net.c        |  109 +
 .../mars/mars_light/light_server_strategy.c        |  403 ++
 drivers/staging/mars/mars_light/light_strategy.c   | 2132 +++++++
 drivers/staging/mars/mars_light/mars_light.c       | 5880 ++++++++++++++++++++
 drivers/staging/mars/mars_light/mars_proc.c        |  369 ++
 drivers/staging/mars/xio_bricks/lib_log.c          |  505 ++
 drivers/staging/mars/xio_bricks/lib_mapfree.c      |  380 ++
 drivers/staging/mars/xio_bricks/xio.c              |  161 +
 drivers/staging/mars/xio_bricks/xio_bio.c          |  845 +++
 drivers/staging/mars/xio_bricks/xio_client.c       | 1055 ++++
 drivers/staging/mars/xio_bricks/xio_copy.c         | 1005 ++++
 drivers/staging/mars/xio_bricks/xio_if.c           |  961 ++++
 drivers/staging/mars/xio_bricks/xio_net.c          | 1830 ++++++
 drivers/staging/mars/xio_bricks/xio_server.c       |  486 ++
 drivers/staging/mars/xio_bricks/xio_sio.c          |  571 ++
 drivers/staging/mars/xio_bricks/xio_trans_logger.c | 3309 +++++++++++
 include/linux/brick/brick.h                        |  642 +++
 include/linux/brick/brick_checking.h               |  104 +
 include/linux/brick/brick_mem.h                    |  218 +
 include/linux/brick/brick_say.h                    |   96 +
 include/linux/brick/lamport.h                      |   26 +
 include/linux/brick/lib_limiter.h                  |   49 +
 include/linux/brick/lib_pairing_heap.h             |  110 +
 include/linux/brick/lib_queue.h                    |  166 +
 include/linux/brick/lib_rank.h                     |  135 +
 include/linux/brick/lib_timing.h                   |  181 +
 include/linux/brick/meta.h                         |  106 +
 include/linux/brick/vfs_compat.h                   |   45 +
 include/linux/mars_light/light_strategy.h          |  236 +
 include/linux/mars_light/mars_proc.h               |   34 +
 include/linux/xio/lib_log.h                        |  329 ++
 include/linux/xio/lib_mapfree.h                    |   84 +
 include/linux/xio/xio.h                            |  313 ++
 include/linux/xio/xio_bio.h                        |   85 +
 include/linux/xio/xio_client.h                     |  105 +
 include/linux/xio/xio_copy.h                       |  115 +
 include/linux/xio/xio_if.h                         |  108 +
 include/linux/xio/xio_net.h                        |  171 +
 include/linux/xio/xio_server.h                     |   91 +
 include/linux/xio/xio_sio.h                        |   68 +
 include/linux/xio/xio_trans_logger.h               |  263 +
 52 files changed, 27284 insertions(+)
 create mode 100644 drivers/staging/mars/Kconfig
 create mode 100644 drivers/staging/mars/Makefile
 create mode 100644 drivers/staging/mars/brick.c
 create mode 100644 drivers/staging/mars/brick_mem.c
 create mode 100644 drivers/staging/mars/brick_say.c
 create mode 100644 drivers/staging/mars/lamport.c
 create mode 100644 drivers/staging/mars/lib/lib_limiter.c
 create mode 100644 drivers/staging/mars/lib/lib_rank.c
 create mode 100644 drivers/staging/mars/lib/lib_timing.c
 create mode 100644 drivers/staging/mars/mars_light/light_net.c
 create mode 100644 drivers/staging/mars/mars_light/light_server_strategy.c
 create mode 100644 drivers/staging/mars/mars_light/light_strategy.c
 create mode 100644 drivers/staging/mars/mars_light/mars_light.c
 create mode 100644 drivers/staging/mars/mars_light/mars_proc.c
 create mode 100644 drivers/staging/mars/xio_bricks/lib_log.c
 create mode 100644 drivers/staging/mars/xio_bricks/lib_mapfree.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_bio.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_client.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_copy.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_if.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_net.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_server.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_sio.c
 create mode 100644 drivers/staging/mars/xio_bricks/xio_trans_logger.c
 create mode 100644 include/linux/brick/brick.h
 create mode 100644 include/linux/brick/brick_checking.h
 create mode 100644 include/linux/brick/brick_mem.h
 create mode 100644 include/linux/brick/brick_say.h
 create mode 100644 include/linux/brick/lamport.h
 create mode 100644 include/linux/brick/lib_limiter.h
 create mode 100644 include/linux/brick/lib_pairing_heap.h
 create mode 100644 include/linux/brick/lib_queue.h
 create mode 100644 include/linux/brick/lib_rank.h
 create mode 100644 include/linux/brick/lib_timing.h
 create mode 100644 include/linux/brick/meta.h
 create mode 100644 include/linux/brick/vfs_compat.h
 create mode 100644 include/linux/mars_light/light_strategy.h
 create mode 100644 include/linux/mars_light/mars_proc.h
 create mode 100644 include/linux/xio/lib_log.h
 create mode 100644 include/linux/xio/lib_mapfree.h
 create mode 100644 include/linux/xio/xio.h
 create mode 100644 include/linux/xio/xio_bio.h
 create mode 100644 include/linux/xio/xio_client.h
 create mode 100644 include/linux/xio/xio_copy.h
 create mode 100644 include/linux/xio/xio_if.h
 create mode 100644 include/linux/xio/xio_net.h
 create mode 100644 include/linux/xio/xio_server.h
 create mode 100644 include/linux/xio/xio_sio.h
 create mode 100644 include/linux/xio/xio_trans_logger.h

-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1299643 — [RFC 07/31] mars: add new module lib_pairing_heap

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:40 +0100
Subject[RFC 07/31] mars: add new module lib_pairing_heap
Message-ID<qLJKj-3PB-37@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 include/linux/brick/lib_pairing_heap.h | 110 +++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 include/linux/brick/lib_pairing_heap.h

diff --git a/include/linux/brick/lib_pairing_heap.h b/include/linux/brick/lib_pairing_heap.h
new file mode 100644
index 0000000..eb97097
--- /dev/null
+++ b/include/linux/brick/lib_pairing_heap.h
@@ -0,0 +1,110 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 PAIRING_HEAP_H
+#define PAIRING_HEAP_H
+
+/* Algorithm: see http://en.wikipedia.org/wiki/Pairing_heap
+ * This is just an efficient translation from recursive to iterative form.
+ *
+ * Note: find_min() is so trivial that we don't implement it.
+ */
+
+/* generic version: KEYDEF is kept separate, allowing you to
+ * embed this structure into other container structures already
+ * possessing some key (just provide an empty KEYDEF in this case).
+ */
+#define _PAIRING_HEAP_TYPEDEF(KEYTYPE, KEYDEF)				\
+									\
+struct pairing_heap_##KEYTYPE {						\
+	KEYDEF								\
+	struct pairing_heap_##KEYTYPE *next;				\
+	struct pairing_heap_##KEYTYPE *subheaps;			\
+};									\
+/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+/* less generic version: define the key inside.
+ */
+#define PAIRING_HEAP_TYPEDEF(KEYTYPE)					\
+	_PAIRING_HEAP_TYPEDEF(KEYTYPE, KEYTYPE key;)
+
+/* generic methods: allow arbitrary CMP() functions.
+ */
+#define _PAIRING_HEAP_FUNCTIONS(_STATIC, KEYTYPE, CMP)			\
+									\
+_STATIC									\
+struct pairing_heap_##KEYTYPE *_ph_merge_##KEYTYPE(struct pairing_heap_##KEYTYPE *heap1,\
+	struct pairing_heap_##KEYTYPE *heap2)				\
+{									\
+	if (!heap1)							\
+		return heap2;						\
+	if (!heap2)							\
+		return heap1;						\
+	if (CMP(heap1, heap2) < 0) {					\
+		heap2->next = heap1->subheaps;				\
+		heap1->subheaps = heap2;				\
+		return heap1;						\
+	}								\
+	heap1->next = heap2->subheaps;					\
+	heap2->subheaps = heap1;					\
+	return heap2;							\
+}									\
+									\
+_STATIC									\
+void ph_insert_##KEYTYPE(struct pairing_heap_##KEYTYPE **heap, struct pairing_heap_##KEYTYPE *new)\
+{									\
+	new->next = NULL;						\
+	new->subheaps = NULL;						\
+	*heap = _ph_merge_##KEYTYPE(*heap, new);			\
+}									\
+									\
+_STATIC									\
+void ph_delete_min_##KEYTYPE(struct pairing_heap_##KEYTYPE **heap)	\
+{									\
+	struct pairing_heap_##KEYTYPE *tmplist = NULL;			\
+	struct pairing_heap_##KEYTYPE *ptr;				\
+	struct pairing_heap_##KEYTYPE *next;				\
+	struct pairing_heap_##KEYTYPE *res;				\
+	if (!*heap) {							\
+		return;							\
+	}								\
+	for (ptr = (*heap)->subheaps; ptr; ptr = next) {		\
+		struct pairing_heap_##KEYTYPE *p2 = ptr->next;		\
+		next = p2;						\
+		if (p2) {						\
+			next = p2->next;				\
+			ptr = _ph_merge_##KEYTYPE(ptr, p2);		\
+		}							\
+		ptr->next = tmplist;					\
+		tmplist = ptr;						\
+	}								\
+	res = NULL;							\
+	for (ptr = tmplist; ptr; ptr = next) {				\
+		next = ptr->next;					\
+		res = _ph_merge_##KEYTYPE(res, ptr);			\
+	}								\
+	*heap = res;							\
+}
+
+/* some default CMP() function */
+#define PAIRING_HEAP_COMPARE(a, b) ((a)->key < (b)->key ? -1 : ((a)->key > (b)->key ? 1 : 0))
+
+/* less generic version: use the default CMP() function */
+#define PAIRING_HEAP_FUNCTIONS(_STATIC, KEYTYPE)			\
+	_PAIRING_HEAP_FUNCTIONS(_STATIC, KEYTYPE, PAIRING_HEAP_COMPARE)
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299644 — [RFC 31/31] mars: activate build

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:40 +0100
Subject[RFC 31/31] mars: activate build
Message-ID<qLJKj-3PB-33@gated-at.bofh.it>
In reply to#1299642
From: Thomas Schoebel-Theuer <tst@1und1.de>

---
 drivers/staging/Kconfig  | 2 ++
 drivers/staging/Makefile | 1 +
 2 files changed, 3 insertions(+)

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 5d3b86a..bbccc4f 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -56,6 +56,8 @@ source "drivers/staging/vt6656/Kconfig"
 
 source "drivers/staging/iio/Kconfig"
 
+source "drivers/staging/mars/Kconfig"
+
 source "drivers/staging/sm750fb/Kconfig"
 
 source "drivers/staging/xgifb/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 30918ed..01732bd 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_VT6655)		+= vt6655/
 obj-$(CONFIG_VT6656)		+= vt6656/
 obj-$(CONFIG_VME_BUS)		+= vme/
 obj-$(CONFIG_IIO)		+= iio/
+obj-$(CONFIG_MARS)		+= mars/
 obj-$(CONFIG_FB_SM750)		+= sm750fb/
 obj-$(CONFIG_FB_XGI)		+= xgifb/
 obj-$(CONFIG_USB_EMXX)		+= emxx_udc/
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299645 — [RFC 04/31] mars: add new module brick_checking

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 04/31] mars: add new module brick_checking
Message-ID<qLJTX-3U9-3@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 include/linux/brick/brick_checking.h | 104 +++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 include/linux/brick/brick_checking.h

diff --git a/include/linux/brick/brick_checking.h b/include/linux/brick/brick_checking.h
new file mode 100644
index 0000000..a02f1bf
--- /dev/null
+++ b/include/linux/brick/brick_checking.h
@@ -0,0 +1,104 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 BRICK_CHECKING_H
+#define BRICK_CHECKING_H
+
+/***********************************************************************/
+
+/*  checking */
+
+#if defined(CONFIG_MARS_DEBUG) || defined(CONFIG_MARS_CHECKS)
+#define BRICK_CHECKING			true
+#else
+#define BRICK_CHECKING			false
+#endif
+
+#define _CHECK_ATOMIC(atom, OP, minval)					\
+do {									\
+	if (BRICK_CHECKING) {						\
+		int __test = atomic_read(atom);				\
+		if (unlikely(__test OP(minval))) {			\
+			atomic_set(atom, minval);			\
+			BRICK_ERR("%d: atomic " #atom " " #OP " " #minval " (%d)\n", __LINE__, __test);\
+		}							\
+	}								\
+} while (0)
+
+#define CHECK_ATOMIC(atom, minval)					\
+	_CHECK_ATOMIC(atom, <, minval)
+
+#define CHECK_HEAD_EMPTY(head)						\
+do {									\
+	if (BRICK_CHECKING && unlikely(!list_empty(head) && (head)->next)) {\
+		list_del_init(head);					\
+		BRICK_ERR("%d: list_head " #head " (%p) not empty\n", __LINE__, head);\
+	}								\
+} while (0)
+
+#ifdef CONFIG_MARS_DEBUG_MEM
+#define CHECK_PTR_DEAD(ptr, label)					\
+do {									\
+	if (BRICK_CHECKING && unlikely((ptr) == (void *)0x5a5a5a5a5a5a5a5a)) {\
+		BRICK_FAT("%d: pointer '" #ptr "' is DEAD\n", __LINE__);\
+		goto label;						\
+	}								\
+} while (0)
+#else
+#define CHECK_PTR_DEAD(ptr, label) /*empty*/
+#endif
+
+#define CHECK_PTR_NULL(ptr, label)					\
+do {									\
+	CHECK_PTR_DEAD(ptr, label);					\
+	if (BRICK_CHECKING && unlikely(!(ptr))) {			\
+		BRICK_FAT("%d: pointer '" #ptr "' is NULL\n", __LINE__);\
+		goto label;						\
+	}								\
+} while (0)
+
+#ifdef CONFIG_MARS_DEBUG
+#define CHECK_PTR(ptr, label)						\
+do {									\
+	CHECK_PTR_NULL(ptr, label);					\
+	if (BRICK_CHECKING && unlikely(!virt_addr_valid(ptr))) {	\
+		BRICK_FAT("%d: pointer '" #ptr "' (%p) is no valid virtual KERNEL address\n", __LINE__, ptr);\
+		goto label;						\
+	}								\
+} while (0)
+#else
+#define CHECK_PTR(ptr, label) CHECK_PTR_NULL(ptr, label)
+#endif
+
+#define CHECK_ASPECT(a_ptr, o_ptr, label)				\
+do {									\
+	if (BRICK_CHECKING && unlikely((a_ptr)->object != o_ptr)) {	\
+		BRICK_FAT("%d: aspect pointer '" #a_ptr "' (%p) belongs to object %p, not to " #o_ptr " (%p)\n",\
+			__LINE__, a_ptr, (a_ptr)->object, o_ptr);	\
+		goto label;						\
+	}								\
+} while (0)
+
+#define _CHECK(ptr, label)						\
+do {									\
+	if (BRICK_CHECKING && unlikely(!(ptr))) {			\
+		BRICK_FAT("%d: condition '" #ptr "' is VIOLATED\n", __LINE__);\
+		goto label;						\
+	}								\
+} while (0)
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299646 — [RFC 29/31] mars: add new module Makefile

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 29/31] mars: add new module Makefile
Message-ID<qLJTX-3U9-9@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/Makefile | 61 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 drivers/staging/mars/Makefile

diff --git a/drivers/staging/mars/Makefile b/drivers/staging/mars/Makefile
new file mode 100644
index 0000000..13d68cc
--- /dev/null
+++ b/drivers/staging/mars/Makefile
@@ -0,0 +1,61 @@
+#
+# Makefile for MARS
+#
+
+# remove_this
+ifndef CONFIG_MARS
+# mars_config.h is generated by a simple Kconfig parser (gen_config.pl)
+# at build time.
+# It does not respect any Kconfig dependencies.
+# Therefore, it is unsafe. Use at your own risk!
+# It is ONLY used for out-of-tree builds.
+#
+CONFIG_MARS_BIGMODULE := m
+CONFIG_MARS_NET_COMPAT := y
+obj-$(CONFIG_MARS_BIGMODULE)	+= mars.o
+extra-y	+= mars_config.h
+GEN_CONFIG_SCRIPT := $(src)/../scripts/gen_config.pl
+$(obj)/mars_config.h: $(obj)/buildtag.h
+$(obj)/mars_config.h: $(src)/Kconfig $(GEN_CONFIG_SCRIPT)
+	$(Q)$(kecho) "MARS: using compiler $($(CC) --version | head -1)"
+	$(CC) -v
+	$(Q)$(kecho) "MARS: Generating $@"
+	$(Q)set -e; \
+	if [ ! -x $(GEN_CONFIG_SCRIPT) ]; then \
+	    $(kecho) "MARS: cannot execute script $(GEN_CONFIG_SCRIPT)"; \
+	    /bin/false; \
+	fi; \
+	cat $< | $(GEN_CONFIG_SCRIPT) > $@;
+	cat $@;
+endif
+# end_remove_this
+
+obj-$(CONFIG_MARS)	+= mars.o
+
+KBUILD_CFLAGS += -fdelete-null-pointer-checks
+
+mars-objs :=				\
+	lamport.o			\
+	brick_say.o			\
+	brick_mem.o			\
+	brick.o				\
+	xio_bricks/xio.o			\
+	xio_bricks/lib_log.o			\
+	lib/lib_rank.o			\
+	lib/lib_limiter.o			\
+	lib/lib_timing.o			\
+	xio_bricks/lib_mapfree.o			\
+	xio_bricks/xio_net.o			\
+	mars_light/light_server_strategy.o		\
+	xio_bricks/xio_server.o			\
+	xio_bricks/xio_client.o			\
+	xio_bricks/xio_sio.o			\
+	xio_bricks/xio_bio.o			\
+	xio_bricks/xio_if.o			\
+	xio_bricks/xio_copy.o			\
+	xio_bricks/xio_trans_logger.o		\
+	mars_light/light_strategy.o		\
+	mars_light/light_net.o			\
+	mars_light/mars_proc.o		\
+	mars_light/mars_light.o
+
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299647 — [RFC 17/31] mars: add new module xio_bio

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 17/31] mars: add new module xio_bio
Message-ID<qLJTX-3U9-7@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_bio.c | 845 ++++++++++++++++++++++++++++++
 include/linux/xio/xio_bio.h               |  85 +++
 2 files changed, 930 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_bio.c
 create mode 100644 include/linux/xio/xio_bio.h

diff --git a/drivers/staging/mars/xio_bricks/xio_bio.c b/drivers/staging/mars/xio_bricks/xio_bio.c
new file mode 100644
index 0000000..ef18325
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_bio.c
@@ -0,0 +1,845 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/*  Bio brick (interface to blkdev IO via kernel bios) */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bio.h>
+
+#include <linux/xio/xio.h>
+#include <linux/brick/lib_timing.h>
+#include <linux/xio/lib_mapfree.h>
+
+#include <linux/xio/xio_bio.h>
+static struct timing_stats timings[2];
+
+struct threshold bio_submit_threshold = {
+	.thr_ban = &xio_global_ban,
+	.thr_parent = &global_io_threshold,
+	.thr_limit = BIO_SUBMIT_MAX_LATENCY,
+	.thr_factor = 100,
+	.thr_plus = 0,
+};
+
+struct threshold bio_io_threshold[2] = {
+	[0] = {
+		.thr_ban = &xio_global_ban,
+		.thr_parent = &global_io_threshold,
+		.thr_limit = BIO_IO_R_MAX_LATENCY,
+		.thr_factor = 10,
+		.thr_plus = 10000,
+	},
+	[1] = {
+		.thr_ban = &xio_global_ban,
+		.thr_parent = &global_io_threshold,
+		.thr_limit = BIO_IO_W_MAX_LATENCY,
+		.thr_factor = 10,
+		.thr_plus = 10000,
+	},
+};
+
+/************************ own type definitions ***********************/
+
+/************************ own helper functions ***********************/
+
+/* This is called from the kernel bio layer.
+ */
+static
+void bio_callback(struct bio *bio)
+{
+	struct bio_aio_aspect *aio_a = bio->bi_private;
+	struct bio_brick *brick;
+	unsigned long flags;
+
+	CHECK_PTR(aio_a, err);
+	CHECK_PTR(aio_a->output, err);
+	brick = aio_a->output->brick;
+	CHECK_PTR(brick, err);
+
+	aio_a->status_code = bio->bi_error;
+
+	spin_lock_irqsave(&brick->lock, flags);
+	list_del(&aio_a->io_head);
+	list_add_tail(&aio_a->io_head, &brick->completed_list);
+	atomic_inc(&brick->completed_count);
+	spin_unlock_irqrestore(&brick->lock, flags);
+
+	wake_up_interruptible(&brick->response_event);
+	goto out_return;
+err:
+	XIO_FAT("cannot handle bio callback\n");
+out_return:;
+}
+
+/* Map from kernel address/length to struct page (if not already known),
+ * check alignment constraints, create bio from it.
+ * Return the length (may be smaller than requested).
+ */
+static
+int make_bio(struct bio_brick *brick,
+	void *data,
+	int len,
+	loff_t pos,
+	struct bio_aio_aspect *private,
+	struct bio **_bio)
+{
+	unsigned long long sector;
+	int sector_offset;
+	int data_offset;
+	int page_offset;
+	int page_len;
+	int bvec_count;
+	int rest_len = len;
+	int result_len = 0;
+	int status;
+	int i;
+	struct bio *bio = NULL;
+	struct block_device *bdev;
+
+	status = -EINVAL;
+	CHECK_PTR(brick, out);
+	bdev = brick->bdev;
+	CHECK_PTR(bdev, out);
+
+	if (unlikely(rest_len <= 0)) {
+		XIO_ERR("bad bio len %d\n", rest_len);
+		goto out;
+	}
+
+	sector = pos >> 9;		       /*  TODO: make dynamic */
+	sector_offset = pos & ((1 << 9) - 1);  /*  TODO: make dynamic */
+	data_offset = ((unsigned long)data) & ((1 << 9) - 1);  /*  TODO: make dynamic */
+
+	if (unlikely(sector_offset > 0)) {
+		XIO_ERR("odd sector offset %d\n", sector_offset);
+		goto out;
+	}
+	if (unlikely(sector_offset != data_offset)) {
+		XIO_ERR("bad alignment: sector_offset %d != data_offset %d\n", sector_offset, data_offset);
+		goto out;
+	}
+	if (unlikely(rest_len & ((1 << 9) - 1))) {
+		XIO_ERR("odd length %d\n", rest_len);
+		goto out;
+	}
+
+	page_offset = ((unsigned long)data) & (PAGE_SIZE-1);
+	page_len = rest_len + page_offset;
+	bvec_count = (page_len - 1) / PAGE_SIZE + 1;
+	if (bvec_count > brick->bvec_max) {
+		bvec_count = brick->bvec_max;
+	} else if (unlikely(bvec_count <= 0)) {
+		XIO_WRN("bvec_count=%d\n", bvec_count);
+		bvec_count = 1;
+	}
+
+	bio = bio_alloc(GFP_BRICK, bvec_count);
+	status = -ENOMEM;
+
+	for (i = 0; i < bvec_count && rest_len > 0; i++) {
+		struct page *page;
+		int this_rest = PAGE_SIZE - page_offset;
+		int this_len = rest_len;
+
+		if (this_len > this_rest)
+			this_len = this_rest;
+
+		page = brick_iomap(data, &page_offset, &this_len);
+		if (unlikely(!page)) {
+			XIO_ERR("cannot iomap() kernel address %p\n", data);
+			status = -EINVAL;
+			goto out;
+		}
+
+		bio->bi_io_vec[i].bv_page = page;
+		bio->bi_io_vec[i].bv_len = this_len;
+		bio->bi_io_vec[i].bv_offset = page_offset;
+
+		data += this_len;
+		rest_len -= this_len;
+		result_len += this_len;
+		page_offset = 0;
+	}
+
+	if (unlikely(rest_len != 0)) {
+		XIO_ERR("computation of bvec_count %d was wrong, diff=%d\n", bvec_count, rest_len);
+		status = -EINVAL;
+		goto out;
+	}
+
+	bio->bi_vcnt = i;
+	bio->bi_iter.bi_idx = 0;
+	bio->bi_iter.bi_size = result_len;
+	bio->bi_iter.bi_sector = sector;
+	bio->bi_bdev = bdev;
+	bio->bi_private = private;
+	bio->bi_end_io = bio_callback;
+	bio->bi_rw = 0; /*  must be filled in later */
+	status = result_len;
+
+out:
+	if (unlikely(status < 0)) {
+		XIO_ERR("error %d\n", status);
+		if (bio) {
+			bio_put(bio);
+			bio = NULL;
+		}
+	}
+	*_bio = bio;
+	return status;
+}
+
+/***************** own brick * input * output operations *****************/
+
+#define PRIO_INDEX(aio) ((aio)->io_prio + 1)
+
+static int bio_get_info(struct bio_output *output, struct xio_info *info)
+{
+	struct bio_brick *brick = output->brick;
+	struct inode *inode;
+	int status = -ENOENT;
+
+	if (unlikely(!brick->mf ||
+		     !brick->mf->mf_filp ||
+		     !brick->mf->mf_filp->f_mapping)) {
+		goto done;
+	}
+	inode = brick->mf->mf_filp->f_mapping->host;
+	if (unlikely(!inode))
+		goto done;
+
+	info->tf_align = 512;
+	info->tf_min_size = 512;
+	brick->total_size = i_size_read(inode);
+	info->current_size = brick->total_size;
+	XIO_DBG("determined device size = %lld\n", info->current_size);
+	status = 0;
+
+done:
+	return status;
+}
+
+static int bio_io_get(struct bio_output *output, struct aio_object *aio)
+{
+	struct bio_aio_aspect *aio_a;
+	int status = -EINVAL;
+
+	CHECK_PTR(output, done);
+	CHECK_PTR(output->brick, done);
+
+	if (aio->obj_initialized) {
+		obj_get(aio);
+		return aio->io_len;
+	}
+
+	aio_a = bio_aio_get_aspect(output->brick, aio);
+	CHECK_PTR(aio_a, done);
+	aio_a->output = output;
+	aio_a->bio = NULL;
+
+	if (!aio->io_data) { /*  buffered IO. */
+		if (unlikely(aio->io_len <= 0))
+			goto done;
+		status = -ENOMEM;
+		aio->io_data = brick_block_alloc(aio->io_pos, (aio_a->alloc_len = aio->io_len));
+		aio_a->do_dealloc = true;
+	}
+
+	status = make_bio(output->brick, aio->io_data, aio->io_len, aio->io_pos, aio_a, &aio_a->bio);
+	if (unlikely(status < 0 || !aio_a->bio)) {
+		XIO_ERR("could not create bio, status = %d\n", status);
+		goto done;
+	}
+
+	if (unlikely(aio->io_prio < XIO_PRIO_HIGH))
+		aio->io_prio = XIO_PRIO_HIGH;
+	else if (unlikely(aio->io_prio > XIO_PRIO_LOW))
+		aio->io_prio = XIO_PRIO_LOW;
+
+	aio->io_len = status;
+	obj_get_first(aio);
+	status = 0;
+
+done:
+	return status;
+}
+
+static
+void _bio_io_put(struct bio_output *output, struct aio_object *aio)
+{
+	struct bio_aio_aspect *aio_a;
+
+	aio->io_total_size = output->brick->total_size;
+
+	aio_a = bio_aio_get_aspect(output->brick, aio);
+	CHECK_PTR(aio_a, err);
+
+	if (likely(aio_a->bio)) {
+		bio_put(aio_a->bio);
+		aio_a->bio = NULL;
+	}
+	if (aio_a->do_dealloc) {
+		brick_block_free(aio->io_data, aio_a->alloc_len);
+		aio->io_data = NULL;
+	}
+	obj_free(aio);
+
+	goto out_return;
+err:
+	XIO_FAT("cannot work\n");
+out_return:;
+}
+
+#define BIO_AIO_PUT(output, aio)					\
+	({								\
+		if (obj_put(aio)) {					\
+			_bio_io_put(output, aio);			\
+		}							\
+	})
+
+static
+void bio_io_put(struct bio_output *output, struct aio_object *aio)
+{
+	BIO_AIO_PUT(output, aio);
+}
+
+static
+void _bio_io_io(struct bio_output *output, struct aio_object *aio, bool cork)
+{
+	struct bio_brick *brick = output->brick;
+	struct bio_aio_aspect *aio_a = bio_aio_get_aspect(output->brick, aio);
+	struct bio *bio;
+	unsigned long long latency;
+	unsigned long flags;
+	int rw;
+	int status = -EINVAL;
+
+	CHECK_PTR(aio_a, err);
+	bio = aio_a->bio;
+	CHECK_PTR(bio, err);
+
+	obj_get(aio);
+	atomic_inc(&brick->fly_count[PRIO_INDEX(aio)]);
+
+	bio_get(bio);
+
+	rw = aio->io_rw & 1;
+	if (brick->do_noidle && !cork)
+		rw |= REQ_NOIDLE;
+	if (!aio->io_skip_sync) {
+		if (brick->do_sync)
+			rw |= REQ_SYNC;
+	}
+
+	aio_a->start_stamp = cpu_clock(raw_smp_processor_id());
+	spin_lock_irqsave(&brick->lock, flags);
+	list_add_tail(&aio_a->io_head, &brick->submitted_list[rw & 1]);
+	spin_unlock_irqrestore(&brick->lock, flags);
+
+	bio->bi_rw = rw;
+	latency = TIME_STATS(
+		&timings[rw & 1],
+		submit_bio(rw, bio)
+		);
+
+	threshold_check(&bio_submit_threshold, latency);
+
+	status = 0;
+#ifdef BIO_EOPNOTSUPP /* missing since b25de9d6da49b1a8760a89672283128aa8c78345 */
+	if (unlikely(bio_flagged(bio, BIO_EOPNOTSUPP)))
+		status = -EOPNOTSUPP;
+#endif
+
+	if (likely(status >= 0))
+		goto done;
+
+	bio_put(bio);
+	atomic_dec(&brick->fly_count[PRIO_INDEX(aio)]);
+
+err:
+	XIO_ERR("IO error %d\n", status);
+	CHECKED_CALLBACK(aio, status, done);
+	atomic_dec(&xio_global_io_flying);
+
+done:;
+}
+
+static
+void bio_io_io(struct bio_output *output, struct aio_object *aio)
+{
+	CHECK_PTR(aio, fatal);
+
+	obj_get(aio);
+	atomic_inc(&xio_global_io_flying);
+
+	if (aio->io_prio == XIO_PRIO_LOW ||
+	    (aio->io_prio == XIO_PRIO_NORMAL && aio->io_rw)) {
+		struct bio_aio_aspect *aio_a = bio_aio_get_aspect(output->brick, aio);
+		struct bio_brick *brick = output->brick;
+		unsigned long flags;
+
+		spin_lock_irqsave(&brick->lock, flags);
+		list_add_tail(&aio_a->io_head, &brick->queue_list[PRIO_INDEX(aio)]);
+		atomic_inc(&brick->queue_count[PRIO_INDEX(aio)]);
+		spin_unlock_irqrestore(&brick->lock, flags);
+		brick->submitted = true;
+
+		wake_up_interruptible(&brick->submit_event);
+		goto out_return;
+	}
+
+	/*  realtime IO: start immediately */
+	_bio_io_io(output, aio, false);
+	BIO_AIO_PUT(output, aio);
+	goto out_return;
+fatal:
+	XIO_FAT("cannot handle aio %p on output %p\n", aio, output);
+out_return:;
+}
+
+static
+int bio_response_thread(void *data)
+{
+	struct bio_brick *brick = data;
+
+	XIO_INF("bio response thread has started on '%s'.\n", brick->brick_path);
+
+	for (;;) {
+		LIST_HEAD(tmp_list);
+		unsigned long flags;
+		int thr_limit;
+		int sleeptime;
+		int count;
+		int i;
+
+		thr_limit = bio_io_threshold[0].thr_limit;
+		if (bio_io_threshold[1].thr_limit < thr_limit)
+			thr_limit = bio_io_threshold[1].thr_limit;
+
+		sleeptime = HZ / 10;
+		if (thr_limit > 0) {
+			sleeptime = thr_limit / (1000000 * 2 / HZ);
+			if (unlikely(sleeptime < 2))
+				sleeptime = 2;
+		}
+
+		wait_event_interruptible_timeout(
+			brick->response_event,
+			atomic_read(&brick->completed_count) > 0,
+			sleeptime);
+
+		spin_lock_irqsave(&brick->lock, flags);
+		list_replace_init(&brick->completed_list, &tmp_list);
+		spin_unlock_irqrestore(&brick->lock, flags);
+
+		count = 0;
+		for (;;) {
+			struct list_head *tmp;
+			struct bio_aio_aspect *aio_a;
+			struct aio_object *aio;
+			unsigned long long latency;
+			int code;
+
+			if (list_empty(&tmp_list)) {
+				if (brick_thread_should_stop() &&
+				    atomic_read(&brick->fly_count[0]) +
+				    atomic_read(&brick->fly_count[1]) +
+				    atomic_read(&brick->fly_count[2]) <= 0)
+					goto done;
+				break;
+			}
+
+			tmp = tmp_list.next;
+			list_del_init(tmp);
+			atomic_dec(&brick->completed_count);
+
+			aio_a = container_of(tmp, struct bio_aio_aspect, io_head);
+			aio = aio_a->object;
+
+			latency = cpu_clock(raw_smp_processor_id()) - aio_a->start_stamp;
+			threshold_check(&bio_io_threshold[aio->io_rw & 1], latency);
+
+			code = aio_a->status_code;
+
+			if (code < 0) {
+				XIO_ERR("IO error %d\n", code);
+			} else {
+				aio_checksum(aio);
+				aio->io_flags |= AIO_UPTODATE;
+			}
+
+			SIMPLE_CALLBACK(aio, code);
+
+			atomic_dec(&brick->fly_count[PRIO_INDEX(aio)]);
+			atomic_inc(&brick->total_completed_count[PRIO_INDEX(aio)]);
+			count++;
+
+			if (likely(aio_a->bio))
+				bio_put(aio_a->bio);
+			BIO_AIO_PUT(aio_a->output, aio);
+
+			atomic_dec(&xio_global_io_flying);
+		}
+
+		/* Try to detect slow requests as early as possible,
+		 * even before they have completed.
+		 */
+		for (i = 0; i < 2; i++) {
+			unsigned long long eldest = 0;
+
+			spin_lock_irqsave(&brick->lock, flags);
+			if (!list_empty(&brick->submitted_list[i])) {
+				struct bio_aio_aspect *aio_a;
+
+				aio_a = container_of(brick->submitted_list[i].next, struct bio_aio_aspect, io_head);
+				eldest = aio_a->start_stamp;
+			}
+			spin_unlock_irqrestore(&brick->lock, flags);
+
+			if (eldest)
+				threshold_check(&bio_io_threshold[i], cpu_clock(raw_smp_processor_id()) - eldest);
+		}
+
+		if (count) {
+			brick->submitted = true;
+			wake_up_interruptible(&brick->submit_event);
+		}
+	}
+done:
+	XIO_INF("bio response thread has stopped.\n");
+	return 0;
+}
+
+static
+bool _bg_should_run(struct bio_brick *brick)
+{
+	return (atomic_read(&brick->queue_count[2]) > 0 &&
+		atomic_read(&brick->fly_count[0]) + atomic_read(&brick->fly_count[1]) <= brick->bg_threshold &&
+		(brick->bg_maxfly <= 0 || atomic_read(&brick->fly_count[2]) < brick->bg_maxfly));
+}
+
+static
+int bio_submit_thread(void *data)
+{
+	struct bio_brick *brick = data;
+
+	XIO_INF("bio submit thread has started on '%s'.\n", brick->brick_path);
+
+	while (!brick_thread_should_stop()) {
+		int prio;
+
+		wait_event_interruptible_timeout(
+			brick->submit_event,
+			brick->submitted,
+			HZ / 2);
+
+		brick->submitted = false;
+
+		for (prio = 0; prio < XIO_PRIO_NR; prio++) {
+			LIST_HEAD(tmp_list);
+			unsigned long flags;
+
+			if (prio == XIO_PRIO_NR-1 && !_bg_should_run(brick))
+				break;
+
+			spin_lock_irqsave(&brick->lock, flags);
+			list_replace_init(&brick->queue_list[prio], &tmp_list);
+			spin_unlock_irqrestore(&brick->lock, flags);
+
+			while (!list_empty(&tmp_list)) {
+				struct list_head *tmp = tmp_list.next;
+				struct bio_aio_aspect *aio_a;
+				struct aio_object *aio;
+				bool cork;
+
+				list_del_init(tmp);
+
+				aio_a = container_of(tmp, struct bio_aio_aspect, io_head);
+				aio = aio_a->object;
+				if (unlikely(!aio)) {
+					XIO_ERR("invalid aio\n");
+					continue;
+				}
+
+				atomic_dec(&brick->queue_count[PRIO_INDEX(aio)]);
+				cork = atomic_read(&brick->queue_count[PRIO_INDEX(aio)]) > 0;
+
+				_bio_io_io(aio_a->output, aio, cork);
+
+				BIO_AIO_PUT(aio_a->output, aio);
+			}
+		}
+	}
+
+	XIO_INF("bio submit thread has stopped.\n");
+	return 0;
+}
+
+static int bio_switch(struct bio_brick *brick)
+{
+	int status = 0;
+
+	if (brick->power.button) {
+		if (brick->power.on_led)
+			goto done;
+
+		xio_set_power_off_led((void *)brick, false);
+
+		if (!brick->bdev) {
+			static int index;
+			const char *path = brick->brick_path;
+			int flags = O_RDWR | O_EXCL | O_LARGEFILE;
+			struct address_space *mapping;
+			struct inode *inode = NULL;
+			struct request_queue *q;
+
+			brick->mf = mapfree_get(path, flags);
+			if (unlikely(!brick->mf || !brick->mf->mf_filp)) {
+				status = -ENOENT;
+				XIO_ERR("cannot open file '%s'\n", path);
+				goto done;
+			}
+			mapfree_pages(brick->mf, -1);
+			mapping = brick->mf->mf_filp->f_mapping;
+			if (likely(mapping))
+				inode = mapping->host;
+			if (unlikely(!mapping || !inode)) {
+				XIO_ERR("internal problem with '%s'\n", path);
+				status = -EINVAL;
+				goto done;
+			}
+			if (unlikely(!S_ISBLK(inode->i_mode) || !inode->i_bdev)) {
+				XIO_ERR("sorry, '%s' is not a block device\n", path);
+				status = -ENODEV;
+				goto done;
+			}
+
+			mapping_set_gfp_mask(mapping, mapping_gfp_mask(mapping) & ~(__GFP_IO | __GFP_FS));
+
+			q = bdev_get_queue(inode->i_bdev);
+			if (unlikely(!q)) {
+				XIO_ERR("internal queue '%s' does not exist\n", path);
+				status = -EINVAL;
+				goto done;
+			}
+
+			XIO_INF("'%s' ra_pages OLD=%lu NEW=%d\n",
+				path,
+				q->backing_dev_info.ra_pages,
+				brick->ra_pages);
+			q->backing_dev_info.ra_pages = brick->ra_pages;
+
+			brick->bvec_max = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+			if (brick->bvec_max > BIO_MAX_PAGES)
+				brick->bvec_max = BIO_MAX_PAGES;
+			else if (brick->bvec_max <= 1)
+				brick->bvec_max = 1;
+			brick->total_size = i_size_read(inode);
+			XIO_INF("'%s' size=%lld bvec_max=%d\n",
+				 path, brick->total_size, brick->bvec_max);
+
+			brick->response_thread = brick_thread_create(bio_response_thread,
+				brick,
+				"xio_bio_r%d",
+				index);
+			brick->submit_thread = brick_thread_create(bio_submit_thread, brick, "xio_bio_s%d", index);
+			status = -ENOMEM;
+			if (likely(brick->submit_thread && brick->response_thread)) {
+				brick->bdev = inode->i_bdev;
+				brick->mode_ptr = &brick->mf->mf_mode;
+				index++;
+				status = 0;
+			}
+		}
+	}
+
+	xio_set_power_on_led((void *)brick, brick->power.button && brick->bdev != NULL);
+
+done:
+	if (status < 0 || !brick->power.button) {
+		if (brick->submit_thread) {
+			brick_thread_stop(brick->submit_thread);
+			brick->submit_thread = NULL;
+		}
+		if (brick->response_thread) {
+			brick_thread_stop(brick->response_thread);
+			brick->response_thread = NULL;
+		}
+		if (brick->mf) {
+			mapfree_put(brick->mf);
+			brick->mf = NULL;
+		}
+		brick->mode_ptr = NULL;
+		brick->bdev = NULL;
+		if (!brick->power.button) {
+			xio_set_power_off_led((void *)brick, true);
+			brick->total_size = 0;
+		}
+	}
+	return status;
+}
+
+/*************** informational * statistics **************/
+
+static noinline
+char *bio_statistics(struct bio_brick *brick, int verbose)
+{
+	char *res = brick_string_alloc(4096);
+	int pos = 0;
+
+	pos += report_timing(&timings[0], res + pos, 4096 - pos);
+	pos += report_timing(&timings[1], res + pos, 4096 - pos);
+
+	snprintf(res + pos, 4096 - pos,
+		 "total completed[0] = %d completed[1] = %d completed[2] = %d | queued[0] = %d queued[1] = %d queued[2] = %d flying[0] = %d flying[1] = %d flying[2] = %d completing = %d\n",
+		 atomic_read(&brick->total_completed_count[0]),
+		 atomic_read(&brick->total_completed_count[1]),
+		 atomic_read(&brick->total_completed_count[2]),
+		 atomic_read(&brick->fly_count[0]),
+		 atomic_read(&brick->queue_count[0]),
+		 atomic_read(&brick->queue_count[1]),
+		 atomic_read(&brick->queue_count[2]),
+		 atomic_read(&brick->fly_count[1]),
+		 atomic_read(&brick->fly_count[2]),
+		 atomic_read(&brick->completed_count));
+
+	return res;
+}
+
+static noinline
+void bio_reset_statistics(struct bio_brick *brick)
+{
+	atomic_set(&brick->total_completed_count[0], 0);
+	atomic_set(&brick->total_completed_count[1], 0);
+	atomic_set(&brick->total_completed_count[2], 0);
+}
+
+/*************** object * aspect constructors * destructors **************/
+
+static int bio_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct bio_aio_aspect *ini = (void *)_ini;
+
+	INIT_LIST_HEAD(&ini->io_head);
+	return 0;
+}
+
+static void bio_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct bio_aio_aspect *ini = (void *)_ini;
+
+	(void)ini;
+}
+
+XIO_MAKE_STATICS(bio);
+
+/********************* brick constructors * destructors *******************/
+
+static int bio_brick_construct(struct bio_brick *brick)
+{
+	spin_lock_init(&brick->lock);
+	INIT_LIST_HEAD(&brick->queue_list[0]);
+	INIT_LIST_HEAD(&brick->queue_list[1]);
+	INIT_LIST_HEAD(&brick->queue_list[2]);
+	INIT_LIST_HEAD(&brick->submitted_list[0]);
+	INIT_LIST_HEAD(&brick->submitted_list[1]);
+	INIT_LIST_HEAD(&brick->completed_list);
+	init_waitqueue_head(&brick->submit_event);
+	init_waitqueue_head(&brick->response_event);
+	return 0;
+}
+
+static int bio_brick_destruct(struct bio_brick *brick)
+{
+	return 0;
+}
+
+static int bio_output_construct(struct bio_output *output)
+{
+	return 0;
+}
+
+static int bio_output_destruct(struct bio_output *output)
+{
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct bio_brick_ops bio_brick_ops = {
+	.brick_switch = bio_switch,
+	.brick_statistics = bio_statistics,
+	.reset_statistics = bio_reset_statistics,
+};
+
+static struct bio_output_ops bio_output_ops = {
+	.xio_get_info = bio_get_info,
+	.aio_get = bio_io_get,
+	.aio_put = bio_io_put,
+	.aio_io = bio_io_io,
+};
+
+const struct bio_input_type bio_input_type = {
+	.type_name = "bio_input",
+	.input_size = sizeof(struct bio_input),
+};
+
+static const struct bio_input_type *bio_input_types[] = {
+	&bio_input_type,
+};
+
+const struct bio_output_type bio_output_type = {
+	.type_name = "bio_output",
+	.output_size = sizeof(struct bio_output),
+	.master_ops = &bio_output_ops,
+	.output_construct = &bio_output_construct,
+	.output_destruct = &bio_output_destruct,
+};
+
+static const struct bio_output_type *bio_output_types[] = {
+	&bio_output_type,
+};
+
+const struct bio_brick_type bio_brick_type = {
+	.type_name = "bio_brick",
+	.brick_size = sizeof(struct bio_brick),
+	.max_inputs = 0,
+	.max_outputs = 1,
+	.master_ops = &bio_brick_ops,
+	.aspect_types = bio_aspect_types,
+	.default_input_types = bio_input_types,
+	.default_output_types = bio_output_types,
+	.brick_construct = &bio_brick_construct,
+	.brick_destruct = &bio_brick_destruct,
+};
+
+/***************** module init stuff ************************/
+
+int __init init_xio_bio(void)
+{
+	XIO_INF("init_bio()\n");
+	_bio_brick_type = (void *)&bio_brick_type;
+	return bio_register_brick_type();
+}
+
+void exit_xio_bio(void)
+{
+	XIO_INF("exit_bio()\n");
+	bio_unregister_brick_type();
+}
diff --git a/include/linux/xio/xio_bio.h b/include/linux/xio/xio_bio.h
new file mode 100644
index 0000000..a0d98be
--- /dev/null
+++ b/include/linux/xio/xio_bio.h
@@ -0,0 +1,85 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_BIO_H
+#define XIO_BIO_H
+
+#define BIO_SUBMIT_MAX_LATENCY		250 /*	250 us */
+#define BIO_IO_R_MAX_LATENCY		40000 /*   40 ms */
+#define BIO_IO_W_MAX_LATENCY		100000 /*  100 ms */
+
+extern struct threshold bio_submit_threshold;
+extern struct threshold bio_io_threshold[2];
+
+#include <linux/blkdev.h>
+
+struct bio_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct list_head io_head;
+	struct bio *bio;
+	struct bio_output *output;
+	unsigned long long start_stamp;
+	int status_code;
+	int hash_pos;
+	int alloc_len;
+	bool do_dealloc;
+};
+
+struct bio_brick {
+	XIO_BRICK(bio);
+	/*  tunables */
+	int ra_pages;
+	int bg_threshold;
+	int bg_maxfly;
+	bool do_noidle;
+	bool do_sync;
+	bool do_unplug;
+
+	/*  readonly */
+	loff_t total_size;
+	atomic_t fly_count[XIO_PRIO_NR];
+	atomic_t queue_count[XIO_PRIO_NR];
+	atomic_t completed_count;
+	atomic_t total_completed_count[XIO_PRIO_NR];
+
+	/*  private */
+	spinlock_t lock;
+	struct list_head queue_list[XIO_PRIO_NR];
+	struct list_head submitted_list[2];
+	struct list_head completed_list;
+
+	wait_queue_head_t submit_event;
+	wait_queue_head_t response_event;
+	struct mapfree_info *mf;
+	struct block_device *bdev;
+	struct task_struct *submit_thread;
+	struct task_struct *response_thread;
+	int bvec_max;
+	bool submitted;
+};
+
+struct bio_input {
+	XIO_INPUT(bio);
+};
+
+struct bio_output {
+	XIO_OUTPUT(bio);
+};
+
+XIO_TYPES(bio);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299648 — [RFC 15/31] mars: add new module lib_mapfree

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 15/31] mars: add new module lib_mapfree
Message-ID<qLJTY-3U9-11@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/lib_mapfree.c | 380 ++++++++++++++++++++++++++
 include/linux/xio/lib_mapfree.h               |  84 ++++++
 2 files changed, 464 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/lib_mapfree.c
 create mode 100644 include/linux/xio/lib_mapfree.h

diff --git a/drivers/staging/mars/xio_bricks/lib_mapfree.c b/drivers/staging/mars/xio_bricks/lib_mapfree.c
new file mode 100644
index 0000000..6b464d7
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/lib_mapfree.c
@@ -0,0 +1,380 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/xio/lib_mapfree.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/file.h>
+
+/*  time to wait between background mapfree operations */
+int mapfree_period_sec = 10;
+
+/*  some grace space where no regular cleanup should occur */
+int mapfree_grace_keep_mb = 16;
+
+static
+DECLARE_RWSEM(mapfree_mutex);
+
+static
+LIST_HEAD(mapfree_list);
+
+void mapfree_pages(struct mapfree_info *mf, int grace_keep)
+{
+	struct address_space *mapping;
+	pgoff_t start;
+	pgoff_t end;
+
+	if (unlikely(!mf))
+	    goto done;
+	if (unlikely(!mf->mf_filp))
+		goto done;
+
+	mapping = mf->mf_filp->f_mapping;
+	if (unlikely(!mapping))
+		goto done;
+
+	if (grace_keep < 0) { /*  force full flush */
+		start = 0;
+		end = -1;
+	} else {
+		unsigned long flags;
+		loff_t tmp;
+		loff_t min;
+
+		spin_lock_irqsave(&mf->mf_lock, flags);
+
+		min = tmp = mf->mf_min[0];
+		if (likely(mf->mf_min[1] < min))
+			min = mf->mf_min[1];
+		if (tmp) {
+			mf->mf_min[1] = tmp;
+			mf->mf_min[0] = 0;
+		}
+
+		spin_unlock_irqrestore(&mf->mf_lock, flags);
+
+		min -= (loff_t)grace_keep * (1024 * 1024); /*  megabytes */
+		end = 0;
+
+		if (min > 0 || mf->mf_last) {
+			start = mf->mf_last / PAGE_SIZE;
+			/*  add some grace overlapping */
+			if (likely(start > 0))
+				start--;
+			mf->mf_last = min;
+			end = min / PAGE_SIZE;
+		} else	{ /*  there was no progress for at least 2 rounds */
+			start = 0;
+			if (!grace_keep) /*  also flush thoroughly */
+				end = -1;
+		}
+
+		XIO_DBG("file = '%s' start = %lu end = %lu\n", mf->mf_name, start, end);
+	}
+
+	if (end > start || end == -1)
+		invalidate_mapping_pages(mapping, start, end);
+
+done:;
+}
+
+static
+void _mapfree_put(struct mapfree_info *mf)
+{
+	if (atomic_dec_and_test(&mf->mf_count)) {
+		XIO_DBG("closing file '%s' filp = %p\n", mf->mf_name, mf->mf_filp);
+		list_del_init(&mf->mf_head);
+		CHECK_HEAD_EMPTY(&mf->mf_dirty_anchor);
+		if (likely(mf->mf_filp)) {
+			mapfree_pages(mf, -1);
+			filp_close(mf->mf_filp, NULL);
+		}
+		brick_string_free(mf->mf_name);
+		brick_mem_free(mf);
+	}
+}
+
+void mapfree_put(struct mapfree_info *mf)
+{
+	if (likely(mf)) {
+		down_write(&mapfree_mutex);
+		_mapfree_put(mf);
+		up_write(&mapfree_mutex);
+	}
+}
+
+struct mapfree_info *mapfree_get(const char *name, int flags)
+{
+	struct mapfree_info *mf = NULL;
+	struct list_head *tmp;
+
+	if (!(flags & O_DIRECT)) {
+		down_read(&mapfree_mutex);
+		for (tmp = mapfree_list.next; tmp != &mapfree_list; tmp = tmp->next) {
+			struct mapfree_info *_mf = container_of(tmp, struct mapfree_info, mf_head);
+
+			if (_mf->mf_flags == flags && !strcmp(_mf->mf_name, name)) {
+				mf = _mf;
+				atomic_inc(&mf->mf_count);
+				break;
+			}
+		}
+		up_read(&mapfree_mutex);
+
+		if (mf)
+			goto done;
+	}
+
+	for (;;) {
+		struct address_space *mapping;
+		struct inode *inode = NULL;
+		int ra = 1;
+		int prot = 0600;
+
+		mm_segment_t oldfs;
+
+		mf = brick_zmem_alloc(sizeof(struct mapfree_info));
+
+		mf->mf_name = brick_strdup(name);
+
+		mf->mf_flags = flags;
+		INIT_LIST_HEAD(&mf->mf_head);
+		INIT_LIST_HEAD(&mf->mf_dirty_anchor);
+		atomic_set(&mf->mf_count, 1);
+		spin_lock_init(&mf->mf_lock);
+		mf->mf_max = -1;
+
+		oldfs = get_fs();
+		set_fs(get_ds());
+		mf->mf_filp = filp_open(name, flags, prot);
+		set_fs(oldfs);
+
+		XIO_DBG("file '%s' flags = %d prot = %d filp = %p\n", name, flags, prot, mf->mf_filp);
+
+		if (unlikely(!mf->mf_filp || IS_ERR(mf->mf_filp))) {
+			int err = PTR_ERR(mf->mf_filp);
+
+			XIO_ERR("can't open file '%s' status=%d\n", name, err);
+			mf->mf_filp = NULL;
+			_mapfree_put(mf);
+			mf = NULL;
+			break;
+		}
+
+		mapping = mf->mf_filp->f_mapping;
+		if (likely(mapping))
+			inode = mapping->host;
+		if (unlikely(!mapping || !inode)) {
+			XIO_ERR("file '%s' has no mapping\n", name);
+			mf->mf_filp = NULL;
+			_mapfree_put(mf);
+			mf = NULL;
+			break;
+		}
+
+		mapping_set_gfp_mask(mapping, mapping_gfp_mask(mapping) & ~(__GFP_IO | __GFP_FS));
+
+		mf->mf_max = i_size_read(inode);
+
+		if (S_ISBLK(inode->i_mode)) {
+			XIO_INF("changing blkdev readahead from %lu to %d\n",
+				inode->i_bdev->bd_disk->queue->backing_dev_info.ra_pages,
+				ra);
+			inode->i_bdev->bd_disk->queue->backing_dev_info.ra_pages = ra;
+		}
+
+		if (flags & O_DIRECT) { /*  never share them */
+			break;
+		}
+
+		/*  maintain global list of all open files */
+		down_write(&mapfree_mutex);
+		for (tmp = mapfree_list.next; tmp != &mapfree_list; tmp = tmp->next) {
+			struct mapfree_info *_mf = container_of(tmp, struct mapfree_info, mf_head);
+
+			if (unlikely(_mf->mf_flags == flags && !strcmp(_mf->mf_name, name))) {
+				XIO_WRN("race on creation of '%s' detected\n", name);
+				_mapfree_put(mf);
+				mf = _mf;
+				atomic_inc(&mf->mf_count);
+				goto leave;
+			}
+		}
+		list_add_tail(&mf->mf_head, &mapfree_list);
+leave:
+		up_write(&mapfree_mutex);
+		break;
+	}
+done:
+	return mf;
+}
+
+void mapfree_set(struct mapfree_info *mf, loff_t min, loff_t max)
+{
+	unsigned long flags;
+
+	if (likely(mf)) {
+		spin_lock_irqsave(&mf->mf_lock, flags);
+		if (!mf->mf_min[0] || mf->mf_min[0] > min)
+			mf->mf_min[0] = min;
+		if (max >= 0 && mf->mf_max < max)
+			mf->mf_max = max;
+		spin_unlock_irqrestore(&mf->mf_lock, flags);
+	}
+}
+
+static
+int mapfree_thread(void *data)
+{
+	while (!brick_thread_should_stop()) {
+		struct mapfree_info *mf = NULL;
+		struct list_head *tmp;
+		long long eldest = 0;
+
+		brick_msleep(500);
+
+		if (mapfree_period_sec <= 0)
+			continue;
+
+		down_read(&mapfree_mutex);
+
+		for (tmp = mapfree_list.next; tmp != &mapfree_list; tmp = tmp->next) {
+			struct mapfree_info *_mf = container_of(tmp, struct mapfree_info, mf_head);
+
+			if (unlikely(!_mf->mf_jiffies)) {
+				_mf->mf_jiffies = jiffies;
+				continue;
+			}
+			if ((long long)jiffies - _mf->mf_jiffies > mapfree_period_sec * HZ &&
+			    (!mf || _mf->mf_jiffies < eldest)) {
+				mf = _mf;
+				eldest = _mf->mf_jiffies;
+			}
+		}
+		if (mf)
+			atomic_inc(&mf->mf_count);
+
+		up_read(&mapfree_mutex);
+
+		if (!mf)
+			continue;
+
+		mapfree_pages(mf, mapfree_grace_keep_mb);
+
+		mf->mf_jiffies = jiffies;
+		mapfree_put(mf);
+	}
+	return 0;
+}
+
+/***************** dirty IOs on the fly  *****************/
+
+void mf_insert_dirty(struct mapfree_info *mf, struct dirty_info *di)
+{
+	unsigned long flags;
+
+	if (likely(di->dirty_aio && mf)) {
+		spin_lock_irqsave(&mf->mf_lock, flags);
+		list_del(&di->dirty_head);
+		list_add(&di->dirty_head, &mf->mf_dirty_anchor);
+		spin_unlock_irqrestore(&mf->mf_lock, flags);
+	}
+}
+
+void mf_remove_dirty(struct mapfree_info *mf, struct dirty_info *di)
+{
+	unsigned long flags;
+
+	if (!list_empty(&di->dirty_head) && mf) {
+		spin_lock_irqsave(&mf->mf_lock, flags);
+		list_del_init(&di->dirty_head);
+		spin_unlock_irqrestore(&mf->mf_lock, flags);
+	}
+}
+
+void mf_get_dirty(struct mapfree_info *mf, loff_t *min, loff_t *max, int min_stage, int max_stage)
+{
+	unsigned long flags;
+
+	struct list_head *tmp;
+
+	if (unlikely(!mf))
+	    goto done;
+
+	spin_lock_irqsave(&mf->mf_lock, flags);
+	for (tmp = mf->mf_dirty_anchor.next; tmp != &mf->mf_dirty_anchor; tmp = tmp->next) {
+		struct dirty_info *di = container_of(tmp, struct dirty_info, dirty_head);
+		struct aio_object *aio = di->dirty_aio;
+
+		if (unlikely(!aio))
+			continue;
+		if (di->dirty_stage < min_stage || di->dirty_stage > max_stage)
+			continue;
+		if (aio->io_pos < *min)
+			*min = aio->io_pos;
+		if (aio->io_pos + aio->io_len > *max)
+			*max = aio->io_pos + aio->io_len;
+	}
+	spin_unlock_irqrestore(&mf->mf_lock, flags);
+done:;
+}
+
+void mf_get_any_dirty(const char *filename, loff_t *min, loff_t *max, int min_stage, int max_stage)
+{
+	struct list_head *tmp;
+
+	down_read(&mapfree_mutex);
+	for (tmp = mapfree_list.next; tmp != &mapfree_list; tmp = tmp->next) {
+		struct mapfree_info *mf = container_of(tmp, struct mapfree_info, mf_head);
+
+		if (!strcmp(mf->mf_name, filename))
+			mf_get_dirty(mf, min, max, min_stage, max_stage);
+	}
+	up_read(&mapfree_mutex);
+}
+
+/***************** module init stuff ************************/
+
+static
+struct task_struct *mf_thread;
+
+int __init init_xio_mapfree(void)
+{
+	XIO_DBG("init_mapfree()\n");
+	mf_thread = brick_thread_create(mapfree_thread, NULL, "xio_mapfree");
+	if (unlikely(!mf_thread)) {
+		XIO_ERR("could not create mapfree thread\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void exit_xio_mapfree(void)
+{
+	XIO_DBG("exit_mapfree()\n");
+	if (likely(mf_thread)) {
+		brick_thread_stop(mf_thread);
+		mf_thread = NULL;
+	}
+}
diff --git a/include/linux/xio/lib_mapfree.h b/include/linux/xio/lib_mapfree.h
new file mode 100644
index 0000000..e7594e12
--- /dev/null
+++ b/include/linux/xio/lib_mapfree.h
@@ -0,0 +1,84 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_LIB_MAPFREE_H
+#define XIO_LIB_MAPFREE_H
+
+/* Mapfree infrastructure.
+ *
+ * Purposes:
+ *
+ * 1) Open files only once when possible, do ref-counting on struct mapfree_info
+ *
+ * 2) Automatically call invalidate_mapping_pages() in the background on
+ *    "unused" areas to free resources.
+ *    Used areas can be indicated by calling mapfree_set() frequently.
+ *    Usage model: tailored to sequential logfiles.
+ *
+ * 3) Do it all in a completely decoupled manner, in order to prevent resource deadlocks.
+ *
+ * 4) Also to prevent deadlocks: always set mapping_set_gfp_mask() accordingly.
+ */
+
+#include <linux/xio/xio.h>
+
+extern int mapfree_period_sec;
+extern int mapfree_grace_keep_mb;
+
+struct mapfree_info {
+	struct list_head mf_head;
+	struct list_head mf_dirty_anchor;
+	char		*mf_name;
+	struct file	*mf_filp;
+	int		 mf_flags;
+	int		 mf_mode;
+	atomic_t	 mf_count;
+	spinlock_t	 mf_lock;
+	loff_t		 mf_min[2];
+	loff_t		 mf_last;
+	loff_t		 mf_max;
+	long long	 mf_jiffies;
+};
+
+struct dirty_info {
+	struct list_head dirty_head;
+	struct aio_object *dirty_aio;
+	int dirty_stage;
+};
+
+struct mapfree_info *mapfree_get(const char *filename, int flags);
+
+void mapfree_put(struct mapfree_info *mf);
+
+void mapfree_set(struct mapfree_info *mf, loff_t min, loff_t max);
+
+void mapfree_pages(struct mapfree_info *mf, int grace_keep);
+
+/***************** dirty IOs on the fly  *****************/
+
+void mf_insert_dirty(struct mapfree_info *mf, struct dirty_info *di);
+void mf_remove_dirty(struct mapfree_info *mf, struct dirty_info *di);
+void mf_get_dirty(struct mapfree_info *mf, loff_t *min, loff_t *max, int min_stage, int max_stage);
+void mf_get_any_dirty(const char *filename, loff_t *min, loff_t *max, int min_stage, int max_stage);
+
+/***************** module init stuff ************************/
+
+int __init init_xio_mapfree(void);
+
+void exit_xio_mapfree(void);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299649 — [RFC 27/31] mars: add new module mars_proc

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 27/31] mars: add new module mars_proc
Message-ID<qLJTY-3U9-17@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/mars_light/mars_proc.c | 369 ++++++++++++++++++++++++++++
 include/linux/mars_light/mars_proc.h        |  34 +++
 2 files changed, 403 insertions(+)
 create mode 100644 drivers/staging/mars/mars_light/mars_proc.c
 create mode 100644 include/linux/mars_light/mars_proc.h

diff --git a/drivers/staging/mars/mars_light/mars_proc.c b/drivers/staging/mars/mars_light/mars_proc.c
new file mode 100644
index 0000000..2a96614
--- /dev/null
+++ b/drivers/staging/mars/mars_light/mars_proc.c
@@ -0,0 +1,369 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/string.h>
+
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+
+#include <linux/mars_light/light_strategy.h>
+#include <linux/mars_light/mars_proc.h>
+#include <linux/xio/lib_mapfree.h>
+#include <linux/xio/xio_bio.h>
+#include <linux/xio/xio_if.h>
+#include <linux/xio/xio_copy.h>
+#include <linux/xio/xio_client.h>
+#include <linux/xio/xio_server.h>
+#include <linux/xio/xio_trans_logger.h>
+
+xio_info_fn xio_info;
+
+static
+int trigger_sysctl_handler(
+	struct ctl_table *table,
+	int write,
+	void __user *buffer,
+	size_t *length,
+	loff_t *ppos)
+{
+	ssize_t res = 0;
+	size_t len = *length;
+
+	XIO_DBG("write = %d len = %ld pos = %lld\n", write, len, *ppos);
+
+	if (!len || *ppos > 0)
+		goto done;
+
+	if (write) {
+		char tmp[8] = {};
+
+		res = len; /*  fake consumption of all data */
+
+		if (len > 7)
+			len = 7;
+		if (!copy_from_user(tmp, buffer, len)) {
+			int code = 0;
+			int status = kstrtoint(tmp, 10, &code);
+
+			/* the return value from ssanf() does not matter */
+			(void)status;
+			if (code > 0)
+				local_trigger();
+			if (code > 1)
+				remote_trigger();
+		}
+	} else {
+		char *answer = "MARS module not operational\n";
+		char *tmp = NULL;
+		int mylen;
+
+		if (xio_info) {
+			answer = "internal error while determining xio_info\n";
+			tmp = xio_info();
+			if (tmp)
+				answer = tmp;
+		}
+
+		mylen = strlen(answer);
+		if (len > mylen)
+			len = mylen;
+		res = len;
+		if (copy_to_user(buffer, answer, len)) {
+			XIO_ERR("write %ld bytes at %p failed\n", len, buffer);
+			res = -EFAULT;
+		}
+		brick_string_free(tmp);
+	}
+
+done:
+	XIO_DBG("res = %ld\n", res);
+	*length = res;
+	if (res >= 0) {
+		*ppos += res;
+		return 0;
+	}
+	return res;
+}
+
+static
+int lamport_sysctl_handler(
+	struct ctl_table *table,
+	int write,
+	void __user *buffer,
+	size_t *length,
+	loff_t *ppos)
+{
+	ssize_t res = 0;
+	size_t len = *length;
+
+	XIO_DBG("write = %d len = %ld pos = %lld\n", write, len, *ppos);
+
+	if (!len || *ppos > 0)
+		goto done;
+
+	if (write) {
+		return -EINVAL;
+	} else {
+		int my_len = 128;
+		char *tmp = brick_string_alloc(my_len);
+		struct timespec know = CURRENT_TIME;
+		struct timespec lnow;
+
+		get_lamport(&lnow);
+
+		res = scnprintf(tmp, my_len,
+			       "CURRENT_TIME=%ld.%09ld\nlamport_now=%ld.%09ld\n",
+			       know.tv_sec, know.tv_nsec,
+			       lnow.tv_sec, lnow.tv_nsec
+			);
+
+		if (copy_to_user(buffer, tmp, res)) {
+			XIO_ERR("write %ld bytes at %p failed\n", res, buffer);
+			res = -EFAULT;
+		}
+		brick_string_free(tmp);
+	}
+
+done:
+	XIO_DBG("res = %ld\n", res);
+	*length = res;
+	if (res >= 0) {
+		*ppos += res;
+		return 0;
+	}
+	return res;
+}
+
+#ifdef CTL_UNNUMBERED
+#define _CTL_NAME			.ctl_name = CTL_UNNUMBERED,
+#define _CTL_STRATEGY(handler)	.strategy = &handler,
+#else
+#define _CTL_NAME			/*empty*/
+#define _CTL_STRATEGY(handler)	/*empty*/
+#endif
+
+#define VEC_ENTRY(NAME, VAR, MODE, COUNT)				\
+	{								\
+		_CTL_NAME						\
+		.procname = NAME,					\
+		.data = &(VAR),						\
+		.maxlen = sizeof(int) * (COUNT),			\
+		.mode = MODE,						\
+		.proc_handler = &proc_dointvec,				\
+		_CTL_STRATEGY(sysctl_intvec)				\
+	}
+
+#define INT_ENTRY(NAME, VAR, MODE)					\
+	VEC_ENTRY(NAME, VAR, MODE, 1)
+
+/* checkpatch.pl: no, these complex values cannot be easily enclosed
+ * in parentheses. If { ... } were used inside the macro body, it would
+ * no longer be possible to add additional fields externally.
+ * I could inject further fields externally via parameters, but
+ * that would make it less understandable.
+ */
+#define LIMITER_ENTRIES(VAR, PREFIX, SUFFIX)				\
+	INT_ENTRY(PREFIX "_total_ops_" SUFFIX, (VAR)->lim_total_ops, 0400),\
+	INT_ENTRY(PREFIX "_total_sum_" SUFFIX, (VAR)->lim_total_sum, 0400),\
+	INT_ENTRY(PREFIX "_ratelimit_" SUFFIX, (VAR)->lim_max_rate, 0600),\
+	INT_ENTRY(PREFIX "_maxdelay_ms",   (VAR)->lim_max_delay, 0600), \
+	INT_ENTRY(PREFIX "_minwindow_ms",  (VAR)->lim_min_window, 0600),\
+	INT_ENTRY(PREFIX "_maxwindow_ms",  (VAR)->lim_max_window, 0600),\
+	INT_ENTRY(PREFIX "_cumul_" SUFFIX, (VAR)->lim_cumul,	0600),	\
+	INT_ENTRY(PREFIX "_count_ops",	   (VAR)->lim_count,	0600),	\
+	INT_ENTRY(PREFIX "_rate_"  SUFFIX, (VAR)->lim_rate,	0400)	\
+
+#define THRESHOLD_ENTRIES(VAR, PREFIX)					\
+	INT_ENTRY(PREFIX "_threshold_us",   (VAR)->thr_limit,	 0600), \
+	INT_ENTRY(PREFIX "_factor_percent", (VAR)->thr_factor,	 0600), \
+	INT_ENTRY(PREFIX "_plus_us",	    (VAR)->thr_plus,	 0600), \
+	INT_ENTRY(PREFIX "_max_ms",	    (VAR)->thr_max,	 0600), \
+	INT_ENTRY(PREFIX "_triggered",	    (VAR)->thr_triggered, 0400),\
+	INT_ENTRY(PREFIX "_true_hit",	    (VAR)->thr_true_hit, 0400)	\
+
+static
+struct ctl_table traffic_tuning_table[] = {
+	LIMITER_ENTRIES(&client_limiter,    "client_role_traffic",    "kb"),
+	LIMITER_ENTRIES(&server_limiter,    "server_role_traffic",    "kb"),
+	{}
+};
+
+static
+struct ctl_table io_tuning_table[] = {
+	LIMITER_ENTRIES(&global_writeback.limiter, "writeback",       "kb"),
+	INT_ENTRY("writeback_until_percent", global_writeback.until_percent, 0600),
+	THRESHOLD_ENTRIES(&global_io_threshold,  "global_io"),
+	THRESHOLD_ENTRIES(&bio_submit_threshold, "bio_submit"),
+	THRESHOLD_ENTRIES(&bio_io_threshold[0],  "bio_io_r"),
+	THRESHOLD_ENTRIES(&bio_io_threshold[1],  "bio_io_w"),
+	{}
+};
+
+static
+struct ctl_table tcp_tuning_table[] = {
+	INT_ENTRY("ip_tos",	     default_tcp_params.ip_tos,		 0600),
+	INT_ENTRY("tcp_window_size", default_tcp_params.tcp_window_size, 0600),
+	INT_ENTRY("tcp_nodelay",     default_tcp_params.tcp_nodelay,	 0600),
+	INT_ENTRY("tcp_timeout",     default_tcp_params.tcp_timeout,	 0600),
+	INT_ENTRY("tcp_keepcnt",     default_tcp_params.tcp_keepcnt,	 0600),
+	INT_ENTRY("tcp_keepintvl",   default_tcp_params.tcp_keepintvl,	 0600),
+	INT_ENTRY("tcp_keepidle",    default_tcp_params.tcp_keepidle,	 0600),
+	{}
+};
+
+static
+struct ctl_table mars_table[] = {
+	{
+		_CTL_NAME
+		.procname = "trigger",
+		.mode = 0200,
+		.proc_handler = &trigger_sysctl_handler,
+	},
+	{
+		_CTL_NAME
+		.procname = "info",
+		.mode = 0400,
+		.proc_handler = &trigger_sysctl_handler,
+	},
+	{
+		_CTL_NAME
+		.procname = "lamport_clock",
+		.mode = 0400,
+		.proc_handler = &lamport_sysctl_handler,
+	},
+	INT_ENTRY("show_log_messages",	  brick_say_logging,	  0600),
+	INT_ENTRY("show_debug_messages",  brick_say_debug,	  0600),
+	INT_ENTRY("show_statistics_global", global_show_statist,  0600),
+	INT_ENTRY("show_statistics_server", server_show_statist,  0600),
+	INT_ENTRY("logger_completion_semantics", trans_logger_completion_semantics, 0600),
+	INT_ENTRY("logger_do_crc",	  trans_logger_do_crc,	  0600),
+	INT_ENTRY("syslog_min_class",	  brick_say_syslog_min,   0600),
+	INT_ENTRY("syslog_max_class",	  brick_say_syslog_max,   0600),
+	INT_ENTRY("syslog_flood_class",   brick_say_syslog_flood_class, 0600),
+	INT_ENTRY("syslog_flood_limit",   brick_say_syslog_flood_limit, 0600),
+	INT_ENTRY("syslog_flood_recovery_s", brick_say_syslog_flood_recovery, 0600),
+	INT_ENTRY("delay_say_on_overflow", delay_say_on_overflow,  0600),
+	INT_ENTRY("mapfree_period_sec",   mapfree_period_sec,	  0600),
+	INT_ENTRY("mapfree_grace_keep_mb", mapfree_grace_keep_mb, 0600),
+	INT_ENTRY("logger_max_interleave", trans_logger_max_interleave, 0600),
+	INT_ENTRY("logger_resume",	  trans_logger_resume,	  0600),
+	INT_ENTRY("logger_replay_timeout_sec", trans_logger_replay_timeout, 0600),
+	INT_ENTRY("mem_limit_percent",	  mars_mem_percent,	  0600),
+	INT_ENTRY("logger_mem_used_kb",   trans_logger_mem_usage, 0400),
+	INT_ENTRY("mem_used_raw_kb",	  brick_global_block_used, 0400),
+#ifdef CONFIG_MARS_MEM_PREALLOC
+	INT_ENTRY("mem_allow_freelist",   brick_allow_freelist,   0600),
+	VEC_ENTRY("mem_freelist_max",	  brick_mem_freelist_max,  0600, BRICK_MAX_ORDER+1),
+	VEC_ENTRY("mem_alloc_count",	  brick_mem_alloc_count,  0400, BRICK_MAX_ORDER+1),
+	VEC_ENTRY("mem_alloc_max",	  brick_mem_alloc_count,  0600, BRICK_MAX_ORDER+1),
+#endif
+	INT_ENTRY("io_flying_count",	  xio_global_io_flying,  0400),
+	INT_ENTRY("copy_overlap",	  xio_copy_overlap,	  0600),
+	INT_ENTRY("copy_read_prio",	  xio_copy_read_prio,	  0600),
+	INT_ENTRY("copy_write_prio",	  xio_copy_write_prio,	 0600),
+	INT_ENTRY("copy_read_max_fly",	  xio_copy_read_max_fly, 0600),
+	INT_ENTRY("copy_write_max_fly",   xio_copy_write_max_fly, 0600),
+	INT_ENTRY("statusfiles_rollover_sec", mars_rollover_interval, 0600),
+	INT_ENTRY("scan_interval_sec",	  mars_scan_interval,	  0600),
+	INT_ENTRY("propagate_interval_sec", mars_propagate_interval, 0600),
+	INT_ENTRY("sync_flip_interval_sec", mars_sync_flip_interval, 0600),
+	INT_ENTRY("peer_abort",		  mars_peer_abort,	  0600),
+	INT_ENTRY("client_abort",	  xio_client_abort,	  0600),
+	INT_ENTRY("do_fast_fullsync",	  mars_fast_fullsync,	  0600),
+	INT_ENTRY("logrot_auto_gb",	  global_logrot_auto,	  0600),
+	INT_ENTRY("remaining_space_kb",   global_remaining_space, 0400),
+	INT_ENTRY("required_total_space_0_gb", global_free_space_0, 0600),
+	INT_ENTRY("required_free_space_1_gb", global_free_space_1, 0600),
+	INT_ENTRY("required_free_space_2_gb", global_free_space_2, 0600),
+	INT_ENTRY("required_free_space_3_gb", global_free_space_3, 0600),
+	INT_ENTRY("required_free_space_4_gb", global_free_space_4, 0600),
+	INT_ENTRY("sync_want",		  global_sync_want,	  0400),
+	INT_ENTRY("sync_nr",		  global_sync_nr,	  0400),
+	INT_ENTRY("sync_limit",		  global_sync_limit,	  0600),
+	INT_ENTRY("mars_emergency_mode",  mars_emergency_mode,	  0600),
+	INT_ENTRY("mars_reset_emergency", mars_reset_emergency,   0600),
+	INT_ENTRY("mars_keep_msg_s",	  mars_keep_msg,	  0600),
+	INT_ENTRY("write_throttle_start_percent", xio_throttle_start,	  0600),
+	INT_ENTRY("write_throttle_end_percent",   xio_throttle_end,	  0600),
+	INT_ENTRY("write_throttle_size_threshold_kb", if_throttle_start_size, 0400),
+	LIMITER_ENTRIES(&if_throttle,	  "write_throttle",	  "kb"),
+	/*  changing makes no sense because the server will immediately start upon modprobe */
+	INT_ENTRY("xio_port",		  xio_net_default_port,  0400),
+#ifdef __HAVE_LZO
+	INT_ENTRY("network_compress_data", xio_net_compress_data, 0600),
+#endif
+	INT_ENTRY("net_bind_before_listen", xio_net_bind_before_listen, 0600),
+	INT_ENTRY("net_bind_before_connect", xio_net_bind_before_connect, 0600),
+	INT_ENTRY("network_io_timeout",   global_net_io_timeout,  0600),
+	INT_ENTRY("parallel_connections", max_client_channels,	  0600),
+	INT_ENTRY("parallel_bulk_feed",   max_client_bulk,	  0600),
+	{
+		_CTL_NAME
+		.procname = "traffic_tuning",
+		.mode = 0500,
+		.child = traffic_tuning_table,
+	},
+	{
+		_CTL_NAME
+		.procname = "io_tuning",
+		.mode = 0500,
+		.child = io_tuning_table,
+	},
+	{
+		_CTL_NAME
+		.procname = "tcp_tuning",
+		.mode = 0500,
+		.child = tcp_tuning_table,
+	},
+	{}
+};
+
+static
+struct ctl_table mars_root_table[] = {
+	{
+		_CTL_NAME
+		.procname = "mars",
+		.mode = 0500,
+		.child = mars_table,
+	},
+	{}
+};
+
+/***************** module init stuff ************************/
+
+static struct ctl_table_header *header;
+
+int __init init_xio_proc(void)
+{
+
+	XIO_INF("init_proc()\n");
+
+	header = register_sysctl_table(mars_root_table);
+
+	return 0;
+}
+
+void exit_xio_proc(void)
+{
+	XIO_INF("exit_proc()\n");
+	if (header) {
+		unregister_sysctl_table(header);
+		header = NULL;
+	}
+}
diff --git a/include/linux/mars_light/mars_proc.h b/include/linux/mars_light/mars_proc.h
new file mode 100644
index 0000000..883b819
--- /dev/null
+++ b/include/linux/mars_light/mars_proc.h
@@ -0,0 +1,34 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 MARS_PROC_H
+#define MARS_PROC_H
+
+typedef char * (*xio_info_fn)(void);
+
+extern xio_info_fn xio_info;
+
+extern int min_free_kbytes;
+
+/***********************************************************************/
+
+/*  init */
+
+extern int init_xio_proc(void);
+extern void exit_xio_proc(void);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299650 — [RFC 21/31] mars: add new module xio_copy

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 21/31] mars: add new module xio_copy
Message-ID<qLJTY-3U9-15@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_copy.c | 1005 ++++++++++++++++++++++++++++
 include/linux/xio/xio_copy.h               |  115 ++++
 2 files changed, 1120 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_copy.c
 create mode 100644 include/linux/xio/xio_copy.h

diff --git a/drivers/staging/mars/xio_bricks/xio_copy.c b/drivers/staging/mars/xio_bricks/xio_copy.c
new file mode 100644
index 0000000..aa5bc56
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_copy.c
@@ -0,0 +1,1005 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/*  Copy brick (just for demonstration) */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/xio/xio.h>
+#include <linux/brick/lib_limiter.h>
+
+#ifndef READ
+#define READ				0
+#define WRITE				1
+#endif
+
+#define COPY_CHUNK			(PAGE_SIZE)
+#define NR_COPY_REQUESTS		(32 * 1024 * 1024 / COPY_CHUNK)
+
+#define STATES_PER_PAGE			(PAGE_SIZE / sizeof(struct copy_state))
+#define MAX_SUB_TABLES			(NR_COPY_REQUESTS / STATES_PER_PAGE + (NR_COPY_REQUESTS % STATES_PER_PAGE ? 1 : 0)\
+									\
+)
+#define MAX_COPY_REQUESTS		(PAGE_SIZE / sizeof(struct copy_state *) * STATES_PER_PAGE)
+
+#define GET_STATE(brick, index)						\
+	((brick)->st[(index) / STATES_PER_PAGE][(index) % STATES_PER_PAGE])
+
+/************************ own type definitions ***********************/
+
+#include <linux/xio/xio_copy.h>
+
+int xio_copy_overlap = 1;
+
+int xio_copy_read_prio = XIO_PRIO_NORMAL;
+
+int xio_copy_write_prio = XIO_PRIO_NORMAL;
+
+int xio_copy_read_max_fly;
+
+int xio_copy_write_max_fly;
+
+#define is_read_limited(brick)						\
+	(xio_copy_read_max_fly > 0 && atomic_read(&(brick)->copy_read_flight) >= xio_copy_read_max_fly)
+
+#define is_write_limited(brick)						\
+	(xio_copy_write_max_fly > 0 && atomic_read(&(brick)->copy_write_flight) >= xio_copy_write_max_fly)
+
+/************************ own helper functions ***********************/
+
+/* TODO:
+ * The clash logic is untested / alpha stage (Feb. 2011).
+ *
+ * For now, the output is never used, so this cannot do harm.
+ *
+ * In order to get the output really working / enterprise grade,
+ * some larger test effort should be invested.
+ */
+static inline
+void _clash(struct copy_brick *brick)
+{
+	brick->trigger = true;
+	set_bit(0, &brick->clash);
+	atomic_inc(&brick->total_clash_count);
+	wake_up_interruptible(&brick->event);
+}
+
+static inline
+int _clear_clash(struct copy_brick *brick)
+{
+	int old;
+
+	old = test_and_clear_bit(0, &brick->clash);
+	return old;
+}
+
+/* Current semantics:
+ *
+ * All writes are always going to the original input A. They are _not_
+ * replicated to B.
+ *
+ * In order to get B really uptodate, you have to replay the right
+ * transaction logs there (at the right time).
+ * [If you had no writes on A at all during the copy, of course
+ * this is not necessary]
+ *
+ * When utilize_mode is on, reads can utilize the already copied
+ * region from B, but only as long as this region has not been
+ * invalidated by writes (indicated by low_dirty).
+ *
+ * TODO: implement replicated writes, together with some transaction
+ * replay logic applying the transaction logs _only_ after
+ * crashes during inconsistency caused by partial replication of writes.
+ */
+static
+int _determine_input(struct copy_brick *brick, struct aio_object *aio)
+{
+	int rw;
+	int below;
+	int behind;
+	loff_t io_end;
+
+	if (!brick->utilize_mode || brick->low_dirty)
+		return INPUT_A_IO;
+
+	io_end = aio->io_pos + aio->io_len;
+	below = io_end <= brick->copy_start;
+	behind = !brick->copy_end || aio->io_pos >= brick->copy_end;
+	rw = aio->io_may_write | aio->io_rw;
+	if (rw) {
+		if (!behind) {
+			brick->low_dirty = true;
+			if (!below) {
+				_clash(brick);
+				wake_up_interruptible(&brick->event);
+			}
+		}
+		return INPUT_A_IO;
+	}
+
+	if (below)
+		return INPUT_B_IO;
+
+	return INPUT_A_IO;
+}
+
+#define GET_INDEX(pos)	  (((pos) / COPY_CHUNK) % NR_COPY_REQUESTS)
+#define GET_OFFSET(pos)   ((pos) % COPY_CHUNK)
+
+static
+void __clear_aio(struct copy_brick *brick, struct aio_object *aio, int queue)
+{
+	struct copy_input *input;
+
+	input = queue ? brick->inputs[INPUT_B_COPY] : brick->inputs[INPUT_A_COPY];
+	GENERIC_INPUT_CALL(input, aio_put, aio);
+}
+
+static
+void _clear_aio(struct copy_brick *brick, int index, int queue)
+{
+	struct copy_state *st = &GET_STATE(brick, index);
+	struct aio_object *aio = st->table[queue];
+
+	if (aio) {
+		if (unlikely(st->active[queue])) {
+			XIO_ERR("clearing active aio, index = %d queue = %d\n", index, queue);
+			st->active[queue] = false;
+		}
+		__clear_aio(brick, aio, queue);
+		st->table[queue] = NULL;
+	}
+}
+
+static
+void _clear_all_aio(struct copy_brick *brick)
+{
+	int i;
+
+	for (i = 0; i < NR_COPY_REQUESTS; i++) {
+		GET_STATE(brick, i).state = COPY_STATE_START;
+		_clear_aio(brick, i, 0);
+		_clear_aio(brick, i, 1);
+	}
+}
+
+static
+void _clear_state_table(struct copy_brick *brick)
+{
+	int i;
+
+	for (i = 0; i < MAX_SUB_TABLES; i++) {
+		struct copy_state *sub_table = brick->st[i];
+
+		memset(sub_table, 0, PAGE_SIZE);
+	}
+}
+
+static
+void copy_endio(struct generic_callback *cb)
+{
+	struct copy_aio_aspect *aio_a;
+	struct aio_object *aio;
+	struct copy_brick *brick;
+	struct copy_state *st;
+	int index;
+	int queue;
+	int error = 0;
+
+	LAST_CALLBACK(cb);
+	aio_a = cb->cb_private;
+	CHECK_PTR(aio_a, err);
+	aio = aio_a->object;
+	CHECK_PTR(aio, err);
+	brick = aio_a->brick;
+	CHECK_PTR(brick, err);
+
+	queue = aio_a->queue;
+	index = GET_INDEX(aio->io_pos);
+	st = &GET_STATE(brick, index);
+
+	if (unlikely(queue < 0 || queue >= 2)) {
+		XIO_ERR("bad queue %d\n", queue);
+		error = -EINVAL;
+		goto exit;
+	}
+	st->active[queue] = false;
+	if (unlikely(st->table[queue])) {
+		XIO_ERR("table corruption at %d %d (%p => %p)\n", index, queue, st->table[queue], aio);
+		error = -EEXIST;
+		goto exit;
+	}
+	if (unlikely(cb->cb_error < 0)) {
+		error = cb->cb_error;
+		__clear_aio(brick, aio, queue);
+		/* This is racy, but does no harm.
+		 * Worst case just produces more error output.
+		 */
+		if (!brick->copy_error_count++)
+			XIO_WRN("IO error %d on index %d, old state = %d\n", cb->cb_error, index, st->state);
+	} else {
+		if (unlikely(st->table[queue])) {
+			XIO_ERR("overwriting index %d, state = %d\n", index, st->state);
+			_clear_aio(brick, index, queue);
+		}
+		st->table[queue] = aio;
+	}
+
+exit:
+	if (unlikely(error < 0)) {
+		st->error = error;
+		_clash(brick);
+	}
+	if (aio->io_rw)
+		atomic_dec(&brick->copy_write_flight);
+	else
+		atomic_dec(&brick->copy_read_flight);
+	brick->trigger = true;
+	wake_up_interruptible(&brick->event);
+	goto out_return;
+err:
+	XIO_FAT("cannot handle callback\n");
+out_return:;
+}
+
+static
+int _make_aio(struct copy_brick *brick,
+	int index,
+	int queue,
+	void *data,
+	loff_t pos,
+	loff_t end_pos,
+	int rw,
+	int cs_mode)
+{
+	struct aio_object *aio;
+	struct copy_aio_aspect *aio_a;
+	struct copy_input *input;
+	int offset;
+	int len;
+	int status = -EAGAIN;
+
+	if (brick->clash || end_pos <= 0)
+		goto done;
+
+	aio = copy_alloc_aio(brick);
+	status = -ENOMEM;
+
+	aio_a = copy_aio_get_aspect(brick, aio);
+	if (unlikely(!aio_a)) {
+		XIO_FAT("cannot get own apsect\n");
+		goto done;
+	}
+
+	aio_a->brick = brick;
+	aio_a->queue = queue;
+	aio->io_may_write = rw;
+	aio->io_rw = rw;
+	aio->io_data = data;
+	aio->io_pos = pos;
+	aio->io_cs_mode = cs_mode;
+	offset = GET_OFFSET(pos);
+	len = COPY_CHUNK - offset;
+	if (pos + len > end_pos)
+		len = end_pos - pos;
+	aio->io_len = len;
+	aio->io_prio = rw ?
+		xio_copy_write_prio :
+		xio_copy_read_prio;
+	if (aio->io_prio < XIO_PRIO_HIGH || aio->io_prio > XIO_PRIO_LOW)
+		aio->io_prio = brick->io_prio;
+
+	SETUP_CALLBACK(aio, copy_endio, aio_a);
+
+	input = queue ? brick->inputs[INPUT_B_COPY] : brick->inputs[INPUT_A_COPY];
+	status = GENERIC_INPUT_CALL(input, aio_get, aio);
+	if (unlikely(status < 0)) {
+		XIO_ERR("status = %d\n", status);
+		obj_free(aio);
+		goto done;
+	}
+	if (unlikely(aio->io_len < len))
+		XIO_DBG("shorten len %d < %d\n", aio->io_len, len);
+	if (queue == 0) {
+		GET_STATE(brick, index).len = aio->io_len;
+	} else if (unlikely(aio->io_len < GET_STATE(brick, index).len)) {
+		XIO_DBG("shorten len %d < %d at index %d\n", aio->io_len, GET_STATE(brick, index).len, index);
+		GET_STATE(brick, index).len = aio->io_len;
+	}
+
+	GET_STATE(brick, index).active[queue] = true;
+	if (rw)
+		atomic_inc(&brick->copy_write_flight);
+	else
+		atomic_inc(&brick->copy_read_flight);
+	GENERIC_INPUT_CALL(input, aio_io, aio);
+
+done:
+	return status;
+}
+
+static
+void _update_percent(struct copy_brick *brick, bool force)
+{
+	if (force
+	   || brick->copy_last > brick->copy_start + 8 * 1024 * 1024
+	   || time_is_before_jiffies(brick->last_jiffies + 5 * HZ)
+	   || (brick->copy_last == brick->copy_end && brick->copy_end > 0)) {
+		brick->copy_start = brick->copy_last;
+		brick->last_jiffies = jiffies;
+		brick->power.percent_done = brick->copy_end > 0 ? brick->copy_start * 100 / brick->copy_end : 0;
+		XIO_INF("'%s' copied %lld / %lld bytes (%d%%)\n",
+			brick->brick_path,
+			brick->copy_last,
+			brick->copy_end,
+			brick->power.percent_done);
+	}
+}
+
+/* The heart of this brick.
+ * State transition function of the finite automaton.
+ * In case no progress is possible (e.g. preconditions not
+ * yet true), the state is left as is (idempotence property:
+ * calling this too often does no harm, just costs performance).
+ */
+static
+int _next_state(struct copy_brick *brick, int index, loff_t pos)
+{
+	struct aio_object *aio0;
+	struct aio_object *aio1;
+	struct copy_state *st;
+	char state;
+	char next_state;
+	bool do_restart = false;
+	int progress = 0;
+	int status;
+
+	st = &GET_STATE(brick, index);
+	next_state = st->state;
+
+restart:
+	state = next_state;
+
+	do_restart = false;
+
+	switch (state) {
+	case COPY_STATE_RESET:
+		/* This state is only entered after errors or
+		 * in restarting situations.
+		 */
+		_clear_aio(brick, index, 1);
+		_clear_aio(brick, index, 0);
+		next_state = COPY_STATE_START;
+		/* fallthrough */
+	case COPY_STATE_START:
+		/* This is the relgular starting state.
+		 * It must be zero, automatically entered via memset()
+		 */
+		if (st->table[0] || st->table[1]) {
+			XIO_ERR("index %d not startable\n", index);
+			progress = -EPROTO;
+			goto idle;
+		}
+
+		_clear_aio(brick, index, 1);
+		_clear_aio(brick, index, 0);
+		st->writeout = false;
+		st->error = 0;
+
+		if (brick->is_aborting ||
+		    is_read_limited(brick))
+			goto idle;
+
+		status = _make_aio(brick, index, 0, NULL, pos, brick->copy_end, READ, brick->verify_mode ? 2 : 0);
+		if (unlikely(status < 0)) {
+			XIO_DBG("status = %d\n", status);
+			progress = status;
+			break;
+		}
+
+		next_state = COPY_STATE_READ1;
+		if (!brick->verify_mode)
+			break;
+
+		next_state = COPY_STATE_START2;
+		/* fallthrough */
+	case COPY_STATE_START2:
+		status = _make_aio(brick, index, 1, NULL, pos, brick->copy_end, READ, 2);
+		if (unlikely(status < 0)) {
+			XIO_DBG("status = %d\n", status);
+			progress = status;
+			break;
+		}
+		next_state = COPY_STATE_READ2;
+		/* fallthrough */
+	case COPY_STATE_READ2:
+		aio1 = st->table[1];
+		if (!aio1) { /*  idempotence: wait by unchanged state */
+			goto idle;
+		}
+		/* fallthrough = > wait for both aios to appear */
+	case COPY_STATE_READ1:
+	case COPY_STATE_READ3:
+		aio0 = st->table[0];
+		if (!aio0) { /*  idempotence: wait by unchanged state */
+			goto idle;
+		}
+		if (brick->copy_limiter) {
+			int amount = (aio0->io_len - 1) / 1024 + 1;
+
+			rate_limit_sleep(brick->copy_limiter, amount);
+		}
+		/*  on append mode: increase the end pointer dynamically */
+		if (brick->append_mode > 0 && aio0->io_total_size && aio0->io_total_size > brick->copy_end)
+			brick->copy_end = aio0->io_total_size;
+		/*  do verify (when applicable) */
+		aio1 = st->table[1];
+		if (aio1 && state != COPY_STATE_READ3) {
+			int len = aio0->io_len;
+			bool ok;
+
+			if (len != aio1->io_len) {
+				ok = false;
+			} else if (aio0->io_cs_mode) {
+				static unsigned char null[sizeof(aio0->io_checksum)];
+
+				ok = !memcmp(aio0->io_checksum, aio1->io_checksum, sizeof(aio0->io_checksum));
+				if (ok)
+					ok = memcmp(aio0->io_checksum, null, sizeof(aio0->io_checksum)) != 0;
+			} else if (!aio0->io_data || !aio1->io_data) {
+				ok = false;
+			} else {
+				ok = !memcmp(aio0->io_data, aio1->io_data, len);
+			}
+
+			_clear_aio(brick, index, 1);
+
+			if (ok)
+				brick->verify_ok_count++;
+			else
+				brick->verify_error_count++;
+
+			if (ok || !brick->repair_mode) {
+				/* skip start of writing, goto final treatment of writeout */
+				next_state = COPY_STATE_CLEANUP;
+				break;
+			}
+		}
+
+		if (aio0->io_cs_mode > 1) { /*	re-read, this time with data */
+			_clear_aio(brick, index, 0);
+			status = _make_aio(brick, index, 0, NULL, pos, brick->copy_end, READ, 0);
+			if (unlikely(status < 0)) {
+				XIO_DBG("status = %d\n", status);
+				progress = status;
+				next_state = COPY_STATE_RESET;
+				break;
+			}
+			next_state = COPY_STATE_READ3;
+			break;
+		}
+		next_state = COPY_STATE_WRITE;
+		/* fallthrough */
+	case COPY_STATE_WRITE:
+		if (is_write_limited(brick))
+			goto idle;
+		/* Obey ordering to get a strict "append" behaviour.
+		 * We assume that we don't need to wait for completion
+		 * of the previous write to avoid a sparse result file
+		 * under all circumstances, i.e. we only assure that
+		 * _starting_ the writes is in order.
+		 * This is only correct when all lower bricks obey the
+		 * order of io_io() operations.
+		 * Currenty, bio and aio are obeying this. Be careful when
+		 * implementing new IO bricks!
+		 */
+		if (st->prev >= 0 && !GET_STATE(brick, st->prev).writeout)
+			goto idle;
+		aio0 = st->table[0];
+		if (unlikely(!aio0 || !aio0->io_data)) {
+			XIO_ERR("src buffer for write does not exist, state %d at index %d\n", state, index);
+			progress = -EILSEQ;
+			break;
+		}
+		if (unlikely(brick->is_aborting)) {
+			progress = -EINTR;
+			break;
+		}
+		/* start writeout */
+		status = _make_aio(brick, index, 1, aio0->io_data, pos, pos + aio0->io_len, WRITE, 0);
+		if (unlikely(status < 0)) {
+			XIO_DBG("status = %d\n", status);
+			progress = status;
+			next_state = COPY_STATE_RESET;
+			break;
+		}
+		/* Attention! overlapped IO behind EOF could
+		 * lead to temporary inconsistent state of the
+		 * file, because the write order may be different from
+		 * strict O_APPEND behaviour.
+		 */
+		if (xio_copy_overlap)
+			st->writeout = true;
+		next_state = COPY_STATE_WRITTEN;
+		/* fallthrough */
+	case COPY_STATE_WRITTEN:
+		aio1 = st->table[1];
+		if (!aio1) { /*  idempotence: wait by unchanged state */
+			goto idle;
+		}
+		st->writeout = true;
+		/* rechecking means to start over again.
+		 * ATTENTIION! this may lead to infinite request
+		 * submission loops, intentionally.
+		 * TODO: implement some timeout means.
+		 */
+		if (brick->recheck_mode && brick->repair_mode) {
+			next_state = COPY_STATE_RESET;
+			break;
+		}
+		next_state = COPY_STATE_CLEANUP;
+		/* fallthrough */
+	case COPY_STATE_CLEANUP:
+		_clear_aio(brick, index, 1);
+		_clear_aio(brick, index, 0);
+		next_state = COPY_STATE_FINISHED;
+		/* fallthrough */
+	case COPY_STATE_FINISHED:
+		/* Indicate successful completion by remaining in this state.
+		 * Restart of the finite automaton must be done externally.
+		 */
+		goto idle;
+	default:
+		XIO_ERR("illegal state %d at index %d\n", state, index);
+		_clash(brick);
+		progress = -EILSEQ;
+	}
+
+	do_restart = (state != next_state);
+
+idle:
+	if (unlikely(progress < 0)) {
+		if (st->error >= 0)
+			st->error = progress;
+		XIO_DBG("progress = %d\n", progress);
+		progress = 0;
+		_clash(brick);
+	} else if (do_restart) {
+		goto restart;
+	} else if (st->state != next_state) {
+		progress++;
+	}
+
+	/*  save the resulting state */
+	st->state = next_state;
+	return progress;
+}
+
+static
+int _run_copy(struct copy_brick *brick)
+{
+	int max;
+	loff_t pos;
+	loff_t limit = -1;
+
+	short prev;
+	int progress;
+
+	if (unlikely(_clear_clash(brick))) {
+		XIO_DBG("clash\n");
+		if (atomic_read(&brick->copy_read_flight) + atomic_read(&brick->copy_write_flight) > 0) {
+			/* wait until all pending copy IO has finished
+			 */
+			_clash(brick);
+			XIO_DBG("re-clash\n");
+			brick_msleep(100);
+			return 0;
+		}
+		_clear_all_aio(brick);
+		_clear_state_table(brick);
+	}
+
+	/* Do at most max iterations in the below loop
+	 */
+	max = NR_COPY_REQUESTS - atomic_read(&brick->io_flight) * 2;
+
+	prev = -1;
+	progress = 0;
+	for (pos = brick->copy_last; pos < brick->copy_end || brick->append_mode > 1; pos = ((pos / COPY_CHUNK) + 1) * COPY_CHUNK) {
+		int index = GET_INDEX(pos);
+		struct copy_state *st = &GET_STATE(brick, index);
+
+		if (max-- <= 0)
+			break;
+		st->prev = prev;
+		prev = index;
+		/*  call the finite state automaton */
+		if (!(st->active[0] | st->active[1])) {
+			progress += _next_state(brick, index, pos);
+			limit = pos;
+		}
+	}
+
+	/*  check the resulting state: can we advance the copy_last pointer? */
+	if (likely(progress && !brick->clash)) {
+		int count = 0;
+
+		for (pos = brick->copy_last; pos <= limit; pos = ((pos / COPY_CHUNK) + 1) * COPY_CHUNK) {
+			int index = GET_INDEX(pos);
+			struct copy_state *st = &GET_STATE(brick, index);
+
+			if (st->state != COPY_STATE_FINISHED)
+				break;
+			if (unlikely(st->error < 0)) {
+				/* check for fatal consistency errors */
+				if (st->error == -EMEDIUMTYPE) {
+					brick->copy_error = st->error;
+					brick->abort_mode = true;
+					XIO_WRN("Consistency is violated\n");
+				}
+				if (!brick->copy_error) {
+					brick->copy_error = st->error;
+					XIO_WRN("IO error = %d\n", st->error);
+				}
+				if (brick->abort_mode)
+					brick->is_aborting = true;
+				break;
+			}
+			/*  rollover */
+			st->state = COPY_STATE_START;
+			count += st->len;
+			/*  check contiguity */
+			if (unlikely(GET_OFFSET(pos) + st->len != COPY_CHUNK))
+				break;
+		}
+		if (count > 0) {
+			brick->copy_last += count;
+			get_lamport(&brick->copy_last_stamp);
+			_update_percent(brick, false);
+		}
+	}
+	return progress;
+}
+
+static
+bool _is_done(struct copy_brick *brick)
+{
+	if (brick_thread_should_stop())
+		brick->is_aborting = true;
+	return brick->is_aborting &&
+		atomic_read(&brick->copy_read_flight) + atomic_read(&brick->copy_write_flight) <= 0;
+}
+
+static int _copy_thread(void *data)
+{
+	struct copy_brick *brick = data;
+	int rounds = 0;
+
+	XIO_DBG("--------------- copy_thread %p starting\n", brick);
+	brick->copy_error = 0;
+	brick->copy_error_count = 0;
+	brick->verify_ok_count = 0;
+	brick->verify_error_count = 0;
+
+	_update_percent(brick, true);
+
+	xio_set_power_on_led((void *)brick, true);
+	brick->trigger = true;
+
+	while (!_is_done(brick)) {
+		loff_t old_start = brick->copy_start;
+		loff_t old_end = brick->copy_end;
+		int progress = 0;
+
+		if (old_end > 0) {
+			progress = _run_copy(brick);
+			if (!progress || ++rounds > 1000)
+				rounds = 0;
+		}
+
+		wait_event_interruptible_timeout(brick->event,
+						 progress > 0 ||
+						 brick->trigger ||
+						 brick->copy_start != old_start ||
+						 brick->copy_end != old_end ||
+						 _is_done(brick),
+						 1 * HZ);
+		brick->trigger = false;
+	}
+
+	/* check for fatal consistency errors */
+	if (brick->copy_error == -EMEDIUMTYPE) {
+		/* reset the whole area */
+		brick->copy_start = 0;
+		brick->copy_last = 0;
+		XIO_WRN("resetting the full copy area\n");
+	}
+	_update_percent(brick, true);
+
+	XIO_DBG("--------------- copy_thread terminating (%d read requests / %d write requests flying, copy_start = %lld copy_end = %lld)\n",
+		 atomic_read(&brick->copy_read_flight),
+		 atomic_read(&brick->copy_write_flight),
+		 brick->copy_start,
+		 brick->copy_end);
+
+	_clear_all_aio(brick);
+	xio_set_power_off_led((void *)brick, true);
+	XIO_DBG("--------------- copy_thread done.\n");
+	return 0;
+}
+
+/***************** own brick * input * output operations *****************/
+
+static int copy_get_info(struct copy_output *output, struct xio_info *info)
+{
+	struct copy_input *input = output->brick->inputs[INPUT_B_IO];
+
+	return GENERIC_INPUT_CALL(input, xio_get_info, info);
+}
+
+static int copy_io_get(struct copy_output *output, struct aio_object *aio)
+{
+	struct copy_input *input;
+	int index;
+	int status;
+
+	index = _determine_input(output->brick, aio);
+	input = output->brick->inputs[index];
+	status = GENERIC_INPUT_CALL(input, aio_get, aio);
+	if (status >= 0)
+		atomic_inc(&output->brick->io_flight);
+	return status;
+}
+
+static void copy_io_put(struct copy_output *output, struct aio_object *aio)
+{
+	struct copy_input *input;
+	int index;
+
+	index = _determine_input(output->brick, aio);
+	input = output->brick->inputs[index];
+	GENERIC_INPUT_CALL(input, aio_put, aio);
+	if (atomic_dec_and_test(&output->brick->io_flight)) {
+		output->brick->trigger = true;
+		wake_up_interruptible(&output->brick->event);
+	}
+}
+
+static void copy_io_io(struct copy_output *output, struct aio_object *aio)
+{
+	struct copy_input *input;
+	int index;
+
+	index = _determine_input(output->brick, aio);
+	input = output->brick->inputs[index];
+	GENERIC_INPUT_CALL(input, aio_io, aio);
+}
+
+static int copy_switch(struct copy_brick *brick)
+{
+	static int version;
+
+	XIO_DBG("power.button = %d\n", brick->power.button);
+	if (brick->power.button) {
+		if (brick->power.on_led)
+			goto done;
+		xio_set_power_off_led((void *)brick, false);
+		brick->is_aborting = false;
+		if (!brick->thread) {
+			brick->copy_last = brick->copy_start;
+			get_lamport(&brick->copy_last_stamp);
+			brick->thread = brick_thread_create(_copy_thread, brick, "xio_copy%d", version++);
+			if (brick->thread) {
+				brick->trigger = true;
+			} else {
+				xio_set_power_off_led((void *)brick, true);
+				XIO_ERR("could not start copy thread\n");
+			}
+		}
+	} else {
+		if (brick->power.off_led)
+			goto done;
+		xio_set_power_on_led((void *)brick, false);
+		if (brick->thread) {
+			XIO_INF("stopping thread...\n");
+			brick_thread_stop(brick->thread);
+		}
+	}
+done:
+	return 0;
+}
+
+/*************** informational * statistics **************/
+
+static
+char *copy_statistics(struct copy_brick *brick, int verbose)
+{
+	char *res = brick_string_alloc(1024);
+
+	snprintf(res, 1024,
+		 "copy_start = %lld copy_last = %lld copy_end = %lld copy_error = %d copy_error_count = %d verify_ok_count = %d verify_error_count = %d low_dirty = %d is_aborting = %d clash = %lu | total clash_count = %d | io_flight = %d copy_read_flight = %d copy_write_flight = %d\n",
+		 brick->copy_start,
+		 brick->copy_last,
+		 brick->copy_end,
+		 brick->copy_error,
+		 brick->copy_error_count,
+		 brick->verify_ok_count,
+		 brick->verify_error_count,
+		 brick->low_dirty,
+		 brick->is_aborting,
+		 brick->clash,
+		 atomic_read(&brick->total_clash_count),
+		 atomic_read(&brick->io_flight),
+		 atomic_read(&brick->copy_read_flight),
+		 atomic_read(&brick->copy_write_flight));
+
+	return res;
+}
+
+static
+void copy_reset_statistics(struct copy_brick *brick)
+{
+	atomic_set(&brick->total_clash_count, 0);
+}
+
+/*************** object * aspect constructors * destructors **************/
+
+static int copy_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct copy_aio_aspect *ini = (void *)_ini;
+
+	(void)ini;
+	return 0;
+}
+
+static void copy_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct copy_aio_aspect *ini = (void *)_ini;
+
+	(void)ini;
+}
+
+XIO_MAKE_STATICS(copy);
+
+/********************* brick constructors * destructors *******************/
+
+static
+void _free_pages(struct copy_brick *brick)
+{
+	int i;
+
+	for (i = 0; i < MAX_SUB_TABLES; i++) {
+		struct copy_state *sub_table = brick->st[i];
+
+		if (!sub_table)
+			continue;
+
+		brick_block_free(sub_table, PAGE_SIZE);
+	}
+	brick_block_free(brick->st, PAGE_SIZE);
+}
+
+static int copy_brick_construct(struct copy_brick *brick)
+{
+	int i;
+
+	brick->st = brick_block_alloc(0, PAGE_SIZE);
+	memset(brick->st, 0, PAGE_SIZE);
+
+	for (i = 0; i < MAX_SUB_TABLES; i++) {
+		struct copy_state *sub_table;
+
+		/*  this should be usually optimized away as dead code */
+		if (unlikely(i >= MAX_SUB_TABLES)) {
+			XIO_ERR("sorry, subtable index %d is too large.\n", i);
+			_free_pages(brick);
+			return -EINVAL;
+		}
+
+		sub_table = brick_block_alloc(0, PAGE_SIZE);
+		brick->st[i] = sub_table;
+		memset(sub_table, 0, PAGE_SIZE);
+	}
+
+	init_waitqueue_head(&brick->event);
+	sema_init(&brick->mutex, 1);
+	return 0;
+}
+
+static int copy_brick_destruct(struct copy_brick *brick)
+{
+	_free_pages(brick);
+	return 0;
+}
+
+static int copy_output_construct(struct copy_output *output)
+{
+	return 0;
+}
+
+static int copy_output_destruct(struct copy_output *output)
+{
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct copy_brick_ops copy_brick_ops = {
+	.brick_switch = copy_switch,
+	.brick_statistics = copy_statistics,
+	.reset_statistics = copy_reset_statistics,
+};
+
+static struct copy_output_ops copy_output_ops = {
+	.xio_get_info = copy_get_info,
+	.aio_get = copy_io_get,
+	.aio_put = copy_io_put,
+	.aio_io = copy_io_io,
+};
+
+const struct copy_input_type copy_input_type = {
+	.type_name = "copy_input",
+	.input_size = sizeof(struct copy_input),
+};
+
+static const struct copy_input_type *copy_input_types[] = {
+	&copy_input_type,
+	&copy_input_type,
+	&copy_input_type,
+	&copy_input_type,
+};
+
+const struct copy_output_type copy_output_type = {
+	.type_name = "copy_output",
+	.output_size = sizeof(struct copy_output),
+	.master_ops = &copy_output_ops,
+	.output_construct = &copy_output_construct,
+	.output_destruct = &copy_output_destruct,
+};
+
+static const struct copy_output_type *copy_output_types[] = {
+	&copy_output_type,
+};
+
+const struct copy_brick_type copy_brick_type = {
+	.type_name = "copy_brick",
+	.brick_size = sizeof(struct copy_brick),
+	.max_inputs = 4,
+	.max_outputs = 1,
+	.master_ops = &copy_brick_ops,
+	.aspect_types = copy_aspect_types,
+	.default_input_types = copy_input_types,
+	.default_output_types = copy_output_types,
+	.brick_construct = &copy_brick_construct,
+	.brick_destruct = &copy_brick_destruct,
+};
+
+/***************** module init stuff ************************/
+
+int __init init_xio_copy(void)
+{
+	XIO_INF("init_copy()\n");
+	return copy_register_brick_type();
+}
+
+void exit_xio_copy(void)
+{
+	XIO_INF("exit_copy()\n");
+	copy_unregister_brick_type();
+}
diff --git a/include/linux/xio/xio_copy.h b/include/linux/xio/xio_copy.h
new file mode 100644
index 0000000..f92e898
--- /dev/null
+++ b/include/linux/xio/xio_copy.h
@@ -0,0 +1,115 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_COPY_H
+#define XIO_COPY_H
+
+#include <linux/wait.h>
+#include <linux/semaphore.h>
+
+#define INPUT_A_IO			0
+#define INPUT_A_COPY			1
+#define INPUT_B_IO			2
+#define INPUT_B_COPY			3
+
+extern int xio_copy_overlap;
+extern int xio_copy_read_prio;
+extern int xio_copy_write_prio;
+extern int xio_copy_read_max_fly;
+extern int xio_copy_write_max_fly;
+
+enum {
+	COPY_STATE_RESET = -1,
+	COPY_STATE_START = 0, /*  don't change this, it _must_ be zero */
+	COPY_STATE_START2,
+	COPY_STATE_READ1,
+	COPY_STATE_READ2,
+	COPY_STATE_READ3,
+	COPY_STATE_WRITE,
+	COPY_STATE_WRITTEN,
+	COPY_STATE_CLEANUP,
+	COPY_STATE_FINISHED,
+};
+
+struct copy_state {
+	struct aio_object *table[2];
+	bool active[2];
+	char state;
+	bool writeout;
+
+	short prev;
+	short len;
+	short error;
+};
+
+struct copy_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct copy_brick *brick;
+	int queue;
+};
+
+struct copy_brick {
+	XIO_BRICK(copy);
+	/*  parameters */
+	struct rate_limiter *copy_limiter;
+	loff_t copy_start;
+
+	loff_t copy_end; /*  stop working if == 0 */
+	int io_prio;
+
+	int append_mode; /*  1 = passively, 2 = actively */
+	bool verify_mode; /*  0 = copy, 1 = checksum+compare */
+	bool repair_mode; /*  whether to repair in case of verify errors */
+	bool recheck_mode; /*  whether to re-check after repairs (costs performance) */
+	bool utilize_mode; /*  utilize already copied data */
+	bool abort_mode;  /*  abort on IO error (default is retry forever) */
+	/*  readonly from outside */
+	loff_t copy_last; /*  current working position */
+	struct timespec copy_last_stamp;
+	int copy_error;
+	int copy_error_count;
+	int verify_ok_count;
+	int verify_error_count;
+	bool low_dirty;
+	bool is_aborting;
+
+	/*  internal */
+	bool trigger;
+	unsigned long clash;
+	atomic_t total_clash_count;
+	atomic_t io_flight;
+	atomic_t copy_read_flight;
+	atomic_t copy_write_flight;
+	unsigned long last_jiffies;
+
+	wait_queue_head_t event;
+	struct semaphore mutex;
+	struct task_struct *thread;
+	struct copy_state **st;
+};
+
+struct copy_input {
+	XIO_INPUT(copy);
+};
+
+struct copy_output {
+	XIO_OUTPUT(copy);
+};
+
+XIO_TYPES(copy);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299651 — [RFC 20/31] mars: add new module xio_if

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 20/31] mars: add new module xio_if
Message-ID<qLJTY-3U9-19@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_if.c | 961 +++++++++++++++++++++++++++++++
 include/linux/xio/xio_if.h               | 108 ++++
 2 files changed, 1069 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_if.c
 create mode 100644 include/linux/xio/xio_if.h

diff --git a/drivers/staging/mars/xio_bricks/xio_if.c b/drivers/staging/mars/xio_bricks/xio_if.c
new file mode 100644
index 0000000..65e023c
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_if.c
@@ -0,0 +1,961 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/* Interface to a Linux device.
+ * 1 Input, 0 Outputs.
+ */
+
+#define REQUEST_MERGING
+#define ALWAYS_UNPLUG			true
+#define PREFETCH_LEN			PAGE_SIZE
+
+/*  low-level device parameters */
+#define IF_MAX_SEGMENT_SIZE		PAGE_SIZE
+#define USE_MAX_SECTORS			(IF_MAX_SEGMENT_SIZE >> 9)
+#define USE_MAX_PHYS_SEGMENTS		(IF_MAX_SEGMENT_SIZE >> 9)
+#define USE_MAX_SEGMENT_SIZE		IF_MAX_SEGMENT_SIZE
+#define USE_LOGICAL_BLOCK_SIZE		512
+#define USE_SEGMENT_BOUNDARY		(PAGE_SIZE-1)
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/bio.h>
+#include <linux/major.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+
+#include <linux/xio/xio.h>
+#include <linux/brick/lib_limiter.h>
+
+#ifndef XIO_MAJOR
+#define XIO_MAJOR			(DRBD_MAJOR + 1)
+#endif
+
+/************************ global tuning ***********************/
+
+int if_throttle_start_size;
+
+struct rate_limiter if_throttle = {
+	.lim_max_rate = 5000,
+};
+
+/************************ own type definitions ***********************/
+
+#include <linux/xio/xio_if.h>
+
+#define IF_HASH_MAX			(PAGE_SIZE / sizeof(struct if_hash_anchor))
+#define IF_HASH_CHUNK			(PAGE_SIZE * 32)
+
+struct if_hash_anchor {
+	spinlock_t hash_lock;
+	struct list_head hash_anchor;
+};
+
+/************************ own static definitions ***********************/
+
+/*  TODO: check bounds, ensure that free minor numbers are recycled */
+static int device_minor;
+
+/*************** object * aspect constructors * destructors **************/
+
+/************************ linux operations ***********************/
+
+static
+void _if_start_io_acct(struct if_input *input, struct bio_wrapper *biow)
+{
+	struct bio *bio = biow->bio;
+	const int rw = bio_data_dir(bio);
+	const int cpu = part_stat_lock();
+
+	(void)cpu;
+	part_round_stats(cpu, &input->disk->part0);
+	part_stat_inc(cpu, &input->disk->part0, ios[rw]);
+	part_stat_add(cpu, &input->disk->part0, sectors[rw], bio->bi_iter.bi_size >> 9);
+	part_inc_in_flight(&input->disk->part0, rw);
+	part_stat_unlock();
+	biow->start_time = jiffies;
+}
+
+static
+void _if_end_io_acct(struct if_input *input, struct bio_wrapper *biow)
+{
+	unsigned long duration = jiffies - biow->start_time;
+	struct bio *bio = biow->bio;
+	const int rw = bio_data_dir(bio);
+	const int cpu = part_stat_lock();
+
+	(void)cpu;
+	part_stat_add(cpu, &input->disk->part0, ticks[rw], duration);
+	part_round_stats(cpu, &input->disk->part0);
+	part_dec_in_flight(&input->disk->part0, rw);
+	part_stat_unlock();
+}
+
+/* callback
+ */
+static
+void if_endio(struct generic_callback *cb)
+{
+	struct if_aio_aspect *aio_a = cb->cb_private;
+	struct if_input *input;
+	int k;
+	int rw;
+	int error;
+
+	LAST_CALLBACK(cb);
+	if (unlikely(!aio_a || !aio_a->object)) {
+		XIO_FAT("aio_a = %p aio = %p, something is very wrong here!\n", aio_a, aio_a->object);
+		goto out_return;
+	}
+	input = aio_a->input;
+	CHECK_PTR(input, err);
+
+	rw = aio_a->object->io_rw;
+
+	for (k = 0; k < aio_a->bio_count; k++) {
+		struct bio_wrapper *biow;
+		struct bio *bio;
+
+		biow = aio_a->orig_biow[k];
+		aio_a->orig_biow[k] = NULL;
+		CHECK_PTR(biow, err);
+
+		CHECK_ATOMIC(&biow->bi_comp_cnt, 1);
+		if (!atomic_dec_and_test(&biow->bi_comp_cnt))
+			continue;
+
+		bio = biow->bio;
+		CHECK_PTR_NULL(bio, err);
+
+		_if_end_io_acct(input, biow);
+
+		error = CALLBACK_ERROR(aio_a->object);
+		if (unlikely(error < 0)) {
+			int bi_size = bio->bi_iter.bi_size;
+
+			XIO_ERR("NYI: error=%d RETRY LOGIC %u\n", error, bi_size);
+		} else { /*  bio conventions are slightly different... */
+			error = 0;
+			bio->bi_iter.bi_size = 0;
+		}
+		bio->bi_error = error;
+		bio_endio(bio);
+		bio_put(bio);
+		brick_mem_free(biow);
+	}
+	atomic_dec(&input->flying_count);
+	if (rw)
+		atomic_dec(&input->write_flying_count);
+	else
+		atomic_dec(&input->read_flying_count);
+	goto out_return;
+err:
+	XIO_FAT("error in callback, giving up\n");
+out_return:;
+}
+
+/* Kick off plugged aios
+ */
+static
+void _if_unplug(struct if_input *input)
+{
+	/* struct if_brick *brick = input->brick; */
+	LIST_HEAD(tmp_list);
+	unsigned long flags;
+
+#ifdef CONFIG_MARS_DEBUG
+	might_sleep();
+#endif
+
+	spin_lock_irqsave(&input->req_lock, flags);
+	if (!list_empty(&input->plug_anchor)) {
+		/*  move over the whole list */
+		list_replace_init(&input->plug_anchor, &tmp_list);
+		atomic_set(&input->plugged_count, 0);
+	}
+	spin_unlock_irqrestore(&input->req_lock, flags);
+
+	while (!list_empty(&tmp_list)) {
+		struct if_aio_aspect *aio_a;
+		struct aio_object *aio;
+		int hash_index;
+
+		aio_a = container_of(tmp_list.next, struct if_aio_aspect, plug_head);
+		list_del_init(&aio_a->plug_head);
+
+		hash_index = aio_a->hash_index;
+		spin_lock_irqsave(&input->hash_table[hash_index].hash_lock, flags);
+		list_del_init(&aio_a->hash_head);
+		spin_unlock_irqrestore(&input->hash_table[hash_index].hash_lock, flags);
+
+		aio = aio_a->object;
+
+		if (unlikely(aio_a->current_len > aio_a->max_len))
+			XIO_ERR("request len %d > %d\n", aio_a->current_len, aio_a->max_len);
+		aio->io_len = aio_a->current_len;
+
+		atomic_inc(&input->flying_count);
+		atomic_inc(&input->total_fire_count);
+		if (aio->io_rw)
+			atomic_inc(&input->write_flying_count);
+		else
+			atomic_inc(&input->read_flying_count);
+		if (aio->io_skip_sync)
+			atomic_inc(&input->total_skip_sync_count);
+
+		GENERIC_INPUT_CALL(input, aio_io, aio);
+		GENERIC_INPUT_CALL(input, aio_put, aio);
+	}
+}
+
+/* accept a linux bio, convert to aio and call buf_io() on it.
+ */
+static
+blk_qc_t if_make_request(struct request_queue *q, struct bio *bio)
+{
+	struct if_input *input = q->queuedata;
+	struct if_brick *brick = input->brick;
+
+	/* Original flags of the source bio
+	 */
+	const int  rw = bio_data_dir(bio);
+	const int  sectors = bio_sectors(bio);
+
+	const bool ahead = bio_flagged(bio, __REQ_RAHEAD) && rw == READ;
+	const bool barrier = bio_flagged(bio, __REQ_SOFTBARRIER);
+	const bool syncio = bio_flagged(bio, __REQ_SYNC);
+	const bool unplug = false;
+	const bool meta = bio_flagged(bio, __REQ_META);
+	const bool discard = bio_flagged(bio, __REQ_DISCARD);
+	const bool noidle = bio_flagged(bio, __REQ_NOIDLE);
+
+	const int  prio = bio_prio(bio);
+
+	/* Transform into XIO flags
+	 */
+	const int  io_prio =
+		(prio == IOPRIO_CLASS_RT || (meta | syncio)) ?
+		XIO_PRIO_HIGH :
+		(prio == IOPRIO_CLASS_IDLE) ?
+		XIO_PRIO_LOW :
+		XIO_PRIO_NORMAL;
+	const bool do_unplug = ALWAYS_UNPLUG | unplug | noidle;
+	const bool do_skip_sync = brick->skip_sync && !(barrier | syncio);
+
+	struct bio_wrapper *biow;
+	struct aio_object *aio = NULL;
+	struct if_aio_aspect *aio_a;
+
+	struct bio_vec bvec;
+	struct bvec_iter i;
+
+	loff_t pos = ((loff_t)bio->bi_iter.bi_sector) << 9; /*	TODO: make dynamic */
+	int total_len = bio->bi_iter.bi_size;
+
+	bool assigned = false;
+	int error = -EINVAL;
+
+	bind_to_channel(brick->say_channel, current);
+
+	might_sleep();
+
+	blk_queue_split(q, &bio, q->bio_split);
+
+	if (unlikely(!sectors)) {
+		_if_unplug(input);
+		/* THINK: usually this happens only at write barriers.
+		 * We have no "barrier" operation in XIO, since
+		 * callback semantics should always denote
+		 * "writethrough accomplished".
+		 * In case of exceptional semantics, we need to do
+		 * something here. For now, we do just nothing.
+		 */
+		error = 0;
+		bio->bi_error = error;
+		bio_endio(bio);
+		goto done;
+	}
+
+	/*  throttling of too big write requests */
+	if (rw && if_throttle_start_size > 0) {
+		int kb = (total_len + 512) / 1024;
+
+		if (kb >= if_throttle_start_size)
+			rate_limit_sleep(&if_throttle, kb);
+	}
+
+	(void)ahead; /*  shut up gcc */
+	if (unlikely(discard)) { /*  NYI */
+		error = 0;
+		bio->bi_error = error;
+		bio_endio(bio);
+		goto done;
+	}
+
+	biow = brick_mem_alloc(sizeof(struct bio_wrapper));
+	biow->bio = bio;
+	atomic_set(&biow->bi_comp_cnt, 0);
+
+	if (rw)
+		atomic_inc(&input->total_write_count);
+	else
+		atomic_inc(&input->total_read_count);
+	_if_start_io_acct(input, biow);
+
+	/* Get a reference to the bio.
+	 * Will be released after bio_endio().
+	 */
+	bio_get(bio);
+
+	/* FIXME: THIS IS PROVISIONARY (use event instead)
+	 */
+	while (unlikely(!brick->power.on_led))
+		brick_msleep(100);
+
+	bio_for_each_segment(bvec, bio, i) {
+		struct page *page = bvec.bv_page;
+		int bv_len = bvec.bv_len;
+		int offset = bvec.bv_offset;
+
+		void *data;
+
+#ifdef ARCH_HAS_KMAP
+#error FIXME/TODO: the current infrastructure cannot deal with HIGHMEM / kmap()
+#error HINT: XIO is supposed to run on big 64bit (storage) servers.
+#endif
+		data = page_address(page);
+		error = -EINVAL;
+		if (unlikely(!data))
+			break;
+
+		data += offset;
+
+		while (bv_len > 0) {
+			struct list_head *tmp;
+			int hash_index;
+			int this_len = 0;
+			unsigned long flags;
+
+			aio = NULL;
+			aio_a = NULL;
+
+			hash_index = (pos / IF_HASH_CHUNK) % IF_HASH_MAX;
+
+#ifdef REQUEST_MERGING
+			spin_lock_irqsave(&input->hash_table[hash_index].hash_lock, flags);
+			for (tmp = input->hash_table[hash_index].hash_anchor.next; tmp != &input->hash_table[hash_index].hash_anchor; tmp = tmp->next) {
+				struct if_aio_aspect *tmp_a;
+				struct aio_object *tmp_aio;
+				int i;
+
+				tmp_a = container_of(tmp, struct if_aio_aspect, hash_head);
+				tmp_aio = tmp_a->object;
+				if (tmp_a->orig_page != page || tmp_aio->io_rw != rw || tmp_a->bio_count >= MAX_BIO || tmp_a->current_len + bv_len > tmp_a->max_len)
+					continue;
+
+				if (tmp_aio->io_data + tmp_a->current_len == data)
+					goto merge_end;
+				continue;
+
+merge_end:
+				tmp_a->current_len += bv_len;
+				aio = tmp_aio;
+				aio_a = tmp_a;
+				this_len = bv_len;
+				if (!do_skip_sync)
+					aio->io_skip_sync = false;
+
+				for (i = 0; i < aio_a->bio_count; i++) {
+					if (aio_a->orig_biow[i]->bio == bio)
+						goto unlock;
+				}
+
+				CHECK_ATOMIC(&biow->bi_comp_cnt, 0);
+				atomic_inc(&biow->bi_comp_cnt);
+				aio_a->orig_biow[aio_a->bio_count++] = biow;
+				assigned = true;
+				goto unlock;
+			} /*  foreach hash collision list member */
+
+unlock:
+			spin_unlock_irqrestore(&input->hash_table[hash_index].hash_lock, flags);
+#endif
+			if (!aio) {
+				int prefetch_len;
+
+				error = -ENOMEM;
+				aio = if_alloc_aio(brick);
+				aio_a = if_aio_get_aspect(brick, aio);
+				if (unlikely(!aio_a))
+					goto err;
+
+#ifdef PREFETCH_LEN
+				prefetch_len = PREFETCH_LEN - offset;
+/**/
+				if (prefetch_len > total_len)
+					prefetch_len = total_len;
+				if (pos + prefetch_len > brick->dev_size)
+					prefetch_len = brick->dev_size - pos;
+				if (prefetch_len < bv_len)
+					prefetch_len = bv_len;
+#else
+				prefetch_len = bv_len;
+#endif
+
+				SETUP_CALLBACK(aio, if_endio, aio_a);
+
+				aio_a->input = input;
+				aio->io_rw = aio->io_may_write = rw;
+				aio->io_pos = pos;
+				aio->io_len = prefetch_len;
+				aio->io_data = data; /*  direct IO */
+				aio->io_prio = io_prio;
+				aio_a->orig_page = page;
+
+				error = GENERIC_INPUT_CALL(input, aio_get, aio);
+				if (unlikely(error < 0))
+					goto err;
+
+				this_len = aio->io_len; /*  now may be shorter than originally requested. */
+				aio_a->max_len = this_len;
+				if (this_len > bv_len)
+					this_len = bv_len;
+				aio_a->current_len = this_len;
+				if (rw)
+					atomic_inc(&input->total_aio_write_count);
+				else
+					atomic_inc(&input->total_aio_read_count);
+				CHECK_ATOMIC(&biow->bi_comp_cnt, 0);
+				atomic_inc(&biow->bi_comp_cnt);
+				aio_a->orig_biow[0] = biow;
+				aio_a->bio_count = 1;
+				assigned = true;
+
+				/* When a bio with multiple biovecs is split into
+				 * multiple aios, only the last one should be
+				 * working in synchronous writethrough mode.
+				 */
+				aio->io_skip_sync = true;
+				if (!do_skip_sync && i.bi_idx + 1 >= bio->bi_iter.bi_idx)
+					aio->io_skip_sync = false;
+
+				atomic_inc(&input->plugged_count);
+
+				aio_a->hash_index = hash_index;
+				spin_lock_irqsave(&input->hash_table[hash_index].hash_lock, flags);
+				list_add_tail(&aio_a->hash_head, &input->hash_table[hash_index].hash_anchor);
+				spin_unlock_irqrestore(&input->hash_table[hash_index].hash_lock, flags);
+
+				spin_lock_irqsave(&input->req_lock, flags);
+				list_add_tail(&aio_a->plug_head, &input->plug_anchor);
+				spin_unlock_irqrestore(&input->req_lock, flags);
+			} /*  !aio */
+
+			pos += this_len;
+			data += this_len;
+			bv_len -= this_len;
+		} /*  while bv_len > 0 */
+	} /*  foreach bvec */
+
+	error = 0;
+
+err:
+	if (error < 0) {
+		XIO_ERR("cannot submit request from bio, status=%d\n", error);
+		if (!assigned) {
+			bio->bi_error = error;
+			bio_endio(bio);
+		}
+	}
+
+	if (do_unplug ||
+	   (brick && brick->max_plugged > 0 && atomic_read(&input->plugged_count) > brick->max_plugged)) {
+		_if_unplug(input);
+	}
+
+done:
+	remove_binding_from(brick->say_channel, current);
+
+	return BLK_QC_T_NONE;
+}
+
+static
+int xio_congested(void *data, int bdi_bits)
+{
+	struct if_input *input = data;
+	int ret = 0;
+
+	if (bdi_bits & (1 << WB_sync_congested) &&
+	    atomic_read(&input->read_flying_count) > 0) {
+		ret |= (1 << WB_sync_congested);
+	}
+	if (bdi_bits & (1 << WB_async_congested) &&
+	    atomic_read(&input->write_flying_count) > 0) {
+		ret |= (1 << WB_async_congested);
+	}
+	return ret;
+}
+
+static
+loff_t if_get_capacity(struct if_brick *brick)
+{
+	/* Don't read always, read only when unknown.
+	 * brick->dev_size may be different from underlying sizes,
+	 * e.g. when the size symlink indicates a logically smaller
+	 * device than physically.
+	 */
+	if (brick->dev_size <= 0) {
+		struct xio_info info = {};
+		struct if_input *input = brick->inputs[0];
+		int status;
+
+		status = GENERIC_INPUT_CALL(input, xio_get_info, &info);
+		if (unlikely(status < 0)) {
+			XIO_ERR("cannot get device info, status=%d\n", status);
+			return 0;
+		}
+		XIO_INF("determined default capacity: %lld bytes\n", info.current_size);
+		brick->dev_size = info.current_size;
+	}
+	return brick->dev_size;
+}
+
+static
+void if_set_capacity(struct if_input *input, loff_t capacity)
+{
+	CHECK_PTR(input->disk, done);
+	CHECK_PTR(input->disk->disk_name, done);
+	XIO_INF("new capacity of '%s': %lld bytes\n", input->disk->disk_name, capacity);
+	input->capacity = capacity;
+	set_capacity(input->disk, capacity >> 9);
+	if (likely(input->bdev && input->bdev->bd_inode))
+		i_size_write(input->bdev->bd_inode, capacity);
+done:;
+}
+
+static const struct block_device_operations if_blkdev_ops;
+
+static int if_switch(struct if_brick *brick)
+{
+	struct if_input *input = brick->inputs[0];
+	struct request_queue *q;
+	struct gendisk *disk;
+	int minor;
+	int status = 0;
+
+	down(&brick->switch_sem);
+
+	/*  brick is in operation */
+	if (brick->power.button && brick->power.on_led) {
+		loff_t capacity;
+
+		capacity = if_get_capacity(brick);
+		if (capacity > 0 && capacity != input->capacity) {
+			XIO_INF("changing capacity from %lld to %lld\n",
+				(long long)input->capacity,
+				(long long)capacity);
+			if_set_capacity(input, capacity);
+		}
+	}
+
+	/*  brick should be switched on */
+	if (brick->power.button && brick->power.off_led) {
+		loff_t capacity;
+
+		xio_set_power_off_led((void *)brick,  false);
+		brick->say_channel = get_binding(current);
+
+		status = -ENOMEM;
+		q = blk_alloc_queue(GFP_BRICK);
+		if (!q) {
+			XIO_ERR("cannot allocate device request queue\n");
+			goto is_down;
+		}
+		q->queuedata = input;
+		input->q = q;
+
+		disk = alloc_disk(1);
+		if (!disk) {
+			XIO_ERR("cannot allocate gendisk\n");
+			goto is_down;
+		}
+
+		minor = device_minor++; /* TODO: protect against races (e.g. atomic_t) */
+		set_disk_ro(disk, true);
+
+		disk->queue = q;
+		disk->major = XIO_MAJOR; /* TODO: make this dynamic for >256 devices */
+		disk->first_minor = minor;
+		disk->fops = &if_blkdev_ops;
+		snprintf(disk->disk_name, sizeof(disk->disk_name),  "%s", brick->brick_name);
+		disk->private_data = input;
+		input->disk = disk;
+		capacity = if_get_capacity(brick);
+		XIO_DBG("created device name %s, capacity=%lld\n", disk->disk_name, capacity);
+		if_set_capacity(input, capacity);
+
+		blk_queue_make_request(q, if_make_request);
+		blk_set_stacking_limits(&q->limits);
+		blk_queue_max_hw_sectors(q, USE_MAX_SECTORS);
+		blk_queue_max_segments(q, PAGE_SIZE);
+		blk_queue_max_segment_size(q, USE_MAX_SEGMENT_SIZE);
+		blk_queue_logical_block_size(q, USE_LOGICAL_BLOCK_SIZE);
+		blk_queue_segment_boundary(q, USE_SEGMENT_BOUNDARY);
+		blk_queue_bounce_limit(q, BLK_BOUNCE_ANY);
+		blk_queue_max_write_same_sectors(q, 0);
+		q->queue_lock = &input->req_lock; /*  needed! */
+
+		input->bdev = bdget(MKDEV(disk->major, minor));
+		/* we have no partitions. we contain only ourselves. */
+		input->bdev->bd_contains = input->bdev;
+
+		q->backing_dev_info.congested_fn = xio_congested;
+		q->backing_dev_info.congested_data = input;
+
+		/*  point of no return */
+		XIO_DBG("add_disk()\n");
+		add_disk(disk);
+		set_disk_ro(disk, false);
+
+		/*  report success */
+		xio_set_power_on_led((void *)brick, true);
+		status = 0;
+	}
+
+	/*  brick should be switched off */
+	if (!brick->power.button && !brick->power.off_led) {
+		int opened;
+		int plugged;
+		int flying;
+
+		xio_set_power_on_led((void *)brick, false);
+		disk = input->disk;
+		if (!disk)
+			goto is_down;
+
+		opened = atomic_read(&brick->open_count);
+		if (unlikely(opened > 0)) {
+			XIO_INF("device '%s' is open %d times, cannot shutdown\n", disk->disk_name, opened);
+			status = -EBUSY;
+			goto done; /*  don't indicate "off" status */
+		}
+		plugged = atomic_read(&input->plugged_count);
+		if (unlikely(plugged > 0)) {
+			XIO_INF("device '%s' has %d plugged requests, cannot shutdown\n", disk->disk_name, plugged);
+			status = -EBUSY;
+			goto done; /*  don't indicate "off" status */
+		}
+		flying = atomic_read(&input->flying_count);
+		if (unlikely(flying > 0)) {
+			XIO_INF("device '%s' has %d flying requests, cannot shutdown\n", disk->disk_name, flying);
+			status = -EBUSY;
+			goto done; /*  don't indicate "off" status */
+		}
+		XIO_DBG("calling del_gendisk()\n");
+		del_gendisk(input->disk);
+		/* There might be subtle races */
+		while (atomic_read(&input->flying_count) > 0) {
+			XIO_WRN("device '%s' unexpectedly has %d flying requests\n", disk->disk_name, flying);
+			brick_msleep(1000);
+		}
+		if (input->bdev) {
+			XIO_DBG("calling bdput()\n");
+			bdput(input->bdev);
+			input->bdev = NULL;
+		}
+		XIO_DBG("calling put_disk()\n");
+		put_disk(input->disk);
+		input->disk = NULL;
+		q = input->q;
+		if (q) {
+			blk_cleanup_queue(q);
+			input->q = NULL;
+		}
+		status = 0;
+is_down:
+		xio_set_power_off_led((void *)brick, true);
+	}
+
+done:
+	up(&brick->switch_sem);
+	return status;
+}
+
+/*************** interface to the outer world (kernel) **************/
+
+static int if_open(struct block_device *bdev, fmode_t mode)
+{
+	struct if_input *input;
+	struct if_brick *brick;
+
+	if (unlikely(!bdev || !bdev->bd_disk)) {
+		XIO_ERR("----------------------- INVAL ------------------------------\n");
+		return -EINVAL;
+	}
+
+	input = bdev->bd_disk->private_data;
+
+	if (unlikely(!input || !input->brick)) {
+		XIO_ERR("----------------------- BAD IF SETUP ------------------------------\n");
+		return -EINVAL;
+	}
+	brick = input->brick;
+
+	down(&brick->switch_sem);
+
+	if (unlikely(!brick->power.on_led)) {
+		XIO_INF("----------------------- BUSY %d ------------------------------\n",
+			atomic_read(&brick->open_count));
+		up(&brick->switch_sem);
+		return -EBUSY;
+	}
+
+	atomic_inc(&brick->open_count);
+
+	XIO_INF("----------------------- OPEN %d ------------------------------\n", atomic_read(&brick->open_count));
+
+	up(&brick->switch_sem);
+	return 0;
+}
+
+static
+void
+if_release(struct gendisk *gd, fmode_t mode)
+{
+	struct if_input *input = gd->private_data;
+	struct if_brick *brick = input->brick;
+	int nr;
+
+	XIO_INF("----------------------- CLOSE %d ------------------------------\n", atomic_read(&brick->open_count));
+
+	if (atomic_dec_and_test(&brick->open_count)) {
+		while ((nr = atomic_read(&input->flying_count)) > 0) {
+			XIO_INF("%d IO requests not yet completed\n", nr);
+			brick_msleep(1000);
+		}
+
+		XIO_DBG("status button=%d on_led=%d off_led=%d\n",
+			brick->power.button,
+			brick->power.on_led,
+			brick->power.off_led);
+		local_trigger();
+	}
+}
+
+static const struct block_device_operations if_blkdev_ops = {
+	.owner = THIS_MODULE,
+	.open = if_open,
+	.release = if_release,
+
+};
+
+/*************** informational * statistics **************/
+
+static
+char *if_statistics(struct if_brick *brick, int verbose)
+{
+	struct if_input *input = brick->inputs[0];
+	char *res = brick_string_alloc(512);
+	int tmp0 = atomic_read(&input->total_reada_count);
+	int tmp1 = atomic_read(&input->total_read_count);
+	int tmp2 = atomic_read(&input->total_aio_read_count);
+	int tmp3 = atomic_read(&input->total_write_count);
+	int tmp4 = atomic_read(&input->total_aio_write_count);
+
+	snprintf(res, 512,
+		 "total reada = %d reads = %d aio_reads = %d (%d%%) writes = %d aio_writes = %d (%d%%) empty = %d fired = %d skip_sync = %d | plugged = %d flying = %d (reads = %d writes = %d)\n",
+		 tmp0,
+		 tmp1,
+		 tmp2,
+		 tmp1 ? tmp2 * 100 / tmp1 : 0,
+		 tmp3,
+		 tmp4,
+		 tmp3 ? tmp4 * 100 / tmp3 : 0,
+		 atomic_read(&input->total_empty_count),
+		 atomic_read(&input->total_fire_count),
+		 atomic_read(&input->total_skip_sync_count),
+		 atomic_read(&input->plugged_count),
+		 atomic_read(&input->flying_count),
+		 atomic_read(&input->read_flying_count),
+		 atomic_read(&input->write_flying_count));
+	return res;
+}
+
+static
+void if_reset_statistics(struct if_brick *brick)
+{
+	struct if_input *input = brick->inputs[0];
+
+	atomic_set(&input->total_read_count, 0);
+	atomic_set(&input->total_write_count, 0);
+	atomic_set(&input->total_empty_count, 0);
+	atomic_set(&input->total_fire_count, 0);
+	atomic_set(&input->total_skip_sync_count, 0);
+	atomic_set(&input->total_aio_read_count, 0);
+	atomic_set(&input->total_aio_write_count, 0);
+}
+
+/***************** own brick * input * output operations *****************/
+
+/*  none */
+
+/*************** object * aspect constructors * destructors **************/
+
+static int if_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct if_aio_aspect *ini = (void *)_ini;
+
+	INIT_LIST_HEAD(&ini->plug_head);
+	INIT_LIST_HEAD(&ini->hash_head);
+	return 0;
+}
+
+static void if_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct if_aio_aspect *ini = (void *)_ini;
+
+	CHECK_HEAD_EMPTY(&ini->plug_head);
+	CHECK_HEAD_EMPTY(&ini->hash_head);
+}
+
+XIO_MAKE_STATICS(if);
+
+/*********************** constructors * destructors ***********************/
+
+static int if_brick_construct(struct if_brick *brick)
+{
+	sema_init(&brick->switch_sem, 1);
+	atomic_set(&brick->open_count, 0);
+	return 0;
+}
+
+static int if_brick_destruct(struct if_brick *brick)
+{
+	return 0;
+}
+
+static int if_input_construct(struct if_input *input)
+{
+	int i;
+
+	input->hash_table = brick_block_alloc(0, PAGE_SIZE);
+	for (i = 0; i < IF_HASH_MAX; i++) {
+		spin_lock_init(&input->hash_table[i].hash_lock);
+		INIT_LIST_HEAD(&input->hash_table[i].hash_anchor);
+	}
+	INIT_LIST_HEAD(&input->plug_anchor);
+	spin_lock_init(&input->req_lock);
+	atomic_set(&input->flying_count, 0);
+	atomic_set(&input->read_flying_count, 0);
+	atomic_set(&input->write_flying_count, 0);
+	atomic_set(&input->plugged_count, 0);
+	return 0;
+}
+
+static int if_input_destruct(struct if_input *input)
+{
+	int i;
+
+	for (i = 0; i < IF_HASH_MAX; i++)
+		CHECK_HEAD_EMPTY(&input->hash_table[i].hash_anchor);
+	CHECK_HEAD_EMPTY(&input->plug_anchor);
+	brick_block_free(input->hash_table, PAGE_SIZE);
+	return 0;
+}
+
+static int if_output_construct(struct if_output *output)
+{
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct if_brick_ops if_brick_ops = {
+	.brick_switch = if_switch,
+	.brick_statistics = if_statistics,
+	.reset_statistics = if_reset_statistics,
+};
+
+static struct if_output_ops if_output_ops;
+
+const struct if_input_type if_input_type = {
+	.type_name = "if_input",
+	.input_size = sizeof(struct if_input),
+	.input_construct = &if_input_construct,
+	.input_destruct = &if_input_destruct,
+};
+
+static const struct if_input_type *if_input_types[] = {
+	&if_input_type,
+};
+
+const struct if_output_type if_output_type = {
+	.type_name = "if_output",
+	.output_size = sizeof(struct if_output),
+	.master_ops = &if_output_ops,
+	.output_construct = &if_output_construct,
+};
+
+static const struct if_output_type *if_output_types[] = {
+	&if_output_type,
+};
+
+const struct if_brick_type if_brick_type = {
+	.type_name = "if_brick",
+	.brick_size = sizeof(struct if_brick),
+	.max_inputs = 1,
+	.max_outputs = 0,
+	.master_ops = &if_brick_ops,
+	.aspect_types = if_aspect_types,
+	.default_input_types = if_input_types,
+	.default_output_types = if_output_types,
+	.brick_construct = &if_brick_construct,
+	.brick_destruct = &if_brick_destruct,
+};
+
+/***************** module init stuff ************************/
+
+void exit_xio_if(void)
+{
+	int status;
+
+	XIO_INF("exit_if()\n");
+	status = if_unregister_brick_type();
+	unregister_blkdev(XIO_MAJOR, "xio");
+}
+
+int __init init_xio_if(void)
+{
+	int status;
+
+	(void)if_aspect_types; /*  not used, shut up gcc */
+
+	XIO_INF("init_if()\n");
+	status = register_blkdev(XIO_MAJOR, "xio");
+	if (status)
+		return status;
+	status = if_register_brick_type();
+	if (status)
+		goto err_device;
+	return status;
+err_device:
+	XIO_ERR("init_if() status=%d\n", status);
+	exit_xio_if();
+	return status;
+}
diff --git a/include/linux/xio/xio_if.h b/include/linux/xio/xio_if.h
new file mode 100644
index 0000000..7de1b28
--- /dev/null
+++ b/include/linux/xio/xio_if.h
@@ -0,0 +1,108 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_IF_H
+#define XIO_IF_H
+
+#include <linux/semaphore.h>
+
+#define HT_SHIFT			6 /* ???? */
+#define XIO_MAX_SEGMENT_SIZE		(1U << (9+HT_SHIFT))
+
+#define MAX_BIO				32
+
+/************************ global tuning ***********************/
+
+extern int if_throttle_start_size; /*  in kb */
+extern struct rate_limiter if_throttle;
+
+/***********************************************/
+
+/* I don't want to enhance / intrude into struct bio for compatibility reasons
+ * (support for a variety of kernel versions).
+ * The following is just a silly workaround which could be removed again.
+ */
+struct bio_wrapper {
+	struct bio *bio;
+	atomic_t bi_comp_cnt;
+	unsigned long start_time;
+};
+
+struct if_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct list_head plug_head;
+	struct list_head hash_head;
+	int hash_index;
+	int bio_count;
+	int current_len;
+	int max_len;
+	struct page *orig_page;
+	struct bio_wrapper *orig_biow[MAX_BIO];
+	struct if_input *input;
+};
+
+struct if_hash_anchor;
+
+struct if_input {
+	XIO_INPUT(if);
+	/*  TODO: move this to if_brick (better systematics) */
+	struct list_head plug_anchor;
+	struct request_queue *q;
+	struct gendisk *disk;
+	struct block_device *bdev;
+	loff_t capacity;
+	atomic_t plugged_count;
+	atomic_t flying_count;
+
+	/*  only for statistics */
+	atomic_t read_flying_count;
+	atomic_t write_flying_count;
+	atomic_t total_reada_count;
+	atomic_t total_read_count;
+	atomic_t total_write_count;
+	atomic_t total_empty_count;
+	atomic_t total_fire_count;
+	atomic_t total_skip_sync_count;
+	atomic_t total_aio_read_count;
+	atomic_t total_aio_write_count;
+	spinlock_t req_lock;
+	struct if_hash_anchor *hash_table;
+};
+
+struct if_output {
+	XIO_OUTPUT(if);
+};
+
+struct if_brick {
+	XIO_BRICK(if);
+	/*  parameters */
+	loff_t dev_size;
+	int max_plugged;
+	int readahead;
+	bool skip_sync;
+
+	/*  inspectable */
+	atomic_t open_count;
+
+	/*  private */
+	struct semaphore switch_sem;
+	struct say_channel *say_channel;
+};
+
+XIO_TYPES(if);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299652 — [RFC 13/31] mars: add new module xio

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 13/31] mars: add new module xio
Message-ID<qLJTY-3U9-23@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio.c | 161 +++++++++++++++++
 include/linux/xio/xio.h               | 313 ++++++++++++++++++++++++++++++++++
 2 files changed, 474 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio.c
 create mode 100644 include/linux/xio/xio.h

diff --git a/drivers/staging/mars/xio_bricks/xio.c b/drivers/staging/mars/xio_bricks/xio.c
new file mode 100644
index 0000000..94aeb60
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio.c
@@ -0,0 +1,161 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/string.h>
+#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+
+#include <linux/xio/xio.h>
+
+/************************************************************/
+
+/*  infrastructure */
+
+struct banning xio_global_ban = {};
+atomic_t xio_global_io_flying = ATOMIC_INIT(0);
+
+/************************************************************/
+
+/*  object stuff */
+
+const struct generic_object_type aio_type = {
+	.object_type_name = "aio",
+	.default_size = sizeof(struct aio_object),
+	.object_type_nr = OBJ_TYPE_AIO,
+};
+
+/************************************************************/
+
+/*  brick stuff */
+
+/*******************************************************************/
+
+/*  meta descriptions */
+
+const struct meta xio_info_meta[] = {
+	META_INI(current_size,	  struct xio_info, FIELD_INT),
+	META_INI(tf_align,	  struct xio_info, FIELD_INT),
+	META_INI(tf_min_size,	  struct xio_info, FIELD_INT),
+	{}
+};
+
+const struct meta xio_aio_user_meta[] = {
+	META_INI(_object_cb.cb_error, struct aio_object, FIELD_INT),
+	META_INI(io_pos,	   struct aio_object, FIELD_INT),
+	META_INI(io_len,	   struct aio_object, FIELD_INT),
+	META_INI(io_may_write,	  struct aio_object, FIELD_INT),
+	META_INI(io_prio,	   struct aio_object, FIELD_INT),
+	META_INI(io_cs_mode,	   struct aio_object, FIELD_INT),
+	META_INI(io_timeout,	   struct aio_object, FIELD_INT),
+	META_INI(io_total_size,   struct aio_object, FIELD_INT),
+	META_INI(io_checksum,	   struct aio_object, FIELD_RAW),
+	META_INI(io_flags,	   struct aio_object, FIELD_INT),
+	META_INI(io_rw,    struct aio_object, FIELD_INT),
+	META_INI(io_id,    struct aio_object, FIELD_INT),
+	META_INI(io_skip_sync,	  struct aio_object, FIELD_INT),
+	{}
+};
+
+const struct meta xio_timespec_meta[] = {
+	META_INI_TRANSFER(tv_sec,  struct timespec, FIELD_UINT, 8),
+	META_INI_TRANSFER(tv_nsec, struct timespec, FIELD_UINT, 4),
+	{}
+};
+
+/************************************************************/
+
+/*  crypto stuff */
+
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+
+static struct crypto_hash *xio_tfm;
+static struct semaphore tfm_sem;
+int xio_digest_size;
+
+void xio_digest(unsigned char *digest, void *data, int len)
+{
+	struct hash_desc desc = {
+		.tfm = xio_tfm,
+		.flags = 0,
+	};
+	struct scatterlist sg;
+
+	memset(digest, 0, xio_digest_size);
+
+	/*  TODO: use per-thread instance, omit locking */
+	down(&tfm_sem);
+
+	crypto_hash_init(&desc);
+	sg_init_table(&sg, 1);
+	sg_set_buf(&sg, data, len);
+	crypto_hash_update(&desc, &sg, sg.length);
+	crypto_hash_final(&desc, digest);
+	up(&tfm_sem);
+}
+
+void aio_checksum(struct aio_object *aio)
+{
+	unsigned char checksum[xio_digest_size];
+	int len;
+
+	if (aio->io_cs_mode <= 0 || !aio->io_data)
+		goto out_return;
+	xio_digest(checksum, aio->io_data, aio->io_len);
+
+	len = sizeof(aio->io_checksum);
+	if (len > xio_digest_size)
+		len = xio_digest_size;
+	memcpy(&aio->io_checksum, checksum, len);
+out_return:;
+}
+
+/*******************************************************************/
+
+/*  init stuff */
+
+int __init init_xio(void)
+{
+	XIO_INF("init_xio()\n");
+
+	sema_init(&tfm_sem, 1);
+
+	xio_tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+	if (!xio_tfm) {
+		XIO_ERR("cannot alloc crypto hash\n");
+		return -ENOMEM;
+	}
+	if (IS_ERR(xio_tfm)) {
+		XIO_ERR("alloc crypto hash failed, status = %d\n", (int)PTR_ERR(xio_tfm));
+		return PTR_ERR(xio_tfm);
+	}
+	xio_digest_size = crypto_hash_digestsize(xio_tfm);
+	XIO_INF("digest_size = %d\n", xio_digest_size);
+
+	return 0;
+}
+
+void exit_xio(void)
+{
+	XIO_INF("exit_xio()\n");
+
+	if (xio_tfm)
+		crypto_free_hash(xio_tfm);
+}
diff --git a/include/linux/xio/xio.h b/include/linux/xio/xio.h
new file mode 100644
index 0000000..2b35446
--- /dev/null
+++ b/include/linux/xio/xio.h
@@ -0,0 +1,313 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_H
+#define XIO_H
+
+#include <linux/semaphore.h>
+#include <linux/rwsem.h>
+#include <linux/major.h>
+
+#if defined(CONFIG_CRYPTO_LZO) || defined(CONFIG_CRYPTO_LZO_MODULE)
+#define __HAVE_LZO
+#endif
+
+#ifdef __enabled_CONFIG_CRYPTO_LZO
+#if __enabled_CONFIG_CRYPTO_LZO
+#define __HAVE_LZO
+#endif
+#endif
+
+#ifdef __enabled_CONFIG_CRYPTO_LZO_MODULE
+#if __enabled_CONFIG_CRYPTO_LZO_MODULE
+#define __HAVE_LZO
+#endif
+#endif
+
+/* TRANSITIONAL compatibility to BOTH the old prepatch
+ * and the new wrapper around vfs_*(). Both will be replaced
+ * for kernel upstream.
+ */
+#include <linux/brick/vfs_compat.h>
+#ifndef MARS_MAJOR
+#define __USE_COMPAT
+#endif
+
+/***********************************************************************/
+
+/*  include the generic brick infrastructure */
+
+#define OBJ_TYPE_AIO			0
+#define OBJ_TYPE_MAX			1
+
+#include <linux/brick/brick.h>
+#include <linux/brick/brick_mem.h>
+#include <linux/brick/lamport.h>
+#include <linux/brick/lib_timing.h>
+
+/***********************************************************************/
+
+/*  XIO-specific debugging helpers */
+
+#define _XIO_MSG(_class, _dump, _fmt, _args...)				\
+	brick_say(_class, _dump, "XIO", __BASE_FILE__, __LINE__, __func__, _fmt, ##_args)
+
+#define XIO_FAT(_fmt, _args...) _XIO_MSG(SAY_FATAL, true,  _fmt, ##_args)
+#define XIO_ERR(_fmt, _args...) _XIO_MSG(SAY_ERROR, false, _fmt, ##_args)
+#define XIO_WRN(_fmt, _args...) _XIO_MSG(SAY_WARN,  false, _fmt, ##_args)
+#define XIO_INF(_fmt, _args...) _XIO_MSG(SAY_INFO,  false, _fmt, ##_args)
+
+#ifdef XIO_DEBUGGING
+#define XIO_DBG(_fmt, _args...) _XIO_MSG(SAY_DEBUG, false, _fmt, ##_args)
+#else
+#define XIO_DBG(_args...) /**/
+#endif
+
+/***********************************************************************/
+
+/*  XIO-specific definitions */
+
+#define XIO_PRIO_HIGH			-1
+#define XIO_PRIO_NORMAL			0 /*  this is automatically used by memset() */
+#define XIO_PRIO_LOW			1
+#define XIO_PRIO_NR			3
+
+/*  object stuff */
+
+/* aio */
+
+#define AIO_UPTODATE			1
+#define AIO_READING			2
+#define AIO_WRITING			4
+
+extern const struct generic_object_type aio_type;
+
+#define XIO_CHECKSUM_SIZE		16
+
+#define AIO_OBJECT(OBJTYPE)						\
+	CALLBACK_OBJECT(OBJTYPE);					\
+	/* supplied by caller */					\
+	void  *io_data;  /* preset to NULL for buffered IO */		\
+	loff_t io_pos;							\
+	int    io_len;							\
+	int    io_may_write;						\
+	int    io_prio;							\
+	int    io_timeout;						\
+	int    io_cs_mode; /* 0 = off, 1 = checksum + data, 2 = checksum only */\
+	/* maintained by the aio implementation, readable for callers */\
+	loff_t io_total_size; /* just for info, need not be implemented */\
+	unsigned char io_checksum[XIO_CHECKSUM_SIZE];			\
+	int    io_flags;						\
+	int    io_rw;							\
+	int    io_id; /* not mandatory; may be used for identification */\
+	bool   io_skip_sync; /* skip sync for this particular aio */	\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+struct aio_object {
+	AIO_OBJECT(aio);
+};
+
+/*  internal helper structs */
+
+struct xio_info {
+	loff_t current_size;
+
+	int tf_align;	 /*  transfer alignment constraint */
+	int tf_min_size; /*  transfer is only possible in multiples of this */
+};
+
+/*  brick stuff */
+
+#define XIO_BRICK(BRITYPE)						\
+	GENERIC_BRICK(BRITYPE);						\
+	struct generic_object_layout aio_object_layout;			\
+	struct list_head global_brick_link;				\
+	struct list_head dent_brick_link;				\
+	const char *brick_name;						\
+	const char *brick_path;						\
+	void *private_ptr;						\
+	void **kill_ptr;						\
+	int *mode_ptr;							\
+	int kill_round;							\
+	bool killme;							\
+	void (*show_status)(struct xio_brick *brick, bool shutdown);	\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+struct xio_brick {
+	XIO_BRICK(xio);
+};
+
+#define XIO_INPUT(BRITYPE)						\
+	GENERIC_INPUT(BRITYPE);						\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+struct xio_input {
+	XIO_INPUT(xio);
+};
+
+#define XIO_OUTPUT(BRITYPE)						\
+	GENERIC_OUTPUT(BRITYPE);					\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+struct xio_output {
+	XIO_OUTPUT(xio);
+};
+
+#define XIO_BRICK_OPS(BRITYPE)						\
+	GENERIC_BRICK_OPS(BRITYPE);					\
+	char *(*brick_statistics)(struct BRITYPE##_brick *brick, int verbose);\
+	void (*reset_statistics)(struct BRITYPE##_brick *brick);	\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+#define XIO_OUTPUT_OPS(BRITYPE)						\
+	GENERIC_OUTPUT_OPS(BRITYPE);					\
+	int  (*xio_get_info)(struct BRITYPE##_output *output, struct xio_info *info);\
+	/* aio */							\
+	int  (*aio_get)(struct BRITYPE##_output *output, struct aio_object *aio);\
+	void (*aio_io)(struct BRITYPE##_output *output, struct aio_object *aio);\
+	void (*aio_put)(struct BRITYPE##_output *output, struct aio_object *aio);\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+/*  all non-extendable types */
+
+#define _XIO_TYPES(BRITYPE)						\
+									\
+struct BRITYPE##_brick_ops {						\
+	XIO_BRICK_OPS(BRITYPE);						\
+};									\
+									\
+struct BRITYPE##_output_ops {						\
+	XIO_OUTPUT_OPS(BRITYPE);					\
+};									\
+									\
+struct BRITYPE##_brick_type {						\
+	GENERIC_BRICK_TYPE(BRITYPE);					\
+};									\
+									\
+struct BRITYPE##_input_type {						\
+	GENERIC_INPUT_TYPE(BRITYPE);					\
+};									\
+									\
+struct BRITYPE##_output_type {						\
+	GENERIC_OUTPUT_TYPE(BRITYPE);					\
+};									\
+									\
+struct BRITYPE##_callback {						\
+	GENERIC_CALLBACK(BRITYPE);					\
+};									\
+									\
+DECLARE_BRICK_FUNCTIONS(BRITYPE);					\
+/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+#define XIO_TYPES(BRITYPE)						\
+									\
+_XIO_TYPES(BRITYPE)							\
+									\
+DECLARE_ASPECT_FUNCTIONS(BRITYPE, aio);					\
+extern int init_xio_##BRITYPE(void);					\
+extern void exit_xio_##BRITYPE(void);					\
+/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+/*  instantiate pseudo base-classes */
+
+DECLARE_OBJECT_FUNCTIONS(aio);
+_XIO_TYPES(xio);
+DECLARE_ASPECT_FUNCTIONS(xio, aio);
+
+/***********************************************************************/
+
+/*  XIO-specific helpers */
+
+#define XIO_MAKE_STATICS(BRITYPE)					\
+									\
+int BRITYPE##_brick_nr = -EEXIST;					\
+									\
+static const struct generic_aspect_type BRITYPE##_aio_aspect_type = {	\
+	.aspect_type_name = #BRITYPE "_aio_aspect_type",		\
+	.object_type = &aio_type,					\
+	.aspect_size = sizeof(struct BRITYPE##_aio_aspect),		\
+	.init_fn = BRITYPE##_aio_aspect_init_fn,			\
+	.exit_fn = BRITYPE##_aio_aspect_exit_fn,			\
+};									\
+									\
+static const struct generic_aspect_type *BRITYPE##_aspect_types[OBJ_TYPE_MAX] = {\
+	[OBJ_TYPE_AIO] = &BRITYPE##_aio_aspect_type,			\
+};									\
+/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+extern const struct meta xio_info_meta[];
+extern const struct meta xio_aio_user_meta[];
+extern const struct meta xio_timespec_meta[];
+
+/***********************************************************************/
+
+/* Some minimal upcalls from generic IO layer to the strategy layer.
+ * TODO: abstract away.
+ */
+
+extern void xio_set_power_on_led(struct xio_brick *brick, bool val);
+extern void xio_set_power_off_led(struct xio_brick *brick, bool val);
+
+/* this should disappear!
+ */
+extern void (*_local_trigger)(void);
+extern void (*_remote_trigger)(void);
+#define local_trigger() do { if (_local_trigger) { XIO_DBG("trigger...\n"); _local_trigger(); } } while (0)
+#define remote_trigger()						\
+do { if (_remote_trigger) { XIO_DBG("remote_trigger...\n"); _remote_trigger(); } } while (0)
+
+/***********************************************************************/
+
+/* Some global stuff.
+ */
+
+extern struct banning xio_global_ban;
+
+extern atomic_t xio_global_io_flying;
+
+extern int xio_throttle_start;
+extern int xio_throttle_end;
+
+/***********************************************************************/
+
+/* Some special brick types for avoidance of cyclic references.
+ *
+ * The client/server network bricks use this for independent instantiation
+ * from the main instantiation logic (separate modprobe for xio_server
+ * is possible).
+ */
+extern const struct generic_brick_type *_client_brick_type;
+extern const struct generic_brick_type *_bio_brick_type;
+extern const struct generic_brick_type *_sio_brick_type;
+
+/***********************************************************************/
+
+/* Crypto stuff
+ */
+
+extern int xio_digest_size;
+extern void xio_digest(unsigned char *digest, void *data, int len);
+extern void aio_checksum(struct aio_object *aio);
+
+/***********************************************************************/
+
+/*  init */
+
+extern int init_xio(void);
+extern void exit_xio(void);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299653 — [RFC 25/31] mars: add new module light_net

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 25/31] mars: add new module light_net
Message-ID<qLJTY-3U9-37@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/mars_light/light_net.c | 109 ++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)
 create mode 100644 drivers/staging/mars/mars_light/light_net.c

diff --git a/drivers/staging/mars/mars_light/light_net.c b/drivers/staging/mars/mars_light/light_net.c
new file mode 100644
index 0000000..9890edd
--- /dev/null
+++ b/drivers/staging/mars/mars_light/light_net.c
@@ -0,0 +1,109 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/string.h>
+
+#include <linux/mars_light/light_strategy.h>
+#include <linux/xio/xio_net.h>
+
+static
+char *_xio_translate_hostname(const char *name)
+{
+	char *res = brick_strdup(name);
+	char *test;
+	char *tmp;
+
+	for (tmp = res; *tmp; tmp++) {
+		if (*tmp == ':') {
+			*tmp = '\0';
+			break;
+		}
+	}
+
+	tmp = path_make("/mars/ips/ip-%s", res);
+	if (unlikely(!tmp))
+		goto done;
+
+	test = mars_readlink(tmp);
+	if (test && test[0]) {
+		XIO_DBG("'%s' => '%s'\n", tmp, test);
+		brick_string_free(res);
+		res = test;
+	} else {
+		brick_string_free(test);
+		XIO_WRN("no hostname translation for '%s'\n", tmp);
+	}
+	brick_string_free(tmp);
+
+done:
+	return res;
+}
+
+int xio_send_dent_list(struct xio_socket *sock, struct list_head *anchor)
+{
+	struct list_head *tmp;
+	struct mars_dent *dent;
+	int status = 0;
+
+	for (tmp = anchor->next; tmp != anchor; tmp = tmp->next) {
+		dent = container_of(tmp, struct mars_dent, dent_link);
+		status = xio_send_struct(sock, dent, mars_dent_meta);
+		if (status < 0)
+			break;
+	}
+	if (status >= 0) { /*  send EOR */
+		status = xio_send_struct(sock, NULL, mars_dent_meta);
+	}
+	return status;
+}
+
+int xio_recv_dent_list(struct xio_socket *sock, struct list_head *anchor)
+{
+	int status;
+
+	for (;;) {
+		struct mars_dent *dent = brick_zmem_alloc(sizeof(struct mars_dent));
+
+		INIT_LIST_HEAD(&dent->dent_link);
+		INIT_LIST_HEAD(&dent->brick_list);
+
+		status = xio_recv_struct(sock, dent, mars_dent_meta);
+		if (status <= 0) {
+			xio_free_dent(dent);
+			goto done;
+		}
+		list_add_tail(&dent->dent_link, anchor);
+	}
+done:
+	return status;
+}
+
+/***************** module init stuff ************************/
+
+int __init init_sy_net(void)
+{
+	XIO_INF("init_sy_net()\n");
+	xio_translate_hostname = _xio_translate_hostname;
+	return 0;
+}
+
+void exit_sy_net(void)
+{
+	XIO_INF("exit_sy_net()\n");
+}
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299654 — [RFC 23/31] mars: add new module xio_server

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 23/31] mars: add new module xio_server
Message-ID<qLJTY-3U9-21@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_server.c | 486 +++++++++++++++++++++++++++
 include/linux/xio/xio_server.h               |  91 +++++
 2 files changed, 577 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_server.c
 create mode 100644 include/linux/xio/xio_server.h

diff --git a/drivers/staging/mars/xio_bricks/xio_server.c b/drivers/staging/mars/xio_bricks/xio_server.c
new file mode 100644
index 0000000..95a3327
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_server.c
@@ -0,0 +1,486 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/*  Server brick (just for demonstration) */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/brick/brick.h>
+#include <linux/xio/xio.h>
+#include <linux/xio/xio_bio.h>
+#include <linux/xio/xio_sio.h>
+
+/************************ own type definitions ***********************/
+
+#include <linux/xio/xio_server.h>
+
+static struct xio_socket server_socket[NR_SERVER_SOCKETS];
+static struct task_struct *server_threads[NR_SERVER_SOCKETS];
+
+/************************ own helper functions ***********************/
+
+int cb_thread(void *data)
+{
+	struct server_brick *brick = data;
+	struct xio_socket *sock = &brick->handler_socket;
+	bool aborted = false;
+	bool ok = xio_get_socket(sock);
+	int status = -EINVAL;
+
+	XIO_DBG("--------------- cb_thread starting on socket #%d, ok = %d\n", sock->s_debug_nr, ok);
+	if (!ok)
+		goto done;
+
+	brick->cb_running = true;
+	wake_up_interruptible(&brick->startup_event);
+
+	while (!brick_thread_should_stop() || !list_empty(&brick->cb_read_list) || !list_empty(&brick->cb_write_list) || atomic_read(&brick->in_flight) > 0) {
+		struct server_aio_aspect *aio_a;
+		struct aio_object *aio;
+		struct list_head *tmp;
+		unsigned long flags;
+
+		wait_event_interruptible_timeout(
+			brick->cb_event,
+			!list_empty(&brick->cb_read_list) ||
+			!list_empty(&brick->cb_write_list),
+			1 * HZ);
+
+		spin_lock_irqsave(&brick->cb_lock, flags);
+		tmp = brick->cb_write_list.next;
+		if (tmp == &brick->cb_write_list) {
+			tmp = brick->cb_read_list.next;
+			if (tmp == &brick->cb_read_list) {
+				spin_unlock_irqrestore(&brick->cb_lock, flags);
+				brick_msleep(1000 / HZ);
+				continue;
+			}
+		}
+		list_del_init(tmp);
+		spin_unlock_irqrestore(&brick->cb_lock, flags);
+
+		aio_a = container_of(tmp, struct server_aio_aspect, cb_head);
+		aio = aio_a->object;
+		status = -EINVAL;
+		CHECK_PTR(aio, err);
+
+		status = 0;
+		/* Report a remote error when consistency cannot be guaranteed,
+		 * e.g. emergency mode during sync.
+		 */
+		if (brick->conn_brick && brick->conn_brick->mode_ptr && *brick->conn_brick->mode_ptr < 0
+		    && aio->object_cb)
+			aio->object_cb->cb_error = *brick->conn_brick->mode_ptr;
+		if (!aborted) {
+			down(&brick->socket_sem);
+			status = xio_send_cb(sock, aio);
+			up(&brick->socket_sem);
+		}
+
+err:
+		if (unlikely(status < 0) && !aborted) {
+			aborted = true;
+			XIO_WRN("cannot send response, status = %d\n", status);
+			/* Just shutdown the socket and forget all pending
+			 * requests.
+			 * The _client_ is responsible for resending
+			 * any lost operations.
+			 */
+			xio_shutdown_socket(sock);
+		}
+
+		if (aio_a->data) {
+			brick_block_free(aio_a->data, aio_a->len);
+			aio->io_data = NULL;
+		}
+		if (aio_a->do_put) {
+			GENERIC_INPUT_CALL(brick->inputs[0], aio_put, aio);
+			atomic_dec(&brick->in_flight);
+		} else {
+			obj_free(aio);
+		}
+	}
+
+	xio_shutdown_socket(sock);
+	xio_put_socket(sock);
+
+done:
+	XIO_DBG("---------- cb_thread terminating, status = %d\n", status);
+	wake_up_interruptible(&brick->startup_event);
+	return status;
+}
+
+static
+void server_endio(struct generic_callback *cb)
+{
+	struct server_aio_aspect *aio_a;
+	struct aio_object *aio;
+	struct server_brick *brick;
+	int rw;
+	unsigned long flags;
+
+	aio_a = cb->cb_private;
+	CHECK_PTR(aio_a, err);
+	aio = aio_a->object;
+	CHECK_PTR(aio, err);
+	LAST_CALLBACK(cb);
+	if (unlikely(cb != &aio->_object_cb))
+		XIO_ERR("bad cb pointer %p != %p\n", cb, &aio->_object_cb);
+
+	brick = aio_a->brick;
+	if (unlikely(!brick)) {
+		XIO_WRN("late IO callback -- cannot do anything\n");
+		goto out_return;
+	}
+
+	rw = aio->io_rw;
+
+	spin_lock_irqsave(&brick->cb_lock, flags);
+	if (rw)
+		list_add_tail(&aio_a->cb_head, &brick->cb_write_list);
+	else
+		list_add_tail(&aio_a->cb_head, &brick->cb_read_list);
+	spin_unlock_irqrestore(&brick->cb_lock, flags);
+
+	wake_up_interruptible(&brick->cb_event);
+	goto out_return;
+err:
+	XIO_FAT("cannot handle callback - giving up\n");
+out_return:;
+}
+
+int server_io(struct server_brick *brick, struct xio_socket *sock, struct xio_cmd *cmd)
+{
+	struct aio_object *aio;
+	struct server_aio_aspect *aio_a;
+	int amount;
+	int status = -ENOTRECOVERABLE;
+
+	if (!brick->cb_running || !brick->handler_running || !xio_socket_is_alive(sock))
+		goto done;
+
+	aio = server_alloc_aio(brick);
+	status = -ENOMEM;
+	aio_a = server_aio_get_aspect(brick, aio);
+	if (unlikely(!aio_a)) {
+		obj_free(aio);
+		goto done;
+	}
+
+	status = xio_recv_aio(sock, aio, cmd);
+	if (status < 0) {
+		obj_free(aio);
+		goto done;
+	}
+
+	aio_a->brick = brick;
+	aio_a->data = aio->io_data;
+	aio_a->len = aio->io_len;
+	SETUP_CALLBACK(aio, server_endio, aio_a);
+
+	amount = 0;
+	if (!aio->io_cs_mode < 2)
+		amount = (aio->io_len - 1) / 1024 + 1;
+	rate_limit_sleep(&server_limiter, amount);
+
+	status = GENERIC_INPUT_CALL(brick->inputs[0], aio_get, aio);
+	if (unlikely(status < 0)) {
+		XIO_WRN("aio_get execution error = %d\n", status);
+		SIMPLE_CALLBACK(aio, status);
+		status = 0; /*	continue serving requests */
+		goto done;
+	}
+	aio_a->do_put = true;
+	atomic_inc(&brick->in_flight);
+	GENERIC_INPUT_CALL(brick->inputs[0], aio_io, aio);
+
+done:
+	return status;
+}
+
+/***************** own brick * input * output operations *****************/
+
+static int server_get_info(struct server_output *output, struct xio_info *info)
+{
+	struct server_input *input = output->brick->inputs[0];
+
+	return GENERIC_INPUT_CALL(input, xio_get_info, info);
+}
+
+static int server_io_get(struct server_output *output, struct aio_object *aio)
+{
+	struct server_input *input = output->brick->inputs[0];
+
+	return GENERIC_INPUT_CALL(input, aio_get, aio);
+}
+
+static void server_io_put(struct server_output *output, struct aio_object *aio)
+{
+	struct server_input *input = output->brick->inputs[0];
+
+	GENERIC_INPUT_CALL(input, aio_put, aio);
+}
+
+static void server_io_io(struct server_output *output, struct aio_object *aio)
+{
+	struct server_input *input = output->brick->inputs[0];
+
+	GENERIC_INPUT_CALL(input, aio_io, aio);
+}
+
+int server_switch(struct server_brick *brick)
+{
+	struct xio_socket *sock = &brick->handler_socket;
+	int status = 0;
+
+	if (brick->power.button) {
+		static int version;
+		bool ok;
+
+		if (brick->power.on_led)
+			goto done;
+
+		ok = xio_get_socket(sock);
+		if (unlikely(!ok)) {
+			status = -ENOENT;
+			goto err;
+		}
+
+		xio_set_power_off_led((void *)brick, false);
+
+		brick->version = version++;
+		brick->handler_thread = brick_thread_create(handler_thread, brick, "xio_handler%d", brick->version);
+		if (unlikely(!brick->handler_thread)) {
+			XIO_ERR("cannot create handler thread\n");
+			status = -ENOENT;
+			goto err;
+		}
+
+		xio_set_power_on_led((void *)brick, true);
+	} else if (!brick->power.off_led) {
+		struct task_struct *thread;
+
+		xio_set_power_on_led((void *)brick, false);
+
+		xio_shutdown_socket(sock);
+
+		thread = brick->handler_thread;
+		if (thread) {
+			brick->handler_thread = NULL;
+			brick->handler_running = false;
+			XIO_DBG("#%d stopping handler thread....\n", sock->s_debug_nr);
+			brick_thread_stop(thread);
+		}
+
+		xio_put_socket(sock);
+		XIO_DBG("#%d socket s_count = %d\n", sock->s_debug_nr, atomic_read(&sock->s_count));
+
+		xio_set_power_off_led((void *)brick, true);
+	}
+err:
+	if (unlikely(status < 0)) {
+		xio_set_power_off_led((void *)brick, true);
+		xio_shutdown_socket(sock);
+		xio_put_socket(sock);
+	}
+done:
+	return status;
+}
+
+/*************** informational * statistics **************/
+
+static
+char *server_statistics(struct server_brick *brick, int verbose)
+{
+	char *res = brick_string_alloc(1024);
+
+	snprintf(res, 1024,
+		 "cb_running = %d handler_running = %d in_flight = %d\n",
+		 brick->cb_running,
+		 brick->handler_running,
+		 atomic_read(&brick->in_flight));
+
+	return res;
+}
+
+static
+void server_reset_statistics(struct server_brick *brick)
+{
+}
+
+/*************** object * aspect constructors * destructors **************/
+
+static int server_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct server_aio_aspect *ini = (void *)_ini;
+
+	INIT_LIST_HEAD(&ini->cb_head);
+	return 0;
+}
+
+static void server_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct server_aio_aspect *ini = (void *)_ini;
+
+	CHECK_HEAD_EMPTY(&ini->cb_head);
+}
+
+XIO_MAKE_STATICS(server);
+
+/********************* brick constructors * destructors *******************/
+
+static int server_brick_construct(struct server_brick *brick)
+{
+	init_waitqueue_head(&brick->startup_event);
+	init_waitqueue_head(&brick->cb_event);
+	sema_init(&brick->socket_sem, 1);
+	spin_lock_init(&brick->cb_lock);
+	INIT_LIST_HEAD(&brick->cb_read_list);
+	INIT_LIST_HEAD(&brick->cb_write_list);
+	return 0;
+}
+
+static int server_brick_destruct(struct server_brick *brick)
+{
+	CHECK_HEAD_EMPTY(&brick->cb_read_list);
+	CHECK_HEAD_EMPTY(&brick->cb_write_list);
+	return 0;
+}
+
+static int server_output_construct(struct server_output *output)
+{
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct server_brick_ops server_brick_ops = {
+	.brick_switch = server_switch,
+	.brick_statistics = server_statistics,
+	.reset_statistics = server_reset_statistics,
+};
+
+static struct server_output_ops server_output_ops = {
+	.xio_get_info = server_get_info,
+	.aio_get = server_io_get,
+	.aio_put = server_io_put,
+	.aio_io = server_io_io,
+};
+
+const struct server_input_type server_input_type = {
+	.type_name = "server_input",
+	.input_size = sizeof(struct server_input),
+};
+
+static const struct server_input_type *server_input_types[] = {
+	&server_input_type,
+};
+
+const struct server_output_type server_output_type = {
+	.type_name = "server_output",
+	.output_size = sizeof(struct server_output),
+	.master_ops = &server_output_ops,
+	.output_construct = &server_output_construct,
+};
+
+static const struct server_output_type *server_output_types[] = {
+	&server_output_type,
+};
+
+const struct server_brick_type server_brick_type = {
+	.type_name = "server_brick",
+	.brick_size = sizeof(struct server_brick),
+	.max_inputs = 1,
+	.max_outputs = 0,
+	.master_ops = &server_brick_ops,
+	.aspect_types = server_aspect_types,
+	.default_input_types = server_input_types,
+	.default_output_types = server_output_types,
+	.brick_construct = &server_brick_construct,
+	.brick_destruct = &server_brick_destruct,
+};
+
+/*********************************************************************/
+
+/*  strategy layer */
+
+int server_show_statist;
+
+/***************** module init stuff ************************/
+
+struct rate_limiter server_limiter = {
+	.lim_max_rate = 0,
+};
+
+void exit_xio_server(void)
+{
+	int i;
+
+	XIO_INF("exit_server()\n");
+	server_unregister_brick_type();
+
+	for (i = 0; i < NR_SERVER_SOCKETS; i++) {
+		if (server_threads[i]) {
+			XIO_INF("stopping server thread %d...\n", i);
+			brick_thread_stop(server_threads[i]);
+		}
+		XIO_INF("closing server socket %d...\n", i);
+		xio_put_socket(&server_socket[i]);
+	}
+}
+
+int __init init_xio_server(void)
+{
+	int i;
+
+	XIO_INF("init_server()\n");
+
+	for (i = 0; i < NR_SERVER_SOCKETS; i++) {
+		struct sockaddr_storage sockaddr = {};
+		char tmp[64];
+		int status;
+
+		if (xio_translate_hostname)
+			snprintf(tmp, sizeof(tmp), "%s:%d", my_id(), xio_net_default_port + i);
+		else
+			snprintf(tmp, sizeof(tmp), ":%d", xio_net_default_port + i);
+
+		status = xio_create_sockaddr(&sockaddr, tmp);
+		if (unlikely(status < 0)) {
+			exit_xio_server();
+			return status;
+		}
+
+		status = xio_create_socket(&server_socket[i], &sockaddr, NULL);
+		if (unlikely(status < 0)) {
+			XIO_ERR("could not create server socket %d, status = %d\n", i, status);
+			exit_xio_server();
+			return status;
+		}
+
+		server_threads[i] = brick_thread_create(server_thread, &server_socket[i], "xio_server_%d", i);
+		if (unlikely(!server_threads[i] || IS_ERR(server_threads[i]))) {
+			XIO_ERR("could not create server thread %d\n", i);
+			exit_xio_server();
+			return -ENOENT;
+		}
+	}
+
+	return server_register_brick_type();
+}
diff --git a/include/linux/xio/xio_server.h b/include/linux/xio/xio_server.h
new file mode 100644
index 0000000..2c13f26
--- /dev/null
+++ b/include/linux/xio/xio_server.h
@@ -0,0 +1,91 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_SERVER_H
+#define XIO_SERVER_H
+
+#include <linux/wait.h>
+
+#include <linux/xio/xio_net.h>
+#include <linux/brick/lib_limiter.h>
+
+#define NR_SERVER_SOCKETS		3
+
+extern int server_show_statist;
+
+extern struct rate_limiter server_limiter;
+
+struct server_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct server_brick *brick;
+	struct list_head cb_head;
+	void *data;
+	int len;
+	bool do_put;
+};
+
+struct server_output {
+	XIO_OUTPUT(server);
+};
+
+struct server_brick {
+	XIO_BRICK(server);
+	struct semaphore socket_sem;
+	struct xio_socket handler_socket;
+	struct xio_brick *conn_brick;
+	struct task_struct *handler_thread;
+	struct task_struct *cb_thread;
+
+	wait_queue_head_t startup_event;
+	wait_queue_head_t cb_event;
+	spinlock_t cb_lock;
+	struct list_head cb_read_list;
+	struct list_head cb_write_list;
+	atomic_t in_flight;
+	int version;
+	bool cb_running;
+	bool handler_running;
+};
+
+struct server_input {
+	XIO_INPUT(server);
+};
+
+XIO_TYPES(server);
+
+/* Internal interface to specific implementations.
+ * This is used for a rough separation of the strategy layer
+ * from the ordinary XIO layer.
+ * Currently, separation is at linker level.
+ * TODO: implement a dynamic separation later.
+ */
+
+/* Implemented separately, used by generic part */
+
+extern int server_thread(void *data);
+
+extern int handler_thread(void *data);
+
+extern int cb_thread(void *data);
+
+extern int server_io(struct server_brick *brick, struct xio_socket *sock, struct xio_cmd *cmd);
+
+/* Implemented by generic part, used by specific part */
+
+extern int server_switch(struct server_brick *brick);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299655 — [RFC 01/31] mars: add new module lamport

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 01/31] mars: add new module lamport
Message-ID<qLJTY-3U9-39@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/lamport.c | 61 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/brick/lamport.h  | 26 ++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 drivers/staging/mars/lamport.c
 create mode 100644 include/linux/brick/lamport.h

diff --git a/drivers/staging/mars/lamport.c b/drivers/staging/mars/lamport.c
new file mode 100644
index 0000000..373093f
--- /dev/null
+++ b/drivers/staging/mars/lamport.c
@@ -0,0 +1,61 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/semaphore.h>
+
+#include <linux/brick/lamport.h>
+
+/*  TODO: replace with spinlock if possible (first check) */
+struct semaphore lamport_sem = __SEMAPHORE_INITIALIZER(lamport_sem, 1);
+struct timespec lamport_now = {};
+
+void get_lamport(struct timespec *now)
+{
+	int diff;
+
+	down(&lamport_sem);
+
+	*now = CURRENT_TIME;
+	diff = timespec_compare(now, &lamport_now);
+	if (diff >= 0) {
+		timespec_add_ns(now, 1);
+		memcpy(&lamport_now, now, sizeof(lamport_now));
+		timespec_add_ns(&lamport_now, 1);
+	} else {
+		timespec_add_ns(&lamport_now, 1);
+		memcpy(now, &lamport_now, sizeof(*now));
+	}
+
+	up(&lamport_sem);
+}
+
+void set_lamport(struct timespec *old)
+{
+	int diff;
+
+	down(&lamport_sem);
+
+	diff = timespec_compare(old, &lamport_now);
+	if (diff >= 0) {
+		memcpy(&lamport_now, old, sizeof(lamport_now));
+		timespec_add_ns(&lamport_now, 1);
+	}
+
+	up(&lamport_sem);
+}
diff --git a/include/linux/brick/lamport.h b/include/linux/brick/lamport.h
new file mode 100644
index 0000000..9aac0ce
--- /dev/null
+++ b/include/linux/brick/lamport.h
@@ -0,0 +1,26 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 LAMPORT_H
+#define LAMPORT_H
+
+#include <linux/time.h>
+
+extern void get_lamport(struct timespec *now);
+extern void set_lamport(struct timespec *old);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299656 — [RFC 19/31] mars: add new module xio_client

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 19/31] mars: add new module xio_client
Message-ID<qLJTY-3U9-13@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_client.c | 1055 ++++++++++++++++++++++++++
 include/linux/xio/xio_client.h               |  105 +++
 2 files changed, 1160 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_client.c
 create mode 100644 include/linux/xio/xio_client.h

diff --git a/drivers/staging/mars/xio_bricks/xio_client.c b/drivers/staging/mars/xio_bricks/xio_client.c
new file mode 100644
index 0000000..6fdc261
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_client.c
@@ -0,0 +1,1055 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/string.h>
+#include <linux/jiffies.h>
+
+#include <linux/xio/xio.h>
+
+/************************ own type definitions ***********************/
+
+#include <linux/xio/xio_client.h>
+
+#define CLIENT_HASH_MAX			(PAGE_SIZE / sizeof(struct list_head))
+
+int xio_client_abort = 10;
+
+int max_client_channels = 1;
+
+int max_client_bulk = 16;
+
+/************************ own helper functions ***********************/
+
+static int thread_count;
+
+static
+void _do_resubmit(struct client_channel *ch)
+{
+	struct client_output *output = ch->output;
+	unsigned long flags;
+
+	spin_lock_irqsave(&output->lock, flags);
+	if (!list_empty(&ch->wait_list)) {
+		struct list_head *first = ch->wait_list.next;
+		struct list_head *last = ch->wait_list.prev;
+		struct list_head *old_start = output->aio_list.next;
+
+#define list_connect __list_del /*  the original routine has a misleading name: in reality it is more general */
+		list_connect(&output->aio_list, first);
+		list_connect(last, old_start);
+		INIT_LIST_HEAD(&ch->wait_list);
+	}
+	spin_unlock_irqrestore(&output->lock, flags);
+}
+
+static
+void _kill_thread(struct client_threadinfo *ti, const char *name)
+{
+	struct task_struct *thread = ti->thread;
+
+	if (thread) {
+		XIO_DBG("stopping %s thread\n", name);
+		ti->thread = NULL;
+		brick_thread_stop(thread);
+	}
+}
+
+static
+void _kill_channel(struct client_channel *ch)
+{
+	XIO_DBG("channel = %p\n", ch);
+	if (xio_socket_is_alive(&ch->socket)) {
+		XIO_DBG("shutdown socket\n");
+		xio_shutdown_socket(&ch->socket);
+	}
+	_kill_thread(&ch->receiver, "receiver");
+	if (ch->is_open) {
+		XIO_DBG("close socket\n");
+		xio_put_socket(&ch->socket);
+	}
+	ch->recv_error = 0;
+	ch->is_used = false;
+	ch->is_open = false;
+	ch->is_connected = false;
+	/* Re-Submit any waiting requests
+	 */
+	_do_resubmit(ch);
+}
+
+static inline
+void _kill_all_channels(struct client_bundle *bundle)
+{
+	int i;
+
+	/*  first pass: shutdown in parallel without waiting */
+	for (i = 0; i < MAX_CLIENT_CHANNELS; i++) {
+		struct client_channel *ch = &bundle->channel[i];
+
+		if (xio_socket_is_alive(&ch->socket)) {
+			XIO_DBG("shutdown socket %d\n", i);
+			xio_shutdown_socket(&ch->socket);
+		}
+	}
+	/*  separate pass (may wait) */
+	for (i = 0; i < MAX_CLIENT_CHANNELS; i++)
+		_kill_channel(&bundle->channel[i]);
+}
+
+static int receiver_thread(void *data);
+
+static
+int _setup_channel(struct client_bundle *bundle, int ch_nr)
+{
+	struct client_channel *ch = &bundle->channel[ch_nr];
+	struct sockaddr_storage src_sockaddr;
+	struct sockaddr_storage dst_sockaddr;
+	int status;
+
+	ch->ch_nr = ch_nr;
+	if (unlikely(ch->receiver.thread)) {
+		XIO_WRN("receiver thread %d unexpectedly not dead\n", ch_nr);
+		_kill_thread(&ch->receiver, "receiver");
+	}
+
+	status = xio_create_sockaddr(&src_sockaddr, my_id());
+	if (unlikely(status < 0)) {
+		XIO_DBG("no src sockaddr, status = %d\n", status);
+		goto done;
+	}
+
+	status = xio_create_sockaddr(&dst_sockaddr, bundle->host);
+	if (unlikely(status < 0)) {
+		XIO_DBG("no dst sockaddr, status = %d\n", status);
+		goto done;
+	}
+
+	status = xio_create_socket(&ch->socket, &src_sockaddr, &dst_sockaddr);
+	if (unlikely(status < 0)) {
+		XIO_DBG("no socket, status = %d\n", status);
+		goto really_done;
+	}
+	ch->socket.s_shutdown_on_err = true;
+	ch->socket.s_send_abort = xio_client_abort;
+	ch->socket.s_recv_abort = xio_client_abort;
+	ch->is_open = true;
+
+	ch->receiver.thread = brick_thread_create(receiver_thread,
+		ch,
+		"xio_receiver%d.%d.%d",
+		bundle->thread_count,
+		ch_nr,
+		ch->thread_count++);
+	if (unlikely(!ch->receiver.thread)) {
+		XIO_ERR("cannot start receiver thread %d, status = %d\n", ch_nr, status);
+		status = -ENOENT;
+		goto done;
+	}
+	ch->is_used = true;
+
+done:
+	if (status < 0) {
+		XIO_INF("cannot connect channel %d to remote host '%s' (status = %d) -- retrying\n",
+			 ch_nr,
+			 bundle->host ? bundle->host : "NULL",
+			 status);
+		_kill_channel(ch);
+	}
+
+really_done:
+	return status;
+}
+
+static
+void _kill_bundle(struct client_bundle *bundle)
+{
+	XIO_DBG("\n");
+	_kill_thread(&bundle->sender, "sender");
+	_kill_all_channels(bundle);
+}
+
+static
+void _maintain_bundle(struct client_bundle *bundle)
+{
+	int i;
+
+	/* Re-open _any_ failed channel, even old ones.
+	 * Reason: the number of channels might change during operation.
+	 */
+	for (i = 0; i < MAX_CLIENT_CHANNELS; i++) {
+		struct client_channel *ch = &bundle->channel[i];
+
+		if (!ch->is_used ||
+		    (!ch->recv_error && xio_socket_is_alive(&ch->socket)))
+			continue;
+
+		XIO_DBG("killing channel %d\n", i);
+		_kill_channel(ch);
+		/* Re-setup including connect optiona is done later.
+		 */
+	}
+}
+
+static
+struct client_channel *_get_channel(struct client_bundle *bundle, int min_channel, int max_channel)
+{
+	struct client_channel *res;
+	long best_space;
+	int best_channel;
+	int i;
+
+	if (unlikely(max_channel <= 0 || max_channel > MAX_CLIENT_CHANNELS))
+		max_channel = MAX_CLIENT_CHANNELS;
+	if (unlikely(min_channel < 0 || min_channel >= max_channel)) {
+		min_channel = max_channel - 1;
+		if (unlikely(min_channel < 0))
+			min_channel = 0;
+	}
+
+	/* Fast path.
+	 * Speculate that the next channel is already usable,
+	 * and that it has enough room.
+	 */
+	best_channel = bundle->old_channel + 1;
+	if (best_channel >= max_channel)
+		best_channel = min_channel;
+	res = &bundle->channel[best_channel];
+	if (res->is_connected && !res->recv_error && xio_socket_is_alive(&res->socket)) {
+		res->current_space = xio_socket_send_space_available(&res->socket);
+		if (res->current_space > (PAGE_SIZE + PAGE_SIZE / 4))
+			goto found;
+	}
+
+	/* Slow path. Do all the teady work.
+	 */
+	_maintain_bundle(bundle);
+
+	res = NULL;
+	best_space = -1;
+	best_channel = -1;
+	for (i = min_channel; i < max_channel; i++) {
+		struct client_channel *ch = &bundle->channel[i];
+		long this_space;
+
+		/*  create new channels when necessary */
+		if (unlikely(!ch->is_open)) {
+			int status;
+
+			/*  only create one new channel at a time */
+			status = _setup_channel(bundle, i);
+			XIO_DBG("setup channel %d status=%d\n", i, status);
+			if (unlikely(status < 0))
+				continue;
+
+			this_space = xio_socket_send_space_available(&ch->socket);
+			ch->current_space = this_space;
+			/* Always prefer the newly opened channel */
+			res = ch;
+			best_channel = i;
+			break;
+		}
+
+		/*  select the best usable channel */
+		this_space = xio_socket_send_space_available(&ch->socket);
+		ch->current_space = this_space;
+		if (this_space > best_space) {
+			best_space = this_space;
+			best_channel = i;
+			res = ch;
+		}
+	}
+
+	if (unlikely(!res)) {
+		XIO_WRN("cannot setup communication channel '%s' @%s\n",
+			 bundle->path,
+			 bundle->host);
+		goto done;
+	}
+
+	/*  send initial connect command */
+	if (unlikely(!res->is_connected)) {
+		struct xio_cmd cmd = {
+			.cmd_code = CMD_CONNECT,
+			.cmd_str1 = bundle->path,
+		};
+		int status = xio_send_struct(&res->socket, &cmd, xio_cmd_meta);
+
+		XIO_DBG("send CMD_CONNECT status = %d\n", status);
+		if (unlikely(status < 0)) {
+			XIO_WRN("connect '%s' @%s on channel %d failed, status = %d\n",
+				 bundle->path,
+				 bundle->host,
+				 best_channel,
+				 status);
+			_kill_channel(res);
+			res = NULL;
+			goto done;
+		}
+		res->is_connected = true;
+	}
+
+found:
+	bundle->old_channel = best_channel;
+
+done:
+	return res;
+}
+
+static
+int _request_info(struct client_channel *ch)
+{
+	struct xio_cmd cmd = {
+		.cmd_code = CMD_GETINFO,
+	};
+	int status;
+
+	XIO_DBG("\n");
+	status = xio_send_struct(&ch->socket, &cmd, xio_cmd_meta);
+	XIO_DBG("send CMD_GETINFO status = %d\n", status);
+	if (unlikely(status < 0))
+		XIO_DBG("send of getinfo failed, status = %d\n", status);
+	return status;
+}
+
+static int sender_thread(void *data);
+
+static
+int _setup_bundle(struct client_bundle *bundle, const char *str)
+{
+	int status = -ENOMEM;
+
+	XIO_DBG("\n");
+	_kill_bundle(bundle);
+	brick_string_free(bundle->path);
+
+	bundle->path = brick_strdup(str);
+
+	status = -EINVAL;
+	bundle->host = strchr(bundle->path, '@');
+	if (unlikely(!bundle->host)) {
+		brick_string_free(bundle->path);
+		bundle->path = NULL;
+		XIO_ERR("parameter string '%s' contains no remote specifier with '@'-syntax\n", str);
+		goto done;
+	}
+	*bundle->host++ = '\0';
+
+	bundle->thread_count = thread_count++;
+	bundle->sender.thread = brick_thread_create(sender_thread, bundle, "xio_sender%d", bundle->thread_count);
+	if (unlikely(!bundle->sender.thread)) {
+		XIO_ERR("cannot start sender thread for '%s' @%s\n",
+			 bundle->path,
+			 bundle->host);
+		status = -ENOENT;
+		goto done;
+	}
+
+	status = 0;
+
+done:
+	XIO_DBG("status = %d\n", status);
+	return status;
+}
+
+/***************** own brick * input * output operations *****************/
+
+static int client_get_info(struct client_output *output, struct xio_info *info)
+{
+	int status;
+
+	output->got_info = false;
+	output->get_info = true;
+	wake_up_interruptible_all(&output->bundle.sender_event);
+
+	wait_event_interruptible_timeout(output->info_event, output->got_info, 60 * HZ);
+	status = -ETIME;
+	if (output->got_info && info) {
+		memcpy(info, &output->info, sizeof(*info));
+		status = 0;
+	}
+
+	return status;
+}
+
+static int client_io_get(struct client_output *output, struct aio_object *aio)
+{
+	int maxlen;
+
+	if (aio->obj_initialized) {
+		obj_get(aio);
+		return aio->io_len;
+	}
+
+	/* Limit transfers to page boundaries.
+	 * Currently, this is more restrictive than necessary.
+	 * TODO: improve performance by doing better when possible.
+	 * This needs help from the server in some efficient way.
+	 */
+	maxlen = PAGE_SIZE - (aio->io_pos & (PAGE_SIZE-1));
+	if (aio->io_len > maxlen)
+		aio->io_len = maxlen;
+
+	if (!aio->io_data) { /*  buffered IO */
+		struct client_aio_aspect *aio_a = client_aio_get_aspect(output->brick, aio);
+
+		if (!aio_a)
+			return -EILSEQ;
+
+		aio->io_data = brick_block_alloc(aio->io_pos, (aio_a->alloc_len = aio->io_len));
+
+		aio_a->do_dealloc = true;
+		aio->io_flags = 0;
+	}
+
+	obj_get_first(aio);
+	return 0;
+}
+
+static void client_io_put(struct client_output *output, struct aio_object *aio)
+{
+	struct client_aio_aspect *aio_a;
+
+	if (!obj_put(aio))
+		goto out_return;
+	aio_a = client_aio_get_aspect(output->brick, aio);
+	if (aio_a && aio_a->do_dealloc)
+		brick_block_free(aio->io_data, aio_a->alloc_len);
+	obj_free(aio);
+out_return:;
+}
+
+static
+void _hash_insert(struct client_output *output, struct client_aio_aspect *aio_a)
+{
+	struct aio_object *aio = aio_a->object;
+	unsigned long flags;
+	int hash_index;
+
+	spin_lock_irqsave(&output->lock, flags);
+	list_del(&aio_a->io_head);
+	list_add_tail(&aio_a->io_head, &output->aio_list);
+	list_del(&aio_a->hash_head);
+	aio->io_id = ++output->last_id;
+	hash_index = aio->io_id % CLIENT_HASH_MAX;
+	list_add_tail(&aio_a->hash_head, &output->hash_table[hash_index]);
+	spin_unlock_irqrestore(&output->lock, flags);
+}
+
+static void client_io_io(struct client_output *output, struct aio_object *aio)
+{
+	struct client_aio_aspect *aio_a;
+	int error = -EINVAL;
+
+	aio_a = client_aio_get_aspect(output->brick, aio);
+	if (unlikely(!aio_a))
+		goto error;
+
+	while (output->brick->max_flying > 0 && atomic_read(&output->fly_count) > output->brick->max_flying)
+		brick_msleep(1000 * 2 / HZ);
+
+	if (!output->brick->power.on_led)
+		XIO_ERR("IO submission on dead instance\n");
+
+	atomic_inc(&xio_global_io_flying);
+	atomic_inc(&output->fly_count);
+	obj_get(aio);
+
+	aio_a->submit_jiffies = jiffies;
+	_hash_insert(output, aio_a);
+
+	wake_up_interruptible_all(&output->bundle.sender_event);
+
+	goto out_return;
+error:
+	XIO_ERR("IO error = %d\n", error);
+	SIMPLE_CALLBACK(aio, error);
+	client_io_put(output, aio);
+out_return:;
+}
+
+static
+int receiver_thread(void *data)
+{
+	struct client_channel *ch = data;
+	struct client_output *output = ch->output;
+	int status = 0;
+
+	while (!brick_thread_should_stop()) {
+		struct xio_cmd cmd = {};
+		struct list_head *tmp;
+		struct client_aio_aspect *aio_a = NULL;
+		struct aio_object *aio = NULL;
+		unsigned long flags;
+
+		if (ch->recv_error) {
+			/* The protocol may be out of sync.
+			 * Consume some data to avoid distributed deadlocks.
+			 */
+			(void)xio_recv_raw(&ch->socket, &cmd, 0, sizeof(cmd));
+			brick_msleep(100);
+			status = ch->recv_error;
+			continue;
+		}
+
+		status = xio_recv_struct(&ch->socket, &cmd, xio_cmd_meta);
+		if (status <= 0) {
+			if (!xio_socket_is_alive(&ch->socket)) {
+				XIO_DBG("socket is dead\n");
+				brick_msleep(1000);
+				continue;
+			}
+			goto done;
+		}
+
+		switch (cmd.cmd_code & CMD_FLAG_MASK) {
+		case CMD_NOTIFY:
+			local_trigger();
+			break;
+		case CMD_CONNECT:
+			if (cmd.cmd_int1 < 0) {
+				status = cmd.cmd_int1;
+				XIO_ERR("remote brick connect '%s' @%s failed, remote status = %d\n",
+					 output->bundle.path,
+					 output->bundle.host,
+					 status);
+				goto done;
+			}
+			break;
+		case CMD_CB:
+		{
+			int hash_index = cmd.cmd_int1 % CLIENT_HASH_MAX;
+
+			spin_lock_irqsave(&output->lock, flags);
+			for (tmp = output->hash_table[hash_index].next; tmp != &output->hash_table[hash_index]; tmp = tmp->next) {
+				struct aio_object *tmp_aio;
+
+				aio_a = container_of(tmp, struct client_aio_aspect, hash_head);
+				tmp_aio = aio_a->object;
+				CHECK_PTR(tmp_aio, err);
+				if (tmp_aio->io_id != cmd.cmd_int1)
+					continue;
+				aio = tmp_aio;
+				list_del_init(&aio_a->hash_head);
+				list_del_init(&aio_a->io_head);
+				break;
+
+err:
+				spin_unlock_irqrestore(&output->lock, flags);
+				status = -EBADR;
+				goto done;
+			}
+			spin_unlock_irqrestore(&output->lock, flags);
+
+			if (unlikely(!aio)) {
+				XIO_WRN("got unknown callback id %d on '%s' @%s\n",
+					 cmd.cmd_int1,
+					 output->bundle.path,
+					 output->bundle.host);
+				/*  try to consume the corresponding payload */
+				aio = client_alloc_aio(output->brick);
+				status = xio_recv_cb(&ch->socket, aio, &cmd);
+				obj_free(aio);
+				goto done;
+			}
+
+			status = xio_recv_cb(&ch->socket, aio, &cmd);
+			if (unlikely(status < 0)) {
+				XIO_WRN("interrupted data transfer during callback on '%s' @%s, status = %d\n",
+					 output->bundle.path,
+					 output->bundle.host,
+					 status);
+				_hash_insert(output, aio_a);
+				goto done;
+			}
+
+			if (aio->_object_cb.cb_error < 0)
+				XIO_DBG("ERROR %d\n", aio->_object_cb.cb_error);
+			SIMPLE_CALLBACK(aio, aio->_object_cb.cb_error);
+
+			client_io_put(output, aio);
+
+			atomic_dec(&output->fly_count);
+			atomic_dec(&xio_global_io_flying);
+			break;
+		}
+		case CMD_GETINFO:
+			status = xio_recv_struct(&ch->socket, &output->info, xio_info_meta);
+			if (status < 0) {
+				XIO_WRN("got bad info from remote '%s' @%s, status = %d\n",
+					 output->bundle.path,
+					 output->bundle.host,
+					 status);
+				goto done;
+			}
+			output->got_info = true;
+			wake_up_interruptible_all(&output->info_event);
+			break;
+		default:
+			XIO_ERR("got bad command %d from remote '%s' @%s, terminating.\n",
+				 cmd.cmd_code,
+				 output->bundle.path,
+				 output->bundle.host);
+			status = -EBADR;
+			goto done;
+		}
+done:
+		brick_string_free(cmd.cmd_str1);
+		if (unlikely(status < 0)) {
+			if (!ch->recv_error) {
+				XIO_DBG("signalling recv_error = %d\n", status);
+				ch->recv_error = status;
+			}
+			brick_msleep(100);
+		}
+		/*  wake up sender in any case */
+		wake_up_interruptible_all(&output->bundle.sender_event);
+	}
+
+	if (unlikely(status < 0)) {
+		XIO_WRN("receiver thread '%s' @%s terminated with status = %d\n",
+			 output->bundle.path,
+			 output->bundle.host,
+			 status);
+	}
+
+	xio_shutdown_socket(&ch->socket);
+	return status;
+}
+
+static
+void _do_timeout(struct client_output *output, struct list_head *anchor, int *rounds, bool force)
+{
+	struct client_brick *brick = output->brick;
+	struct list_head *tmp;
+	struct list_head *next;
+	LIST_HEAD(tmp_list);
+	long io_timeout = brick->power.io_timeout;
+	unsigned long flags;
+
+	if (list_empty(anchor))
+		goto out_return;
+	if (io_timeout <= 0)
+		io_timeout = global_net_io_timeout;
+
+	if (!xio_net_is_alive)
+		force = true;
+
+	if (!force && io_timeout <= 0)
+		goto out_return;
+	io_timeout *= HZ;
+
+	spin_lock_irqsave(&output->lock, flags);
+	for (tmp = anchor->next, next = tmp->next; tmp != anchor; tmp = next, next = tmp->next) {
+		struct client_aio_aspect *aio_a;
+
+		aio_a = container_of(tmp, struct client_aio_aspect, io_head);
+
+		if (!force &&
+		    !time_is_before_jiffies(aio_a->submit_jiffies + io_timeout)) {
+			continue;
+		}
+
+		list_del_init(&aio_a->hash_head);
+		list_del_init(&aio_a->io_head);
+		list_add_tail(&aio_a->tmp_head, &tmp_list);
+	}
+	spin_unlock_irqrestore(&output->lock, flags);
+
+	while (!list_empty(&tmp_list)) {
+		struct client_aio_aspect *aio_a;
+		struct aio_object *aio;
+
+		tmp = tmp_list.next;
+		list_del_init(tmp);
+		aio_a = container_of(tmp, struct client_aio_aspect, tmp_head);
+		aio = aio_a->object;
+
+		if (unlikely(!(*rounds)++)) {
+			XIO_WRN("'%s' @%s timeout after %ld: signalling IO error at pos = %lld len = %d\n",
+				 output->bundle.path,
+				 output->bundle.host,
+				 io_timeout,
+				 aio->io_pos,
+				 aio->io_len);
+		}
+
+		atomic_inc(&output->timeout_count);
+
+		SIMPLE_CALLBACK(aio, -ESTALE);
+
+		client_io_put(output, aio);
+
+		atomic_dec(&output->fly_count);
+		atomic_dec(&xio_global_io_flying);
+	}
+out_return:;
+}
+
+static
+void _do_timeout_all(struct client_output *output, bool force)
+{
+	int rounds = 0;
+	int i;
+
+	for (i = 0; i < MAX_CLIENT_CHANNELS; i++) {
+		struct client_channel *ch = &output->bundle.channel[i];
+
+		if (!ch->is_used)
+			continue;
+		_do_timeout(output, &ch->wait_list, &rounds, force);
+	}
+	_do_timeout(output, &output->aio_list, &rounds, force);
+	if (unlikely(rounds > 0)) {
+		XIO_WRN("'%s' @%s had %d timeouts, force = %d\n",
+			 output->bundle.path,
+			 output->bundle.host,
+			 rounds,
+			 force);
+	}
+}
+
+static int sender_thread(void *data)
+{
+	struct client_bundle *bundle = data;
+	struct client_output *output = container_of(bundle, struct client_output, bundle);
+	struct client_brick *brick = output->brick;
+	struct client_channel *ch = NULL;
+	bool do_timeout = false;
+	int ch_skip = max_client_bulk;
+	int status = -ESHUTDOWN;
+	unsigned long flags;
+
+	while (!brick_thread_should_stop()) {
+		struct list_head *tmp = NULL;
+		struct client_aio_aspect *aio_a;
+		struct aio_object *aio;
+		int min_nr;
+		int max_nr;
+
+		/*  timeouting is a rather expensive operation, don't do it too often */
+		if (do_timeout) {
+			do_timeout = false;
+			_maintain_bundle(&output->bundle);
+			_do_timeout_all(output, false);
+		}
+
+		wait_event_interruptible_timeout(output->bundle.sender_event,
+						 !list_empty(&output->aio_list) ||
+						 output->get_info,
+						 2 * HZ);
+
+		if (output->get_info) {
+			ch = _get_channel(bundle, 0, 1);
+			if (unlikely(!ch)) {
+				do_timeout = true;
+				brick_msleep(1000);
+				continue;
+			}
+			status = _request_info(ch);
+			if (unlikely(status < 0)) {
+				XIO_WRN("cannot send info request '%s' @%s, status = %d\n",
+					 output->bundle.path,
+					 output->bundle.host,
+					 status);
+				do_timeout = true;
+				brick_msleep(1000);
+				continue;
+			}
+			output->get_info = false;
+		}
+
+		/* Grab the next aio from the queue
+		 */
+		spin_lock_irqsave(&output->lock, flags);
+		tmp = output->aio_list.next;
+		if (tmp == &output->aio_list) {
+			spin_unlock_irqrestore(&output->lock, flags);
+			XIO_DBG("empty %d %d\n", output->get_info, brick_thread_should_stop());
+			do_timeout = true;
+			continue;
+		}
+		list_del_init(tmp);
+		/*  notice: hash_head remains in its list! */
+		spin_unlock_irqrestore(&output->lock, flags);
+
+		aio_a = container_of(tmp, struct client_aio_aspect, io_head);
+		aio = aio_a->object;
+
+		if (brick->limit_mode) {
+			int amount = 0;
+
+			if (aio->io_cs_mode < 2)
+				amount = (aio->io_len - 1) / 1024 + 1;
+			rate_limit_sleep(&client_limiter, amount);
+		}
+
+		/*  try to spread reads over multiple channels.... */
+		min_nr = 0;
+		max_nr = max_client_channels;
+		if (!aio->io_rw) {
+			/* optionally separate reads from writes */
+			if (brick->separate_reads && max_nr > 1)
+				min_nr = 1;
+		} else if (!brick->allow_permuting_writes) {
+			max_nr = 1;
+		}
+		if (!ch || ch->recv_error ||
+		    !xio_socket_is_alive(&ch->socket) ||
+		    ch->ch_nr >= max_nr || --ch_skip < 0) {
+			ch = _get_channel(bundle, min_nr, max_nr);
+			if (unlikely(!ch)) {
+				/*  notice: this will re-assign hash_head without harm */
+				_hash_insert(output, aio_a);
+				do_timeout = true;
+				brick_msleep(1000);
+				continue;
+			}
+			/* estimate: add some headroom for overhead */
+			ch_skip = ch->current_space / PAGE_SIZE +
+				ch->current_space / (PAGE_SIZE * 8);
+			if (ch_skip > max_client_bulk)
+				ch_skip = max_client_bulk;
+		}
+
+		spin_lock_irqsave(&output->lock, flags);
+		list_add(tmp, &ch->wait_list);
+		/*  notice: hash_head is already there! */
+		spin_unlock_irqrestore(&output->lock, flags);
+
+		status = xio_send_aio(&ch->socket, aio);
+		if (unlikely(status < 0)) {
+			_hash_insert(output, aio_a);
+			do_timeout = true;
+			ch = NULL;
+			/*  retry submission on next occasion.. */
+			XIO_WRN("aio send '%s' @%s failed, status = %d\n",
+				 output->bundle.path,
+				 output->bundle.host,
+				 status);
+
+			brick_msleep(100);
+			continue;
+		}
+	}
+
+	if (unlikely(status < 0)) {
+		XIO_WRN("sender thread '%s' @%s terminated with status = %d\n",
+			 output->bundle.path,
+			 output->bundle.host,
+			 status);
+	}
+
+	_kill_all_channels(bundle);
+
+	/* Signal error on all pending IO requests.
+	 * We have no other chance (except probably delaying
+	 * this until destruction which is probably not what
+	 * we want).
+	 */
+	_do_timeout_all(output, true);
+	wake_up_interruptible_all(&output->bundle.sender_event);
+	XIO_DBG("sender terminated\n");
+	return status;
+}
+
+static int client_switch(struct client_brick *brick)
+{
+	struct client_output *output = brick->outputs[0];
+	int status = 0;
+
+	if (brick->power.button) {
+		if (brick->power.on_led)
+			goto done;
+		xio_set_power_off_led((void *)brick, false);
+		status = _setup_bundle(&output->bundle, brick->brick_name);
+		if (likely(status >= 0)) {
+			output->get_info = true;
+			brick->connection_state = 1;
+			xio_set_power_on_led((void *)brick, true);
+		}
+	} else {
+		if (brick->power.off_led)
+			goto done;
+		xio_set_power_on_led((void *)brick, false);
+		_kill_bundle(&output->bundle);
+		_do_timeout_all(output, true);
+		output->got_info = false;
+		brick->connection_state = 0;
+		xio_set_power_off_led((void *)brick, !output->bundle.sender.thread);
+	}
+done:
+	return status;
+}
+
+/*************** informational * statistics **************/
+
+static
+char *client_statistics(struct client_brick *brick, int verbose)
+{
+	struct client_output *output = brick->outputs[0];
+	char *res = brick_string_alloc(1024);
+
+	snprintf(res, 1024,
+		 "max_flying = %d io_timeout = %d | timeout_count = %d fly_count = %d\n",
+		 brick->max_flying,
+		 brick->power.io_timeout,
+		 atomic_read(&output->timeout_count),
+		 atomic_read(&output->fly_count));
+
+	return res;
+}
+
+static
+void client_reset_statistics(struct client_brick *brick)
+{
+	struct client_output *output = brick->outputs[0];
+
+	atomic_set(&output->timeout_count, 0);
+}
+
+/*************** object * aspect constructors * destructors **************/
+
+static int client_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct client_aio_aspect *ini = (void *)_ini;
+
+	INIT_LIST_HEAD(&ini->io_head);
+	INIT_LIST_HEAD(&ini->hash_head);
+	INIT_LIST_HEAD(&ini->tmp_head);
+	return 0;
+}
+
+static void client_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct client_aio_aspect *ini = (void *)_ini;
+
+	CHECK_HEAD_EMPTY(&ini->io_head);
+	CHECK_HEAD_EMPTY(&ini->hash_head);
+}
+
+XIO_MAKE_STATICS(client);
+
+/********************* brick constructors * destructors *******************/
+
+static int client_brick_construct(struct client_brick *brick)
+{
+	return 0;
+}
+
+static int client_output_construct(struct client_output *output)
+{
+	int i;
+
+	output->hash_table = brick_block_alloc(0, PAGE_SIZE);
+
+	for (i = 0; i < CLIENT_HASH_MAX; i++)
+		INIT_LIST_HEAD(&output->hash_table[i]);
+
+	for (i = 0; i < MAX_CLIENT_CHANNELS; i++) {
+		struct client_channel *ch = &output->bundle.channel[i];
+
+		ch->output = output;
+		INIT_LIST_HEAD(&ch->wait_list);
+	}
+
+	init_waitqueue_head(&output->bundle.sender_event);
+
+	spin_lock_init(&output->lock);
+	INIT_LIST_HEAD(&output->aio_list);
+	init_waitqueue_head(&output->info_event);
+	return 0;
+}
+
+static int client_output_destruct(struct client_output *output)
+{
+	brick_string_free(output->bundle.path);
+	output->bundle.path = NULL;
+	brick_block_free(output->hash_table, PAGE_SIZE);
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct client_brick_ops client_brick_ops = {
+	.brick_switch = client_switch,
+	.brick_statistics = client_statistics,
+	.reset_statistics = client_reset_statistics,
+};
+
+static struct client_output_ops client_output_ops = {
+	.xio_get_info = client_get_info,
+	.aio_get = client_io_get,
+	.aio_put = client_io_put,
+	.aio_io = client_io_io,
+};
+
+const struct client_input_type client_input_type = {
+	.type_name = "client_input",
+	.input_size = sizeof(struct client_input),
+};
+
+static const struct client_input_type *client_input_types[] = {
+	&client_input_type,
+};
+
+const struct client_output_type client_output_type = {
+	.type_name = "client_output",
+	.output_size = sizeof(struct client_output),
+	.master_ops = &client_output_ops,
+	.output_construct = &client_output_construct,
+	.output_destruct = &client_output_destruct,
+};
+
+static const struct client_output_type *client_output_types[] = {
+	&client_output_type,
+};
+
+const struct client_brick_type client_brick_type = {
+	.type_name = "client_brick",
+	.brick_size = sizeof(struct client_brick),
+	.max_inputs = 0,
+	.max_outputs = 1,
+	.master_ops = &client_brick_ops,
+	.aspect_types = client_aspect_types,
+	.default_input_types = client_input_types,
+	.default_output_types = client_output_types,
+	.brick_construct = &client_brick_construct,
+};
+
+/***************** module init stuff ************************/
+
+struct rate_limiter client_limiter = {
+	.lim_max_rate = 0,
+};
+
+int global_net_io_timeout = 30;
+
+module_param_named(net_io_timeout, global_net_io_timeout, int, 0);
+
+int __init init_xio_client(void)
+{
+	XIO_INF("init_client()\n");
+	_client_brick_type = (void *)&client_brick_type;
+	return client_register_brick_type();
+}
+
+void exit_xio_client(void)
+{
+	XIO_INF("exit_client()\n");
+	client_unregister_brick_type();
+}
diff --git a/include/linux/xio/xio_client.h b/include/linux/xio/xio_client.h
new file mode 100644
index 0000000..5accb48
--- /dev/null
+++ b/include/linux/xio/xio_client.h
@@ -0,0 +1,105 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_CLIENT_H
+#define XIO_CLIENT_H
+
+#include <linux/xio/xio_net.h>
+#include <linux/brick/lib_limiter.h>
+
+extern struct rate_limiter client_limiter;
+extern int global_net_io_timeout;
+extern int xio_client_abort;
+extern int max_client_channels;
+extern int max_client_bulk;
+
+#define MAX_CLIENT_CHANNELS		4
+
+struct client_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct list_head io_head;
+	struct list_head hash_head;
+	struct list_head tmp_head;
+	unsigned long submit_jiffies;
+	int alloc_len;
+	bool do_dealloc;
+};
+
+struct client_brick {
+	XIO_BRICK(client);
+	/*  tunables */
+	int max_flying; /*  limit on parallelism */
+	bool limit_mode;
+	bool allow_permuting_writes;
+	bool separate_reads;
+
+	/*  readonly from outside */
+	int connection_state; /*  0 = switched off, 1 = not connected, 2 = connected */
+};
+
+struct client_input {
+	XIO_INPUT(client);
+};
+
+struct client_threadinfo {
+	struct task_struct *thread;
+};
+
+struct client_channel {
+	struct xio_socket socket;
+	struct client_threadinfo receiver;
+	struct list_head wait_list;
+	struct client_output *output;
+	long current_space;
+	int thread_count;
+	int recv_error;
+	int ch_nr;
+	bool is_used;
+	bool is_open;
+	bool is_connected;
+};
+
+struct client_bundle {
+	char *host;
+	char *path;
+	int thread_count;
+	int old_channel;
+
+	wait_queue_head_t sender_event;
+	struct client_threadinfo sender;
+	struct client_channel channel[MAX_CLIENT_CHANNELS];
+};
+
+struct client_output {
+	XIO_OUTPUT(client);
+	atomic_t fly_count;
+	atomic_t timeout_count;
+	spinlock_t lock;
+	struct list_head aio_list;
+	int  last_id;
+	struct client_bundle bundle;
+	struct xio_info info;
+
+	wait_queue_head_t info_event;
+	bool get_info;
+	bool got_info;
+	struct list_head *hash_table;
+};
+
+XIO_TYPES(client);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299657 — [RFC 11/31] mars: add new module lib_timing

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 11/31] mars: add new module lib_timing
Message-ID<qLJTY-3U9-31@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/lib/lib_timing.c |  71 +++++++++++++
 include/linux/brick/lib_timing.h      | 181 ++++++++++++++++++++++++++++++++++
 2 files changed, 252 insertions(+)
 create mode 100644 drivers/staging/mars/lib/lib_timing.c
 create mode 100644 include/linux/brick/lib_timing.h

diff --git a/drivers/staging/mars/lib/lib_timing.c b/drivers/staging/mars/lib/lib_timing.c
new file mode 100644
index 0000000..7421dc4
--- /dev/null
+++ b/drivers/staging/mars/lib/lib_timing.c
@@ -0,0 +1,71 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/brick/lib_timing.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_DEBUG_KERNEL
+
+int report_timing(struct timing_stats *tim, char *str, int maxlen)
+{
+	int len = 0;
+	int time = 1;
+	int resol = 1;
+
+	static const char * const units[] = {
+		"us",
+		"ms",
+		"s",
+		"ERROR"
+	};
+	const char *unit = units[0];
+	int unit_index = 0;
+	int i;
+
+	for (i = 0; i < TIMING_MAX; i++) {
+		int this_len = scnprintf(str,
+
+			maxlen,
+			"<%d%s = %d (%lld) ",
+			resol,
+			unit,
+			tim->tim_count[i],
+			(long long)tim->tim_count[i] * time);
+		str += this_len;
+		len += this_len;
+		maxlen -= this_len;
+		if (maxlen <= 1)
+			break;
+		resol <<= 1;
+		time <<= 1;
+		if (resol >= 1000) {
+			resol = 1;
+			unit = units[++unit_index];
+		}
+	}
+	return len;
+}
+
+#endif /*  CONFIG_DEBUG_KERNEL */
+
+struct threshold global_io_threshold = {
+	.thr_limit = 30 * 1000000, /*  30 seconds */
+	.thr_factor = 100,
+	.thr_plus = 0,
+};
diff --git a/include/linux/brick/lib_timing.h b/include/linux/brick/lib_timing.h
new file mode 100644
index 0000000..8a7a1e9
--- /dev/null
+++ b/include/linux/brick/lib_timing.h
@@ -0,0 +1,181 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 LIB_TIMING_H
+#define LIB_TIMING_H
+
+#include <linux/sched.h>
+
+/* Simple infrastructure for timing of arbitrary operations and creation
+ * of some simple histogram statistics.
+ */
+
+#define TIMING_MAX			24
+
+struct timing_stats {
+#ifdef CONFIG_DEBUG_KERNEL
+	int tim_count[TIMING_MAX];
+
+#endif
+};
+
+#define _TIME_THIS(_stamp1, _stamp2, _CODE)				\
+	({								\
+		(_stamp1) = cpu_clock(raw_smp_processor_id());		\
+									\
+		_CODE;							\
+									\
+		(_stamp2) = cpu_clock(raw_smp_processor_id());		\
+		(_stamp2) - (_stamp1);					\
+	})
+
+#define TIME_THIS(_CODE)						\
+	({								\
+		unsigned long long _stamp1;				\
+		unsigned long long _stamp2;				\
+		_TIME_THIS(_stamp1, _stamp2, _CODE);			\
+	})
+
+#ifdef CONFIG_DEBUG_KERNEL
+
+#define _TIME_STATS(_timing, _stamp1, _stamp2, _CODE)			\
+	({								\
+		unsigned long long _time;				\
+		unsigned long _tmp;					\
+		int _i;							\
+									\
+		_time = _TIME_THIS(_stamp1, _stamp2, _CODE);		\
+									\
+		_tmp = _time / 1000;	/* convert to us */		\
+		_i = 0;							\
+		while (_tmp > 0 && _i < TIMING_MAX - 1) {		\
+			_tmp >>= 1;					\
+			_i++;						\
+		}							\
+		(_timing)->tim_count[_i]++;				\
+		_time;							\
+	})
+
+#define TIME_STATS(_timing, _CODE)					\
+	({								\
+		unsigned long long _stamp1;				\
+		unsigned long long _stamp2;				\
+		_TIME_STATS(_timing, _stamp1, _stamp2, _CODE);		\
+	})
+
+extern int report_timing(struct timing_stats *tim, char *str, int maxlen);
+
+#else  /*  CONFIG_DEBUG_KERNEL */
+
+#define _TIME_STATS(_timing, _stamp1, _stamp2, _CODE)			\
+	((void)_timing, (_stamp1) = (_stamp2) = cpu_clock(raw_smp_processor_id()), _CODE, 0)
+
+#define TIME_STATS(_timing, _CODE)					\
+	((void)_timing, _CODE, 0)
+
+#define report_timing(tim, str, maxlen)   ((void)tim, 0)
+
+#endif /*  CONFIG_DEBUG_KERNEL */
+
+/* A banning represents some overloaded resource.
+ *
+ * Whenever overload is detected, you should call banning_hit()
+ * telling that the overload is assumed / estimated to continue
+ * for some duration in time.
+ *
+ * ATTENTION! These operations are deliberately raceful.
+ * They are meant to deliver _hints_ (e.g. for IO scheduling
+ * decisions etc), not hard facts!
+ *
+ * If you need locking, just surround these operations
+ * with locking by yourself.
+ */
+struct banning {
+	long long ban_last_hit;
+
+	/*  statistical */
+	int ban_renew_count;
+	int ban_count;
+};
+
+extern inline
+bool banning_hit(struct banning *ban, long long duration)
+{
+	long long now = cpu_clock(raw_smp_processor_id());
+	bool hit = ban->ban_last_hit >= now;
+	long long new_hit = now + duration;
+
+	ban->ban_renew_count++;
+	if (!ban->ban_last_hit || ban->ban_last_hit < new_hit) {
+		ban->ban_last_hit = new_hit;
+		ban->ban_count++;
+	}
+	return hit;
+}
+
+extern inline
+bool banning_is_hit(struct banning *ban)
+{
+	long long now = cpu_clock(raw_smp_processor_id());
+
+	return (ban->ban_last_hit && ban->ban_last_hit >= now);
+}
+
+extern inline
+void banning_reset(struct banning *ban)
+{
+	ban->ban_last_hit = 0;
+}
+
+/* Threshold: trigger a banning whenever some latency threshold
+ * is exceeded.
+ */
+struct threshold {
+	struct banning *thr_ban;
+
+	struct threshold *thr_parent; /* support hierarchies */
+	/*  tunables */
+	int  thr_limit;   /*  in us */
+	int  thr_factor;  /*  in % */
+	int  thr_plus;	  /*  in us */
+	/*  statistical */
+	int thr_max;	  /*  in ms */
+	int thr_triggered;
+	int thr_true_hit;
+};
+
+extern inline
+void threshold_check(struct threshold *thr, long long latency)
+{
+	int ms = latency >> 6; /*  ignore small rounding error */
+	while (thr) {
+		if (ms > thr->thr_max)
+			thr->thr_max = ms;
+		if (thr->thr_limit &&
+		    latency > (long long)thr->thr_limit * 1000) {
+			thr->thr_triggered++;
+			if (thr->thr_ban &&
+			    !banning_hit(thr->thr_ban, latency * thr->thr_factor / 100 + thr->thr_plus * 1000))
+				thr->thr_true_hit++;
+		}
+		thr = thr->thr_parent;
+	}
+}
+
+extern struct threshold global_io_threshold;
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299658 — [RFC 08/31] mars: add new module lib_queue

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 08/31] mars: add new module lib_queue
Message-ID<qLJTY-3U9-33@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 include/linux/brick/lib_queue.h | 166 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
 create mode 100644 include/linux/brick/lib_queue.h

diff --git a/include/linux/brick/lib_queue.h b/include/linux/brick/lib_queue.h
new file mode 100644
index 0000000..f1b1a9e
--- /dev/null
+++ b/include/linux/brick/lib_queue.h
@@ -0,0 +1,166 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 LIB_QUEUE_H
+#define LIB_QUEUE_H
+
+#define QUEUE_ANCHOR(PREFIX, KEYTYPE, HEAPTYPE)				\
+	/* parameters */						\
+	/* readonly from outside */					\
+	atomic_t q_queued;						\
+	atomic_t q_flying;						\
+	atomic_t q_total;						\
+	/* tunables */							\
+	int q_batchlen;							\
+	int q_io_prio;							\
+	bool q_ordering;						\
+	/* private */							\
+	wait_queue_head_t *q_event;					\
+	spinlock_t q_lock;						\
+	struct list_head q_anchor;					\
+	struct pairing_heap_##HEAPTYPE *heap_high;			\
+	struct pairing_heap_##HEAPTYPE *heap_low;			\
+	long long q_last_insert; /* jiffies */				\
+	KEYTYPE heap_margin;						\
+	KEYTYPE last_pos;						\
+	/* this comment is for keeping TRAILING_SEMICOLON happy */
+
+#define QUEUE_FUNCTIONS(PREFIX, ELEM_TYPE, HEAD, KEYFN, KEYCMP, HEAPTYPE)\
+									\
+static inline								\
+void q_##PREFIX##_trigger(struct PREFIX##_queue *q)			\
+{									\
+	if (q->q_event) {						\
+		wake_up_interruptible(q->q_event);			\
+	}								\
+}									\
+									\
+static inline								\
+void q_##PREFIX##_init(struct PREFIX##_queue *q)			\
+{									\
+	INIT_LIST_HEAD(&q->q_anchor);					\
+	q->heap_low = NULL;						\
+	q->heap_high = NULL;						\
+	spin_lock_init(&q->q_lock);					\
+	atomic_set(&q->q_queued, 0);					\
+	atomic_set(&q->q_flying, 0);					\
+}									\
+									\
+static inline								\
+void q_##PREFIX##_insert(struct PREFIX##_queue *q, ELEM_TYPE * elem)	\
+{									\
+	unsigned long flags;						\
+									\
+	spin_lock_irqsave(&q->q_lock, flags);				\
+									\
+	if (q->q_ordering) {						\
+		struct pairing_heap_##HEAPTYPE **use = &q->heap_high;	\
+		if (KEYCMP(KEYFN(elem), &q->heap_margin) <= 0) {	\
+			use = &q->heap_low;				\
+		}							\
+		ph_insert_##HEAPTYPE(use, &elem->ph);			\
+	} else {							\
+		list_add_tail(&elem->HEAD, &q->q_anchor);		\
+	}								\
+	atomic_inc(&q->q_queued);					\
+	atomic_inc(&q->q_total);					\
+	q->q_last_insert = jiffies;					\
+									\
+	spin_unlock_irqrestore(&q->q_lock, flags);			\
+									\
+	q_##PREFIX##_trigger(q);					\
+}									\
+									\
+static inline								\
+void q_##PREFIX##_pushback(struct PREFIX##_queue *q, ELEM_TYPE * elem)	\
+{									\
+	unsigned long flags;						\
+									\
+	if (q->q_ordering) {						\
+		atomic_dec(&q->q_total);				\
+		q_##PREFIX##_insert(q, elem);				\
+		return;							\
+	}								\
+									\
+	spin_lock_irqsave(&q->q_lock, flags);				\
+									\
+	list_add(&elem->HEAD, &q->q_anchor);				\
+	atomic_inc(&q->q_queued);					\
+									\
+	spin_unlock_irqrestore(&q->q_lock, flags);			\
+}									\
+									\
+static inline								\
+ELEM_TYPE *q_##PREFIX##_fetch(struct PREFIX##_queue *q)			\
+{									\
+	ELEM_TYPE *elem = NULL;						\
+	unsigned long flags;						\
+									\
+	spin_lock_irqsave(&q->q_lock, flags);				\
+									\
+	if (q->q_ordering) {						\
+		if (!q->heap_high) {					\
+			q->heap_high = q->heap_low;			\
+			q->heap_low = NULL;				\
+			q->heap_margin = 0;				\
+			q->last_pos = 0;				\
+		}							\
+		if (q->heap_high) {					\
+			elem = container_of(q->heap_high, ELEM_TYPE, ph);\
+									\
+			if (unlikely(KEYCMP(KEYFN(elem), &q->last_pos) < 0)) {\
+				printk(KERN_ERR "backskip pos %lld -> %lld\n",\
+					(long long)q->last_pos, (long long)KEYFN(elem));\
+			}						\
+			memcpy(&q->last_pos, KEYFN(elem), sizeof(q->last_pos));\
+									\
+			if (KEYCMP(KEYFN(elem), &q->heap_margin) > 0) { \
+				memcpy(&q->heap_margin, KEYFN(elem), sizeof(q->heap_margin));\
+			}						\
+			ph_delete_min_##HEAPTYPE(&q->heap_high);	\
+			atomic_dec(&q->q_queued);			\
+		}							\
+	} else if (!list_empty(&q->q_anchor)) {				\
+		struct list_head *next = q->q_anchor.next;		\
+		list_del_init(next);					\
+		atomic_dec(&q->q_queued);				\
+		elem = container_of(next, ELEM_TYPE, HEAD);		\
+	}								\
+									\
+	spin_unlock_irqrestore(&q->q_lock, flags);			\
+									\
+	q_##PREFIX##_trigger(q);					\
+									\
+	return elem;							\
+}									\
+									\
+static inline								\
+void q_##PREFIX##_inc_flying(struct PREFIX##_queue *q)			\
+{									\
+	atomic_inc(&q->q_flying);					\
+	q_##PREFIX##_trigger(q);					\
+}									\
+									\
+static inline								\
+void q_##PREFIX##_dec_flying(struct PREFIX##_queue *q)			\
+{									\
+	atomic_dec(&q->q_flying);					\
+	q_##PREFIX##_trigger(q);					\
+}									\
+									\
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299660 — [RFC 18/31] mars: add new module xio_sio

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 18/31] mars: add new module xio_sio
Message-ID<qLJTY-3U9-35@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/xio_sio.c | 571 ++++++++++++++++++++++++++++++
 include/linux/xio/xio_sio.h               |  68 ++++
 2 files changed, 639 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/xio_sio.c
 create mode 100644 include/linux/xio/xio_sio.h

diff --git a/drivers/staging/mars/xio_bricks/xio_sio.c b/drivers/staging/mars/xio_bricks/xio_sio.c
new file mode 100644
index 0000000..5822847
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/xio_sio.c
@@ -0,0 +1,571 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/version.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <linux/xio/xio.h>
+
+/************************ own type definitions ***********************/
+
+#include <linux/xio/xio_sio.h>
+
+/***************** own brick * input * output operations *****************/
+
+static int sio_io_get(struct sio_output *output, struct aio_object *aio)
+{
+	struct file *file;
+
+	if (unlikely(!output->brick->power.on_led))
+		return -EBADFD;
+
+	if (aio->obj_initialized) {
+		obj_get(aio);
+		return aio->io_len;
+	}
+
+	file = output->mf->mf_filp;
+	if (file) {
+		loff_t total_size = i_size_read(file->f_mapping->host);
+
+		aio->io_total_size = total_size;
+		/* Only check reads.
+		 * Writes behind EOF are always allowed (sparse files)
+		 */
+		if (!aio->io_may_write) {
+			loff_t len = total_size - aio->io_pos;
+
+			if (unlikely(len <= 0)) {
+				/* Special case: allow reads starting _exactly_ at EOF when a timeout is specified.
+				 */
+				if (len < 0 || aio->io_timeout <= 0) {
+					XIO_DBG("ENODATA %lld\n", len);
+					return -ENODATA;
+				}
+			}
+			/*  Shorten below EOF, but allow special case */
+			if (aio->io_len > len && len > 0)
+				aio->io_len = len;
+		}
+	}
+
+	/* Buffered IO.
+	 */
+	if (!aio->io_data) {
+		struct sio_aio_aspect *aio_a = sio_aio_get_aspect(output->brick, aio);
+
+		if (unlikely(!aio_a))
+			return -EILSEQ;
+		if (unlikely(aio->io_len <= 0)) {
+			XIO_ERR("bad io_len = %d\n", aio->io_len);
+			return -ENOMEM;
+		}
+		aio->io_data = brick_block_alloc(aio->io_pos, (aio_a->alloc_len = aio->io_len));
+		aio_a->do_dealloc = true;
+		/* atomic_inc(&output->total_alloc_count); */
+		/* atomic_inc(&output->alloc_count); */
+	}
+
+	obj_get_first(aio);
+	return aio->io_len;
+}
+
+static void sio_io_put(struct sio_output *output, struct aio_object *aio)
+{
+	struct file *file;
+	struct sio_aio_aspect *aio_a;
+
+	if (!obj_put(aio))
+		goto out_return;
+	file = output->mf->mf_filp;
+	aio->io_total_size = i_size_read(file->f_mapping->host);
+
+	aio_a = sio_aio_get_aspect(output->brick, aio);
+	if (aio_a && aio_a->do_dealloc) {
+		brick_block_free(aio->io_data, aio_a->alloc_len);
+		/* atomic_dec(&output->alloc_count); */
+	}
+
+	obj_free(aio);
+out_return:;
+}
+
+static
+int write_aops(struct sio_output *output, struct aio_object *aio)
+{
+	struct file *file = output->mf->mf_filp;
+	loff_t pos = aio->io_pos;
+	void *data = aio->io_data;
+	int  len = aio->io_len;
+	int ret = 0;
+
+	mm_segment_t oldfs;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	ret = vfs_write(file, data, len, &pos);
+	set_fs(oldfs);
+	return ret;
+}
+
+static
+int read_aops(struct sio_output *output, struct aio_object *aio)
+{
+	loff_t pos = aio->io_pos;
+	int len = aio->io_len;
+	int ret;
+
+	mm_segment_t oldfs;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	ret = vfs_read(output->mf->mf_filp, aio->io_data, len, &pos);
+	set_fs(oldfs);
+
+	if (unlikely(ret < 0))
+		XIO_ERR("%p %p status=%d\n", output, aio, ret);
+	return ret;
+}
+
+static void sync_file(struct sio_output *output)
+{
+	struct file *file = output->mf->mf_filp;
+	int ret;
+
+#if defined(S_BIAS) || (defined(RHEL_MAJOR) && (RHEL_MAJOR < 7))
+	ret = vfs_fsync(file, file->f_path.dentry, 1);
+#else
+	ret = vfs_fsync(file, 1);
+#endif
+	if (unlikely(ret))
+		XIO_ERR("syncing pages failed: %d\n", ret);
+	goto out_return;
+out_return:;
+}
+
+static
+void _complete(struct sio_output *output, struct aio_object *aio, int err)
+{
+	obj_check(aio);
+
+	if (err < 0) {
+		XIO_ERR("IO error %d at pos=%lld len=%d (aio=%p io_data=%p)\n",
+			err,
+			aio->io_pos,
+			aio->io_len,
+			aio,
+			aio->io_data);
+	} else {
+		aio_checksum(aio);
+		aio->io_flags |= AIO_UPTODATE;
+	}
+
+	CHECKED_CALLBACK(aio, err, err_found);
+
+done:
+	sio_io_put(output, aio);
+
+	atomic_dec(&output->work_count);
+	atomic_dec(&xio_global_io_flying);
+	goto out_return;
+err_found:
+	XIO_FAT("giving up...\n");
+	goto done;
+out_return:;
+}
+
+/* This is called by the threads
+ */
+static
+void _sio_io_io(struct sio_threadinfo *tinfo, struct aio_object *aio)
+{
+	struct sio_output *output = tinfo->output;
+	bool barrier = false;
+	int status;
+
+	obj_check(aio);
+
+	atomic_inc(&tinfo->fly_count);
+
+	if (unlikely(!output->mf || !output->mf->mf_filp)) {
+		status = -EINVAL;
+		goto done;
+	}
+
+	if (barrier) {
+		XIO_INF("got barrier request\n");
+		sync_file(output);
+	}
+
+	if (aio->io_rw == READ) {
+		status = read_aops(output, aio);
+	} else {
+		status = write_aops(output, aio);
+		if (barrier || output->brick->o_fdsync)
+			sync_file(output);
+	}
+
+	mapfree_set(output->mf, aio->io_pos, aio->io_pos + aio->io_len);
+
+done:
+	_complete(output, aio, status);
+
+	atomic_dec(&tinfo->fly_count);
+}
+
+/* This is called from outside
+ */
+static
+void sio_io_io(struct sio_output *output, struct aio_object *aio)
+{
+	int index;
+	struct sio_threadinfo *tinfo;
+	struct sio_aio_aspect *aio_a;
+	unsigned long flags;
+
+	obj_check(aio);
+
+	aio_a = sio_aio_get_aspect(output->brick, aio);
+	if (unlikely(!aio_a)) {
+		XIO_FAT("cannot get aspect\n");
+		SIMPLE_CALLBACK(aio, -EINVAL);
+		goto out_return;
+	}
+
+	if (unlikely(!output->brick->power.on_led)) {
+		SIMPLE_CALLBACK(aio, -EBADFD);
+		goto out_return;
+	}
+
+	atomic_inc(&xio_global_io_flying);
+	atomic_inc(&output->work_count);
+	obj_get(aio);
+
+	mapfree_set(output->mf, aio->io_pos, -1);
+
+	index = 0;
+	if (aio->io_rw == READ) {
+		spin_lock_irqsave(&output->g_lock, flags);
+		index = output->index++;
+		spin_unlock_irqrestore(&output->g_lock, flags);
+		index = (index % WITH_THREAD) + 1;
+	}
+
+	tinfo = &output->tinfo[index];
+
+	atomic_inc(&tinfo->total_count);
+	atomic_inc(&tinfo->queue_count);
+
+	spin_lock_irqsave(&tinfo->lock, flags);
+	list_add_tail(&aio_a->io_head, &tinfo->aio_list);
+	spin_unlock_irqrestore(&tinfo->lock, flags);
+
+	wake_up_interruptible(&tinfo->event);
+out_return:;
+}
+
+static int sio_thread(void *data)
+{
+	struct sio_threadinfo *tinfo = data;
+
+	XIO_INF("sio thread has started.\n");
+	/* set_user_nice(current, -20); */
+
+	while (!brick_thread_should_stop()) {
+		struct list_head *tmp = NULL;
+		struct aio_object *aio;
+		struct sio_aio_aspect *aio_a;
+		unsigned long flags;
+
+		wait_event_interruptible_timeout(
+			tinfo->event,
+			!list_empty(&tinfo->aio_list) || brick_thread_should_stop(),
+			HZ);
+
+		tinfo->last_jiffies = jiffies;
+
+		spin_lock_irqsave(&tinfo->lock, flags);
+
+		if (!list_empty(&tinfo->aio_list)) {
+			tmp = tinfo->aio_list.next;
+			list_del_init(tmp);
+			atomic_dec(&tinfo->queue_count);
+		}
+
+		spin_unlock_irqrestore(&tinfo->lock, flags);
+
+		if (!tmp)
+			continue;
+
+		aio_a = container_of(tmp, struct sio_aio_aspect, io_head);
+		aio = aio_a->object;
+		_sio_io_io(tinfo, aio);
+	}
+
+	XIO_INF("sio thread has stopped.\n");
+	return 0;
+}
+
+static int sio_get_info(struct sio_output *output, struct xio_info *info)
+{
+	struct file *file = output->mf->mf_filp;
+
+	if (unlikely(!file || !file->f_mapping || !file->f_mapping->host))
+		return -EINVAL;
+
+	info->tf_align = 1;
+	info->tf_min_size = 1;
+	info->current_size = i_size_read(file->f_mapping->host);
+	XIO_DBG("determined file size = %lld\n", info->current_size);
+	return 0;
+}
+
+/*************** informational * statistics **************/
+
+static noinline
+char *sio_statistics(struct sio_brick *brick, int verbose)
+{
+	struct sio_output *output = brick->outputs[0];
+	char *res = brick_string_alloc(1024);
+	int queue_sum = 0;
+	int fly_sum = 0;
+	int total_sum = 0;
+	int i;
+
+	for (i = 1; i <= WITH_THREAD; i++) {
+		struct sio_threadinfo *tinfo = &output->tinfo[i];
+
+		queue_sum += atomic_read(&tinfo->queue_count);
+		fly_sum += atomic_read(&tinfo->fly_count);
+		total_sum += atomic_read(&tinfo->total_count);
+	}
+
+	snprintf(res, 1024,
+		 "queued read = %d write = %d flying read = %d write = %d total  read = %d write = %d\n",
+		 queue_sum, atomic_read(&output->tinfo[0].queue_count),
+		 fly_sum,   atomic_read(&output->tinfo[0].fly_count),
+		 total_sum, atomic_read(&output->tinfo[0].total_count)
+		);
+	return res;
+}
+
+static noinline
+void sio_reset_statistics(struct sio_brick *brick)
+{
+	struct sio_output *output = brick->outputs[0];
+	int i;
+
+	for (i = 0; i <= WITH_THREAD; i++) {
+		struct sio_threadinfo *tinfo = &output->tinfo[i];
+
+		atomic_set(&tinfo->total_count, 0);
+	}
+}
+
+/*************** object * aspect constructors * destructors **************/
+
+static int sio_aio_aspect_init_fn(struct generic_aspect *_ini)
+{
+	struct sio_aio_aspect *ini = (void *)_ini;
+
+	INIT_LIST_HEAD(&ini->io_head);
+	return 0;
+}
+
+static void sio_aio_aspect_exit_fn(struct generic_aspect *_ini)
+{
+	struct sio_aio_aspect *ini = (void *)_ini;
+
+	(void)ini;
+	CHECK_HEAD_EMPTY(&ini->io_head);
+}
+
+XIO_MAKE_STATICS(sio);
+
+/********************* brick constructors * destructors *******************/
+
+static int sio_brick_construct(struct sio_brick *brick)
+{
+	return 0;
+}
+
+static int sio_switch(struct sio_brick *brick)
+{
+	static int sio_nr;
+	struct sio_output *output = brick->outputs[0];
+	const char *path = output->brick->brick_path;
+	int status = 0;
+
+	if (brick->power.button) {
+		int flags = O_CREAT | O_RDWR | O_LARGEFILE;
+		int index;
+
+		if (brick->power.on_led)
+			goto done;
+
+		if (brick->o_direct) {
+			flags |= O_DIRECT;
+			XIO_INF("using O_DIRECT on %s\n", path);
+		}
+
+		xio_set_power_off_led((void *)brick, false);
+
+		output->mf = mapfree_get(path, flags);
+		if (unlikely(IS_ERR(output->mf))) {
+			XIO_ERR("could not open file = '%s' flags = %d\n", path, flags);
+			status = -ENOENT;
+			goto done;
+		}
+
+		output->index = 0;
+		for (index = 0; index <= WITH_THREAD; index++) {
+			struct sio_threadinfo *tinfo = &output->tinfo[index];
+
+			tinfo->last_jiffies = jiffies;
+			tinfo->thread = brick_thread_create(sio_thread, tinfo, "xio_sio%d", sio_nr++);
+			if (unlikely(!tinfo->thread)) {
+				XIO_ERR("cannot create thread\n");
+				status = -ENOENT;
+				goto done;
+			}
+		}
+		xio_set_power_on_led((void *)brick, true);
+	}
+done:
+	if (unlikely(status < 0) || !brick->power.button) {
+		int index;
+		int count;
+
+		xio_set_power_on_led((void *)brick, false);
+		for (;;) {
+			count = atomic_read(&output->work_count);
+			if (count <= 0)
+				break;
+			XIO_DBG("working on %d requests\n", count);
+			brick_msleep(1000);
+		}
+		for (index = 0; index <= WITH_THREAD; index++) {
+			struct sio_threadinfo *tinfo = &output->tinfo[index];
+
+			if (!tinfo->thread)
+				continue;
+			XIO_DBG("stopping thread %d\n", index);
+			brick_thread_stop(tinfo->thread);
+			tinfo->thread = NULL;
+		}
+		if (output->mf) {
+			XIO_DBG("closing file\n");
+			mapfree_put(output->mf);
+			output->mf = NULL;
+		}
+		xio_set_power_off_led((void *)brick, true);
+	}
+	return status;
+}
+
+static int sio_output_construct(struct sio_output *output)
+{
+	int index;
+
+	spin_lock_init(&output->g_lock);
+	for (index = 0; index <= WITH_THREAD; index++) {
+		struct sio_threadinfo *tinfo = &output->tinfo[index];
+
+		tinfo->output = output;
+		spin_lock_init(&tinfo->lock);
+		init_waitqueue_head(&tinfo->event);
+		INIT_LIST_HEAD(&tinfo->aio_list);
+	}
+
+	return 0;
+}
+
+static int sio_output_destruct(struct sio_output *output)
+{
+	return 0;
+}
+
+/************************ static structs ***********************/
+
+static struct sio_brick_ops sio_brick_ops = {
+	.brick_switch = sio_switch,
+	.brick_statistics = sio_statistics,
+	.reset_statistics = sio_reset_statistics,
+};
+
+static struct sio_output_ops sio_output_ops = {
+	.aio_get = sio_io_get,
+	.aio_put = sio_io_put,
+	.aio_io = sio_io_io,
+	.xio_get_info = sio_get_info,
+};
+
+const struct sio_input_type sio_input_type = {
+	.type_name = "sio_input",
+	.input_size = sizeof(struct sio_input),
+};
+
+static const struct sio_input_type *sio_input_types[] = {
+	&sio_input_type,
+};
+
+const struct sio_output_type sio_output_type = {
+	.type_name = "sio_output",
+	.output_size = sizeof(struct sio_output),
+	.master_ops = &sio_output_ops,
+	.output_construct = &sio_output_construct,
+	.output_destruct = &sio_output_destruct,
+};
+
+static const struct sio_output_type *sio_output_types[] = {
+	&sio_output_type,
+};
+
+const struct sio_brick_type sio_brick_type = {
+	.type_name = "sio_brick",
+	.brick_size = sizeof(struct sio_brick),
+	.max_inputs = 0,
+	.max_outputs = 1,
+	.master_ops = &sio_brick_ops,
+	.aspect_types = sio_aspect_types,
+	.default_input_types = sio_input_types,
+	.default_output_types = sio_output_types,
+	.brick_construct = &sio_brick_construct,
+};
+
+/***************** module init stuff ************************/
+
+int __init init_xio_sio(void)
+{
+	XIO_INF("init_sio()\n");
+	_sio_brick_type = (void *)&sio_brick_type;
+	return sio_register_brick_type();
+}
+
+void exit_xio_sio(void)
+{
+	XIO_INF("exit_sio()\n");
+	sio_unregister_brick_type();
+}
diff --git a/include/linux/xio/xio_sio.h b/include/linux/xio/xio_sio.h
new file mode 100644
index 0000000..170733f
--- /dev/null
+++ b/include/linux/xio/xio_sio.h
@@ -0,0 +1,68 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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 XIO_SIO_H
+#define XIO_SIO_H
+
+#include <linux/xio/lib_mapfree.h>
+
+#define WITH_THREAD			16
+
+struct sio_aio_aspect {
+	GENERIC_ASPECT(aio);
+	struct list_head io_head;
+	int alloc_len;
+	bool do_dealloc;
+};
+
+struct sio_brick {
+	XIO_BRICK(sio);
+	/*  parameters */
+	bool o_direct;
+	bool o_fdsync;
+};
+
+struct sio_input {
+	XIO_INPUT(sio);
+};
+
+struct sio_threadinfo {
+	struct sio_output *output;
+	struct list_head aio_list;
+	struct task_struct *thread;
+
+	wait_queue_head_t event;
+	spinlock_t lock;
+	atomic_t queue_count;
+	atomic_t fly_count;
+	atomic_t total_count;
+	unsigned long last_jiffies;
+};
+
+struct sio_output {
+	XIO_OUTPUT(sio);
+	/*  private */
+	struct mapfree_info *mf;
+	struct sio_threadinfo tinfo[WITH_THREAD+1];
+	spinlock_t g_lock;
+	atomic_t work_count;
+	int index;
+};
+
+XIO_TYPES(sio);
+
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299661 — [RFC 16/31] mars: add new module lib_log

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 16/31] mars: add new module lib_log
Message-ID<qLJTZ-3U9-41@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 drivers/staging/mars/xio_bricks/lib_log.c | 505 ++++++++++++++++++++++++++++++
 include/linux/xio/lib_log.h               | 329 +++++++++++++++++++
 2 files changed, 834 insertions(+)
 create mode 100644 drivers/staging/mars/xio_bricks/lib_log.c
 create mode 100644 include/linux/xio/lib_log.h

diff --git a/drivers/staging/mars/xio_bricks/lib_log.c b/drivers/staging/mars/xio_bricks/lib_log.c
new file mode 100644
index 0000000..a8382e5
--- /dev/null
+++ b/drivers/staging/mars/xio_bricks/lib_log.c
@@ -0,0 +1,505 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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/bio.h>
+
+#include <linux/xio/lib_log.h>
+
+atomic_t global_aio_flying = ATOMIC_INIT(0);
+
+void exit_logst(struct log_status *logst)
+{
+	int count;
+
+	log_flush(logst);
+
+	/*  TODO: replace by event */
+	count = 0;
+	while (atomic_read(&logst->aio_flying) > 0) {
+		if (!count++)
+			XIO_DBG("waiting for IO terminating...");
+		brick_msleep(500);
+	}
+	if (logst->read_aio) {
+		XIO_DBG("putting read_aio\n");
+		GENERIC_INPUT_CALL(logst->input, aio_put, logst->read_aio);
+		logst->read_aio = NULL;
+	}
+	if (logst->log_aio) {
+		XIO_DBG("putting log_aio\n");
+		GENERIC_INPUT_CALL(logst->input, aio_put, logst->log_aio);
+		logst->log_aio = NULL;
+	}
+}
+
+void init_logst(struct log_status *logst, struct xio_input *input, loff_t start_pos, loff_t end_pos)
+{
+	exit_logst(logst);
+
+	memset(logst, 0, sizeof(struct log_status));
+
+	logst->input = input;
+	logst->brick = input->brick;
+	logst->start_pos = start_pos;
+	logst->log_pos = start_pos;
+	logst->end_pos = end_pos;
+	init_waitqueue_head(&logst->event);
+}
+
+#define XIO_LOG_CB_MAX			32
+
+struct log_cb_info {
+	struct aio_object *aio;
+	struct log_status *logst;
+	struct semaphore mutex;
+	atomic_t refcount;
+	int nr_cb;
+	void (*endios[XIO_LOG_CB_MAX])(void *private, int error);
+	void *privates[XIO_LOG_CB_MAX];
+};
+
+static
+void put_log_cb_info(struct log_cb_info *cb_info)
+{
+	if (atomic_dec_and_test(&cb_info->refcount))
+		brick_mem_free(cb_info);
+}
+
+static
+void _do_callbacks(struct log_cb_info *cb_info, int error)
+{
+	int i;
+
+	down(&cb_info->mutex);
+	for (i = 0; i < cb_info->nr_cb; i++) {
+		void (*end_fn)(void *private, int error);
+
+		end_fn = cb_info->endios[i];
+		cb_info->endios[i] = NULL;
+		if (end_fn)
+			end_fn(cb_info->privates[i], error);
+	}
+	up(&cb_info->mutex);
+}
+
+static
+void log_write_endio(struct generic_callback *cb)
+{
+	struct log_cb_info *cb_info = cb->cb_private;
+	struct log_status *logst;
+
+	LAST_CALLBACK(cb);
+	CHECK_PTR(cb_info, err);
+
+	logst = cb_info->logst;
+	CHECK_PTR(logst, done);
+
+	_do_callbacks(cb_info, cb->cb_error);
+
+done:
+	put_log_cb_info(cb_info);
+	atomic_dec(&logst->aio_flying);
+	atomic_dec(&global_aio_flying);
+	if (logst->signal_event)
+		wake_up_interruptible(logst->signal_event);
+
+	goto out_return;
+err:
+	XIO_FAT("internal pointer corruption\n");
+out_return:;
+}
+
+void log_flush(struct log_status *logst)
+{
+	struct aio_object *aio = logst->log_aio;
+	struct log_cb_info *cb_info;
+	int align_size;
+	int gap;
+
+	if (!aio || !logst->count)
+		goto out_return;
+	gap = 0;
+	align_size = (logst->align_size / PAGE_SIZE) * PAGE_SIZE;
+	if (align_size > 0) {
+		/*  round up to next alignment border */
+		int align_offset = logst->offset & (align_size-1);
+
+		if (align_offset > 0) {
+			int restlen = aio->io_len - logst->offset;
+
+			gap = align_size - align_offset;
+			if (unlikely(gap > restlen))
+				gap = restlen;
+		}
+	}
+	if (gap > 0) {
+		/*  don't leak information from kernelspace */
+		memset(aio->io_data + logst->offset, 0, gap);
+		logst->offset += gap;
+	}
+	aio->io_len = logst->offset;
+	memcpy(&logst->log_pos_stamp, &logst->tmp_pos_stamp, sizeof(logst->log_pos_stamp));
+
+	cb_info = logst->private;
+	logst->private = NULL;
+	SETUP_CALLBACK(aio, log_write_endio, cb_info);
+	cb_info->logst = logst;
+	aio->io_rw = 1;
+
+	atomic_inc(&logst->aio_flying);
+	atomic_inc(&global_aio_flying);
+
+	GENERIC_INPUT_CALL(logst->input, aio_io, aio);
+	GENERIC_INPUT_CALL(logst->input, aio_put, aio);
+
+	logst->log_pos += logst->offset;
+	logst->offset = 0;
+	logst->count = 0;
+	logst->log_aio = NULL;
+
+	put_log_cb_info(cb_info);
+out_return:;
+}
+
+void *log_reserve(struct log_status *logst, struct log_header *lh)
+{
+	struct log_cb_info *cb_info = logst->private;
+	struct aio_object *aio;
+	void *data;
+
+	short total_len = lh->l_len + OVERHEAD;
+	int offset;
+	int status;
+
+	if (unlikely(lh->l_len <= 0 || lh->l_len > logst->max_size)) {
+		XIO_ERR("trying to write %d bytes, max allowed = %d\n", lh->l_len, logst->max_size);
+		goto err;
+	}
+
+	aio = logst->log_aio;
+	if ((aio && total_len > aio->io_len - logst->offset)
+	   || !cb_info || cb_info->nr_cb >= XIO_LOG_CB_MAX) {
+		log_flush(logst);
+	}
+
+	aio = logst->log_aio;
+	if (!aio) {
+		if (unlikely(logst->private)) {
+			XIO_ERR("oops\n");
+			brick_mem_free(logst->private);
+		}
+		logst->private = brick_zmem_alloc(sizeof(struct log_cb_info));
+		cb_info = logst->private;
+		sema_init(&cb_info->mutex, 1);
+		atomic_set(&cb_info->refcount, 2);
+
+		aio = xio_alloc_aio(logst->brick);
+		cb_info->aio = aio;
+
+		aio->io_pos = logst->log_pos;
+		aio->io_len = logst->chunk_size ? logst->chunk_size : total_len;
+		aio->io_may_write = WRITE;
+		aio->io_prio = logst->io_prio;
+
+		for (;;) {
+			status = GENERIC_INPUT_CALL(logst->input, aio_get, aio);
+			if (likely(status >= 0))
+				break;
+			if (status != -ENOMEM && status != -EAGAIN) {
+				XIO_ERR("aio_get() failed, status = %d\n", status);
+				goto err_free;
+			}
+			brick_msleep(100);
+		}
+
+		if (unlikely(aio->io_len < total_len)) {
+			XIO_ERR("io_len = %d total_len = %d\n", aio->io_len, total_len);
+			goto put;
+		}
+
+		logst->offset = 0;
+		logst->log_aio = aio;
+	}
+
+	offset = logst->offset;
+	data = aio->io_data;
+	DATA_PUT(data, offset, START_MAGIC);
+	DATA_PUT(data, offset, (char)FORMAT_VERSION);
+	logst->validflag_offset = offset;
+	DATA_PUT(data, offset, (char)0); /*  valid_flag */
+	DATA_PUT(data, offset, total_len); /*  start of next header */
+	DATA_PUT(data, offset, lh->l_stamp.tv_sec);
+	DATA_PUT(data, offset, lh->l_stamp.tv_nsec);
+	DATA_PUT(data, offset, lh->l_pos);
+	logst->reallen_offset = offset;
+	DATA_PUT(data, offset, lh->l_len);
+	DATA_PUT(data, offset, (short)0); /*  spare */
+	DATA_PUT(data, offset, (int)0); /*  spare */
+	DATA_PUT(data, offset, lh->l_code);
+	DATA_PUT(data, offset, (short)0); /*  spare */
+
+	/*  remember the last timestamp */
+	memcpy(&logst->tmp_pos_stamp, &lh->l_stamp, sizeof(logst->tmp_pos_stamp));
+
+	logst->payload_offset = offset;
+	logst->payload_len = lh->l_len;
+
+	return data + offset;
+
+put:
+	GENERIC_INPUT_CALL(logst->input, aio_put, aio);
+	logst->log_aio = NULL;
+	return NULL;
+
+err_free:
+	obj_free(aio);
+	if (logst->private) {
+		/*  TODO: if callbacks are already registered, call them here with some error code */
+		brick_mem_free(logst->private);
+		logst->private = NULL;
+	}
+err:
+	return NULL;
+}
+
+bool log_finalize(struct log_status *logst, int len, void (*endio)(void *private, int error), void *private)
+{
+	struct aio_object *aio = logst->log_aio;
+	struct log_cb_info *cb_info = logst->private;
+	struct timespec now;
+	void *data;
+	int offset;
+	int restlen;
+	int nr_cb;
+	int crc;
+	bool ok = false;
+
+	CHECK_PTR(aio, err);
+
+	if (unlikely(len > logst->payload_len)) {
+		XIO_ERR("trying to write more than reserved (%d > %d)\n", len, logst->payload_len);
+		goto err;
+	}
+	restlen = aio->io_len - logst->offset;
+	if (unlikely(len + END_OVERHEAD > restlen)) {
+		XIO_ERR("trying to write more than available (%d > %d)\n", len, (int)(restlen - END_OVERHEAD));
+		goto err;
+	}
+	if (unlikely(!cb_info || cb_info->nr_cb >= XIO_LOG_CB_MAX)) {
+		XIO_ERR("too many endio() calls\n");
+		goto err;
+	}
+
+	data = aio->io_data;
+
+	crc = 0;
+	if (logst->do_crc) {
+		unsigned char checksum[xio_digest_size];
+
+		xio_digest(checksum, data + logst->payload_offset, len);
+		crc = *(int *)checksum;
+	}
+
+	/* Correct the length in the header.
+	 */
+	offset = logst->reallen_offset;
+	DATA_PUT(data, offset, (short)len);
+
+	/* Write the trailer.
+	 */
+	offset = logst->payload_offset + len;
+	DATA_PUT(data, offset, END_MAGIC);
+	DATA_PUT(data, offset, crc);
+	DATA_PUT(data, offset, (char)1);  /*  valid_flag copy */
+	DATA_PUT(data, offset, (char)0);  /*  spare */
+	DATA_PUT(data, offset, (short)0); /*  spare */
+	DATA_PUT(data, offset, logst->seq_nr + 1);
+	get_lamport(&now);    /*  when the log entry was ready. */
+	DATA_PUT(data, offset, now.tv_sec);
+	DATA_PUT(data, offset, now.tv_nsec);
+
+	if (unlikely(offset > aio->io_len)) {
+		XIO_FAT("length calculation was wrong: %d > %d\n", offset, aio->io_len);
+		goto err;
+	}
+	logst->offset = offset;
+
+	/* This must come last. In case of incomplete
+	 * or even overlapping disk transfers, this indicates
+	 * the completeness / integrity of the payload at
+	 * the time of starting the transfer.
+	 */
+	offset = logst->validflag_offset;
+	DATA_PUT(data, offset, (char)1);
+
+	nr_cb = cb_info->nr_cb++;
+	cb_info->endios[nr_cb] = endio;
+	cb_info->privates[nr_cb] = private;
+
+	/*  report success */
+	logst->seq_nr++;
+	logst->count++;
+	ok = true;
+
+err:
+	return ok;
+}
+
+static
+void log_read_endio(struct generic_callback *cb)
+{
+	struct log_status *logst = cb->cb_private;
+
+	LAST_CALLBACK(cb);
+	CHECK_PTR(logst, err);
+	logst->error_code = cb->cb_error;
+	logst->got = true;
+	wake_up_interruptible(&logst->event);
+	goto out_return;
+err:
+	XIO_FAT("internal pointer corruption\n");
+out_return:;
+}
+
+int log_read(struct log_status *logst, bool sloppy, struct log_header *lh, void **payload, int *payload_len)
+{
+	struct aio_object *aio;
+	int old_offset;
+	int status;
+
+restart:
+	status = 0;
+	aio = logst->read_aio;
+	if (!aio || logst->do_free) {
+		loff_t this_len;
+
+		if (aio) {
+			GENERIC_INPUT_CALL(logst->input, aio_put, aio);
+			logst->read_aio = NULL;
+			logst->log_pos += logst->offset;
+			logst->offset = 0;
+		}
+
+		this_len = logst->end_pos - logst->log_pos;
+		if (this_len > logst->chunk_size) {
+			this_len = logst->chunk_size;
+		} else if (unlikely(this_len <= 0)) {
+			XIO_ERR("tried bad IO len %lld, start_pos = %lld log_pos = %lld end_pos = %lld\n",
+				this_len,
+				logst->start_pos,
+				logst->log_pos,
+				logst->end_pos);
+			status = -EOVERFLOW;
+			goto done;
+		}
+
+		aio = xio_alloc_aio(logst->brick);
+		aio->io_pos = logst->log_pos;
+		aio->io_len = this_len;
+		aio->io_prio = logst->io_prio;
+
+		status = GENERIC_INPUT_CALL(logst->input, aio_get, aio);
+		if (unlikely(status < 0)) {
+			if (status != -ENODATA)
+				XIO_ERR("aio_get() failed, status = %d\n", status);
+			goto done_free;
+		}
+		if (unlikely(aio->io_len <= OVERHEAD)) { /*  EOF */
+			status = 0;
+			goto done_put;
+		}
+
+		SETUP_CALLBACK(aio, log_read_endio, logst);
+		aio->io_rw = READ;
+		logst->offset = 0;
+		logst->got = false;
+		logst->do_free = false;
+
+		GENERIC_INPUT_CALL(logst->input, aio_io, aio);
+
+		wait_event_interruptible_timeout(logst->event, logst->got, 60 * HZ);
+		status = -ETIME;
+		if (!logst->got)
+			goto done_put;
+		status = logst->error_code;
+		if (status < 0)
+			goto done_put;
+		logst->read_aio = aio;
+	}
+
+	status = log_scan(aio->io_data + logst->offset,
+			  aio->io_len - logst->offset,
+			  aio->io_pos,
+			  logst->offset,
+			  sloppy,
+			  lh,
+			  payload,
+			  payload_len,
+			  &logst->seq_nr);
+
+	if (unlikely(status == 0)) {
+		XIO_ERR("bad logfile scan\n");
+		status = -EINVAL;
+	}
+	if (unlikely(status < 0))
+		goto done_put;
+
+	/*  memoize success */
+	logst->offset += status;
+	if (logst->offset + (logst->max_size + OVERHEAD) * 2 >= aio->io_len)
+		logst->do_free = true;
+
+done:
+	if (status == -ENODATA) {
+		/*  indicate EOF */
+		status = 0;
+	}
+	return status;
+
+done_put:
+	old_offset = logst->offset;
+	if (aio) {
+		GENERIC_INPUT_CALL(logst->input, aio_put, aio);
+		logst->read_aio = NULL;
+		logst->log_pos += logst->offset;
+		logst->offset = 0;
+	}
+	if (status == -EAGAIN && old_offset > 0)
+		goto restart;
+	goto done;
+
+done_free:
+	obj_free(aio);
+	logst->read_aio = NULL;
+	goto done;
+
+}
+
+/***************** module init stuff ************************/
+
+int __init init_log_format(void)
+{
+	XIO_INF("init_log_format()\n");
+	return 0;
+}
+
+void exit_log_format(void)
+{
+	XIO_INF("exit_log_format()\n");
+}
diff --git a/include/linux/xio/lib_log.h b/include/linux/xio/lib_log.h
new file mode 100644
index 0000000..62bd45a
--- /dev/null
+++ b/include/linux/xio/lib_log.h
@@ -0,0 +1,329 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/* Definitions for logfile format.
+ *
+ * This is meant for sharing between different transaction logger variants,
+ * and/or for sharing with userspace tools (e.g. logfile analyzers).
+ * TODO: factor out some remaining kernelspace issues.
+ */
+
+#ifndef LIB_LOG_H
+#define LIB_LOG_H
+
+#ifdef __KERNEL__
+#include <linux/xio/xio.h>
+
+extern atomic_t global_aio_flying;
+#endif
+
+/* The following structure is memory-only.
+ * Transfers to disk are indirectly via the
+ * format conversion functions below.
+ * The advantage is that even newer disk formats can be parsed
+ * by old code (of course, not all information / features will be
+ * available then).
+ */
+#define log_header log_header_v1
+
+struct log_header_v1 {
+	struct timespec l_stamp;
+	struct timespec l_written;
+	loff_t l_pos;
+
+	short  l_len;
+	short  l_code;
+	unsigned int l_seq_nr;
+	int    l_crc;
+};
+
+#define FORMAT_VERSION			1 /*  version of disk format, currently there is no other one */
+
+#define CODE_UNKNOWN			0
+#define CODE_WRITE_NEW			1
+#define CODE_WRITE_OLD			2
+
+#define START_MAGIC			0xa8f7e908d9177957ll
+#define END_MAGIC			0x74941fb74ab5726dll
+
+#define START_OVERHEAD							\
+	(								\
+		sizeof(START_MAGIC) +					\
+		sizeof(char) +						\
+		sizeof(char) +						\
+		sizeof(short) +						\
+		sizeof(struct timespec) +				\
+		sizeof(loff_t) +					\
+		sizeof(int) +						\
+		sizeof(int) +						\
+		sizeof(short) +						\
+		sizeof(short) +						\
+		0							\
+	)
+
+#define END_OVERHEAD							\
+	(								\
+		sizeof(END_MAGIC) +					\
+		sizeof(int) +						\
+		sizeof(char) +						\
+		3 + 4 /*spare*/ +					\
+		sizeof(struct timespec) +				\
+		0							\
+	)
+
+#define OVERHEAD			(START_OVERHEAD + END_OVERHEAD)
+
+/*  TODO: make this bytesex-aware. */
+#define DATA_PUT(data, offset, val)					\
+	do {								\
+		*((typeof(val) *)((data)+offset)) = val;		\
+		offset += sizeof(val);					\
+	} while (0)
+
+#define DATA_GET(data, offset, val)					\
+	do {								\
+		val = *((typeof(val) *)((data)+offset));		\
+		offset += sizeof(val);					\
+	} while (0)
+
+#define SCAN_TXT							\
+"at file_pos = %lld file_offset = %d scan_offset = %d (%lld) test_offset = %d (%lld) restlen = %d: "
+#define SCAN_PAR							\
+file_pos, file_offset, offset, file_pos + file_offset + offset, i, file_pos + file_offset + i, restlen
+
+static inline
+int log_scan(void *buf,
+	int len,
+	loff_t file_pos,
+	int file_offset,
+	bool sloppy,
+	struct log_header *lh,
+	void **payload,
+	int *payload_len,
+	unsigned int *seq_nr)
+{
+	bool dirty = false;
+	int offset;
+	int i;
+
+	*payload = NULL;
+	*payload_len = 0;
+
+	for (i = 0; i < len && i <= len - OVERHEAD; i += sizeof(long)) {
+		long long start_magic;
+		char format_version;
+		char valid_flag;
+
+		short total_len;
+		long long end_magic;
+		char valid_copy;
+
+		int restlen = 0;
+		int found_offset;
+
+		offset = i;
+		if (unlikely(i > 0 && !sloppy)) {
+			XIO_ERR(SCAN_TXT "detected a hole / bad data\n", SCAN_PAR);
+			return -EBADMSG;
+		}
+
+		DATA_GET(buf, offset, start_magic);
+		if (unlikely(start_magic != START_MAGIC)) {
+			if (start_magic != 0)
+				dirty = true;
+			continue;
+		}
+
+		restlen = len - i;
+		if (unlikely(restlen < START_OVERHEAD)) {
+			XIO_WRN(SCAN_TXT "magic found, but restlen is too small\n", SCAN_PAR);
+			return -EAGAIN;
+		}
+
+		DATA_GET(buf, offset, format_version);
+		if (unlikely(format_version != FORMAT_VERSION)) {
+			XIO_ERR(SCAN_TXT "found unknown data format %d\n", SCAN_PAR, (int)format_version);
+			return -EBADMSG;
+		}
+		DATA_GET(buf, offset, valid_flag);
+		if (unlikely(!valid_flag)) {
+			XIO_WRN(SCAN_TXT "data is explicitly marked invalid (was there a short write?)\n", SCAN_PAR);
+			continue;
+		}
+		DATA_GET(buf, offset, total_len);
+		if (unlikely(total_len > restlen)) {
+			XIO_WRN(SCAN_TXT "total_len = %d but available data restlen = %d. Was the logfile truncated?\n",
+				SCAN_PAR,
+				total_len,
+				restlen);
+			return -EAGAIN;
+		}
+
+		memset(lh, 0, sizeof(struct log_header));
+
+		DATA_GET(buf, offset, lh->l_stamp.tv_sec);
+		DATA_GET(buf, offset, lh->l_stamp.tv_nsec);
+		DATA_GET(buf, offset, lh->l_pos);
+		DATA_GET(buf, offset, lh->l_len);
+		offset += 2; /*  skip spare */
+		offset += 4; /*  skip spare */
+		DATA_GET(buf, offset, lh->l_code);
+		offset += 2; /*  skip spare */
+
+		found_offset = offset;
+		offset += lh->l_len;
+
+		restlen = len - offset;
+		if (unlikely(restlen < END_OVERHEAD)) {
+			XIO_WRN(SCAN_TXT "restlen %d is too small\n", SCAN_PAR, restlen);
+			return -EAGAIN;
+		}
+
+		DATA_GET(buf, offset, end_magic);
+		if (unlikely(end_magic != END_MAGIC)) {
+			XIO_WRN(SCAN_TXT "bad end_magic 0x%llx, is the logfile truncated?\n", SCAN_PAR, end_magic);
+			return -EBADMSG;
+		}
+		DATA_GET(buf, offset, lh->l_crc);
+		DATA_GET(buf, offset, valid_copy);
+
+		if (unlikely(valid_copy != 1)) {
+			XIO_WRN(SCAN_TXT "found data marked as uncompleted / invalid, len = %d, valid_flag = %d\n",
+				SCAN_PAR,
+				lh->l_len,
+				(int)valid_copy);
+			return -EBADMSG;
+		}
+
+		/*  skip spares */
+		offset += 3;
+
+		DATA_GET(buf, offset, lh->l_seq_nr);
+		DATA_GET(buf, offset, lh->l_written.tv_sec);
+		DATA_GET(buf, offset, lh->l_written.tv_nsec);
+
+		if (unlikely(lh->l_seq_nr > *seq_nr + 1 && lh->l_seq_nr && *seq_nr)) {
+			XIO_ERR(SCAN_TXT "record sequence number %u mismatch, expected was %u\n",
+				SCAN_PAR,
+				lh->l_seq_nr,
+				*seq_nr + 1);
+			return -EBADMSG;
+		} else if (unlikely(lh->l_seq_nr != *seq_nr + 1 && lh->l_seq_nr && *seq_nr)) {
+			XIO_WRN(SCAN_TXT "record sequence number %u mismatch, expected was %u\n",
+				SCAN_PAR,
+				lh->l_seq_nr,
+				*seq_nr + 1);
+		}
+		*seq_nr = lh->l_seq_nr;
+
+		if (lh->l_crc) {
+			unsigned char checksum[xio_digest_size];
+
+			xio_digest(checksum, buf + found_offset, lh->l_len);
+			if (unlikely(*(int *)checksum != lh->l_crc)) {
+				XIO_ERR(SCAN_TXT "data checksumming mismatch, length = %d\n", SCAN_PAR, lh->l_len);
+				return -EBADMSG;
+			}
+		}
+
+		/*  last check */
+		if (unlikely(total_len != offset - i)) {
+			XIO_ERR(SCAN_TXT "internal size mismatch: %d != %d\n", SCAN_PAR, total_len, offset - i);
+			return -EBADMSG;
+		}
+
+		/*  Success... */
+		*payload = buf + found_offset;
+		*payload_len = lh->l_len;
+
+		/*  don't cry when nullbytes have been skipped */
+		if (i > 0 && dirty)
+			XIO_WRN(SCAN_TXT "skipped %d dirty bytes to find valid data\n", SCAN_PAR, i);
+
+		return offset;
+	}
+
+	XIO_ERR("could not find any useful data within len=%d bytes\n", len);
+	return -EAGAIN;
+}
+
+/**************************************************************************/
+
+#ifdef __KERNEL__
+
+/* Bookkeeping status between calls
+ */
+struct log_status {
+	/*  interfacing */
+	wait_queue_head_t *signal_event;
+	/*  tunables */
+	loff_t start_pos;
+	loff_t end_pos;
+
+	int align_size;   /*  alignment between requests */
+	int chunk_size;   /*  must be at least 8K (better 64k) */
+	int max_size;	  /*  max payload length */
+	int io_prio;
+	bool do_crc;
+
+	/*  informational */
+	atomic_t aio_flying;
+	int count;
+	loff_t log_pos;
+	struct timespec log_pos_stamp;
+
+	/*  internal */
+	struct timespec tmp_pos_stamp;
+	struct xio_input *input;
+	struct xio_brick *brick;
+	struct xio_info info;
+	int offset;
+	int validflag_offset;
+	int reallen_offset;
+	int payload_offset;
+	int payload_len;
+	unsigned int seq_nr;
+	struct aio_object *log_aio;
+	struct aio_object *read_aio;
+
+	wait_queue_head_t event;
+	int error_code;
+	bool got;
+	bool do_free;
+	void *private;
+};
+
+void init_logst(struct log_status *logst, struct xio_input *input, loff_t start_pos, loff_t end_pos);
+void exit_logst(struct log_status *logst);
+
+void log_flush(struct log_status *logst);
+
+void *log_reserve(struct log_status *logst, struct log_header *lh);
+
+bool log_finalize(struct log_status *logst, int len, void (*endio)(void *private, int error), void *private);
+
+int log_read(struct log_status *logst, bool sloppy, struct log_header *lh, void **payload, int *payload_len);
+
+/***********************************************************************/
+
+/*  init */
+
+extern int init_log_format(void);
+extern void exit_log_format(void);
+
+#endif
+#endif
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1299662 — [RFC 26/31] mars: add new module light_server_strategy

FromThomas Schoebel-Theuer <tst@schoebel-theuer.de>
Date2015-12-31 12:50 +0100
Subject[RFC 26/31] mars: add new module light_server_strategy
Message-ID<qLJTZ-3U9-51@gated-at.bofh.it>
In reply to#1299642
Signed-off-by: Thomas Schoebel-Theuer <tst@schoebel-theuer.de>
---
 .../mars/mars_light/light_server_strategy.c        | 403 +++++++++++++++++++++
 1 file changed, 403 insertions(+)
 create mode 100644 drivers/staging/mars/mars_light/light_server_strategy.c

diff --git a/drivers/staging/mars/mars_light/light_server_strategy.c b/drivers/staging/mars/mars_light/light_server_strategy.c
new file mode 100644
index 0000000..6bb5cd7
--- /dev/null
+++ b/drivers/staging/mars/mars_light/light_server_strategy.c
@@ -0,0 +1,403 @@
+/*
+ * MARS Long Distance Replication Software
+ *
+ * Copyright (C) 2010-2014 Thomas Schoebel-Theuer
+ * Copyright (C) 2011-2014 1&1 Internet AG
+ *
+ * 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.
+ */
+
+/* MARS Light specific parts of xio_server
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define _STRATEGY
+#include <linux/brick/brick.h>
+#include <linux/xio/xio.h>
+#include <linux/xio/xio_bio.h>
+#include <linux/xio/xio_sio.h>
+
+#include <linux/mars_light/light_strategy.h>
+
+#include <linux/xio/xio_server.h>
+
+static
+int dummy_worker(struct mars_global *global, struct mars_dent *dent, bool prepare, bool direction)
+{
+	return 0;
+}
+
+static
+int _set_server_sio_params(struct xio_brick *_brick, void *private)
+{
+	struct sio_brick *sio_brick = (void *)_brick;
+
+	if (_brick->type != (void *)_sio_brick_type) {
+		XIO_ERR("bad brick type\n");
+		return -EINVAL;
+	}
+	sio_brick->o_direct = false;
+	sio_brick->o_fdsync = false;
+	XIO_INF("name = '%s' path = '%s'\n", _brick->brick_name, _brick->brick_path);
+	return 1;
+}
+
+static
+int _set_server_bio_params(struct xio_brick *_brick, void *private)
+{
+	struct bio_brick *bio_brick;
+
+	if (_brick->type == (void *)_sio_brick_type)
+		return _set_server_sio_params(_brick, private);
+	if (_brick->type != (void *)_bio_brick_type) {
+		XIO_ERR("bad brick type\n");
+		return -EINVAL;
+	}
+	bio_brick = (void *)_brick;
+	bio_brick->ra_pages = 0;
+	bio_brick->do_noidle = true;
+	bio_brick->do_sync = true;
+	bio_brick->do_unplug = true;
+	XIO_INF("name = '%s' path = '%s'\n", _brick->brick_name, _brick->brick_path);
+	return 1;
+}
+
+int handler_thread(void *data)
+{
+	struct mars_global handler_global = {
+		.dent_anchor = LIST_HEAD_INIT(handler_global.dent_anchor),
+		.brick_anchor = LIST_HEAD_INIT(handler_global.brick_anchor),
+		.global_power = {
+			.button = true,
+		},
+		.main_event = __WAIT_QUEUE_HEAD_INITIALIZER(handler_global.main_event),
+	};
+	struct task_struct *thread = NULL;
+	struct server_brick *brick = data;
+	struct xio_socket *sock = &brick->handler_socket;
+	bool ok = xio_get_socket(sock);
+	unsigned long statist_jiffies = jiffies;
+	int debug_nr;
+	int status = -EINVAL;
+
+	init_rwsem(&handler_global.dent_mutex);
+	init_rwsem(&handler_global.brick_mutex);
+
+	XIO_DBG("#%d --------------- handler_thread starting on socket %p\n", sock->s_debug_nr, sock);
+	if (!ok)
+		goto done;
+
+	thread = brick_thread_create(cb_thread, brick, "xio_cb%d", brick->version);
+	if (unlikely(!thread)) {
+		XIO_ERR("cannot create cb thread\n");
+		status = -ENOENT;
+		goto done;
+	}
+	brick->cb_thread = thread;
+
+	brick->handler_running = true;
+	wake_up_interruptible(&brick->startup_event);
+
+	while (!list_empty(&handler_global.brick_anchor) ||
+	       xio_socket_is_alive(sock)) {
+		struct xio_cmd cmd = {};
+
+		handler_global.global_version++;
+
+		if (!list_empty(&handler_global.brick_anchor)) {
+			if (server_show_statist && !time_is_before_jiffies(statist_jiffies + 10 * HZ)) {
+				show_statistics(&handler_global, "handler");
+				statist_jiffies = jiffies;
+			}
+			if (!xio_socket_is_alive(sock) &&
+			    atomic_read(&brick->in_flight) <= 0 &&
+			    brick->conn_brick) {
+				if (generic_disconnect((void *)brick->inputs[0]) >= 0)
+					brick->conn_brick = NULL;
+			}
+
+			status = xio_kill_brick_when_possible(&handler_global,
+				&handler_global.brick_anchor,
+				false,
+				NULL,
+				true);
+			XIO_DBG("kill handler bricks (when possible) = %d\n", status);
+		}
+
+		status = -EINTR;
+		if (unlikely(!mars_global || !mars_global->global_power.button)) {
+			XIO_DBG("system is not alive\n");
+			goto clean;
+		}
+		if (unlikely(brick_thread_should_stop()))
+			goto clean;
+		if (unlikely(!xio_socket_is_alive(sock))) {
+			/* Dont read any data anymore, the protocol
+			 * may be screwed up completely.
+			 */
+			XIO_DBG("#%d is dead\n", sock->s_debug_nr);
+			goto clean;
+		}
+
+		status = xio_recv_struct(sock, &cmd, xio_cmd_meta);
+		if (unlikely(status < 0)) {
+			XIO_WRN("#%d recv cmd status = %d\n", sock->s_debug_nr, status);
+			goto clean;
+		}
+
+		if (unlikely(!brick->private_ptr || !mars_global || !mars_global->global_power.button)) {
+			XIO_WRN("#%d system is not alive\n", sock->s_debug_nr);
+			status = -EINTR;
+			goto clean;
+		}
+
+		status = -EPROTO;
+		switch (cmd.cmd_code & CMD_FLAG_MASK) {
+		case CMD_NOP:
+			status = 0;
+			XIO_DBG("#%d got NOP operation\n", sock->s_debug_nr);
+			break;
+		case CMD_NOTIFY:
+			status = 0;
+			from_remote_trigger();
+			break;
+		case CMD_GETINFO:
+		{
+			struct xio_info info = {};
+
+			status = GENERIC_INPUT_CALL(brick->inputs[0], xio_get_info, &info);
+			if (status < 0)
+				break;
+			down(&brick->socket_sem);
+			status = xio_send_struct(sock, &cmd, xio_cmd_meta);
+			if (status >= 0)
+				status = xio_send_struct(sock, &info, xio_info_meta);
+			up(&brick->socket_sem);
+			break;
+		}
+		case CMD_GETENTS:
+		{
+			status = -EINVAL;
+			if (unlikely(!cmd.cmd_str1))
+				break;
+
+			status = mars_dent_work(&handler_global,
+				"/mars",
+				sizeof(struct mars_dent),
+				light_checker,
+				dummy_worker,
+				&handler_global,
+				3);
+
+			down(&brick->socket_sem);
+			status = xio_send_dent_list(sock, &handler_global.dent_anchor);
+			up(&brick->socket_sem);
+
+			if (status < 0) {
+				XIO_WRN("#%d could not send dentry information, status = %d\n",
+					sock->s_debug_nr,
+					status);
+			}
+
+			xio_free_dent_all(&handler_global, &handler_global.dent_anchor);
+			break;
+		}
+		case CMD_CONNECT:
+		{
+			struct xio_brick *prev;
+			const char *path = cmd.cmd_str1;
+
+			status = -EINVAL;
+			CHECK_PTR(path, err);
+			CHECK_PTR_NULL(_bio_brick_type, err);
+
+			prev = make_brick_all(
+				&handler_global,
+				NULL,
+				_set_server_bio_params,
+				NULL,
+				path,
+				(const struct generic_brick_type *)_bio_brick_type,
+				(const struct generic_brick_type *[]){},
+				2, /*  start always */
+				path,
+				(const char *[]){},
+				0);
+			if (likely(prev)) {
+				status = generic_connect((void *)brick->inputs[0], (void *)prev->outputs[0]);
+				if (unlikely(status < 0))
+					XIO_ERR("#%d cannot connect to '%s'\n", sock->s_debug_nr, path);
+				prev->killme = true;
+				brick->conn_brick = prev;
+			} else {
+				XIO_ERR("#%d cannot find brick '%s'\n", sock->s_debug_nr, path);
+			}
+
+err:
+			cmd.cmd_int1 = status;
+			down(&brick->socket_sem);
+			status = xio_send_struct(sock, &cmd, xio_cmd_meta);
+			up(&brick->socket_sem);
+			break;
+		}
+		case CMD_AIO:
+		{
+			status = server_io(brick, sock, &cmd);
+			break;
+		}
+		case CMD_CB:
+			XIO_ERR("#%d oops, as a server I should never get CMD_CB; something is wrong here - attack attempt??\n",
+				sock->s_debug_nr);
+			break;
+		default:
+			XIO_ERR("#%d unknown command %d\n", sock->s_debug_nr, cmd.cmd_code);
+		}
+clean:
+		brick_string_free(cmd.cmd_str1);
+		if (unlikely(status < 0)) {
+			xio_shutdown_socket(sock);
+			brick_msleep(1000);
+		}
+	}
+
+	xio_shutdown_socket(sock);
+	xio_put_socket(sock);
+
+done:
+	XIO_DBG("#%d handler_thread terminating, status = %d\n", sock->s_debug_nr, status);
+
+	xio_kill_brick_all(&handler_global, &handler_global.brick_anchor, false);
+
+	if (thread) {
+		brick->cb_thread = NULL;
+		brick->cb_running = false;
+		XIO_DBG("#%d stopping callback thread....\n", sock->s_debug_nr);
+		brick_thread_stop(thread);
+	}
+
+	debug_nr = sock->s_debug_nr;
+
+	XIO_DBG("#%d done.\n", debug_nr);
+	brick->killme = true;
+	return status;
+}
+
+int server_thread(void *data)
+{
+	struct mars_global server_global = {
+		.dent_anchor = LIST_HEAD_INIT(server_global.dent_anchor),
+		.brick_anchor = LIST_HEAD_INIT(server_global.brick_anchor),
+		.global_power = {
+			.button = true,
+		},
+		.main_event = __WAIT_QUEUE_HEAD_INITIALIZER(server_global.main_event),
+	};
+	struct xio_socket *my_socket = data;
+	char *id = my_id();
+	int status = 0;
+
+	init_rwsem(&server_global.dent_mutex);
+	init_rwsem(&server_global.brick_mutex);
+
+	XIO_INF("-------- server starting on host '%s' ----------\n", id);
+
+	while (!brick_thread_should_stop() &&
+	      (!mars_global || !mars_global->global_power.button)) {
+		XIO_DBG("system did not start up\n");
+		brick_msleep(5000);
+	}
+
+	XIO_INF("-------- server now working on host '%s' ----------\n", id);
+
+	while (!brick_thread_should_stop() || !list_empty(&server_global.brick_anchor)) {
+		struct server_brick *brick = NULL;
+		struct xio_socket handler_socket = {};
+
+		server_global.global_version++;
+
+		if (server_show_statist)
+			show_statistics(&server_global, "server");
+
+		status = xio_kill_brick_when_possible(&server_global, &server_global.brick_anchor, false, NULL, true);
+		XIO_DBG("kill server bricks (when possible) = %d\n", status);
+
+		if (!mars_global || !mars_global->global_power.button) {
+			brick_msleep(1000);
+			continue;
+		}
+
+		status = xio_accept_socket(&handler_socket, my_socket);
+		if (unlikely(status < 0 || !xio_socket_is_alive(&handler_socket))) {
+			brick_msleep(500);
+			if (status == -EAGAIN)
+				continue; /*  without error message */
+			XIO_WRN("accept status = %d\n", status);
+			brick_msleep(1000);
+			continue;
+		}
+		handler_socket.s_shutdown_on_err = true;
+
+		XIO_DBG("got new connection #%d\n", handler_socket.s_debug_nr);
+
+		brick = (void *)xio_make_brick(&server_global, NULL, &server_brick_type, "handler", "handler");
+		if (!brick) {
+			XIO_ERR("cannot create server instance\n");
+			xio_shutdown_socket(&handler_socket);
+			xio_put_socket(&handler_socket);
+			brick_msleep(2000);
+			continue;
+		}
+		memcpy(&brick->handler_socket, &handler_socket, sizeof(struct xio_socket));
+
+		/* TODO: check authorization.
+		 */
+
+		brick->power.button = true;
+		status = server_switch(brick);
+		if (unlikely(status < 0)) {
+			XIO_ERR("cannot switch on server brick, status = %d\n", status);
+			goto err;
+		}
+
+		/*  further references are usually held by the threads */
+		xio_put_socket(&brick->handler_socket);
+
+		/* fire and forget....
+		 * the new instance is now responsible for itself.
+		 */
+		brick = NULL;
+		brick_msleep(100);
+		continue;
+
+err:
+		if (brick) {
+			xio_shutdown_socket(&brick->handler_socket);
+			xio_put_socket(&brick->handler_socket);
+			status = xio_kill_brick((void *)brick);
+			if (status < 0)
+				BRICK_ERR("kill status = %d, giving up\n", status);
+			brick = NULL;
+		}
+		brick_msleep(2000);
+	}
+
+	XIO_INF("-------- cleaning up ----------\n");
+
+	xio_kill_brick_all(&server_global, &server_global.brick_anchor, false);
+
+	/* cleanup_mm(); */
+
+	XIO_INF("-------- done status = %d ----------\n", status);
+	return status;
+}
-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web