All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: Venu Busireddy <venu.busireddy@oracle.com>
Cc: Marcel Apfelbaum <marcel@redhat.com>,
	virtio-dev@lists.oasis-open.org, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v3 3/5] virtio_net: Add a query command for FAILOVER_STANDBY_CHANGED event.
Date: Mon, 7 Jan 2019 19:10:01 -0500	[thread overview]
Message-ID: <20190107190855-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <1546900184-27403-4-git-send-email-venu.busireddy@oracle.com>

On Mon, Jan 07, 2019 at 05:29:42PM -0500, Venu Busireddy wrote:
> Add a query command to check the status of the FAILOVER_STANDBY_CHANGED
> state of the virtio_net devices.
> 
> Signed-off-by: Venu Busireddy <venu.busireddy@oracle.com>
> ---
>  hw/net/virtio-net.c            | 16 +++++++++++
>  include/hw/virtio/virtio-net.h |  1 +
>  include/net/net.h              |  2 ++
>  net/net.c                      | 61 ++++++++++++++++++++++++++++++++++++++++++
>  qapi/net.json                  | 46 +++++++++++++++++++++++++++++++
>  5 files changed, 126 insertions(+)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index 7b1bcde..a4e07ac 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -263,9 +263,11 @@ static void virtio_net_failover_notify_event(VirtIONet *n, uint8_t status)
>           */
>          if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
>                  (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
> +            n->standby_enabled = true;
>              qapi_event_send_failover_standby_changed(!!ncn, ncn, path, true);
>          } else if ((!(status & VIRTIO_CONFIG_S_DRIVER_OK)) &&
>                  (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
> +            n->standby_enabled = false;
>              qapi_event_send_failover_standby_changed(!!ncn, ncn, path, false);
>          }
>      }

Here too, we are sending an endless stream of events.

Instead, let's send one "changed" event without data,
and then be silent until management runs the query command.




> @@ -448,6 +450,19 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
>      return info;
>  }
>  
> +static StandbyStatusInfo *virtio_net_query_standby_status(NetClientState *nc)
> +{
> +    StandbyStatusInfo *info;
> +    VirtIONet *n = qemu_get_nic_opaque(nc);
> +
> +    info = g_malloc0(sizeof(*info));
> +    info->device = g_strdup(n->netclient_name);
> +    info->path = g_strdup(object_get_canonical_path(OBJECT(n->qdev)));
> +    info->enabled = n->standby_enabled;
> +
> +    return info;
> +}
> +
>  static void virtio_net_reset(VirtIODevice *vdev)
>  {
>      VirtIONet *n = VIRTIO_NET(vdev);
> @@ -1923,6 +1938,7 @@ static NetClientInfo net_virtio_info = {
>      .receive = virtio_net_receive,
>      .link_status_changed = virtio_net_set_link_status,
>      .query_rx_filter = virtio_net_query_rxfilter,
> +    .query_standby_status = virtio_net_query_standby_status,
>  };
>  
>  static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
> diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
> index 4d7f3c8..9071e96 100644
> --- a/include/hw/virtio/virtio-net.h
> +++ b/include/hw/virtio/virtio-net.h
> @@ -103,6 +103,7 @@ typedef struct VirtIONet {
>      int announce_counter;
>      bool needs_vnet_hdr_swap;
>      bool mtu_bypass_backend;
> +    bool standby_enabled;
>  } VirtIONet;
>  
>  void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
> diff --git a/include/net/net.h b/include/net/net.h
> index ec13702..61e8513 100644
> --- a/include/net/net.h
> +++ b/include/net/net.h
> @@ -50,6 +50,7 @@ typedef void (NetCleanup) (NetClientState *);
>  typedef void (LinkStatusChanged)(NetClientState *);
>  typedef void (NetClientDestructor)(NetClientState *);
>  typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
> +typedef StandbyStatusInfo *(QueryStandbyStatus)(NetClientState *);
>  typedef bool (HasUfo)(NetClientState *);
>  typedef bool (HasVnetHdr)(NetClientState *);
>  typedef bool (HasVnetHdrLen)(NetClientState *, int);
> @@ -71,6 +72,7 @@ typedef struct NetClientInfo {
>      NetCleanup *cleanup;
>      LinkStatusChanged *link_status_changed;
>      QueryRxFilter *query_rx_filter;
> +    QueryStandbyStatus *query_standby_status;
>      NetPoll *poll;
>      HasUfo *has_ufo;
>      HasVnetHdr *has_vnet_hdr;
> diff --git a/net/net.c b/net/net.c
> index 1f7d626..fbf288e 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -1320,6 +1320,67 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
>      return filter_list;
>  }
>  
> +StandbyStatusInfoList *qmp_query_standby_status(bool has_device,
> +                                                const char *device,
> +                                                Error **errp)
> +{
> +    NetClientState *nc;
> +    StandbyStatusInfoList *status_list = NULL, *last_entry = NULL;
> +
> +    QTAILQ_FOREACH(nc, &net_clients, next) {
> +        StandbyStatusInfoList *entry;
> +        StandbyStatusInfo *info;
> +
> +        if (has_device && strcmp(nc->name, device) != 0) {
> +            continue;
> +        }
> +
> +        /* only query standby status information of NIC */
> +        if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
> +            if (has_device) {
> +                error_setg(errp, "net client(%s) isn't a NIC", device);
> +                return NULL;
> +            }
> +            continue;
> +        }
> +
> +        /*
> +         * only query information on queue 0 since the info is per nic,
> +         * not per queue.
> +         */
> +        if (nc->queue_index != 0) {
> +            continue;
> +        }
> +
> +        if (nc->info->query_standby_status) {
> +            info = nc->info->query_standby_status(nc);
> +            entry = g_malloc0(sizeof(*entry));
> +            entry->value = info;
> +
> +            if (!status_list) {
> +                status_list = entry;
> +            } else {
> +                last_entry->next = entry;
> +            }
> +            last_entry = entry;
> +        } else if (has_device) {
> +            error_setg(errp, "net client(%s) doesn't support"
> +                       " standby status querying", device);
> +            return NULL;
> +        }
> +
> +        if (has_device) {
> +            break;
> +        }
> +    }
> +
> +    if (status_list == NULL && has_device) {
> +        error_setg(errp, "invalid net client name: %s", device);
> +    }
> +
> +    return status_list;
> +}
> +
>  void hmp_info_network(Monitor *mon, const QDict *qdict)
>  {
>      NetClientState *nc, *peer;
> diff --git a/qapi/net.json b/qapi/net.json
> index 6a6d6fe..633ac87 100644
> --- a/qapi/net.json
> +++ b/qapi/net.json
> @@ -711,3 +711,49 @@
>  ##
>  { 'event': 'FAILOVER_STANDBY_CHANGED',
>    'data': {'*device': 'str', 'path': 'str', 'enabled': 'bool'} }
> +
> +##
> +# @StandbyStatusInfo:
> +#
> +# Standby status information for a virtio_net device.
> +#
> +# @device: Indicates the virtio_net device.
> +#
> +# @path: Indicates the device path.
> +#
> +# @enabled: true if the virtio_net driver is loaded.
> +#           false if the virtio_net driver is unloaded or the guest rebooted.
> +#
> +# Since: 4.0
> +##
> +{ 'struct': 'StandbyStatusInfo',
> +  'data': {'device': 'str', 'path': 'str', 'enabled': 'bool'} }
> +
> +##
> +# @query-standby-status:
> +#
> +# Return Standby status information for all virtio_net devices,
> +#        or for the given virtio_net device.
> +#
> +# @device: Name of the virtio_net device.
> +#
> +# Returns: List of @StandbyStatusInfo for all virtio_net devices,
> +#          or for the given virtio_net device.
> +#          Returns an error if the given @device doesn't exist.
> +#
> +# Since: 4.0
> +#
> +# Example:
> +#
> +# -> { "execute": "query-standby-status", "arguments": { "device": "net0" } }
> +# <- { "return": [
> +#                  { 'device': 'net0',
> +#                    'path': '/machine/peripheral/net0/virtio-backend',
> +#                    'enabled': 'true'
> +#                  }
> +#                ]
> +#    }
> +#
> +##
> +{ 'command': 'query-standby-status', 'data': { '*device': 'str' },
> +  'returns': ['StandbyStatusInfo'] }

WARNING: multiple messages have this Message-ID (diff)
From: "Michael S. Tsirkin" <mst@redhat.com>
To: Venu Busireddy <venu.busireddy@oracle.com>
Cc: Marcel Apfelbaum <marcel@redhat.com>,
	virtio-dev@lists.oasis-open.org, qemu-devel@nongnu.org
Subject: [virtio-dev] Re: [PATCH v3 3/5] virtio_net: Add a query command for FAILOVER_STANDBY_CHANGED event.
Date: Mon, 7 Jan 2019 19:10:01 -0500	[thread overview]
Message-ID: <20190107190855-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <1546900184-27403-4-git-send-email-venu.busireddy@oracle.com>

On Mon, Jan 07, 2019 at 05:29:42PM -0500, Venu Busireddy wrote:
> Add a query command to check the status of the FAILOVER_STANDBY_CHANGED
> state of the virtio_net devices.
> 
> Signed-off-by: Venu Busireddy <venu.busireddy@oracle.com>
> ---
>  hw/net/virtio-net.c            | 16 +++++++++++
>  include/hw/virtio/virtio-net.h |  1 +
>  include/net/net.h              |  2 ++
>  net/net.c                      | 61 ++++++++++++++++++++++++++++++++++++++++++
>  qapi/net.json                  | 46 +++++++++++++++++++++++++++++++
>  5 files changed, 126 insertions(+)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index 7b1bcde..a4e07ac 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -263,9 +263,11 @@ static void virtio_net_failover_notify_event(VirtIONet *n, uint8_t status)
>           */
>          if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
>                  (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
> +            n->standby_enabled = true;
>              qapi_event_send_failover_standby_changed(!!ncn, ncn, path, true);
>          } else if ((!(status & VIRTIO_CONFIG_S_DRIVER_OK)) &&
>                  (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
> +            n->standby_enabled = false;
>              qapi_event_send_failover_standby_changed(!!ncn, ncn, path, false);
>          }
>      }

Here too, we are sending an endless stream of events.

Instead, let's send one "changed" event without data,
and then be silent until management runs the query command.




> @@ -448,6 +450,19 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
>      return info;
>  }
>  
> +static StandbyStatusInfo *virtio_net_query_standby_status(NetClientState *nc)
> +{
> +    StandbyStatusInfo *info;
> +    VirtIONet *n = qemu_get_nic_opaque(nc);
> +
> +    info = g_malloc0(sizeof(*info));
> +    info->device = g_strdup(n->netclient_name);
> +    info->path = g_strdup(object_get_canonical_path(OBJECT(n->qdev)));
> +    info->enabled = n->standby_enabled;
> +
> +    return info;
> +}
> +
>  static void virtio_net_reset(VirtIODevice *vdev)
>  {
>      VirtIONet *n = VIRTIO_NET(vdev);
> @@ -1923,6 +1938,7 @@ static NetClientInfo net_virtio_info = {
>      .receive = virtio_net_receive,
>      .link_status_changed = virtio_net_set_link_status,
>      .query_rx_filter = virtio_net_query_rxfilter,
> +    .query_standby_status = virtio_net_query_standby_status,
>  };
>  
>  static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
> diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
> index 4d7f3c8..9071e96 100644
> --- a/include/hw/virtio/virtio-net.h
> +++ b/include/hw/virtio/virtio-net.h
> @@ -103,6 +103,7 @@ typedef struct VirtIONet {
>      int announce_counter;
>      bool needs_vnet_hdr_swap;
>      bool mtu_bypass_backend;
> +    bool standby_enabled;
>  } VirtIONet;
>  
>  void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
> diff --git a/include/net/net.h b/include/net/net.h
> index ec13702..61e8513 100644
> --- a/include/net/net.h
> +++ b/include/net/net.h
> @@ -50,6 +50,7 @@ typedef void (NetCleanup) (NetClientState *);
>  typedef void (LinkStatusChanged)(NetClientState *);
>  typedef void (NetClientDestructor)(NetClientState *);
>  typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
> +typedef StandbyStatusInfo *(QueryStandbyStatus)(NetClientState *);
>  typedef bool (HasUfo)(NetClientState *);
>  typedef bool (HasVnetHdr)(NetClientState *);
>  typedef bool (HasVnetHdrLen)(NetClientState *, int);
> @@ -71,6 +72,7 @@ typedef struct NetClientInfo {
>      NetCleanup *cleanup;
>      LinkStatusChanged *link_status_changed;
>      QueryRxFilter *query_rx_filter;
> +    QueryStandbyStatus *query_standby_status;
>      NetPoll *poll;
>      HasUfo *has_ufo;
>      HasVnetHdr *has_vnet_hdr;
> diff --git a/net/net.c b/net/net.c
> index 1f7d626..fbf288e 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -1320,6 +1320,67 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
>      return filter_list;
>  }
>  
> +StandbyStatusInfoList *qmp_query_standby_status(bool has_device,
> +                                                const char *device,
> +                                                Error **errp)
> +{
> +    NetClientState *nc;
> +    StandbyStatusInfoList *status_list = NULL, *last_entry = NULL;
> +
> +    QTAILQ_FOREACH(nc, &net_clients, next) {
> +        StandbyStatusInfoList *entry;
> +        StandbyStatusInfo *info;
> +
> +        if (has_device && strcmp(nc->name, device) != 0) {
> +            continue;
> +        }
> +
> +        /* only query standby status information of NIC */
> +        if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
> +            if (has_device) {
> +                error_setg(errp, "net client(%s) isn't a NIC", device);
> +                return NULL;
> +            }
> +            continue;
> +        }
> +
> +        /*
> +         * only query information on queue 0 since the info is per nic,
> +         * not per queue.
> +         */
> +        if (nc->queue_index != 0) {
> +            continue;
> +        }
> +
> +        if (nc->info->query_standby_status) {
> +            info = nc->info->query_standby_status(nc);
> +            entry = g_malloc0(sizeof(*entry));
> +            entry->value = info;
> +
> +            if (!status_list) {
> +                status_list = entry;
> +            } else {
> +                last_entry->next = entry;
> +            }
> +            last_entry = entry;
> +        } else if (has_device) {
> +            error_setg(errp, "net client(%s) doesn't support"
> +                       " standby status querying", device);
> +            return NULL;
> +        }
> +
> +        if (has_device) {
> +            break;
> +        }
> +    }
> +
> +    if (status_list == NULL && has_device) {
> +        error_setg(errp, "invalid net client name: %s", device);
> +    }
> +
> +    return status_list;
> +}
> +
>  void hmp_info_network(Monitor *mon, const QDict *qdict)
>  {
>      NetClientState *nc, *peer;
> diff --git a/qapi/net.json b/qapi/net.json
> index 6a6d6fe..633ac87 100644
> --- a/qapi/net.json
> +++ b/qapi/net.json
> @@ -711,3 +711,49 @@
>  ##
>  { 'event': 'FAILOVER_STANDBY_CHANGED',
>    'data': {'*device': 'str', 'path': 'str', 'enabled': 'bool'} }
> +
> +##
> +# @StandbyStatusInfo:
> +#
> +# Standby status information for a virtio_net device.
> +#
> +# @device: Indicates the virtio_net device.
> +#
> +# @path: Indicates the device path.
> +#
> +# @enabled: true if the virtio_net driver is loaded.
> +#           false if the virtio_net driver is unloaded or the guest rebooted.
> +#
> +# Since: 4.0
> +##
> +{ 'struct': 'StandbyStatusInfo',
> +  'data': {'device': 'str', 'path': 'str', 'enabled': 'bool'} }
> +
> +##
> +# @query-standby-status:
> +#
> +# Return Standby status information for all virtio_net devices,
> +#        or for the given virtio_net device.
> +#
> +# @device: Name of the virtio_net device.
> +#
> +# Returns: List of @StandbyStatusInfo for all virtio_net devices,
> +#          or for the given virtio_net device.
> +#          Returns an error if the given @device doesn't exist.
> +#
> +# Since: 4.0
> +#
> +# Example:
> +#
> +# -> { "execute": "query-standby-status", "arguments": { "device": "net0" } }
> +# <- { "return": [
> +#                  { 'device': 'net0',
> +#                    'path': '/machine/peripheral/net0/virtio-backend',
> +#                    'enabled': 'true'
> +#                  }
> +#                ]
> +#    }
> +#
> +##
> +{ 'command': 'query-standby-status', 'data': { '*device': 'str' },
> +  'returns': ['StandbyStatusInfo'] }

---------------------------------------------------------------------
To unsubscribe, e-mail: virtio-dev-unsubscribe@lists.oasis-open.org
For additional commands, e-mail: virtio-dev-help@lists.oasis-open.org


  reply	other threads:[~2019-01-08  0:10 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-07 22:29 [Qemu-devel] [PATCH v3 0/5] Support for datapath switching during live migration Venu Busireddy
2019-01-07 22:29 ` [virtio-dev] " Venu Busireddy
2019-01-07 22:29 ` [Qemu-devel] [PATCH v3 1/5] virtio_net: Add VIRTIO_NET_F_STANDBY feature bit Venu Busireddy
2019-01-07 22:29   ` [virtio-dev] " Venu Busireddy
2019-01-08 16:56   ` [Qemu-devel] " Dongli Zhang
2019-01-08 17:25     ` Venu Busireddy
2019-01-08 17:25       ` [virtio-dev] " Venu Busireddy
2019-01-09  0:14       ` Dongli Zhang
2019-01-09  0:18         ` Samudrala, Sridhar
2019-01-09  0:18           ` [virtio-dev] " Samudrala, Sridhar
2019-01-09  0:39           ` Dongli Zhang
2019-01-09  4:17             ` Michael S. Tsirkin
2019-01-09  4:17               ` [virtio-dev] " Michael S. Tsirkin
2019-01-07 22:29 ` [Qemu-devel] [PATCH v3 2/5] virtio_net: Add support for "Data Path Switching" during Live Migration Venu Busireddy
2019-01-07 22:29   ` [virtio-dev] " Venu Busireddy
2019-01-09 13:39   ` [Qemu-devel] " Cornelia Huck
2019-01-09 13:39     ` Cornelia Huck
2019-01-09 15:56   ` [Qemu-devel] " Michael S. Tsirkin
2019-01-09 15:56     ` [virtio-dev] " Michael S. Tsirkin
2019-01-11  2:09     ` [Qemu-devel] " si-wei liu
2019-01-11  2:09       ` [virtio-dev] " si-wei liu
2019-01-11  3:20       ` Michael S. Tsirkin
2019-01-11  3:20         ` [virtio-dev] " Michael S. Tsirkin
2019-01-11  7:09         ` [Qemu-devel] [virtio-dev] " si-wei liu
2019-01-11  7:09           ` [virtio-dev] Re: [Qemu-devel] " si-wei liu
2019-01-14 11:10           ` [Qemu-devel] [virtio-dev] " Roman Kagan
2019-01-07 22:29 ` [Qemu-devel] [PATCH v3 3/5] virtio_net: Add a query command for FAILOVER_STANDBY_CHANGED event Venu Busireddy
2019-01-07 22:29   ` [virtio-dev] " Venu Busireddy
2019-01-08  0:10   ` Michael S. Tsirkin [this message]
2019-01-08  0:10     ` [virtio-dev] " Michael S. Tsirkin
2019-01-07 22:29 ` [Qemu-devel] [PATCH v3 4/5] vfio-pci: Add FAILOVER_PRIMARY_CHANGED event to shorten downtime during failover Venu Busireddy
2019-01-07 22:29   ` [virtio-dev] " Venu Busireddy
2019-01-07 23:17   ` [Qemu-devel] " Alex Williamson
2019-01-07 23:22     ` Michael S. Tsirkin
2019-01-07 23:22       ` [virtio-dev] " Michael S. Tsirkin
2019-01-07 23:41       ` Alex Williamson
2019-01-08  0:12         ` Michael S. Tsirkin
2019-01-08  0:12           ` [virtio-dev] " Michael S. Tsirkin
2019-01-08  0:24           ` Alex Williamson
2019-01-08  0:43             ` Michael S. Tsirkin
2019-01-08  0:43               ` [virtio-dev] " Michael S. Tsirkin
2019-01-08  1:13         ` si-wei liu
2019-01-08  1:13           ` [virtio-dev] " si-wei liu
2019-01-07 22:29 ` [Qemu-devel] [PATCH v3 5/5] pci: query command extension to check the bus master enabling status of the failover-primary device Venu Busireddy
2019-01-07 22:29   ` [virtio-dev] " Venu Busireddy
2019-01-07 23:32 ` [Qemu-devel] [PATCH v3 0/5] Support for datapath switching during live migration Michael S. Tsirkin
2019-01-07 23:32   ` [virtio-dev] " Michael S. Tsirkin
2019-01-08  1:45   ` [Qemu-devel] " si-wei liu
2019-01-08  1:45     ` si-wei liu
2019-01-08  2:25     ` [Qemu-devel] " Michael S. Tsirkin
2019-01-08  2:25       ` Michael S. Tsirkin
2019-01-09  4:55       ` [Qemu-devel] " si-wei liu
2019-01-09  4:55         ` si-wei liu
2019-01-09 13:39         ` [Qemu-devel] " Michael S. Tsirkin
2019-01-09 13:39           ` Michael S. Tsirkin
2019-01-11  6:57           ` [Qemu-devel] " si-wei liu
2019-01-11  6:57             ` si-wei liu

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=20190107190855-mutt-send-email-mst@kernel.org \
    --to=mst@redhat.com \
    --cc=marcel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=venu.busireddy@oracle.com \
    --cc=virtio-dev@lists.oasis-open.org \
    /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.