fio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] zbd: Support zone reset by trim
@ 2021-07-06  0:59 Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 1/4] zbd: Add min_bytes argument to zbd_find_zone() Shin'ichiro Kawasaki
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-06  0:59 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

Trim workload is currently not supported for zonemode=zbd. This patch series
enables it by resetting zones for trim I/Os. This allows fio to measure
performance of zoned block devices using more realistic workloads which mixes
write and trim.

The first patch prepares for trim support by modifying the helper function
zbd_find_zone(). The second patch adds zone reset for trim. The third patch
enables trim workload for libzbc I/O engine. The fourth patch adds a test
case that exercises trim operation with zonemode=zbd.

Shin'ichiro Kawasaki (4):
  zbd: Add min_bytes argument to zbd_find_zone()
  zbd: Support zone reset by trim
  engines/libzbc: Enable trim for libzbc I/O engine
  t/zbd: Add test #58 to test zone reset by trim workload

 HOWTO                  |  3 ++
 engines/libzbc.c       | 16 +++++---
 fio.1                  |  2 +
 io_u.c                 |  9 +++++
 t/zbd/test-zbd-support | 26 ++++++++++++
 zbd.c                  | 89 +++++++++++++++++++++++++++++++++++-------
 zbd.h                  |  2 +
 7 files changed, 127 insertions(+), 20 deletions(-)

-- 
2.31.1



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] zbd: Add min_bytes argument to zbd_find_zone()
  2021-07-06  0:59 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
@ 2021-07-06  0:59 ` Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 2/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-06  0:59 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

The helper function zbd_find_zone() finds a zone with at least
min_bs[DDIR_READ] bytes readable before the zone write pointer. This
patch generalizes this function to allow finding a non-empty zone. To do
so, add the min_bytes argument to specify the minimum readable data of a
zone to filter the search. Specifying 1 to min_bytes then become
equivalent to finding a non-empty zone.

This change will allow to reuse this function to find a suitable zone
for trim I/O.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 zbd.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/zbd.c b/zbd.c
index 04c68dea..f10b3267 100644
--- a/zbd.c
+++ b/zbd.c
@@ -1413,18 +1413,16 @@ static struct fio_zone_info *zbd_replay_write_order(struct thread_data *td,
 }
 
 /*
- * Find another zone for which @io_u fits in the readable data in the zone.
- * Search in zones @zb + 1 .. @zl. For random workload, also search in zones
- * @zb - 1 .. @zf.
+ * Find another zone which has @min_bytes readable data. Search in zones
+ * @zb + 1 .. @zl. For random workload, also search in zones @zb - 1 .. @zf.
  *
  * Either returns NULL or returns a zone pointer. When the zone has write
  * pointer, hold the mutex for the zone.
  */
 static struct fio_zone_info *
-zbd_find_zone(struct thread_data *td, struct io_u *io_u,
+zbd_find_zone(struct thread_data *td, struct io_u *io_u, uint32_t min_bytes,
 	      struct fio_zone_info *zb, struct fio_zone_info *zl)
 {
-	const uint32_t min_bs = td->o.min_bs[io_u->ddir];
 	struct fio_file *f = io_u->file;
 	struct fio_zone_info *z1, *z2;
 	const struct fio_zone_info *const zf = get_zone(f, f->min_zone);
@@ -1437,7 +1435,7 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
 		if (z1 < zl && z1->cond != ZBD_ZONE_COND_OFFLINE) {
 			if (z1->has_wp)
 				zone_lock(td, f, z1);
-			if (z1->start + min_bs <= z1->wp)
+			if (z1->start + min_bytes <= z1->wp)
 				return z1;
 			if (z1->has_wp)
 				zone_unlock(z1);
@@ -1448,14 +1446,14 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
 		    z2->cond != ZBD_ZONE_COND_OFFLINE) {
 			if (z2->has_wp)
 				zone_lock(td, f, z2);
-			if (z2->start + min_bs <= z2->wp)
+			if (z2->start + min_bytes <= z2->wp)
 				return z2;
 			if (z2->has_wp)
 				zone_unlock(z2);
 		}
 	}
-	dprint(FD_ZBD, "%s: adjusting random read offset failed\n",
-	       f->file_name);
+	dprint(FD_ZBD, "%s: no zone has %d bytes readable data\n",
+	       f->file_name, min_bytes);
 	return NULL;
 }
 
@@ -1784,7 +1782,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 		    ((!td_random(td)) && (io_u->offset + min_bs > zb->wp))) {
 			zone_unlock(zb);
 			zl = get_zone(f, f->max_zone);
-			zb = zbd_find_zone(td, io_u, zb, zl);
+			zb = zbd_find_zone(td, io_u, min_bs, zb, zl);
 			if (!zb) {
 				dprint(FD_ZBD,
 				       "%s: zbd_find_zone(%lld, %llu) failed\n",
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] zbd: Support zone reset by trim
  2021-07-06  0:59 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 1/4] zbd: Add min_bytes argument to zbd_find_zone() Shin'ichiro Kawasaki
@ 2021-07-06  0:59 ` Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 3/4] engines/libzbc: Enable trim for libzbc I/O engine Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 4/4] t/zbd: Add test #58 to test zone reset by trim workload Shin'ichiro Kawasaki
  3 siblings, 0 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-06  0:59 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

Enable trim workload for zonemode=zbd by modifying do_io_u_trim() to
call zoned block device unique function zbd_do_io_u_trim() which resets
target zone. This allows fio to emulate workloads which mix data
read/write and zone resets with zonemode=zbd.

To call reset zones, the trim I/O shall have offset aligned to zone
start and block size same as zone size. Reset zone is called only to
sequential write required zones and sequential write preferred zones.
Conventional zones are handled in same manner as regular block devices
by calling os_trim() function.

When zones are reset with random trim workload, choose only non-empty
zones as trim target. This avoids meaningless trim to empty zones and
makes the workload more realistic. To find the non-empty zones, utilize
zbd_find_zone() helper function which is already used for read workload,
specifying 1 byte as the minimum valid data size.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 HOWTO  |  3 +++
 fio.1  |  2 ++
 io_u.c |  9 ++++++++
 zbd.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 zbd.h  |  2 ++
 5 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/HOWTO b/HOWTO
index 86fb2964..1e584645 100644
--- a/HOWTO
+++ b/HOWTO
@@ -992,6 +992,9 @@ Target file/device
 				single zone. The :option:`zoneskip` parameter
 				is ignored. :option:`zonerange` and
 				:option:`zonesize` must be identical.
+				Trim is handled using a zone reset operation.
+				Trim only considers non-empty sequential write
+				required and sequential write preferred zones.
 
 .. option:: zonerange=int
 
diff --git a/fio.1 b/fio.1
index 5aa54a4d..1fab3a6f 100644
--- a/fio.1
+++ b/fio.1
@@ -766,6 +766,8 @@ starts. The \fBzonecapacity\fR parameter is ignored.
 Zoned block device mode. I/O happens sequentially in each zone, even if random
 I/O has been selected. Random I/O happens across all zones instead of being
 restricted to a single zone.
+Trim is handled using a zone reset operation. Trim only considers non-empty
+sequential write required and sequential write preferred zones.
 .RE
 .RE
 .TP
diff --git a/io_u.c b/io_u.c
index b421a579..ccbc4974 100644
--- a/io_u.c
+++ b/io_u.c
@@ -2299,10 +2299,19 @@ int do_io_u_trim(const struct thread_data *td, struct io_u *io_u)
 	struct fio_file *f = io_u->file;
 	int ret;
 
+	if (td->o.zone_mode == ZONE_MODE_ZBD) {
+		ret = zbd_do_io_u_trim(td, io_u);
+		if (ret == io_u_completed)
+			return io_u->xfer_buflen;
+		if (ret)
+			goto err;
+	}
+
 	ret = os_trim(f, io_u->offset, io_u->xfer_buflen);
 	if (!ret)
 		return io_u->xfer_buflen;
 
+err:
 	io_u->error = ret;
 	return 0;
 #endif
diff --git a/zbd.c b/zbd.c
index f10b3267..39060ecd 100644
--- a/zbd.c
+++ b/zbd.c
@@ -375,12 +375,24 @@ static bool zbd_verify_bs(void)
 	int i, j, k;
 
 	for_each_td(td, i) {
+		if (td_trim(td) &&
+		    (td->o.min_bs[DDIR_TRIM] != td->o.max_bs[DDIR_TRIM] ||
+		     td->o.bssplit_nr[DDIR_TRIM])) {
+			log_info("bsrange and bssplit is not allowed for trim with zonemode=zbd\n");
+			return false;
+		}
 		for_each_file(td, f, j) {
 			uint64_t zone_size;
 
 			if (!f->zbd_info)
 				continue;
 			zone_size = f->zbd_info->zone_size;
+			if (td_trim(td) && td->o.bs[DDIR_TRIM] != zone_size) {
+				log_info("%s: trim block size %llu is not the zone size %llu\n",
+					 f->file_name, td->o.bs[DDIR_TRIM],
+					 (unsigned long long)zone_size);
+				return false;
+			}
 			for (k = 0; k < FIO_ARRAY_SIZE(td->o.bs); k++) {
 				if (td->o.verify != VERIFY_NONE &&
 				    zone_size % td->o.bs[k] != 0) {
@@ -1528,9 +1540,6 @@ static void zbd_queue_io(struct thread_data *td, struct io_u *io_u, int q,
 		pthread_mutex_unlock(&zbd_info->mutex);
 		z->wp = zone_end;
 		break;
-	case DDIR_TRIM:
-		assert(z->wp == z->start);
-		break;
 	default:
 		break;
 	}
@@ -1910,8 +1919,23 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 			(zbd_zone_capacity_end(zb) - io_u->offset), min_bs);
 		goto eof;
 	case DDIR_TRIM:
-		/* fall-through */
+		/* Check random trim targets a non-empty zone */
+		if (!td_random(td) || zb->wp > zb->start)
+			goto accept;
+
+		/* Find out a non-empty zone to trim */
+		zone_unlock(zb);
+		zl = get_zone(f, f->max_zone);
+		zb = zbd_find_zone(td, io_u, 1, zb, zl);
+		if (zb) {
+			io_u->offset = zb->start;
+			dprint(FD_ZBD, "%s: found new zone(%lld) for trim\n",
+			       f->file_name, io_u->offset);
+			goto accept;
+		}
+		goto eof;
 	case DDIR_SYNC:
+		/* fall-through */
 	case DDIR_DATASYNC:
 	case DDIR_SYNC_FILE_RANGE:
 	case DDIR_WAIT:
@@ -1952,3 +1976,42 @@ char *zbd_write_status(const struct thread_stat *ts)
 		return NULL;
 	return res;
 }
+
+/**
+ * zbd_do_io_u_trim - If reset zone is applicable, do reset zone instead of trim
+ *
+ * @td: FIO thread data.
+ * @io_u: FIO I/O unit.
+ *
+ * It is assumed that z->mutex is already locked.
+ * Return io_u_completed when reset zone succeeds. Return 0 when the target zone
+ * does not have write pointer. On error, return negative errno.
+ */
+int zbd_do_io_u_trim(const struct thread_data *td, struct io_u *io_u)
+{
+	struct fio_file *f = io_u->file;
+	struct fio_zone_info *z;
+	uint32_t zone_idx;
+	int ret;
+
+	zone_idx = zbd_zone_idx(f, io_u->offset);
+	z = get_zone(f, zone_idx);
+
+	if (!z->has_wp)
+		return 0;
+
+	if (io_u->offset != z->start) {
+		log_err("Trim offset not at zone start (%lld)\n", io_u->offset);
+		return -EINVAL;
+	}
+
+	/*
+	 * Cast td to drop const modifier so that zbd_reset_zone() can change td
+	 * members.
+	 */
+	ret = zbd_reset_zone((struct thread_data *)td, f, z);
+	if (ret < 0)
+		return ret;
+
+	return io_u_completed;
+}
diff --git a/zbd.h b/zbd.h
index 39dc45e3..0a73b41d 100644
--- a/zbd.h
+++ b/zbd.h
@@ -17,6 +17,7 @@ struct fio_file;
 enum io_u_action {
 	io_u_accept	= 0,
 	io_u_eof	= 1,
+	io_u_completed  = 2,
 };
 
 /**
@@ -99,6 +100,7 @@ enum fio_ddir zbd_adjust_ddir(struct thread_data *td, struct io_u *io_u,
 			      enum fio_ddir ddir);
 enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u);
 char *zbd_write_status(const struct thread_stat *ts);
+int zbd_do_io_u_trim(const struct thread_data *td, struct io_u *io_u);
 
 static inline void zbd_close_file(struct fio_file *f)
 {
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] engines/libzbc: Enable trim for libzbc I/O engine
  2021-07-06  0:59 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 1/4] zbd: Add min_bytes argument to zbd_find_zone() Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 2/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
@ 2021-07-06  0:59 ` Shin'ichiro Kawasaki
  2021-07-06  0:59 ` [PATCH 4/4] t/zbd: Add test #58 to test zone reset by trim workload Shin'ichiro Kawasaki
  3 siblings, 0 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-06  0:59 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

The trim workload to zoned block devices is supported as zone reset, and
this feature is available for I/O engines which support both zoned
devices and trim workload. Libzbc I/O engine supports zoned but lacks
trim workload support. To enable trim support with libzbc I/O engine,
remove the check which inhibited trim from requests to libzbc I/O
engine. Also set file open flags for trim same as write, and call
zbd_do_io_u_trim() for trim I/O.

Of note is that libzbc I/O engine now can support trim to sequential
write required zones, but still can not support os_trim() call and
BLKDISCARD ioctl for the conventional zones. The trim I/Os to
conventional zones are reported as an error.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 engines/libzbc.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/engines/libzbc.c b/engines/libzbc.c
index 7f2bc431..5b4c5e8e 100644
--- a/engines/libzbc.c
+++ b/engines/libzbc.c
@@ -14,6 +14,7 @@
 #include "fio.h"
 #include "err.h"
 #include "zbd_types.h"
+#include "zbd.h"
 
 struct libzbc_data {
 	struct zbc_device	*zdev;
@@ -63,7 +64,7 @@ static int libzbc_open_dev(struct thread_data *td, struct fio_file *f,
 		return -EINVAL;
 	}
 
-	if (td_write(td)) {
+	if (td_write(td) || td_trim(td)) {
 		if (!read_only)
 			flags |= O_RDWR;
 	} else if (td_read(td)) {
@@ -71,10 +72,6 @@ static int libzbc_open_dev(struct thread_data *td, struct fio_file *f,
 			flags |= O_RDWR;
 		else
 			flags |= O_RDONLY;
-	} else if (td_trim(td)) {
-		td_verror(td, EINVAL, "libzbc does not support trim");
-		log_err("%s: libzbc does not support trim\n", f->file_name);
-		return -EINVAL;
 	}
 
 	if (td->o.oatomic) {
@@ -411,7 +408,14 @@ static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
 		ret = zbc_flush(ld->zdev);
 		if (ret)
 			log_err("zbc_flush error %zd\n", ret);
-	} else if (io_u->ddir != DDIR_TRIM) {
+	} else if (io_u->ddir == DDIR_TRIM) {
+		ret = zbd_do_io_u_trim(td, io_u);
+		if (!ret) {
+			log_err("libzbc does not support trim to "
+				"conventional zones\n");
+			ret = EINVAL;
+		}
+	} else {
 		log_err("Unsupported operation %u\n", io_u->ddir);
 		ret = -EINVAL;
 	}
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] t/zbd: Add test #58 to test zone reset by trim workload
  2021-07-06  0:59 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
                   ` (2 preceding siblings ...)
  2021-07-06  0:59 ` [PATCH 3/4] engines/libzbc: Enable trim for libzbc I/O engine Shin'ichiro Kawasaki
@ 2021-07-06  0:59 ` Shin'ichiro Kawasaki
  3 siblings, 0 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-06  0:59 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

To exercise zone reset by trim workload, add the test case #58. As the
precondition, it fills several zones. After that, a trim job and a write
job run in parallel for 30 seconds. The ratio of trim commands and write
commands is controlled by --flow option.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 t/zbd/test-zbd-support | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/t/zbd/test-zbd-support b/t/zbd/test-zbd-support
index 57e6d05e..5103c406 100755
--- a/t/zbd/test-zbd-support
+++ b/t/zbd/test-zbd-support
@@ -1215,6 +1215,32 @@ test57() {
 		>> "${logfile}.${test_number}" 2>&1 || return $?
 }
 
+# Random writes and random trims to sequential write required zones for 30s.
+test58() {
+    local off size bs
+
+    require_seq_zones 128 || return $SKIP_TESTCASE
+
+    size=$((zone_size * 128))
+    bs="$(max $((zone_size / 128)) "$logical_block_size")"
+    prep_write
+    off=$((first_sequential_zone_sector * 512))
+    run_fio --zonemode=zbd --direct=1 --zonesize="${zone_size}" --thread=1 \
+	    --filename="${dev}" --norandommap=1 \
+            --name="precondition"  --rw=write "$(ioengine "psync")" \
+            --offset="${off}" --size=$((zone_size * 16)) --bs="${bs}" \
+	    "${job_var_opts[@]}" \
+	    --name=wjob --wait_for="precondition" --rw=randwrite \
+	    "$(ioengine "libaio")" --iodepth=8 \
+	    --offset="${off}" --size="${size}" --bs="${bs}" \
+	    --time_based --runtime=30s --flow=128 "${job_var_opts[@]}" \
+	    --name=trimjob --wait_for="precondition" --rw=randtrim \
+	    "$(ioengine "psync")" \
+	    --offset="${off}" --size="${size}" --bs="${zone_size}" \
+	    --time_based --runtime=30s --flow=1 "${job_var_opts[@]}" \
+	    >>"${logfile}.${test_number}" 2>&1
+}
+
 SECONDS=0
 tests=()
 dynamic_analyzer=()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 0/4] zbd: Support zone reset by trim
@ 2021-07-28 10:47 Shin'ichiro Kawasaki
  0 siblings, 0 replies; 6+ messages in thread
From: Shin'ichiro Kawasaki @ 2021-07-28 10:47 UTC (permalink / raw)
  To: fio, Jens Axboe
  Cc: Damien Le Moal, Dmitry Fomichev, Niklas Cassel, Shinichiro Kawasaki

Let me repost this series to refresh its status. Reviews on the list will be
appreciated. I confirmed it can be applied to the current fio master branch tip.

Trim workload is currently not supported for zonemode=zbd. This patch series
enables it by resetting zones for trim I/Os. This allows fio to measure
performance of zoned block devices using more realistic workloads which mixes
write and trim.

The first patch prepares for trim support by modifying the helper function
zbd_find_zone(). The second patch adds zone reset for trim. The third patch
enables trim workload for libzbc I/O engine. The fourth patch adds a test
case that exercises trim operation with zonemode=zbd.

Shin'ichiro Kawasaki (4):
  zbd: Add min_bytes argument to zbd_find_zone()
  zbd: Support zone reset by trim
  engines/libzbc: Enable trim for libzbc I/O engine
  t/zbd: Add test #58 to test zone reset by trim workload

 HOWTO                  |  3 ++
 engines/libzbc.c       | 16 +++++---
 fio.1                  |  2 +
 io_u.c                 |  9 +++++
 t/zbd/test-zbd-support | 26 ++++++++++++
 zbd.c                  | 89 +++++++++++++++++++++++++++++++++++-------
 zbd.h                  |  2 +
 7 files changed, 127 insertions(+), 20 deletions(-)

-- 
2.31.1



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-07-28 10:47 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-06  0:59 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
2021-07-06  0:59 ` [PATCH 1/4] zbd: Add min_bytes argument to zbd_find_zone() Shin'ichiro Kawasaki
2021-07-06  0:59 ` [PATCH 2/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki
2021-07-06  0:59 ` [PATCH 3/4] engines/libzbc: Enable trim for libzbc I/O engine Shin'ichiro Kawasaki
2021-07-06  0:59 ` [PATCH 4/4] t/zbd: Add test #58 to test zone reset by trim workload Shin'ichiro Kawasaki
2021-07-28 10:47 [PATCH 0/4] zbd: Support zone reset by trim Shin'ichiro Kawasaki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).