mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: tarunsahu@google.com
To: Pasha Tatashin <pasha.tatashin@soleen.com>,
	linux-kselftest@vger.kernel.org,  rppt@kernel.org,
	shuah@kernel.org, akpm@linux-foundation.org,  linux-mm@kvack.org,
	vipinsh@google.com, linux-kernel@vger.kernel.org,
	 dmatlack@google.com, kexec@lists.infradead.org,
	pratyush@kernel.org,  graf@amazon.com
Subject: Re: [PATCH v1 3/7] liveupdate: Pass user argument to file preserve and retrieve
Date: Mon, 19 Oct 2026 19:26:11 +0000	[thread overview]
Message-ID: <9huzwlrdh4q4.fsf@tarunix.c.googlers.com> (raw)
In-Reply-To: <20261015093816.2438942-4-pasha.tatashin@soleen.com>


As Per Today's Hypervisor liveupdate meeting, This patch will be picked
by @dmatlack@google.com for latest tmpfs preservation series.

I have a suggestion to make here.

Let's send this patch as a saperate patch so that I can also use this in
my guest_memfd preservation work. This will help landing this feature
sooner and unblock other subsystem who needs it.

Let me know what you think?

~Tarun


Pasha Tatashin <pasha.tatashin@soleen.com> writes:

> Allow userspace to pass a file-handler-specific argument structure when
> preserving or retrieving an FD via LUO. File handlers may need
> additional context during preservation or retrieval, such as a target
> FD, device location, or handler-specific parameters, without introducing
> separate ioctls for each file type. Whether the argument is required,
> optional, or unused is defined by the file handler.
>
> Extend struct liveupdate_session_preserve_fd and
> struct liveupdate_session_retrieve_fd with a 64-bit arg field pointing
> to a handler-defined argument structure that begins with a 32-bit size
> field. Because IOCTL_OP validates a minimum size up to the token field,
> existing callers passing the 16-byte struct remain compatible and have
> arg zero-filled by copy_struct_from_user().
>
> Add parg and rarg (struct liveupdate_arg_spec) to
> struct liveupdate_file_ops, initialized via LIVEUPDATE_ARG(_struct,
> _min_last), which records the full struct size and minimum required size
> while enforcing at compile time that handler argument structures begin
> with a 32-bit size field. When arg is non-zero, LUO validates the
> minimum size and copies the user argument via copy_struct_from_user() up
> to the handler's expected size and passes the kernel copy in
> struct liveupdate_file_op_args, or returns -EOPNOTSUPP if the handler
> does not accept an argument.
>
> Also provide liveupdate_claim_file() and liveupdate_unclaim_file() so
> handlers whose .retrieve() callback restores state into an existing
> user-supplied file descriptor can claim it in luo_preserved_files before
> modifying the file.
>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> ---
>  include/linux/liveupdate.h       | 60 +++++++++++++++++++
>  include/uapi/linux/liveupdate.h  |  6 ++
>  kernel/liveupdate/luo_file.c     | 99 +++++++++++++++++++++++++++++---
>  kernel/liveupdate/luo_internal.h |  5 +-
>  kernel/liveupdate/luo_session.c  |  6 +-
>  5 files changed, 164 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
> index 885ec0bbc4b8c..52d3f453ae011 100644
> --- a/include/linux/liveupdate.h
> +++ b/include/linux/liveupdate.h
> @@ -22,6 +22,36 @@ struct liveupdate_flb;
>  struct liveupdate_session;
>  struct file;
>  
> +/**
> + * struct liveupdate_arg_spec - Specification of a file handler user argument.
> + * @size:     Full size of the argument structure in the current kernel.
> + * @min_size: Minimum valid user-supplied size (offsetofend of the last
> + *            required field).
> + */
> +struct liveupdate_arg_spec {
> +	size_t size;
> +	size_t min_size;
> +};
> +
> +/**
> + * LIVEUPDATE_ARG - Validated specification of a file handler argument struct.
> + * @_struct:   The argument structure type.
> + * @_min_last: The last required member in @_struct.
> + *
> + * All file handler argument structures passed via
> + * &liveupdate_session_preserve_fd.arg or &liveupdate_session_retrieve_fd.arg
> + * must begin with a 32-bit @size field so they can be extended compatibly.
> + * Use this macro to initialize &liveupdate_file_ops.parg and
> + * &liveupdate_file_ops.rarg.
> + */
> +#define LIVEUPDATE_ARG(_struct, _min_last)				\
> +	{								\
> +		.size = sizeof(_struct) +				\
> +			BUILD_BUG_ON_ZERO(offsetof(_struct, size) != 0) + \
> +			BUILD_BUG_ON_ZERO(sizeof_field(_struct, size) != sizeof(u32)), \
> +		.min_size = offsetofend(_struct, _min_last),		\
> +	}
> +
>  /**
>   * struct liveupdate_file_op_args - Arguments for file operation callbacks.
>   * @handler:          The file handler being called.
> @@ -39,6 +69,11 @@ struct file;
>   *                    is not preserved. Set by the handler's .preserve()
>   *                    callback, and must be freed in the handler's
>   *                    .unpreserve() callback.
> + * @arg:              Kernel copy of the user-provided argument for .preserve()
> + *                    and .retrieve(), or %NULL if none was provided. Allocated
> + *                    to the handler's expected argument size with any trailing
> + *                    bytes beyond the user-supplied size zero-filled. Valid
> + *                    only for the duration of the callback.
>   *
>   * This structure bundles all parameters for the file operation callbacks.
>   * The 'data' and 'file' fields are used for both input and output.
> @@ -49,6 +84,7 @@ struct liveupdate_file_op_args {
>  	struct file *file;
>  	u64 serialized_data;
>  	void *private_data;
> +	const void *arg;
>  };
>  
>  /**
> @@ -67,6 +103,12 @@ struct liveupdate_file_op_args {
>   *                resources in the session.
>   * @finish:       Required. Final cleanup in the new kernel.
>   * @get_id:       Optional. Returns a unique identifier for the file.
> + * @parg:         Optional. Specification of the user argument structure
> + *                accepted by @preserve, initialized via LIVEUPDATE_ARG(). If
> + *                unset, non-zero user arguments are rejected with -EOPNOTSUPP.
> + * @rarg:         Optional. Specification of the user argument structure
> + *                accepted by @retrieve, initialized via LIVEUPDATE_ARG(). If
> + *                unset, non-zero user arguments are rejected with -EOPNOTSUPP.
>   * @owner:        Module reference
>   *
>   * All operations (except can_preserve) receive a pointer to a
> @@ -83,6 +125,8 @@ struct liveupdate_file_ops {
>  	bool (*can_finish)(struct liveupdate_file_op_args *args);
>  	void (*finish)(struct liveupdate_file_op_args *args);
>  	unsigned long (*get_id)(struct file *file);
> +	struct liveupdate_arg_spec parg;
> +	struct liveupdate_arg_spec rarg;
>  	struct module *owner;
>  };
>  
> @@ -237,6 +281,11 @@ int liveupdate_reboot(void);
>  int liveupdate_register_file_handler(struct liveupdate_file_handler *fh);
>  void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh);
>  
> +int liveupdate_claim_file(struct liveupdate_file_handler *fh,
> +			  struct file *file);
> +void liveupdate_unclaim_file(struct liveupdate_file_handler *fh,
> +			     struct file *file);
> +
>  int liveupdate_register_flb(struct liveupdate_file_handler *fh,
>  			    struct liveupdate_flb *flb);
>  void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
> @@ -269,6 +318,17 @@ static inline void liveupdate_unregister_file_handler(struct liveupdate_file_han
>  {
>  }
>  
> +static inline int liveupdate_claim_file(struct liveupdate_file_handler *fh,
> +					struct file *file)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void liveupdate_unclaim_file(struct liveupdate_file_handler *fh,
> +					   struct file *file)
> +{
> +}
> +
>  static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh,
>  					  struct liveupdate_flb *flb)
>  {
> diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
> index 1644c502521e0..57a3c129d5a9d 100644
> --- a/include/uapi/linux/liveupdate.h
> +++ b/include/uapi/linux/liveupdate.h
> @@ -127,6 +127,8 @@ struct liveupdate_ioctl_retrieve_session {
>   * @size:  Input; sizeof(struct liveupdate_session_preserve_fd)
>   * @fd:    Input; The user-space file descriptor to be preserved.
>   * @token: Input; An opaque, unique token for preserved resource.
> + * @arg:   Input; User-space pointer to a file-handler-specific argument
> + *         structure starting with a 32-bit size field, or 0 if unused.
>   *
>   * Holds parameters for preserving a file descriptor.
>   *
> @@ -150,6 +152,7 @@ struct liveupdate_session_preserve_fd {
>  	__u32		size;
>  	__s32		fd;
>  	__aligned_u64	token;
> +	__aligned_u64	arg;
>  };
>  
>  #define LIVEUPDATE_SESSION_PRESERVE_FD					\
> @@ -161,6 +164,8 @@ struct liveupdate_session_preserve_fd {
>   * @fd:    Output; The new file descriptor representing the fully restored
>   *         kernel resource.
>   * @token: Input; An opaque, token that was used to preserve the resource.
> + * @arg:   Input; User-space pointer to a file-handler-specific argument
> + *         structure starting with a 32-bit size field, or 0 if unused.
>   *
>   * Retrieve a previously preserved file descriptor.
>   *
> @@ -180,6 +185,7 @@ struct liveupdate_session_retrieve_fd {
>  	__u32		size;
>  	__s32		fd;
>  	__aligned_u64	token;
> +	__aligned_u64	arg;
>  };
>  
>  #define LIVEUPDATE_SESSION_RETRIEVE_FD					\
> diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
> index 3c1c86ff14d69..e3bfdbc6baedc 100644
> --- a/kernel/liveupdate/luo_file.c
> +++ b/kernel/liveupdate/luo_file.c
> @@ -112,6 +112,7 @@
>  #include <linux/xarray.h>
>  #include <linux/slab.h>
>  #include <linux/string.h>
> +#include <linux/uaccess.h>
>  #include "luo_internal.h"
>  
>  static LIST_HEAD(luo_file_handler_list);
> @@ -176,6 +177,36 @@ static unsigned long luo_get_id(struct liveupdate_file_handler *fh,
>  	return fh->ops->get_id ? fh->ops->get_id(file) : (unsigned long)file;
>  }
>  
> +/**
> + * liveupdate_claim_file - Claim a file in the LUO preserved files table.
> + * @fh:   The file handler claiming @file.
> + * @file: The file to claim.
> + *
> + * File handlers whose .retrieve() callback restores state into an existing
> + * user-supplied &struct file (rather than allocating a new one) should call
> + * this before modifying @file to ensure @file is not already managed by LUO.
> + *
> + * Return: 0 on success, -EBUSY if @file is already managed by LUO, or a
> + *         negative errno on allocation failure.
> + */
> +int liveupdate_claim_file(struct liveupdate_file_handler *fh,
> +			  struct file *file)
> +{
> +	return xa_insert(&luo_preserved_files, luo_get_id(fh, file),
> +			 file, GFP_KERNEL);
> +}
> +
> +/**
> + * liveupdate_unclaim_file - Release a claimed file from the LUO preserved files table.
> + * @fh:   The file handler that claimed @file.
> + * @file: The file to release.
> + */
> +void liveupdate_unclaim_file(struct liveupdate_file_handler *fh,
> +			     struct file *file)
> +{
> +	xa_erase(&luo_preserved_files, luo_get_id(fh, file));
> +}
> +
>  static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
>  {
>  	struct luo_file *iter;
> @@ -188,11 +219,45 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
>  	return false;
>  }
>  
> +static int luo_copy_user_arg(u64 user_arg, const void **argp,
> +			     const struct liveupdate_arg_spec *spec)
> +{
> +	void *karg __free(kfree) = NULL;
> +	void __user *uptr;
> +	u32 user_size;
> +	int err;
> +
> +	if (!user_arg)
> +		return 0;
> +	if (!spec->size)
> +		return -EOPNOTSUPP;
> +
> +	uptr = u64_to_user_ptr(user_arg);
> +	if (get_user(user_size, (u32 __user *)uptr))
> +		return -EFAULT;
> +	if (user_size < spec->min_size)
> +		return -EINVAL;
> +	if (user_size > PAGE_SIZE)
> +		return -E2BIG;
> +
> +	karg = kmalloc(spec->size, GFP_KERNEL);
> +	if (!karg)
> +		return -ENOMEM;
> +
> +	err = copy_struct_from_user(karg, spec->size, uptr, user_size);
> +	if (err)
> +		return err;
> +
> +	*argp = no_free_ptr(karg);
> +	return 0;
> +}
> +
>  /**
>   * luo_preserve_file - Initiate the preservation of a file descriptor.
>   * @file_set: The file_set to which the preserved file will be added.
>   * @token:    A unique, user-provided identifier for the file.
>   * @fd:       The file descriptor to be preserved.
> + * @arg:      User-space pointer to the handler argument structure, or 0.
>   *
>   * This function orchestrates the first phase of preserving a file. Upon entry,
>   * it takes a reference to the 'struct file' via fget(), effectively making LUO
> @@ -208,9 +273,11 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
>   *    (allocates if needed).
>   * 3. Iterates through registered handlers, calling can_preserve() to find one
>   *    compatible with the given @fd.
> - * 4. Calls the handler's .preserve() operation, which saves the file's state
> + * 4. Copies the user argument structure (if @arg is non-zero) according to the
> + *    handler's parg specification.
> + * 5. Calls the handler's .preserve() operation, which saves the file's state
>   *    and returns an opaque private data handle.
> - * 5. Adds the new instance to the file_set's internal list.
> + * 6. Adds the new instance to the file_set's internal list.
>   *
>   * On success, LUO takes a reference to the 'struct file' and considers it
>   * under its management until it is unpreserved or finished.
> @@ -225,10 +292,12 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
>   *         -EBADF if the file descriptor is invalid.
>   *         -ENOSPC if the file_set is full.
>   *         -ENOENT if no compatible handler is found.
> + *         -EOPNOTSUPP if @arg is non-zero and the handler does not accept an arg.
>   *         -ENOMEM on memory allocation failure.
>   *         Other erros might be returned by .preserve().
>   */
> -int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
> +int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd,
> +		      u64 arg)
>  {
>  	struct liveupdate_file_op_args args = {0};
>  	struct liveupdate_file_handler *fh;
> @@ -264,10 +333,14 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
>  	if (err)
>  		goto err_fput;
>  
> +	err = luo_copy_user_arg(arg, &args.arg, &fh->ops->parg);
> +	if (err)
> +		goto err_module_put;
> +
>  	err = xa_insert(&luo_preserved_files, luo_get_id(fh, file),
>  			file, GFP_KERNEL);
>  	if (err)
> -		goto err_module_put;
> +		goto err_free_arg;
>  
>  	err = luo_flb_file_preserve(fh);
>  	if (err)
> @@ -290,6 +363,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
>  	if (err)
>  		goto err_kfree;
>  
> +	kfree(args.arg);
>  	luo_file->serialized_data = args.serialized_data;
>  	luo_file->private_data = args.private_data;
>  	list_add_tail(&luo_file->list, &file_set->files_list);
> @@ -303,6 +377,8 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
>  	luo_flb_file_unpreserve(fh);
>  err_erase_xa:
>  	xa_erase(&luo_preserved_files, luo_get_id(fh, file));
> +err_free_arg:
> +	kfree(args.arg);
>  err_module_put:
>  	module_put(fh->ops->owner);
>  err_fput:
> @@ -533,6 +609,7 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
>   * luo_retrieve_file - Restores a preserved file from a file_set by its token.
>   * @file_set: The file_set from which to retrieve the file.
>   * @token:    The unique token identifying the file to be restored.
> + * @arg:      User-space pointer to the handler argument structure, or 0.
>   * @filep:    Output parameter; on success, this is populated with a pointer
>   *            to the newly retrieved 'struct file'.
>   *
> @@ -552,9 +629,10 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
>   *          kernel.
>   * Return: 0 on success. Returns a negative errno on failure:
>   *         -ENOENT if no file with the matching token is found.
> + *         -EOPNOTSUPP if @arg is non-zero and the handler does not accept an arg.
>   *         Any error code returned by the handler's .retrieve() op.
>   */
> -int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
> +int luo_retrieve_file(struct luo_file_set *file_set, u64 token, u64 arg,
>  		      struct file **filep)
>  {
>  	struct liveupdate_file_op_args args = {0};
> @@ -591,9 +669,14 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
>  		return 0;
>  	}
>  
> +	err = luo_copy_user_arg(arg, &args.arg, &luo_file->fh->ops->rarg);
> +	if (err)
> +		return err;
> +
>  	args.handler = luo_file->fh;
>  	args.serialized_data = luo_file->serialized_data;
>  	err = luo_file->fh->ops->retrieve(&args);
> +	kfree(args.arg);
>  	if (err) {
>  		/* Keep the error code for later use. */
>  		luo_file->retrieve_status = err;
> @@ -604,9 +687,9 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
>  	/* Get reference so we can keep this file in LUO until finish */
>  	get_file(luo_file->file);
>  
> -	WARN_ON(xa_insert(&luo_preserved_files,
> -			  luo_get_id(luo_file->fh, luo_file->file),
> -			  luo_file->file, GFP_KERNEL));
> +	if (xa_load(&luo_preserved_files,
> +		    luo_get_id(luo_file->fh, luo_file->file)) != luo_file->file)
> +		WARN_ON(liveupdate_claim_file(luo_file->fh, luo_file->file));
>  
>  	*filep = luo_file->file;
>  	luo_file->retrieve_status = 1;
> diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> index b42369c3b1f30..3bd9ef442934e 100644
> --- a/kernel/liveupdate/luo_internal.h
> +++ b/kernel/liveupdate/luo_internal.h
> @@ -87,13 +87,14 @@ int __init luo_session_setup_incoming(u64 sessions_pa);
>  int luo_session_serialize(void);
>  int luo_session_deserialize(void);
>  
> -int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd);
> +int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd,
> +		      u64 arg);
>  void luo_file_unpreserve_files(struct luo_file_set *file_set);
>  int luo_file_freeze(struct luo_file_set *file_set,
>  		    struct luo_file_set_ser *file_set_ser);
>  void luo_file_unfreeze(struct luo_file_set *file_set,
>  		       struct luo_file_set_ser *file_set_ser);
> -int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
> +int luo_retrieve_file(struct luo_file_set *file_set, u64 token, u64 arg,
>  		      struct file **filep);
>  int luo_file_finish(struct luo_file_set *file_set);
>  int luo_file_deserialize(struct luo_file_set *file_set,
> diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
> index 403906937bc0f..74f8a1bf1da8c 100644
> --- a/kernel/liveupdate/luo_session.c
> +++ b/kernel/liveupdate/luo_session.c
> @@ -284,7 +284,8 @@ static int luo_session_preserve_fd(struct luo_session *session,
>  	if (session->frozen)
>  		return -EBUSY;
>  
> -	err = luo_preserve_file(&session->file_set, argp->token, argp->fd);
> +	err = luo_preserve_file(&session->file_set, argp->token, argp->fd,
> +				argp->arg);
>  	if (err)
>  		return err;
>  
> @@ -307,7 +308,8 @@ static int luo_session_retrieve_fd(struct luo_session *session,
>  		return argp->fd;
>  
>  	mutex_lock(&session->mutex);
> -	err = luo_retrieve_file(&session->file_set, argp->token, &file);
> +	err = luo_retrieve_file(&session->file_set, argp->token, argp->arg,
> +				&file);
>  	mutex_unlock(&session->mutex);
>  	if (err < 0)
>  		goto err_put_fd;
> -- 
> 2.56.0.385.gd3acb90ef8-goog

  reply	other threads:[~2026-10-19 19:26 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15  9:38 [PATCH v1 0/7] liveupdate: tmpfs preservation and early freeze Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 1/7] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl to freeze sessions early Pasha Tatashin
2026-10-20 11:08   ` Pratyush Yadav
2026-10-20 14:30     ` Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 2/7] selftests: liveupdate: Add tests for LIVEUPDATE_SESSION_FREEZE Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 3/7] liveupdate: Pass user argument to file preserve and retrieve Pasha Tatashin
2026-10-19 19:26   ` tarunsahu [this message]
2026-10-19 19:31     ` Pasha Tatashin
2026-10-20 10:20       ` Pratyush Yadav
2026-10-15  9:38 ` [PATCH v1 4/7] selftests: liveupdate: Add tests for preserve and retrieve user argument Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 5/7] liveupdate: Allow file handlers to mark retrieve failures as retryable Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 6/7] mm/memfd_luo: Support tmpfs preservation and retrieval Pasha Tatashin
2026-10-15  9:38 ` [PATCH v1 7/7] selftests: liveupdate: Add test coverage for " Pasha Tatashin
2026-10-15 17:48 ` [PATCH v1 0/7] liveupdate: tmpfs preservation and early freeze David Matlack
2026-10-15 18:23   ` Pasha Tatashin
2026-10-15 18:54     ` David Matlack
2026-10-15 20:38       ` Pasha Tatashin
2026-10-15 21:53         ` David Matlack

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=9huzwlrdh4q4.fsf@tarunix.c.googlers.com \
    --to=tarunsahu@google.com \
    --cc=akpm@linux-foundation.org \
    --cc=dmatlack@google.com \
    --cc=graf@amazon.com \
    --cc=kexec@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=pasha.tatashin@soleen.com \
    --cc=pratyush@kernel.org \
    --cc=rppt@kernel.org \
    --cc=shuah@kernel.org \
    --cc=vipinsh@google.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®