All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: Sage Weil <sage@newdream.net>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	yehuda@newdream.net, sage@newdream.net
Subject: Re: [PATCH 02/21] ceph: on-wire types
Date: Tue, 29 Sep 2009 16:52:42 -0700	[thread overview]
Message-ID: <20090929165242.99e5bdf2.akpm@linux-foundation.org> (raw)
In-Reply-To: <1253641129-28434-3-git-send-email-sage@newdream.net>

On Tue, 22 Sep 2009 10:38:30 -0700
Sage Weil <sage@newdream.net> wrote:

> These headers describe the types used to exchange messages between the
> Ceph client and various servers.  All types are little-endian and
> packed.
> 
> Additionally, we define a few magic values to identify the current
> version of the protocol(s) in use, so that discrepancies to be
> detected on mount.
> 
> ...
>
> +static inline __u32 frag_make(__u32 b, __u32 v)
> +{
> +	return (b << 24) |
> +		(v & (0xffffffu << (24-b)) & 0xffffffu);
> +}
> +static inline __u32 frag_bits(__u32 f)
> +{
> +	return f >> 24;
> +}
> +static inline __u32 frag_value(__u32 f)
> +{
> +	return f & 0xffffffu;
> +}
> +static inline __u32 frag_mask(__u32 f)
> +{
> +	return (0xffffffu << (24-frag_bits(f))) & 0xffffffu;
> +}
> +static inline __u32 frag_mask_shift(__u32 f)
> +{
> +	return 24 - frag_bits(f);
> +}
> +
> +static inline int frag_contains_value(__u32 f, __u32 v)
> +{
> +	return (v & frag_mask(f)) == frag_value(f);
> +}
> +static inline int frag_contains_frag(__u32 f, __u32 sub)
> +{
> +	/* is sub as specific as us, and contained by us? */
> +	return frag_bits(sub) >= frag_bits(f) &&
> +		(frag_value(sub) & frag_mask(f)) == frag_value(f);
> +}
> +
> +static inline __u32 frag_parent(__u32 f)
> +{
> +	return frag_make(frag_bits(f) - 1,
> +			 frag_value(f) & (frag_mask(f) << 1));
> +}
> +static inline int frag_is_left_child(__u32 f)
> +{
> +	return frag_bits(f) > 0 &&
> +		(frag_value(f) & (0x1000000 >> frag_bits(f))) == 0;
> +}
> +static inline int frag_is_right_child(__u32 f)
> +{
> +	return frag_bits(f) > 0 &&
> +		(frag_value(f) & (0x1000000 >> frag_bits(f))) == 1;
> +}
> +static inline __u32 frag_sibling(__u32 f)
> +{
> +	return frag_make(frag_bits(f),
> +			 frag_value(f) ^ (0x1000000 >> frag_bits(f)));
> +}
> +static inline __u32 frag_left_child(__u32 f)
> +{
> +	return frag_make(frag_bits(f)+1, frag_value(f));
> +}
> +static inline __u32 frag_right_child(__u32 f)
> +{
> +	return frag_make(frag_bits(f)+1,
> +			 frag_value(f) | (0x1000000 >> (1+frag_bits(f))));
> +}
> +static inline __u32 frag_make_child(__u32 f, int by, int i)
> +{
> +	int newbits = frag_bits(f) + by;
> +	return frag_make(newbits,
> +			 frag_value(f) | (i << (24 - newbits)));
> +}
> +static inline int frag_is_leftmost(__u32 f)
> +{
> +	return frag_value(f) == 0;
> +}
> +static inline int frag_is_rightmost(__u32 f)
> +{
> +	return frag_value(f) == frag_mask(f);
> +}
> +static inline __u32 frag_next(__u32 f)
> +{
> +	return frag_make(frag_bits(f),
> +			 frag_value(f) + (0x1000000 >> frag_bits(f)));
> +}
> +
> +/*
> + * comparator to sort frags logically, as when traversing the
> + * number space in ascending order...
> + */
> +static inline int frag_compare(__u32 a, __u32 b)
> +{
> +	unsigned va = frag_value(a);
> +	unsigned vb = frag_value(b);
> +	if (va < vb)
> +		return -1;
> +	if (va > vb)
> +		return 1;
> +	va = frag_bits(a);
> +	vb = frag_bits(b);
> +	if (va < vb)
> +		return -1;
> +	if (va > vb)
> +		return 1;
> +	return 0;
> +}

I expect you'll find that many of these functions expand to a lot of
code, and the filesystem will be smaller and faster if they are
uninlined.

Why does it use __u32 rather than u32?  AIUI, __u32 is only used when
the header is shared with userspace and this one should not be shared.

> +
> +/*
> + * ceph_file_layout - describe data layout for a file/inode
> + */
> +struct ceph_file_layout {
> +	/* file -> object mapping */
> +	__le32 fl_stripe_unit;     /* stripe unit, in bytes.  must be multiple
> +				      of page size. */
> +	__le32 fl_stripe_count;    /* over this many objects */
> +	__le32 fl_object_size;     /* until objects are this big, then move to
> +				      new objects */
> +	__le32 fl_cas_hash;        /* 0 = none; 1 = sha256 */
> +
> +	/* pg -> disk layout */
> +	__le32 fl_object_stripe_unit;  /* for per-object parity, if any */
> +
> +	/* object -> pg layout */
> +	__le32 fl_pg_preferred; /* preferred primary for pg (-1 for none) */
> +	__le32 fl_pg_pool;      /* namespace, crush ruleset, rep level */
> +} __attribute__ ((packed));

It's conventional to use __packed for this.  If the header is shared
with userspace then scrub that.

> +#define ceph_file_layout_su(l) ((__s32)le32_to_cpu((l).fl_stripe_unit))
> +#define ceph_file_layout_stripe_count(l) \
> +	((__s32)le32_to_cpu((l).fl_stripe_count))
> +#define ceph_file_layout_object_size(l) ((__s32)le32_to_cpu((l).fl_object_size))
> +#define ceph_file_layout_cas_hash(l) ((__s32)le32_to_cpu((l).fl_cas_hash))
> +#define ceph_file_layout_object_su(l) \
> +	((__s32)le32_to_cpu((l).fl_object_stripe_unit))
> +#define ceph_file_layout_pg_preferred(l) \
> +	((__s32)le32_to_cpu((l).fl_pg_preferred))
> +#define ceph_file_layout_pg_pool(l) \
> +	((__s32)le32_to_cpu((l).fl_pg_pool))

But the header has a lot of stuff which looks like it shouldn't be
shared with userspace.

> +#define ceph_file_layout_stripe_width(l) (le32_to_cpu((l).fl_stripe_unit) * \
> +					  le32_to_cpu((l).fl_stripe_count))
> +
> +/* "period" == bytes before i start on a new set of objects */
> +#define ceph_file_layout_period(l) (le32_to_cpu((l).fl_object_size) *	\
> +				    le32_to_cpu((l).fl_stripe_count))

Please only implement in cpp that which cannot be implemented in C.

One reason (amongst many) is that the above macros evaluate `l' twice
and will hence misbehave if passed an expression with sode-effects.

Please review the entire fs driver for this mistake.

> +
> +
> +/*
> + * string hash.
> + *
> + * taken from Linux, tho we should probably take care to use this one
> + * in case the upstream hash changes.
> + */
> +
> +/* Name hashing routines. Initial hash value */
> +/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
> +#define ceph_init_name_hash()		0
> +
> +/* partial hash update function. Assume roughly 4 bits per character */
> +static inline unsigned long
> +ceph_partial_name_hash(unsigned long c, unsigned long prevhash)
> +{
> +	return (prevhash + (c << 4) + (c >> 4)) * 11;
> +}
> +
> +/*
> + * Finally: cut down the number of bits to a int value (and try to avoid
> + * losing bits)
> + */
> +static inline unsigned long ceph_end_name_hash(unsigned long hash)
> +{
> +	return (unsigned int) hash;
> +}

Wouldn't

	return hash & 0xffffffff;

be clearer?

> +/* Compute the hash for a name string. */
> +static inline unsigned int
> +ceph_full_name_hash(const char *name, unsigned int len)
> +{
> +	unsigned long hash = ceph_init_name_hash();
> +	while (len--)
> +		hash = ceph_partial_name_hash(*name++, hash);
> +	return ceph_end_name_hash(hash);
> +}

Waaaaaay too big for inlining!

>
> ...
>
> +struct ceph_mon_statfs {
> +	__le64 have_version;
> +	struct ceph_fsid fsid;
> +	__le64 tid;
> +} __attribute__ ((packed));

Might be able to use __packed (please check the whole fs)

>
> ...
>
> +static inline const char *ceph_mds_state_name(int s)
> +{
> +	switch (s) {
> +		/* down and out */
> +	case CEPH_MDS_STATE_DNE:        return "down:dne";
> +	case CEPH_MDS_STATE_STOPPED:    return "down:stopped";
> +		/* up and out */
> +	case CEPH_MDS_STATE_BOOT:       return "up:boot";
> +	case CEPH_MDS_STATE_STANDBY:    return "up:standby";
> +	case CEPH_MDS_STATE_STANDBY_REPLAY:    return "up:standby-replay";
> +	case CEPH_MDS_STATE_CREATING:   return "up:creating";
> +	case CEPH_MDS_STATE_STARTING:   return "up:starting";
> +		/* up and in */
> +	case CEPH_MDS_STATE_REPLAY:     return "up:replay";
> +	case CEPH_MDS_STATE_RESOLVE:    return "up:resolve";
> +	case CEPH_MDS_STATE_RECONNECT:  return "up:reconnect";
> +	case CEPH_MDS_STATE_REJOIN:     return "up:rejoin";
> +	case CEPH_MDS_STATE_CLIENTREPLAY: return "up:clientreplay";
> +	case CEPH_MDS_STATE_ACTIVE:     return "up:active";
> +	case CEPH_MDS_STATE_STOPPING:   return "up:stopping";
> +	default: return "";
> +	}
> +	return NULL;
> +}

geeze.

This might generate decent code as long as it's always called with a
constant arg.  Otherwise, ouch.

If we're lucky, the compiler will instantiate one copy of each string
kernel-wide.  If we're unlucky it'll instantate one copy per
compilation unit.

> +static inline const char *ceph_session_op_name(int op)
> +{
> +	switch (op) {
> +	case CEPH_SESSION_REQUEST_OPEN: return "request_open";
> +	case CEPH_SESSION_OPEN: return "open";
> +	case CEPH_SESSION_REQUEST_CLOSE: return "request_close";
> +	case CEPH_SESSION_CLOSE: return "close";
> +	case CEPH_SESSION_REQUEST_RENEWCAPS: return "request_renewcaps";
> +	case CEPH_SESSION_RENEWCAPS: return "renewcaps";
> +	case CEPH_SESSION_STALE: return "stale";
> +	case CEPH_SESSION_RECALL_STATE: return "recall_state";
> +	default: return "???";
> +	}
> +}

I hereby revoke your inlining license!

>
> ...
>
> +union ceph_mds_request_args {
> +	struct {
> +		__le32 mask;  /* CEPH_CAP_* */
> +	} __attribute__ ((packed)) getattr;
> +	struct {
> +		__le32 mode;
> +		__le32 uid;
> +		__le32 gid;
> +		struct ceph_timespec mtime;
> +		struct ceph_timespec atime;
> +		__le64 size, old_size;
> +		__le32 mask;  /* CEPH_SETATTR_* */
> +	} __attribute__ ((packed)) setattr;
> +	struct {
> +		__le32 frag;
> +		__le32 max_entries;
> +	} __attribute__ ((packed)) readdir;
> +	struct {
> +		__le32 mode;
> +		__le32 rdev;
> +	} __attribute__ ((packed)) mknod;
> +	struct {
> +		__le32 mode;
> +	} __attribute__ ((packed)) mkdir;
> +	struct {
> +		__le32 flags;
> +		__le32 mode;
> +		__le32 stripe_unit;
> +		__le32 stripe_count;
> +		__le32 object_size;
> +		__le32 file_replication;
> +	} __attribute__ ((packed)) open;
> +	struct {
> +		__le32 flags;
> +	} __attribute__ ((packed)) setxattr;
> +	struct {
> +		struct ceph_file_layout layout;
> +	} __attribute__ ((packed)) setlayout;
> +} __attribute__ ((packed));

Some of these data structures look pretty important but they're not
documented (here, at least)?

>
> ...
>
> +struct ceph_entity_name {
> +	__u8 type;
> +	__le64 num;
> +} __attribute__ ((packed));

So we end up with a poorly-aligned u64.  Unfortunate.  I trust the
kernel code uses the correct and appropriate include/linux/unaligned
helpers to access things such as this?

> +#define CEPH_ENTITY_TYPE_MON    1
> +#define CEPH_ENTITY_TYPE_MDS    2
> +#define CEPH_ENTITY_TYPE_OSD    3
> +#define CEPH_ENTITY_TYPE_CLIENT 4
> +#define CEPH_ENTITY_TYPE_ADMIN  5
> +
> +/*
> + * entity_addr -- network address
> + */
> +struct ceph_entity_addr {
> +	__le32 erank;  /* entity's rank in process */
> +	__le32 nonce;  /* unique id for process (e.g. pid) */
> +	struct sockaddr_in ipaddr;
> +} __attribute__ ((packed));
> +
> +static inline bool ceph_entity_addr_is_local(const struct ceph_entity_addr *a,
> +					     const struct ceph_entity_addr *b)
> +{
> +	return a->nonce == b->nonce &&
> +		a->ipaddr.sin_addr.s_addr == b->ipaddr.sin_addr.s_addr;
> +}
> +
> +static inline bool ceph_entity_addr_equal(const struct ceph_entity_addr *a,
> +					  const struct ceph_entity_addr *b)
> +{
> +	return memcmp(a, b, sizeof(*a)) == 0;
> +}

sockaddr_in contains a __pad[] array.  Here you're assuming that code
elsewhere will initialise that padding array to a fixed, always-equal
value.  This seems fragile and possibly incorrect.

> +struct ceph_entity_inst {
> +	struct ceph_entity_name name;
> +	struct ceph_entity_addr addr;
> +} __attribute__ ((packed));
> +
> +
> +/* used by message exchange protocol */
> +#define CEPH_MSGR_TAG_READY         1  /* server->client: ready for messages */
> +#define CEPH_MSGR_TAG_RESETSESSION  2  /* server->client: reset, try again */
> +#define CEPH_MSGR_TAG_WAIT          3  /* server->client: wait for racing
> +					  incoming connection */
> +#define CEPH_MSGR_TAG_RETRY_SESSION 4  /* server->client + cseq: try again
> +					  with higher cseq */
> +#define CEPH_MSGR_TAG_RETRY_GLOBAL  5  /* server->client + gseq: try again
> +					  with higher gseq */
> +#define CEPH_MSGR_TAG_CLOSE         6  /* closing pipe */
> +#define CEPH_MSGR_TAG_MSG          10  /* message */
> +#define CEPH_MSGR_TAG_ACK          11  /* message ack */
> +#define CEPH_MSGR_TAG_KEEPALIVE    12  /* just a keepalive byte! */
> +#define CEPH_MSGR_TAG_BADPROTOVER  13  /* bad protocol version */

what happened to 7, 8 and 9?

> +
> +/*
> + * connection negotiation
> + */
> +struct ceph_msg_connect {
> +	__le32 host_type;  /* CEPH_ENTITY_TYPE_* */
> +	__le32 global_seq;
> +	__le32 connect_seq;
> +	__le32 protocol_version;
> +	__u8  flags;
> +} __attribute__ ((packed));
> +
> +struct ceph_msg_connect_reply {
> +	__u8 tag;
> +	__le32 global_seq;
> +	__le32 connect_seq;
> +	__le32 protocol_version;
> +	__u8 flags;
> +} __attribute__ ((packed));
> +
> +#define CEPH_MSG_CONNECT_LOSSY  1  /* messages i send may be safely dropped */
> +
> +
> +/*
> + * message header
> + */
> +struct ceph_msg_header {
> +	__le64 seq;       /* message seq# for this session */
> +	__le16 type;      /* message type */
> +	__le16 priority;  /* priority.  higher value == higher priority */
> +
> +	__le32 front_len; /* bytes in main payload */
> +	__le32 middle_len;/* bytes in middle payload */
> +	__le32 data_len;  /* bytes of data payload */
> +	__le16 data_off;  /* sender: include full offset;
> +			     receiver: mask against ~PAGE_MASK */
> +
> +	struct ceph_entity_inst src, orig_src;
> +	__le32 dst_erank;
> +	__le32 crc;       /* header crc32c */
> +} __attribute__ ((packed));

ooh, that one got documented a bit.

> +#define CEPH_MSG_PRIO_LOW     64
> +#define CEPH_MSG_PRIO_DEFAULT 127
> +#define CEPH_MSG_PRIO_HIGH    196
> +#define CEPH_MSG_PRIO_HIGHEST 255
> +
> +/*
> + * follows data payload
> + */
> +struct ceph_msg_footer {
> +	__le32 front_crc, middle_crc, data_crc;
> +	__u8 flags;
> +} __attribute__ ((packed));
> +
> +#define CEPH_MSG_FOOTER_COMPLETE  (1<<0)   /* msg wasn't aborted */
> +#define CEPH_MSG_FOOTER_NOCRC     (1<<1)   /* no data crc */

Does the overall on-the-wire format get documented anywhere?

> +
> +#endif
> diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h
> new file mode 100644
> index 0000000..a871577
> --- /dev/null
> +++ b/fs/ceph/rados.h
> @@ -0,0 +1,426 @@
> +#ifndef __RADOS_H
> +#define __RADOS_H
> +
> +/*
> + * Data types for RADOS, the distributed object storage layer used by
> + * the Ceph file system.
> + */

RADOS?

> +#include "msgr.h"
> +
> +/*
> + * fs id
> + */
> +struct ceph_fsid {
> +	unsigned char fsid[16];
> +};
> +
> +static inline int ceph_fsid_compare(const struct ceph_fsid *a,
> +				    const struct ceph_fsid *b)
> +{
> +	return memcmp(a, b, sizeof(*a));
> +}
> +
> +/*
> + * ino, object, etc.
> + */
> +typedef __le64 ceph_snapid_t;
> +#define CEPH_MAXSNAP ((__u64)(-3))
> +#define CEPH_SNAPDIR ((__u64)(-1))
> +#define CEPH_NOSNAP  ((__u64)(-2))
> +
> +struct ceph_timespec {
> +	__le32 tv_sec;
> +	__le32 tv_nsec;
> +} __attribute__ ((packed));
> +
> +
> +/*
> + * object layout - how objects are mapped into PGs
> + */
> +#define CEPH_OBJECT_LAYOUT_HASH     1
> +#define CEPH_OBJECT_LAYOUT_LINEAR   2
> +#define CEPH_OBJECT_LAYOUT_HASHINO  3
> +
> +/*
> + * pg layout -- how PGs are mapped onto (sets of) OSDs
> + */
> +#define CEPH_PG_LAYOUT_CRUSH  0
> +#define CEPH_PG_LAYOUT_HASH   1
> +#define CEPH_PG_LAYOUT_LINEAR 2
> +#define CEPH_PG_LAYOUT_HYBRID 3
> +
> +
> +/*
> + * placement group.
> + * we encode this into one __le64.
> + */
> +#define CEPH_PG_TYPE_REP     1
> +#define CEPH_PG_TYPE_RAID4   2
> +union ceph_pg {
> +	__u64 pg64;
> +	struct {
> +		__s16 preferred; /* preferred primary osd */
> +		__u16 ps;        /* placement seed */
> +		__u32 pool;      /* implies crush ruleset */
> +	} pg;
> +} __attribute__ ((packed));

Does the compiler reliably propagate the __packed attribute into the
nested struct, or is a separate __packed needed?

> +#define ceph_pg_is_rep(pg)   ((pg).pg.type == CEPH_PG_TYPE_REP)
> +#define ceph_pg_is_raid4(pg) ((pg).pg.type == CEPH_PG_TYPE_RAID4)

Could be written in nicer-to-read, more-likely-to-be-documented,
more-type-safe C! (and 10000 dittoes elsewhere).

>
> ...
>

  parent reply	other threads:[~2009-09-29 23:52 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-09-22 17:38 [PATCH 00/21] ceph distributed file system client Sage Weil
2009-09-22 17:38 ` [PATCH 01/21] ceph: documentation Sage Weil
2009-09-22 17:38   ` [PATCH 02/21] ceph: on-wire types Sage Weil
2009-09-22 17:38     ` [PATCH 03/21] ceph: client types Sage Weil
2009-09-22 17:38       ` [PATCH 04/21] ceph: ref counted buffer Sage Weil
2009-09-22 17:38         ` [PATCH 05/21] ceph: super.c Sage Weil
2009-09-22 17:38           ` [PATCH 06/21] ceph: inode operations Sage Weil
2009-09-22 17:38             ` [PATCH 07/21] ceph: directory operations Sage Weil
2009-09-22 17:38               ` [PATCH 08/21] ceph: file operations Sage Weil
2009-09-22 17:38                 ` [PATCH 09/21] ceph: address space operations Sage Weil
2009-09-22 17:38                   ` [PATCH 10/21] ceph: MDS client Sage Weil
2009-09-22 17:38                     ` [PATCH 11/21] ceph: OSD client Sage Weil
2009-09-22 17:38                       ` [PATCH 12/21] ceph: CRUSH mapping algorithm Sage Weil
2009-09-22 17:38                         ` [PATCH 13/21] ceph: monitor client Sage Weil
2009-09-22 17:38                           ` [PATCH 14/21] ceph: capability management Sage Weil
2009-09-22 17:38                             ` [PATCH 15/21] ceph: snapshot management Sage Weil
2009-09-22 17:38                               ` [PATCH 16/21] ceph: messenger library Sage Weil
2009-09-22 17:38                                 ` [PATCH 17/21] ceph: message pools Sage Weil
2009-09-22 17:38                                   ` [PATCH 18/21] ceph: nfs re-export support Sage Weil
2009-09-22 17:38                                     ` [PATCH 19/21] ceph: ioctls Sage Weil
2009-09-22 17:38                                       ` [PATCH 20/21] ceph: debugfs Sage Weil
2009-09-22 17:38                                         ` [PATCH 21/21] ceph: Kconfig, Makefile Sage Weil
2009-10-02  4:18                                       ` [PATCH 19/21] ceph: ioctls Andi Kleen
2009-10-02 15:55                                         ` Sage Weil
2009-10-02 16:36                                           ` Andi Kleen
2009-09-30  0:15             ` [PATCH 06/21] ceph: inode operations Andrew Morton
2009-09-30 17:45               ` Sage Weil
2009-12-03 20:27               ` ceph code review Sage Weil
2009-12-03 20:31                 ` Andrew Morton
2009-12-03 21:22                   ` Randy Dunlap
2009-09-30  0:13           ` [PATCH 05/21] ceph: super.c Andrew Morton
2009-09-30  0:02         ` [PATCH 04/21] ceph: ref counted buffer Andrew Morton
2009-09-22 18:08       ` [PATCH 03/21] ceph: client types Joe Perches
2009-09-29 23:57       ` Andrew Morton
2009-09-30 17:41         ` Sage Weil
2009-09-22 18:01     ` [PATCH 02/21] ceph: on-wire types Joe Perches
2009-09-22 18:21       ` Sage Weil
2009-09-29 23:52     ` Andrew Morton [this message]
2009-09-30 17:40       ` Sage Weil
  -- strict thread matches above, loose matches on Subject: below --
2009-10-05 22:50 [PATCH 00/21] ceph distributed file system client Sage Weil
2009-10-05 22:50 ` [PATCH 01/21] ceph: documentation Sage Weil
2009-10-05 22:50   ` [PATCH 02/21] ceph: on-wire types Sage Weil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090929165242.99e5bdf2.akpm@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sage@newdream.net \
    --cc=yehuda@newdream.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.