* [PATCH V2 02/18] Drivers: hv: Add KVP definitions for IP address injection
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Add the necessary definitions for supporting the IP injection functionality.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/hv/hv_util.c | 4 +-
include/linux/hyperv.h | 76 ++++++++++++++++++++++++++++++++++++++++++++-
tools/hv/hv_kvp_daemon.c | 2 +-
3 files changed, 77 insertions(+), 5 deletions(-)
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index d3ac6a4..a0667de 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev,
(struct hv_util_service *)dev_id->driver_data;
int ret;
- srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!srv->recv_buffer)
return -ENOMEM;
if (srv->util_init) {
@@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev,
}
}
- ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0,
+ ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 68ed7f7..11afc4e 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -122,12 +122,53 @@
#define REG_U32 4
#define REG_U64 8
+/*
+ * As we look at expanding the KVP functionality to include
+ * IP injection functionality, we need to maintain binary
+ * compatibility with older daemons.
+ *
+ * The KVP opcodes are defined by the host and it was unfortunate
+ * that I chose to treat the registration operation as part of the
+ * KVP operations defined by the host.
+ * Here is the level of compatibility
+ * (between the user level daemon and the kernel KVP driver) that we
+ * will implement:
+ *
+ * An older daemon will always be supported on a newer driver.
+ * A given user level daemon will require a minimal version of the
+ * kernel driver.
+ * If we cannot handle the version differences, we will fail gracefully
+ * (this can happen when we have a user level daemon that is more
+ * advanced than the KVP driver.
+ *
+ * We will use values used in this handshake for determining if we have
+ * workable user level daemon and the kernel driver. We begin by taking the
+ * registration opcode out of the KVP opcode namespace. We will however,
+ * maintain compatibility with the existing user-level daemon code.
+ */
+
+/*
+ * Daemon code not supporting IP injection (legacy daemon).
+ */
+
+#define KVP_OP_REGISTER 4
+
+/*
+ * Daemon code supporting IP injection.
+ * The KVP opcode field is used to communicate the
+ * registration information; so define a namespace that
+ * will be distinct from the host defined KVP opcode.
+ */
+
+#define KVP_OP_REGISTER1 100
+
enum hv_kvp_exchg_op {
KVP_OP_GET = 0,
KVP_OP_SET,
KVP_OP_DELETE,
KVP_OP_ENUMERATE,
- KVP_OP_REGISTER,
+ KVP_OP_GET_IP_INFO,
+ KVP_OP_SET_IP_INFO,
KVP_OP_COUNT /* Number of operations, must be last. */
};
@@ -140,6 +181,26 @@ enum hv_kvp_exchg_pool {
KVP_POOL_COUNT /* Number of pools, must be last. */
};
+#define ADDR_FAMILY_NONE 0x00
+#define ADDR_FAMILY_IPV4 0x01
+#define ADDR_FAMILY_IPV6 0x02
+
+#define MAX_ADAPTER_ID_SIZE 128
+#define MAX_IP_ADDR_SIZE 1024
+#define MAX_GATEWAY_SIZE 512
+
+
+struct hv_kvp_ipaddr_value {
+ __u16 adapter_id[MAX_ADAPTER_ID_SIZE];
+ __u8 addr_family;
+ __u8 dhcp_enabled;
+ __u16 ip_addr[MAX_IP_ADDR_SIZE];
+ __u16 sub_net[MAX_IP_ADDR_SIZE];
+ __u16 gate_way[MAX_GATEWAY_SIZE];
+ __u16 dns_addr[MAX_IP_ADDR_SIZE];
+} __attribute__((packed));
+
+
struct hv_kvp_hdr {
__u8 operation;
__u8 pool;
@@ -181,16 +242,26 @@ struct hv_kvp_register {
};
struct hv_kvp_msg {
- struct hv_kvp_hdr kvp_hdr;
+ union {
+ struct hv_kvp_hdr kvp_hdr;
+ int error;
+ };
union {
struct hv_kvp_msg_get kvp_get;
struct hv_kvp_msg_set kvp_set;
struct hv_kvp_msg_delete kvp_delete;
struct hv_kvp_msg_enumerate kvp_enum_data;
+ struct hv_kvp_ipaddr_value kvp_ip_val;
struct hv_kvp_register kvp_register;
} body;
} __attribute__((packed));
+struct hv_kvp_ip_msg {
+ __u8 operation;
+ __u8 pool;
+ struct hv_kvp_ipaddr_value kvp_ip_val;
+} __attribute__((packed));
+
#ifdef __KERNEL__
#include <linux/scatterlist.h>
#include <linux/list.h>
@@ -982,6 +1053,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
#define HV_S_CONT 0x80070103
#define HV_ERROR_NOT_SUPPORTED 0x80070032
#define HV_ERROR_MACHINE_LOCKED 0x800704F7
+#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
/*
* While we want to handle util services as regular devices,
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index d9834b3..8fbcf7b 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -69,7 +69,7 @@ enum key_index {
};
static char kvp_send_buffer[4096];
-static char kvp_recv_buffer[4096];
+static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
static char *os_name = "";
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 03/18] Drivers: hv: kvp: Cleanup error handling in KVP
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
In preparation to implementing IP injection, cleanup the way we propagate
and handle errors both in the driver as well as in the user level daemon.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/hv/hv_kvp.c | 114 ++++++++++++++++++++++++++++++++++++---------
include/linux/hyperv.h | 17 ++++--
tools/hv/hv_kvp_daemon.c | 66 +++++++++++++-------------
3 files changed, 135 insertions(+), 62 deletions(-)
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 0012eed..eb4d073 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -48,13 +48,24 @@ static struct {
void *kvp_context; /* for the channel callback */
} kvp_transaction;
+/*
+ * Before we can accept KVP messages from the host, we need
+ * to handshake with the user level daemon. This state tracks
+ * if we are in the handshake phase.
+ */
+static bool in_hand_shake = true;
+
+/*
+ * This state maintains the version number registered by the daemon.
+ */
+static int dm_reg_value;
+
static void kvp_send_key(struct work_struct *dummy);
-#define TIMEOUT_FIRED 1
static void kvp_respond_to_host(char *key, char *value, int error);
static void kvp_work_func(struct work_struct *dummy);
-static void kvp_register(void);
+static void kvp_register(int);
static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
@@ -68,7 +79,7 @@ static u8 *recv_buffer;
*/
static void
-kvp_register(void)
+kvp_register(int reg_value)
{
struct cn_msg *msg;
@@ -83,7 +94,7 @@ kvp_register(void)
msg->id.idx = CN_KVP_IDX;
msg->id.val = CN_KVP_VAL;
- kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+ kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION);
msg->len = sizeof(struct hv_kvp_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
@@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy)
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
- kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED);
+ kvp_respond_to_host("Unknown key", "Guest timed out", HV_E_FAIL);
+}
+
+static int kvp_handle_handshake(struct hv_kvp_msg *msg)
+{
+ int ret = 1;
+
+ switch (msg->kvp_hdr.operation) {
+ case KVP_OP_REGISTER:
+ dm_reg_value = KVP_OP_REGISTER;
+ pr_info("KVP: IP injection functionality not available\n");
+ pr_info("KVP: Upgrade the KVP daemon\n");
+ break;
+ case KVP_OP_REGISTER1:
+ dm_reg_value = KVP_OP_REGISTER1;
+ break;
+ default:
+ pr_info("KVP: incompatible daemon\n");
+ pr_info("KVP: KVP version: %d, Daemon version: %d\n",
+ KVP_OP_REGISTER1, msg->kvp_hdr.operation);
+ ret = 0;
+ }
+
+ if (ret) {
+ /*
+ * We have a compatible daemon; complete the handshake.
+ */
+ pr_info("KVP: user-mode registering done.\n");
+ kvp_register(dm_reg_value);
+ kvp_transaction.active = false;
+ if (kvp_transaction.kvp_context)
+ hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ }
+ return ret;
}
+
/*
* Callback when data is received from user mode.
*/
@@ -109,27 +154,52 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
{
struct hv_kvp_msg *message;
struct hv_kvp_msg_enumerate *data;
+ int error = 0;
message = (struct hv_kvp_msg *)msg->data;
- switch (message->kvp_hdr.operation) {
+
+ /*
+ * If we are negotiating the version information
+ * with the daemon; handle that first.
+ */
+
+ if (in_hand_shake) {
+ if (kvp_handle_handshake(message))
+ in_hand_shake = false;
+ return;
+ }
+
+ /*
+ * Based on the version of the daemon, we propagate errors from the
+ * daemon differently.
+ */
+
+ data = &message->body.kvp_enum_data;
+
+ switch (dm_reg_value) {
case KVP_OP_REGISTER:
- pr_info("KVP: user-mode registering done.\n");
- kvp_register();
- kvp_transaction.active = false;
- hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ /*
+ * Null string is used to pass back error condition.
+ */
+ if (data->data.key[0] == 0)
+ error = HV_S_CONT;
break;
- default:
- data = &message->body.kvp_enum_data;
+ case KVP_OP_REGISTER1:
/*
- * Complete the transaction by forwarding the key value
- * to the host. But first, cancel the timeout.
+ * We use the message header information from
+ * the user level daemon to transmit errors.
*/
- if (cancel_delayed_work_sync(&kvp_work))
- kvp_respond_to_host(data->data.key,
- data->data.value,
- !strlen(data->data.key));
+ error = message->error;
+ break;
}
+
+ /*
+ * Complete the transaction by forwarding the key value
+ * to the host. But first, cancel the timeout.
+ */
+ if (cancel_delayed_work_sync(&kvp_work))
+ kvp_respond_to_host(data->data.key, data->data.value, error);
}
static void
@@ -287,6 +357,7 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
return;
+ icmsghdrp->status = error;
/*
* If the error parameter is set, terminate the host's enumeration
@@ -294,15 +365,12 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
if (error) {
/*
- * Something failed or the we have timedout;
- * terminate the current host-side iteration.
+ * Something failed or we have timedout;
+ * terminate the current host-side iteration.
*/
- icmsghdrp->status = HV_S_CONT;
goto response_done;
}
- icmsghdrp->status = HV_S_OK;
-
kvp_msg = (struct hv_kvp_msg *)
&recv_buffer[sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 11afc4e..b587c44 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -181,6 +181,17 @@ enum hv_kvp_exchg_pool {
KVP_POOL_COUNT /* Number of pools, must be last. */
};
+/*
+ * Some Hyper-V status codes.
+ */
+
+#define HV_S_OK 0x00000000
+#define HV_E_FAIL 0x80004005
+#define HV_S_CONT 0x80070103
+#define HV_ERROR_NOT_SUPPORTED 0x80070032
+#define HV_ERROR_MACHINE_LOCKED 0x800704F7
+#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
+
#define ADDR_FAMILY_NONE 0x00
#define ADDR_FAMILY_IPV4 0x01
#define ADDR_FAMILY_IPV6 0x02
@@ -1048,12 +1059,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
#define ICMSGHDRFLAG_REQUEST 2
#define ICMSGHDRFLAG_RESPONSE 4
-#define HV_S_OK 0x00000000
-#define HV_E_FAIL 0x80004005
-#define HV_S_CONT 0x80070103
-#define HV_ERROR_NOT_SUPPORTED 0x80070032
-#define HV_ERROR_MACHINE_LOCKED 0x800704F7
-#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
/*
* While we want to handle util services as regular devices,
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 8fbcf7b..069e2b3 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -71,13 +71,14 @@ enum key_index {
static char kvp_send_buffer[4096];
static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
+static int in_hand_shake = 1;
static char *os_name = "";
static char *os_major = "";
static char *os_minor = "";
static char *processor_arch;
static char *os_build;
-static char *lic_version;
+static char *lic_version = "Unknown version";
static struct utsname uts_buf;
@@ -394,7 +395,7 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
return 1;
}
-static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
+static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
__u8 *value, int value_size)
{
struct kvp_record *record;
@@ -406,16 +407,12 @@ static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
record = kvp_file_info[pool].records;
if (index >= kvp_file_info[pool].num_records) {
- /*
- * This is an invalid index; terminate enumeration;
- * - a NULL value will do the trick.
- */
- strcpy(value, "");
- return;
+ return 1;
}
memcpy(key, record[index].key, key_size);
memcpy(value, record[index].value, value_size);
+ return 0;
}
@@ -646,6 +643,8 @@ int main(void)
char *p;
char *key_value;
char *key_name;
+ int op;
+ int pool;
daemon(1, 0);
openlog("KVP", 0, LOG_USER);
@@ -687,7 +686,7 @@ int main(void)
message->id.val = CN_KVP_VAL;
hv_msg = (struct hv_kvp_msg *)message->data;
- hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+ hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
message->ack = 0;
message->len = sizeof(struct hv_kvp_msg);
@@ -721,12 +720,21 @@ int main(void)
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
- switch (hv_msg->kvp_hdr.operation) {
- case KVP_OP_REGISTER:
+ /*
+ * We will use the KVP header information to pass back
+ * the error from this daemon. So, first copy the state
+ * and set the error code to success.
+ */
+ op = hv_msg->kvp_hdr.operation;
+ pool = hv_msg->kvp_hdr.pool;
+ hv_msg->error = HV_S_OK;
+
+ if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
/*
* Driver is registering with us; stash away the version
* information.
*/
+ in_hand_shake = 0;
p = (char *)hv_msg->body.kvp_register.version;
lic_version = malloc(strlen(p) + 1);
if (lic_version) {
@@ -737,44 +745,39 @@ int main(void)
syslog(LOG_ERR, "malloc failed");
}
continue;
+ }
- /*
- * The current protocol with the kernel component uses a
- * NULL key name to pass an error condition.
- * For the SET, GET and DELETE operations,
- * use the existing protocol to pass back error.
- */
-
+ switch (op) {
case KVP_OP_SET:
- if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool,
+ if (kvp_key_add_or_modify(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
- strcpy(hv_msg->body.kvp_set.data.key, "");
+ hv_msg->error = HV_S_CONT;
break;
case KVP_OP_GET:
- if (kvp_get_value(hv_msg->kvp_hdr.pool,
+ if (kvp_get_value(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
- strcpy(hv_msg->body.kvp_set.data.key, "");
+ hv_msg->error = HV_S_CONT;
break;
case KVP_OP_DELETE:
- if (kvp_key_delete(hv_msg->kvp_hdr.pool,
+ if (kvp_key_delete(pool,
hv_msg->body.kvp_delete.key,
hv_msg->body.kvp_delete.key_size))
- strcpy(hv_msg->body.kvp_delete.key, "");
+ hv_msg->error = HV_S_CONT;
break;
default:
break;
}
- if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
+ if (op != KVP_OP_ENUMERATE)
goto kvp_done;
/*
@@ -782,13 +785,14 @@ int main(void)
* both the key and the value; if not read from the
* appropriate pool.
*/
- if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) {
- kvp_pool_enumerate(hv_msg->kvp_hdr.pool,
+ if (pool != KVP_POOL_AUTO) {
+ if (kvp_pool_enumerate(pool,
hv_msg->body.kvp_enum_data.index,
hv_msg->body.kvp_enum_data.data.key,
HV_KVP_EXCHANGE_MAX_KEY_SIZE,
hv_msg->body.kvp_enum_data.data.value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+ hv_msg->error = HV_S_CONT;
goto kvp_done;
}
@@ -841,11 +845,7 @@ int main(void)
strcpy(key_name, "ProcessorArchitecture");
break;
default:
- strcpy(key_value, "Unknown Key");
- /*
- * We use a null key name to terminate enumeration.
- */
- strcpy(key_name, "");
+ hv_msg->error = HV_S_CONT;
break;
}
/*
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 04/18] Drivers: hv: kvp: Support the new IP injection messages
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Implement support for the new IP injection messages in the driver code.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/hv/hv_kvp.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 137 insertions(+), 6 deletions(-)
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index eb4d073..810f176 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -63,7 +63,7 @@ static int dm_reg_value;
static void kvp_send_key(struct work_struct *dummy);
-static void kvp_respond_to_host(char *key, char *value, int error);
+static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
static void kvp_work_func(struct work_struct *dummy);
static void kvp_register(int);
@@ -108,7 +108,7 @@ kvp_work_func(struct work_struct *dummy)
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
- kvp_respond_to_host("Unknown key", "Guest timed out", HV_E_FAIL);
+ kvp_respond_to_host(NULL, HV_E_FAIL);
}
static int kvp_handle_handshake(struct hv_kvp_msg *msg)
@@ -199,9 +199,120 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
* to the host. But first, cancel the timeout.
*/
if (cancel_delayed_work_sync(&kvp_work))
- kvp_respond_to_host(data->data.key, data->data.value, error);
+ kvp_respond_to_host(message, error);
}
+
+static int process_ob_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_msg *in = in_msg;
+ struct hv_kvp_ip_msg *out = out_msg;
+ int len;
+
+ switch (op) {
+ case KVP_OP_GET_IP_INFO:
+ /*
+ * Transform all parameters into utf16 encoding.
+ */
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr,
+ strlen((char *)in->body.kvp_ip_val.ip_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net,
+ strlen((char *)in->body.kvp_ip_val.sub_net),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way,
+ strlen((char *)in->body.kvp_ip_val.gate_way),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr,
+ strlen((char *)in->body.kvp_ip_val.dns_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id,
+ strlen((char *)in->body.kvp_ip_val.adapter_id),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.adapter_id,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ out->kvp_ip_val.dhcp_enabled =
+ in->body.kvp_ip_val.dhcp_enabled;
+ }
+
+ return 0;
+}
+
+static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_ip_msg *in = in_msg;
+ struct hv_kvp_msg *out = out_msg;
+
+ switch (op) {
+ case KVP_OP_SET_IP_INFO:
+ /*
+ * Transform all parameters into utf8 encoding.
+ */
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+
+ out->body.kvp_ip_val.dhcp_enabled =
+ in->kvp_ip_val.dhcp_enabled;
+
+ default:
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE);
+
+ out->body.kvp_ip_val.addr_family =
+ in->kvp_ip_val.addr_family;
+ }
+}
+
+
+
+
static void
kvp_send_key(struct work_struct *dummy)
{
@@ -237,6 +348,12 @@ kvp_send_key(struct work_struct *dummy)
*/
switch (message->kvp_hdr.operation) {
+ case KVP_OP_SET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
+ break;
+ case KVP_OP_GET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO);
+ break;
case KVP_OP_SET:
switch (in_msg->body.kvp_set.data.value_type) {
case REG_SZ:
@@ -313,17 +430,19 @@ kvp_send_key(struct work_struct *dummy)
*/
static void
-kvp_respond_to_host(char *key, char *value, int error)
+kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
{
struct hv_kvp_msg *kvp_msg;
struct hv_kvp_exchg_msg_value *kvp_data;
char *key_name;
+ char *value;
struct icmsg_hdr *icmsghdrp;
int keylen = 0;
int valuelen = 0;
u32 buf_len;
struct vmbus_channel *channel;
u64 req_id;
+ int ret;
/*
* If a transaction is not active; log and return.
@@ -376,6 +495,16 @@ kvp_respond_to_host(char *key, char *value, int error)
sizeof(struct icmsg_hdr)];
switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+ case KVP_OP_GET_IP_INFO:
+ ret = process_ob_ipinfo(msg_to_host,
+ (struct hv_kvp_ip_msg *)kvp_msg,
+ KVP_OP_GET_IP_INFO);
+ if (ret < 0)
+ icmsghdrp->status = HV_E_FAIL;
+
+ goto response_done;
+ case KVP_OP_SET_IP_INFO:
+ goto response_done;
case KVP_OP_GET:
kvp_data = &kvp_msg->body.kvp_get.data;
goto copy_value;
@@ -389,7 +518,7 @@ kvp_respond_to_host(char *key, char *value, int error)
}
kvp_data = &kvp_msg->body.kvp_enum_data.data;
- key_name = key;
+ key_name = msg_to_host->body.kvp_enum_data.data.key;
/*
* The windows host expects the key/value pair to be encoded
@@ -403,6 +532,7 @@ kvp_respond_to_host(char *key, char *value, int error)
kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
copy_value:
+ value = msg_to_host->body.kvp_enum_data.data.value;
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
(wchar_t *) kvp_data->value,
(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
@@ -455,7 +585,8 @@ void hv_kvp_onchannelcallback(void *context)
return;
}
- vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
+ vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
+ &requestid);
if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 05/18] Tools: hv: Prepare to expand kvp_get_ip_address() functionality
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
kvp_get_ip_address() implemented the functionality to retrieve IP address info.
Make this function more generic so that we could retrieve additional
per-interface information.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 129 ++++++++++++++++++++++++++++++----------------
1 files changed, 84 insertions(+), 45 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 069e2b3..3af37f0 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -492,7 +492,8 @@ done:
}
static int
-kvp_get_ip_address(int family, char *buffer, int length)
+kvp_get_ip_address(int family, char *if_name, int op,
+ void *out_buffer, int length)
{
struct ifaddrs *ifap;
struct ifaddrs *curp;
@@ -502,10 +503,19 @@ kvp_get_ip_address(int family, char *buffer, int length)
const char *str;
char tmp[50];
int error = 0;
-
+ char *buffer;
+ struct hv_kvp_ipaddr_value *ip_buffer;
+
+ if (op == KVP_OP_ENUMERATE) {
+ buffer = out_buffer;
+ } else {
+ ip_buffer = out_buffer;
+ buffer = (char *)ip_buffer->ip_addr;
+ ip_buffer->addr_family = 0;
+ }
/*
* On entry into this function, the buffer is capable of holding the
- * maximum key value (2048 bytes).
+ * maximum key value.
*/
if (getifaddrs(&ifap)) {
@@ -515,58 +525,87 @@ kvp_get_ip_address(int family, char *buffer, int length)
curp = ifap;
while (curp != NULL) {
- if ((curp->ifa_addr != NULL) &&
- (curp->ifa_addr->sa_family == family)) {
- if (family == AF_INET) {
- struct sockaddr_in *addr =
- (struct sockaddr_in *) curp->ifa_addr;
-
- str = inet_ntop(family, &addr->sin_addr,
- tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
+ if (curp->ifa_addr == NULL) {
+ curp = curp->ifa_next;
+ continue;
+ }
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv4_len + 1))
- goto getaddr_done;
+ if ((if_name != NULL) &&
+ (strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
+ /*
+ * We want info about a specific interface;
+ * just continue.
+ */
+ curp = curp->ifa_next;
+ continue;
+ }
- } else {
+ /*
+ * We only support two address families: AF_INET and AF_INET6.
+ * If a family value of 0 is specified, we collect both
+ * supported address families; if not we gather info on
+ * the specified address family.
+ */
+ if ((family != 0) && (curp->ifa_addr->sa_family != family)) {
+ curp = curp->ifa_next;
+ continue;
+ }
+ if ((curp->ifa_addr->sa_family != AF_INET) &&
+ (curp->ifa_addr->sa_family != AF_INET6)) {
+ curp = curp->ifa_next;
+ continue;
+ }
+
+ if ((curp->ifa_addr->sa_family == AF_INET) &&
+ ((family == AF_INET) || (family == 0))) {
+ struct sockaddr_in *addr =
+ (struct sockaddr_in *) curp->ifa_addr;
+
+ str = inet_ntop(AF_INET, &addr->sin_addr, tmp, 50);
+ if (str == NULL) {
+ strcpy(buffer, "inet_ntop failed\n");
+ error = 1;
+ goto getaddr_done;
+ }
+ if (offset == 0)
+ strcpy(buffer, tmp);
+ else
+ strcat(buffer, tmp);
+ strcat(buffer, ";");
+
+ offset += strlen(str) + 1;
+ if ((length - offset) < (ipv4_len + 1))
+ goto getaddr_done;
+
+ } else if ((family == AF_INET6) || (family == 0)) {
/*
* We only support AF_INET and AF_INET6
* and the list of addresses is separated by a ";".
*/
- struct sockaddr_in6 *addr =
+ struct sockaddr_in6 *addr =
(struct sockaddr_in6 *) curp->ifa_addr;
- str = inet_ntop(family,
+ str = inet_ntop(AF_INET6,
&addr->sin6_addr.s6_addr,
tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv6_len + 1))
- goto getaddr_done;
-
+ if (str == NULL) {
+ strcpy(buffer, "inet_ntop failed\n");
+ error = 1;
+ goto getaddr_done;
}
+ if (offset == 0)
+ strcpy(buffer, tmp);
+ else
+ strcat(buffer, tmp);
+ strcat(buffer, ";");
+ offset += strlen(str) + 1;
+ if ((length - offset) < (ipv6_len + 1))
+ goto getaddr_done;
}
+
+
curp = curp->ifa_next;
}
@@ -811,13 +850,13 @@ int main(void)
strcpy(key_value, lic_version);
break;
case NetworkAddressIPv4:
- kvp_get_ip_address(AF_INET, key_value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ kvp_get_ip_address(AF_INET, NULL, KVP_OP_ENUMERATE,
+ key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv4");
break;
case NetworkAddressIPv6:
- kvp_get_ip_address(AF_INET6, key_value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ kvp_get_ip_address(AF_INET6, NULL, KVP_OP_ENUMERATE,
+ key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv6");
break;
case OSBuildNumber:
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 06/18] Tools: hv: Further refactor kvp_get_ip_address()
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
In preparation for making kvp_get_ip_address() more generic, factor out
the code for handling IP addresses.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 94 ++++++++++++++++++++-------------------------
1 files changed, 42 insertions(+), 52 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 3af37f0..3dc989f 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -491,17 +491,50 @@ done:
return;
}
+static int kvp_process_ip_address(void *addrp,
+ int family, char *buffer,
+ int length, int *offset)
+{
+ struct sockaddr_in *addr;
+ struct sockaddr_in6 *addr6;
+ int addr_length;
+ char tmp[50];
+ const char *str;
+
+ if (family == AF_INET) {
+ addr = (struct sockaddr_in *)addrp;
+ str = inet_ntop(family, &addr->sin_addr, tmp, 50);
+ addr_length = INET_ADDRSTRLEN;
+ } else {
+ addr6 = (struct sockaddr_in6 *)addrp;
+ str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
+ addr_length = INET6_ADDRSTRLEN;
+ }
+
+ if ((length - *offset) < addr_length + 1)
+ return 1;
+ if (str == NULL) {
+ strcpy(buffer, "inet_ntop failed\n");
+ return 1;
+ }
+ if (*offset == 0)
+ strcpy(buffer, tmp);
+ else
+ strcat(buffer, tmp);
+ strcat(buffer, ";");
+
+ *offset += strlen(str) + 1;
+ return 0;
+}
+
static int
kvp_get_ip_address(int family, char *if_name, int op,
void *out_buffer, int length)
{
struct ifaddrs *ifap;
struct ifaddrs *curp;
- int ipv4_len = strlen("255.255.255.255") + 1;
- int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
int offset = 0;
const char *str;
- char tmp[50];
int error = 0;
char *buffer;
struct hv_kvp_ipaddr_value *ip_buffer;
@@ -556,55 +589,12 @@ kvp_get_ip_address(int family, char *if_name, int op,
continue;
}
- if ((curp->ifa_addr->sa_family == AF_INET) &&
- ((family == AF_INET) || (family == 0))) {
- struct sockaddr_in *addr =
- (struct sockaddr_in *) curp->ifa_addr;
-
- str = inet_ntop(AF_INET, &addr->sin_addr, tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
-
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv4_len + 1))
- goto getaddr_done;
-
- } else if ((family == AF_INET6) || (family == 0)) {
-
- /*
- * We only support AF_INET and AF_INET6
- * and the list of addresses is separated by a ";".
- */
- struct sockaddr_in6 *addr =
- (struct sockaddr_in6 *) curp->ifa_addr;
-
- str = inet_ntop(AF_INET6,
- &addr->sin6_addr.s6_addr,
- tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv6_len + 1))
- goto getaddr_done;
-
- }
-
+ error = kvp_process_ip_address(curp->ifa_addr,
+ curp->ifa_addr->sa_family,
+ buffer,
+ length, &offset);
+ if (error)
+ goto getaddr_done;
curp = curp->ifa_next;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 07/18] Tools: hv: Gather address family information
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Now, gather address family information for the specified interface.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 3dc989f..13ae098 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -589,6 +589,17 @@ kvp_get_ip_address(int family, char *if_name, int op,
continue;
}
+ if (op == KVP_OP_GET_IP_INFO) {
+ /*
+ * Gather info other than the IP address.
+ * IP address info will be gathered later.
+ */
+ if (curp->ifa_addr->sa_family == AF_INET)
+ ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
+ else
+ ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
+ }
+
error = kvp_process_ip_address(curp->ifa_addr,
curp->ifa_addr->sa_family,
buffer,
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 08/18] Tools: hv: Gather subnet information
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Now, gather sub-net information for the specified interface.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 31 +++++++++++++++++++++++++++++--
1 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 13ae098..de1724c 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -534,6 +534,7 @@ kvp_get_ip_address(int family, char *if_name, int op,
struct ifaddrs *ifap;
struct ifaddrs *curp;
int offset = 0;
+ int sn_offset = 0;
const char *str;
int error = 0;
char *buffer;
@@ -594,12 +595,38 @@ kvp_get_ip_address(int family, char *if_name, int op,
* Gather info other than the IP address.
* IP address info will be gathered later.
*/
- if (curp->ifa_addr->sa_family == AF_INET)
+ if (curp->ifa_addr->sa_family == AF_INET) {
ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
- else
+ /*
+ * Get subnet info.
+ */
+ error = kvp_process_ip_address(
+ curp->ifa_netmask,
+ AF_INET,
+ (char *)
+ ip_buffer->sub_net,
+ length,
+ &sn_offset);
+ if (error)
+ goto gather_ipaddr;
+ } else {
ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
+ /*
+ * Get subnet info.
+ */
+ error = kvp_process_ip_address(
+ curp->ifa_netmask,
+ AF_INET6,
+ (char *)
+ ip_buffer->sub_net,
+ length,
+ &sn_offset);
+ if (error)
+ goto gather_ipaddr;
+ }
}
+gather_ipaddr:
error = kvp_process_ip_address(curp->ifa_addr,
curp->ifa_addr->sa_family,
buffer,
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 09/18] Tools: hv: Represent the ipv6 mask using CIDR notation
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Transform ipv6 subnet information to CIDR notation.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 46 ++++++++++++++++++++++++++++++++++++----------
1 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index de1724c..a63e83a 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -491,6 +491,15 @@ done:
return;
}
+static unsigned int hweight32(unsigned int *w)
+{
+ unsigned int res = *w - ((*w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
static int kvp_process_ip_address(void *addrp,
int family, char *buffer,
int length, int *offset)
@@ -535,10 +544,15 @@ kvp_get_ip_address(int family, char *if_name, int op,
struct ifaddrs *curp;
int offset = 0;
int sn_offset = 0;
- const char *str;
int error = 0;
char *buffer;
struct hv_kvp_ipaddr_value *ip_buffer;
+ char cidr_mask[5]; /* /xyz */
+ int weight;
+ int i;
+ unsigned int *w;
+ char *sn_str;
+ struct sockaddr_in6 *addr6;
if (op == KVP_OP_ENUMERATE) {
buffer = out_buffer;
@@ -611,18 +625,30 @@ kvp_get_ip_address(int family, char *if_name, int op,
goto gather_ipaddr;
} else {
ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
+
/*
- * Get subnet info.
+ * Get subnet info in CIDR format.
*/
- error = kvp_process_ip_address(
- curp->ifa_netmask,
- AF_INET6,
- (char *)
- ip_buffer->sub_net,
- length,
- &sn_offset);
- if (error)
+ weight = 0;
+ sn_str = (char *)ip_buffer->sub_net;
+ addr6 = (struct sockaddr_in6 *)
+ curp->ifa_netmask;
+ w = addr6->sin6_addr.s6_addr32;
+
+ for (i = 0; i < 4; i++)
+ weight += hweight32(&w[i]);
+
+ sprintf(cidr_mask, "/%d", weight);
+ if ((length - sn_offset) <
+ (strlen(cidr_mask) + 1))
goto gather_ipaddr;
+
+ if (sn_offset == 0)
+ strcpy(sn_str, cidr_mask);
+ else
+ strcat(sn_str, cidr_mask);
+ strcat((char *)ip_buffer->sub_net, ";");
+ sn_offset += strlen(sn_str) + 1;
}
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 10/18] Tools: hv: Gather ipv[4,6] gateway information
From: K. Y. Srinivasan @ 2012-08-13 17:06 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Gather information on the default gateways - ipv4/ipv6.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 68 insertions(+), 0 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index a63e83a..65d54c8 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -491,6 +491,68 @@ done:
return;
}
+static void kvp_process_ipconfig_file(char *cmd,
+ char *config_buf, int len,
+ int element_size, int offset)
+{
+ char buf[256];
+ char *p;
+ char *x;
+ FILE *file;
+
+ /*
+ * First execute the command.
+ */
+ file = popen(cmd, "r");
+ if (file == NULL)
+ return;
+
+ if (offset == 0)
+ memset(config_buf, 0, len);
+ while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
+ if ((len - strlen(config_buf)) < (element_size + 1))
+ break;
+
+ x = strchr(p, '\n');
+ *x = '\0';
+ strcat(config_buf, p);
+ strcat(config_buf, ";");
+ }
+ pclose(file);
+}
+
+static void kvp_get_ipconfig_info(char *if_name,
+ struct hv_kvp_ipaddr_value *buffer)
+{
+ char cmd[512];
+
+ /*
+ * Get the address of default gateway (ipv4).
+ */
+ sprintf(cmd, "%s %s", "ip route show dev", if_name);
+ strcat(cmd, " | awk '/default/ {print $3 }'");
+
+ /*
+ * Execute the command to gather gateway info.
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
+ (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);
+
+ /*
+ * Get the address of default gateway (ipv6).
+ */
+ sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name);
+ strcat(cmd, " | awk '/default/ {print $3 }'");
+
+ /*
+ * Execute the command to gather gateway info (ipv6).
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
+ (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
+
+}
+
+
static unsigned int hweight32(unsigned int *w)
{
unsigned int res = *w - ((*w >> 1) & 0x55555555);
@@ -650,6 +712,12 @@ kvp_get_ip_address(int family, char *if_name, int op,
strcat((char *)ip_buffer->sub_net, ";");
sn_offset += strlen(sn_str) + 1;
}
+
+ /*
+ * Collect other ip related configuration info.
+ */
+
+ kvp_get_ipconfig_info(if_name, ip_buffer);
}
gather_ipaddr:
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 11/18] Tools: hv: Gather DNS information
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Now, gather DNS information. Invoke an external script (that can be
distro dependent) to gather the DNS information.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 23 +++++++++++++++++++++++
1 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 65d54c8..2f8e0b9 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -550,6 +550,29 @@ static void kvp_get_ipconfig_info(char *if_name,
kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
(MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
+
+ /*
+ * Gather the DNS state.
+ * Since there is no standard way to get this information
+ * across various distributions of interest; we just invoke
+ * an external script that needs to be ported across distros
+ * of interest.
+ *
+ * Following is the expected format of the information from the script:
+ *
+ * ipaddr1 (nameserver1)
+ * ipaddr2 (nameserver2)
+ * .
+ * .
+ */
+
+ sprintf(cmd, "%s", "hv_get_dns_info");
+
+ /*
+ * Execute the command to gather DNS info.
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
+ (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 12/18] Tools: hv: Gather DHCP information
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Collect information on dhcp setting for the specified interface.
We invoke an external script to get this information.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 2f8e0b9..8a18819 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -525,6 +525,9 @@ static void kvp_get_ipconfig_info(char *if_name,
struct hv_kvp_ipaddr_value *buffer)
{
char cmd[512];
+ char dhcp_info[128];
+ char *p;
+ FILE *file;
/*
* Get the address of default gateway (ipv4).
@@ -573,6 +576,34 @@ static void kvp_get_ipconfig_info(char *if_name,
*/
kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
(MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
+
+ /*
+ * Gather the DHCP state.
+ * We will gather this state by invoking an external script.
+ * The parameter to the script is the interface name.
+ * Here is the expected output:
+ *
+ * Enabled: DHCP enabled.
+ */
+
+ sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name);
+
+ file = popen(cmd, "r");
+ if (file == NULL)
+ return;
+
+ p = fgets(dhcp_info, sizeof(dhcp_info), file);
+ if (p == NULL) {
+ pclose(file);
+ return;
+ }
+
+ if (!strncmp(p, "Enabled", 7))
+ buffer->dhcp_enabled = 1;
+ else
+ buffer->dhcp_enabled = 0;
+
+ pclose(file);
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 13/18] Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Implement the KVP verb - KVP_OP_SET_IP_INFO. This operation configures the
specified interface based on the given configuration. Since configuring
an interface is very distro specific, we invoke an external script to
configure the interface.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
include/linux/hyperv.h | 2 +
tools/hv/hv_kvp_daemon.c | 443 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 445 insertions(+), 0 deletions(-)
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index b587c44..588d8f2 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -191,6 +191,8 @@ enum hv_kvp_exchg_pool {
#define HV_ERROR_NOT_SUPPORTED 0x80070032
#define HV_ERROR_MACHINE_LOCKED 0x800704F7
#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
+#define HV_INVALIDARG 0x80070057
+#define HV_GUID_NOTFOUND 0x80041002
#define ADDR_FAMILY_NONE 0x00
#define ADDR_FAMILY_IPV4 0x01
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 8a18819..59669a8 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#include <linux/connector.h>
@@ -41,6 +42,7 @@
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <dirent.h>
/*
* KVP protocol: The user mode component first registers with the
@@ -68,6 +70,14 @@ enum key_index {
ProcessorArchitecture
};
+
+enum {
+ IPADDR = 0,
+ NETMASK,
+ GATEWAY,
+ DNS
+};
+
static char kvp_send_buffer[4096];
static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
@@ -81,6 +91,11 @@ static char *os_build;
static char *lic_version = "Unknown version";
static struct utsname uts_buf;
+/*
+ * The location of the interface configuration file.
+ */
+
+#define KVP_CONFIG_LOC "/var/opt/"
#define MAX_FILE_NAME 100
#define ENTRIES_PER_BLOCK 50
@@ -491,6 +506,104 @@ done:
return;
}
+
+
+/*
+ * Retrieve an interface name corresponding to the specified guid.
+ * If there is a match, the function returns a pointer
+ * to the interface name and if not, a NULL is returned.
+ * If a match is found, the caller is responsible for
+ * freeing the memory.
+ */
+
+static char *kvp_get_if_name(char *guid)
+{
+ DIR *dir;
+ struct dirent *entry;
+ FILE *file;
+ char *p, *q, *x;
+ char *if_name = NULL;
+ char buf[256];
+ char *kvp_net_dir = "/sys/class/net/";
+ char dev_id[256];
+
+ dir = opendir(kvp_net_dir);
+ if (dir == NULL)
+ return NULL;
+
+ snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir);
+ q = dev_id + strlen(kvp_net_dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ /*
+ * Set the state for the next pass.
+ */
+ *q = '\0';
+ strcat(dev_id, entry->d_name);
+ strcat(dev_id, "/device/device_id");
+
+ file = fopen(dev_id, "r");
+ if (file == NULL)
+ continue;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+
+ if (!strcmp(p, guid)) {
+ /*
+ * Found the guid match; return the interface
+ * name. The caller will free the memory.
+ */
+ if_name = strdup(entry->d_name);
+ fclose(file);
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ closedir(dir);
+ return if_name;
+}
+
+/*
+ * Retrieve the MAC address given the interface name.
+ */
+
+static char *kvp_if_name_to_mac(char *if_name)
+{
+ FILE *file;
+ char *p, *x;
+ char buf[256];
+ char addr_file[256];
+ int i;
+ char *mac_addr = NULL;
+
+ snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/",
+ if_name, "/address");
+
+ file = fopen(addr_file, "r");
+ if (file == NULL)
+ return NULL;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+ for (i = 0; i < strlen(p); i++)
+ p[i] = toupper(p[i]);
+ mac_addr = strdup(p);
+ }
+
+ fclose(file);
+ return mac_addr;
+}
+
+
static void kvp_process_ipconfig_file(char *cmd,
char *config_buf, int len,
int element_size, int offset)
@@ -791,6 +904,315 @@ getaddr_done:
}
+static int expand_ipv6(char *addr, int type)
+{
+ int ret;
+ struct in6_addr v6_addr;
+
+ ret = inet_pton(AF_INET6, addr, &v6_addr);
+
+ if (ret != 1) {
+ if (type == NETMASK)
+ return 1;
+ return 0;
+ }
+
+ sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
+ "%02x%02x:%02x%02x:%02x%02x",
+ (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
+ (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
+ (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
+ (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
+ (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
+ (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
+ (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
+ (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
+
+ return 1;
+
+}
+
+static int is_ipv4(char *addr)
+{
+ int ret;
+ struct in_addr ipv4_addr;
+
+ ret = inet_pton(AF_INET, addr, &ipv4_addr);
+
+ if (ret == 1)
+ return 1;
+ return 0;
+}
+
+static int parse_ip_val_buffer(char *in_buf, int *offset,
+ char *out_buf, int out_len)
+{
+ char *x;
+ char *start;
+
+ /*
+ * in_buf has sequence of characters that are seperated by
+ * the character ';'. The last sequence does not have the
+ * terminating ";" character.
+ */
+ start = in_buf + *offset;
+
+ x = strchr(start, ';');
+ if (x)
+ *x = 0;
+ else
+ x = start + strlen(start);
+
+ if (strlen(start) != 0) {
+ int i = 0;
+ /*
+ * Get rid of leading spaces.
+ */
+ while (start[i] == ' ')
+ i++;
+
+ if ((x - start) <= out_len) {
+ strcpy(out_buf, (start + i));
+ *offset += (x - start) + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
+{
+ int ret;
+
+ ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
+
+ if (ret < 0)
+ return HV_E_FAIL;
+
+ return 0;
+}
+
+
+static int process_ip_string(FILE *f, char *ip_string, int type)
+{
+ int error = 0;
+ char addr[INET6_ADDRSTRLEN];
+ int i = 0;
+ int j = 0;
+ char str[256];
+ char sub_str[10];
+ int offset = 0;
+
+ memset(addr, 0, sizeof(addr));
+
+ while (parse_ip_val_buffer(ip_string, &offset, addr,
+ (MAX_IP_ADDR_SIZE * 2))) {
+
+ sub_str[0] = 0;
+ if (is_ipv4(addr)) {
+ switch (type) {
+ case IPADDR:
+ snprintf(str, sizeof(str), "%s", "IPADDR");
+ break;
+ case NETMASK:
+ snprintf(str, sizeof(str), "%s", "NETMASK");
+ break;
+ case GATEWAY:
+ snprintf(str, sizeof(str), "%s", "GATEWAY");
+ break;
+ case DNS:
+ snprintf(str, sizeof(str), "%s", "DNS");
+ break;
+ }
+ if (i != 0) {
+ if (type != DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "_%d", i++);
+ } else {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else if (type == DNS) {
+ snprintf(sub_str, sizeof(sub_str), "%d", ++i);
+ }
+
+
+ } else if (expand_ipv6(addr, type)) {
+ switch (type) {
+ case IPADDR:
+ snprintf(str, sizeof(str), "%s", "IPV6ADDR");
+ break;
+ case NETMASK:
+ snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
+ break;
+ case GATEWAY:
+ snprintf(str, sizeof(str), "%s",
+ "IPV6_DEFAULTGW");
+ break;
+ case DNS:
+ snprintf(str, sizeof(str), "%s", "DNS");
+ break;
+ }
+ if ((j != 0) || (type == DNS)) {
+ if (type != DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "_%d", j++);
+ } else {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else if (type == DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else {
+ return HV_INVALIDARG;
+ }
+
+ error = kvp_write_file(f, str, sub_str, addr);
+ if (error)
+ return error;
+ memset(addr, 0, sizeof(addr));
+ }
+
+ return 0;
+}
+
+static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
+{
+ int error = 0;
+ char if_file[128];
+ FILE *file;
+ char cmd[512];
+ char *mac_addr;
+
+ /*
+ * Set the configuration for the specified interface with
+ * the information provided. Since there is no standard
+ * way to configure an interface, we will have an external
+ * script that does the job of configuring the interface and
+ * flushing the configuration.
+ *
+ * The parameters passed to this external script are:
+ * 1. A configuration file that has the specified configuration.
+ *
+ * We will embed the name of the interface in the configuration
+ * file: ifcfg-ethx (where ethx is the interface name).
+ *
+ * The information provided here may be more than what is needed
+ * in a given distro to configure the interface and so are free
+ * ignore information that may not be relevant.
+ *
+ * Here is the format of the ip configuration file:
+ *
+ * HWADDR=macaddr
+ * IF_NAME=interface name
+ * DHCP=yes (This is optional; if yes, DHCP is configured)
+ *
+ * IPADDR=ipaddr1
+ * IPADDR_1=ipaddr2
+ * IPADDR_x=ipaddry (where y = x + 1)
+ *
+ * NETMASK=netmask1
+ * NETMASK_x=netmasky (where y = x + 1)
+ *
+ * GATEWAY=ipaddr1
+ * GATEWAY_x=ipaddry (where y = x + 1)
+ *
+ * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
+ *
+ * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
+ * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
+ * IPV6NETMASK.
+ *
+ * The host can specify multiple ipv4 and ipv6 addresses to be
+ * configured for the interface. Furthermore, the configuration
+ * needs to be persistent. A subsequent GET call on the interface
+ * is expected to return the configuration that is set via the SET
+ * call.
+ */
+
+ snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
+ "hyperv/ifcfg-", if_name);
+
+ file = fopen(if_file, "w");
+
+ if (file == NULL) {
+ syslog(LOG_ERR, "Failed to open config file");
+ return HV_E_FAIL;
+ }
+
+ /*
+ * First write out the MAC address.
+ */
+
+ mac_addr = kvp_if_name_to_mac(if_name);
+ if (mac_addr == NULL) {
+ error = HV_E_FAIL;
+ goto setval_error;
+ }
+
+ error = kvp_write_file(file, "HWADDR", "", mac_addr);
+ if (error)
+ goto setval_error;
+
+ error = kvp_write_file(file, "IF_NAME", "", if_name);
+ if (error)
+ goto setval_error;
+
+ if (new_val->dhcp_enabled) {
+ error = kvp_write_file(file, "DHCP", "", "yes");
+ if (error)
+ goto setval_error;
+
+ /*
+ * We are done!.
+ */
+ goto setval_done;
+ }
+
+ /*
+ * Write the configuration for ipaddress, netmask, gateway and
+ * name servers.
+ */
+
+ error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
+ if (error)
+ goto setval_error;
+
+setval_done:
+ free(mac_addr);
+ fclose(file);
+
+ /*
+ * Now that we have populated the configuration file,
+ * invoke the external script to do its magic.
+ */
+
+ snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
+ system(cmd);
+ return 0;
+
+setval_error:
+ syslog(LOG_ERR, "Failed to write config file");
+ free(mac_addr);
+ fclose(file);
+ return error;
+}
+
+
static int
kvp_get_domain_name(char *buffer, int length)
{
@@ -860,6 +1282,8 @@ int main(void)
char *key_name;
int op;
int pool;
+ char *if_name;
+ struct hv_kvp_ipaddr_value *kvp_ip_val;
daemon(1, 0);
openlog("KVP", 0, LOG_USER);
@@ -963,6 +1387,25 @@ int main(void)
}
switch (op) {
+ case KVP_OP_SET_IP_INFO:
+ kvp_ip_val = &hv_msg->body.kvp_ip_val;
+ if_name = kvp_get_if_name(
+ (char *)kvp_ip_val->adapter_id);
+ if (if_name == NULL) {
+ /*
+ * We could not map the guid to an
+ * interface name; return error.
+ */
+ hv_msg->error = HV_GUID_NOTFOUND;
+ break;
+ }
+ error = kvp_set_ip_info(if_name, kvp_ip_val);
+ if (error)
+ hv_msg->error = error;
+
+ free(if_name);
+ break;
+
case KVP_OP_SET:
if (kvp_key_add_or_modify(pool,
hv_msg->body.kvp_set.data.key,
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 14/18] Tools: hv: Rename the function kvp_get_ip_address()
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Rename the function kvp_get_ip_address() to better reflect the functionality
being implemented.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 59669a8..bb2a6c4 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -766,7 +766,7 @@ static int kvp_process_ip_address(void *addrp,
}
static int
-kvp_get_ip_address(int family, char *if_name, int op,
+kvp_get_ip_info(int family, char *if_name, int op,
void *out_buffer, int length)
{
struct ifaddrs *ifap;
@@ -1469,12 +1469,12 @@ int main(void)
strcpy(key_value, lic_version);
break;
case NetworkAddressIPv4:
- kvp_get_ip_address(AF_INET, NULL, KVP_OP_ENUMERATE,
+ kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv4");
break;
case NetworkAddressIPv6:
- kvp_get_ip_address(AF_INET6, NULL, KVP_OP_ENUMERATE,
+ kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv6");
break;
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 15/18] Tools: hv: Implement the KVP verb - KVP_OP_GET_IP_INFO
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Now implement the KVP verb - KVP_OP_GET_IP_INFO. This operation retrieves IP
information for the specified interface.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 93 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 90 insertions(+), 3 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index bb2a6c4..e7db9ac 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -604,6 +604,69 @@ static char *kvp_if_name_to_mac(char *if_name)
}
+/*
+ * Retrieve the interface name given tha MAC address.
+ */
+
+static char *kvp_mac_to_if_name(char *mac)
+{
+ DIR *dir;
+ struct dirent *entry;
+ FILE *file;
+ char *p, *q, *x;
+ char *if_name = NULL;
+ char buf[256];
+ char *kvp_net_dir = "/sys/class/net/";
+ char dev_id[256];
+ int i;
+
+ dir = opendir(kvp_net_dir);
+ if (dir == NULL)
+ return NULL;
+
+ snprintf(dev_id, sizeof(dev_id), kvp_net_dir);
+ q = dev_id + strlen(kvp_net_dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ /*
+ * Set the state for the next pass.
+ */
+ *q = '\0';
+
+ strcat(dev_id, entry->d_name);
+ strcat(dev_id, "/address");
+
+ file = fopen(dev_id, "r");
+ if (file == NULL)
+ continue;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+
+ for (i = 0; i < strlen(p); i++)
+ p[i] = toupper(p[i]);
+
+ if (!strcmp(p, mac)) {
+ /*
+ * Found the MAC match; return the interface
+ * name. The caller will free the memory.
+ */
+ if_name = strdup(entry->d_name);
+ fclose(file);
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ closedir(dir);
+ return if_name;
+}
+
+
static void kvp_process_ipconfig_file(char *cmd,
char *config_buf, int len,
int element_size, int offset)
@@ -750,10 +813,10 @@ static int kvp_process_ip_address(void *addrp,
}
if ((length - *offset) < addr_length + 1)
- return 1;
+ return HV_E_FAIL;
if (str == NULL) {
strcpy(buffer, "inet_ntop failed\n");
- return 1;
+ return HV_E_FAIL;
}
if (*offset == 0)
strcpy(buffer, tmp);
@@ -797,7 +860,7 @@ kvp_get_ip_info(int family, char *if_name, int op,
if (getifaddrs(&ifap)) {
strcpy(buffer, "getifaddrs failed\n");
- return 1;
+ return HV_E_FAIL;
}
curp = ifap;
@@ -1387,6 +1450,30 @@ int main(void)
}
switch (op) {
+ case KVP_OP_GET_IP_INFO:
+ kvp_ip_val = &hv_msg->body.kvp_ip_val;
+ if_name =
+ kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id);
+
+ if (if_name == NULL) {
+ /*
+ * We could not map the mac address to an
+ * interface name; return error.
+ */
+ hv_msg->error = HV_E_FAIL;
+ break;
+ }
+ error = kvp_get_ip_info(
+ 0, if_name, KVP_OP_GET_IP_INFO,
+ kvp_ip_val,
+ (MAX_IP_ADDR_SIZE * 2));
+
+ if (error)
+ hv_msg->error = error;
+
+ free(if_name);
+ break;
+
case KVP_OP_SET_IP_INFO:
kvp_ip_val = &hv_msg->body.kvp_ip_val;
if_name = kvp_get_if_name(
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 16/18] Tools: hv: Get rid of some unused variables
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Get rid of unused variables. I would like to thank Ben Hutchings
for pointing out the issue (ben@decadent.org.uk).
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index e7db9ac..4a1428c 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -211,7 +211,7 @@ static void kvp_update_mem_state(int pool)
}
static int kvp_file_init(void)
{
- int ret, fd;
+ int fd;
FILE *filep;
size_t records_read;
__u8 *fname;
@@ -327,7 +327,6 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
int value_size)
{
int i;
- int j, k;
int num_records;
struct kvp_record *record;
int num_blocks;
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 17/18] Tools: hv: Correctly type string variables
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Correctly type character strings. I would like to thank Ben Hutchings
for pointing out the issue (ben@decadent.org.uk).
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 4a1428c..c8e1013 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -101,8 +101,8 @@ static struct utsname uts_buf;
#define ENTRIES_PER_BLOCK 50
struct kvp_record {
- __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
- __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+ char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+ char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
};
struct kvp_file_state {
@@ -110,7 +110,7 @@ struct kvp_file_state {
int num_blocks;
struct kvp_record *records;
int num_records;
- __u8 fname[MAX_FILE_NAME];
+ char fname[MAX_FILE_NAME];
};
static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
@@ -214,7 +214,7 @@ static int kvp_file_init(void)
int fd;
FILE *filep;
size_t records_read;
- __u8 *fname;
+ char *fname;
struct kvp_record *record;
struct kvp_record *readp;
int num_blocks;
--
1.7.4.1
^ permalink raw reply related
* [PATCH V2 18/18] Tools: hv: Properly manage open streams
From: K. Y. Srinivasan @ 2012-08-13 17:07 UTC (permalink / raw)
To: gregkh, linux-kernel, devel, virtualization, olaf, apw, ben
Cc: K. Y. Srinivasan
In-Reply-To: <1344877627-21779-1-git-send-email-kys@microsoft.com>
Close the open streams properly.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Ben Hutchings <ben@decadent.org.uk>
---
tools/hv/hv_kvp_daemon.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index c8e1013..ca6424f 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -160,7 +160,7 @@ static void kvp_update_file(int pool)
sizeof(struct kvp_record),
kvp_file_info[pool].num_records, filep);
- fflush(filep);
+ fclose(filep);
kvp_release_lock(pool);
}
@@ -206,7 +206,7 @@ static void kvp_update_mem_state(int pool)
kvp_file_info[pool].num_blocks = num_blocks;
kvp_file_info[pool].records = record;
kvp_file_info[pool].num_records = records_read;
-
+ fclose(filep);
kvp_release_lock(pool);
}
static int kvp_file_init(void)
--
1.7.4.1
^ permalink raw reply related
* Re: [RFC-v2 1/6] msix: Work-around for vhost-scsi with KVM in-kernel MSI injection
From: Michael S. Tsirkin @ 2012-08-13 18:03 UTC (permalink / raw)
To: Jan Kiszka
Cc: Anthony Liguori, Stefan Hajnoczi, kvm-devel, qemu-devel,
Zhi Yong Wu, Anthony Liguori, target-devel, Paolo Bonzini,
lf-virt, Christoph Hellwig
In-Reply-To: <5028EDB2.10506@siemens.com>
On Mon, Aug 13, 2012 at 02:06:10PM +0200, Jan Kiszka wrote:
> On 2012-08-13 10:35, Nicholas A. Bellinger wrote:
> > From: Nicholas Bellinger <nab@linux-iscsi.org>
> >
> > This is required to get past the following assert with:
> >
> > commit 1523ed9e1d46b0b54540049d491475ccac7e6421
> > Author: Jan Kiszka <jan.kiszka@siemens.com>
> > Date: Thu May 17 10:32:39 2012 -0300
> >
> > virtio/vhost: Add support for KVM in-kernel MSI injection
> >
> > Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> > Cc: Jan Kiszka <jan.kiszka@siemens.com>
> > Cc: Paolo Bonzini <pbonzini@redhat.com>
> > Cc: Anthony Liguori <aliguori@us.ibm.com>
> > Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
> > ---
> > hw/msix.c | 3 +++
> > 1 files changed, 3 insertions(+), 0 deletions(-)
> >
> > diff --git a/hw/msix.c b/hw/msix.c
> > index 800fc32..c1e6dc3 100644
> > --- a/hw/msix.c
> > +++ b/hw/msix.c
> > @@ -544,6 +544,9 @@ void msix_unset_vector_notifiers(PCIDevice *dev)
> > {
> > int vector;
> >
> > + if (!dev->msix_vector_use_notifier && !dev->msix_vector_release_notifier)
> > + return;
> > +
> > assert(dev->msix_vector_use_notifier &&
> > dev->msix_vector_release_notifier);
> >
> >
>
> I think to remember pointing out that there is a bug somewhere in the
> reset code which deactivates a non-active vhost instance, no?
>
> Jan
Could not find it. Could you dig it up pls?
> --
> Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
> Corporate Competence Center Embedded Linux
^ permalink raw reply
* Re: [RFC-v2 1/6] msix: Work-around for vhost-scsi with KVM in-kernel MSI injection
From: Jan Kiszka @ 2012-08-13 18:06 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Anthony Liguori, Stefan Hajnoczi, kvm-devel, qemu-devel,
Zhi Yong Wu, Anthony Liguori, target-devel, Paolo Bonzini,
lf-virt, Christoph Hellwig
In-Reply-To: <20120813180312.GA19379@redhat.com>
On 2012-08-13 20:03, Michael S. Tsirkin wrote:
> On Mon, Aug 13, 2012 at 02:06:10PM +0200, Jan Kiszka wrote:
>> On 2012-08-13 10:35, Nicholas A. Bellinger wrote:
>>> From: Nicholas Bellinger <nab@linux-iscsi.org>
>>>
>>> This is required to get past the following assert with:
>>>
>>> commit 1523ed9e1d46b0b54540049d491475ccac7e6421
>>> Author: Jan Kiszka <jan.kiszka@siemens.com>
>>> Date: Thu May 17 10:32:39 2012 -0300
>>>
>>> virtio/vhost: Add support for KVM in-kernel MSI injection
>>>
>>> Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
>>> Cc: Jan Kiszka <jan.kiszka@siemens.com>
>>> Cc: Paolo Bonzini <pbonzini@redhat.com>
>>> Cc: Anthony Liguori <aliguori@us.ibm.com>
>>> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
>>> ---
>>> hw/msix.c | 3 +++
>>> 1 files changed, 3 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/hw/msix.c b/hw/msix.c
>>> index 800fc32..c1e6dc3 100644
>>> --- a/hw/msix.c
>>> +++ b/hw/msix.c
>>> @@ -544,6 +544,9 @@ void msix_unset_vector_notifiers(PCIDevice *dev)
>>> {
>>> int vector;
>>>
>>> + if (!dev->msix_vector_use_notifier && !dev->msix_vector_release_notifier)
>>> + return;
>>> +
>>> assert(dev->msix_vector_use_notifier &&
>>> dev->msix_vector_release_notifier);
>>>
>>>
>>
>> I think to remember pointing out that there is a bug somewhere in the
>> reset code which deactivates a non-active vhost instance, no?
>>
>> Jan
>
> Could not find it. Could you dig it up pls?
http://thread.gmane.org/gmane.linux.scsi.target.devel/2277/focus=2309
Jan
--
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
^ permalink raw reply
* Re: [RFC-v2 1/6] msix: Work-around for vhost-scsi with KVM in-kernel MSI injection
From: Michael S. Tsirkin @ 2012-08-13 18:17 UTC (permalink / raw)
To: Jan Kiszka
Cc: Anthony Liguori, Stefan Hajnoczi, kvm-devel, qemu-devel,
Zhi Yong Wu, Anthony Liguori, target-devel, Paolo Bonzini,
lf-virt, Christoph Hellwig
In-Reply-To: <50294219.3080800@siemens.com>
On Mon, Aug 13, 2012 at 08:06:17PM +0200, Jan Kiszka wrote:
> On 2012-08-13 20:03, Michael S. Tsirkin wrote:
> > On Mon, Aug 13, 2012 at 02:06:10PM +0200, Jan Kiszka wrote:
> >> On 2012-08-13 10:35, Nicholas A. Bellinger wrote:
> >>> From: Nicholas Bellinger <nab@linux-iscsi.org>
> >>>
> >>> This is required to get past the following assert with:
> >>>
> >>> commit 1523ed9e1d46b0b54540049d491475ccac7e6421
> >>> Author: Jan Kiszka <jan.kiszka@siemens.com>
> >>> Date: Thu May 17 10:32:39 2012 -0300
> >>>
> >>> virtio/vhost: Add support for KVM in-kernel MSI injection
> >>>
> >>> Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> >>> Cc: Jan Kiszka <jan.kiszka@siemens.com>
> >>> Cc: Paolo Bonzini <pbonzini@redhat.com>
> >>> Cc: Anthony Liguori <aliguori@us.ibm.com>
> >>> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
> >>> ---
> >>> hw/msix.c | 3 +++
> >>> 1 files changed, 3 insertions(+), 0 deletions(-)
> >>>
> >>> diff --git a/hw/msix.c b/hw/msix.c
> >>> index 800fc32..c1e6dc3 100644
> >>> --- a/hw/msix.c
> >>> +++ b/hw/msix.c
> >>> @@ -544,6 +544,9 @@ void msix_unset_vector_notifiers(PCIDevice *dev)
> >>> {
> >>> int vector;
> >>>
> >>> + if (!dev->msix_vector_use_notifier && !dev->msix_vector_release_notifier)
> >>> + return;
> >>> +
> >>> assert(dev->msix_vector_use_notifier &&
> >>> dev->msix_vector_release_notifier);
> >>>
> >>>
> >>
> >> I think to remember pointing out that there is a bug somewhere in the
> >> reset code which deactivates a non-active vhost instance, no?
> >>
> >> Jan
> >
> > Could not find it. Could you dig it up pls?
>
> http://thread.gmane.org/gmane.linux.scsi.target.devel/2277/focus=2309
>
> Jan
Ah yes. So let's not work around, need to get to the bottom of that.
> --
> Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
> Corporate Competence Center Embedded Linux
^ permalink raw reply
* Re: [Qemu-devel] [RFC-v2 1/6] msix: Work-around for vhost-scsi with KVM in-kernel MSI injection
From: Blue Swirl @ 2012-08-13 19:39 UTC (permalink / raw)
To: Nicholas A. Bellinger
Cc: Anthony Liguori, Stefan Hajnoczi, kvm-devel, Michael S. Tsirkin,
Jan Kiszka, qemu-devel, Zhi Yong Wu, Anthony Liguori,
target-devel, Paolo Bonzini, lf-virt, Christoph Hellwig
In-Reply-To: <1344846917-7411-2-git-send-email-nab@linux-iscsi.org>
On Mon, Aug 13, 2012 at 8:35 AM, Nicholas A. Bellinger
<nab@linux-iscsi.org> wrote:
> From: Nicholas Bellinger <nab@linux-iscsi.org>
>
> This is required to get past the following assert with:
>
> commit 1523ed9e1d46b0b54540049d491475ccac7e6421
> Author: Jan Kiszka <jan.kiszka@siemens.com>
> Date: Thu May 17 10:32:39 2012 -0300
>
> virtio/vhost: Add support for KVM in-kernel MSI injection
>
> Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> Cc: Jan Kiszka <jan.kiszka@siemens.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Anthony Liguori <aliguori@us.ibm.com>
> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
> ---
> hw/msix.c | 3 +++
> 1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/hw/msix.c b/hw/msix.c
> index 800fc32..c1e6dc3 100644
> --- a/hw/msix.c
> +++ b/hw/msix.c
> @@ -544,6 +544,9 @@ void msix_unset_vector_notifiers(PCIDevice *dev)
> {
> int vector;
>
> + if (!dev->msix_vector_use_notifier && !dev->msix_vector_release_notifier)
> + return;
Missing braces, please read CODING_STYLE.
> +
> assert(dev->msix_vector_use_notifier &&
> dev->msix_vector_release_notifier);
>
> --
> 1.7.2.5
>
>
^ permalink raw reply
* Re: [Qemu-devel] [RFC-v2 3/6] vhost-scsi: add -vhost-scsi host device for use with tcm-vhost
From: Blue Swirl @ 2012-08-13 19:47 UTC (permalink / raw)
To: Nicholas A. Bellinger
Cc: Anthony Liguori, Stefan Hajnoczi, kvm-devel, Michael S. Tsirkin,
Jan Kiszka, qemu-devel, Zhi Yong Wu, Anthony Liguori,
target-devel, Paolo Bonzini, lf-virt, Christoph Hellwig
In-Reply-To: <1344846917-7411-4-git-send-email-nab@linux-iscsi.org>
On Mon, Aug 13, 2012 at 8:35 AM, Nicholas A. Bellinger
<nab@linux-iscsi.org> wrote:
> From: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
>
> This patch adds a new type of host device that drives the vhost_scsi
> device. The syntax to add vhost-scsi is:
>
> qemu -vhost-scsi id=vhost-scsi0,wwpn=...,tpgt=123
>
> The virtio-scsi emulated device will make use of vhost-scsi to process
> virtio-scsi requests inside the kernel and hand them to the in-kernel
> SCSI target stack using the tcm_vhost fabric driver.
>
> The tcm_vhost driver was merged into the upstream linux kernel for 3.6-rc2,
> and the commit can be found here:
>
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=057cbf49a1f08297
>
> Changelog v1 -> v2:
>
> - Expose ABI version via VHOST_SCSI_GET_ABI_VERSION + use Rev 0 as
> starting point for v3.6-rc code (Stefan + ALiguori + nab)
> - Fix upstream qemu conflict in hw/qdev-properties.c
> - Make GET_ABI_VERSION use int (nab + mst)
> - Fix vhost-scsi case lables in configure (reported by paolo)
> - Convert qdev_prop_vhost_scsi to use ->get() + ->set() following
> qdev_prop_netdev (reported by paolo)
> - Fix typo in qemu-options.hx definition of vhost-scsi (reported by paolo)
>
> Changelog v0 -> v1:
>
> - Add VHOST_SCSI_SET_ENDPOINT call (stefan)
> - Enable vhost notifiers for multiple queues (Zhi)
> - clear vhost-scsi endpoint on stopped (Zhi)
> - Add CONFIG_VHOST_SCSI for QEMU build configure (nab)
> - Rename vhost_vring_target -> vhost_scsi_target (mst + nab)
> - Add support for VHOST_SCSI_GET_ABI_VERSION ioctl (aliguori + nab)
>
> Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> Cc: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
> Cc: Anthony Liguori <aliguori@us.ibm.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
> ---
> configure | 10 +++
> hw/Makefile.objs | 1 +
> hw/qdev-properties.c | 40 ++++++++++++
> hw/qdev.h | 3 +
> hw/vhost-scsi.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/vhost-scsi.h | 50 +++++++++++++++
> qemu-common.h | 1 +
> qemu-config.c | 16 +++++
> qemu-options.hx | 4 +
> vl.c | 18 +++++
> 10 files changed, 313 insertions(+), 0 deletions(-)
> create mode 100644 hw/vhost-scsi.c
> create mode 100644 hw/vhost-scsi.h
>
> diff --git a/configure b/configure
> index f0dbc03..1f03202 100755
> --- a/configure
> +++ b/configure
> @@ -168,6 +168,7 @@ libattr=""
> xfs=""
>
> vhost_net="no"
> +vhost_scsi="no"
> kvm="no"
> gprof="no"
> debug_tcg="no"
> @@ -513,6 +514,7 @@ Haiku)
> usb="linux"
> kvm="yes"
> vhost_net="yes"
> + vhost_scsi="yes"
> if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
> audio_possible_drivers="$audio_possible_drivers fmod"
> fi
> @@ -818,6 +820,10 @@ for opt do
> ;;
> --enable-vhost-net) vhost_net="yes"
> ;;
> + --disable-vhost-scsi) vhost_scsi="no"
> + ;;
> + --enable-vhost-scsi) vhost_scsi="yes"
> + ;;
> --disable-opengl) opengl="no"
> ;;
> --enable-opengl) opengl="yes"
> @@ -3116,6 +3122,7 @@ echo "posix_madvise $posix_madvise"
> echo "uuid support $uuid"
> echo "libcap-ng support $cap_ng"
> echo "vhost-net support $vhost_net"
> +echo "vhost-scsi support $vhost_scsi"
> echo "Trace backend $trace_backend"
> echo "Trace output file $trace_file-<pid>"
> echo "spice support $spice"
> @@ -3828,6 +3835,9 @@ case "$target_arch2" in
> if test "$vhost_net" = "yes" ; then
> echo "CONFIG_VHOST_NET=y" >> $config_target_mak
> fi
> + if test "$vhost_scsi" = "yes" ; then
> + echo "CONFIG_VHOST_SCSI=y" >> $config_target_mak
> + fi
> fi
> esac
> case "$target_arch2" in
> diff --git a/hw/Makefile.objs b/hw/Makefile.objs
> index 3ba5dd0..6ab75ec 100644
> --- a/hw/Makefile.objs
> +++ b/hw/Makefile.objs
> @@ -169,6 +169,7 @@ obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
> obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o virtio-scsi.o
> obj-$(CONFIG_SOFTMMU) += vhost_net.o
> obj-$(CONFIG_VHOST_NET) += vhost.o
> +obj-$(CONFIG_VHOST_SCSI) += vhost-scsi.o
> obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
> obj-$(CONFIG_NO_PCI) += pci-stub.o
> obj-$(CONFIG_VGA) += vga.o
> diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
> index 8aca0d4..0266266 100644
> --- a/hw/qdev-properties.c
> +++ b/hw/qdev-properties.c
> @@ -4,6 +4,7 @@
> #include "blockdev.h"
> #include "hw/block-common.h"
> #include "net/hub.h"
> +#include "vhost-scsi.h"
>
> void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
> {
> @@ -696,6 +697,45 @@ PropertyInfo qdev_prop_vlan = {
> .set = set_vlan,
> };
>
> +/* --- vhost-scsi --- */
> +
> +static int parse_vhost_scsi_dev(DeviceState *dev, const char *str, void **ptr)
> +{
> + VHostSCSI *p;
> +
> + p = find_vhost_scsi(str);
> + if (p == NULL)
> + return -ENOENT;
Braces, please.
> +
> + *ptr = p;
> + return 0;
> +}
> +
> +static const char *print_vhost_scsi_dev(void *ptr)
> +{
> + VHostSCSI *p = ptr;
> +
> + return (p) ? vhost_scsi_get_id(p) : "<null>";
> +}
> +
> +static void get_vhost_scsi_dev(Object *obj, Visitor *v, void *opaque,
> + const char *name, Error **errp)
> +{
> + get_pointer(obj, v, opaque, print_vhost_scsi_dev, name, errp);
> +}
> +
> +static void set_vhost_scsi_dev(Object *obj, Visitor *v, void *opaque,
> + const char *name, Error **errp)
> +{
> + set_pointer(obj, v, opaque, parse_vhost_scsi_dev, name, errp);
> +}
> +
> +PropertyInfo qdev_prop_vhost_scsi = {
> + .name = "vhost-scsi",
> + .get = get_vhost_scsi_dev,
> + .set = set_vhost_scsi_dev,
> +};
> +
> /* --- pointer --- */
>
> /* Not a proper property, just for dirty hacks. TODO Remove it! */
> diff --git a/hw/qdev.h b/hw/qdev.h
> index d699194..d5873bb 100644
> --- a/hw/qdev.h
> +++ b/hw/qdev.h
> @@ -238,6 +238,7 @@ extern PropertyInfo qdev_prop_vlan;
> extern PropertyInfo qdev_prop_pci_devfn;
> extern PropertyInfo qdev_prop_blocksize;
> extern PropertyInfo qdev_prop_pci_host_devaddr;
> +extern PropertyInfo qdev_prop_vhost_scsi;
>
> #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \
> .name = (_name), \
> @@ -305,6 +306,8 @@ extern PropertyInfo qdev_prop_pci_host_devaddr;
> DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t)
> #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \
> DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress)
> +#define DEFINE_PROP_VHOST_SCSI(_n, _s, _f) \
> + DEFINE_PROP(_n, _s, _f, qdev_prop_vhost_scsi, VHostSCSI*)
>
> #define DEFINE_PROP_END_OF_LIST() \
> {}
> diff --git a/hw/vhost-scsi.c b/hw/vhost-scsi.c
> new file mode 100644
> index 0000000..7145b2d
> --- /dev/null
> +++ b/hw/vhost-scsi.c
> @@ -0,0 +1,170 @@
> +/*
> + * vhost_scsi host device
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include <sys/ioctl.h>
> +#include "config.h"
> +#include "qemu-queue.h"
> +#include "vhost-scsi.h"
> +#include "vhost.h"
> +
> +struct VHostSCSI {
> + const char *id;
> + const char *wwpn;
> + uint16_t tpgt;
> + struct vhost_dev dev;
> + struct vhost_virtqueue vqs[3];
> + QLIST_ENTRY(VHostSCSI) list;
> +};
> +
> +static QLIST_HEAD(, VHostSCSI) vhost_scsi_list =
> + QLIST_HEAD_INITIALIZER(vhost_scsi_list);
> +
> +VHostSCSI *find_vhost_scsi(const char *id)
> +{
> + VHostSCSI *vs;
> +
> + QLIST_FOREACH(vs, &vhost_scsi_list, list) {
> + if (strcmp(id, vs->id) == 0) {
> + return vs;
> + }
> + }
> + return NULL;
> +}
> +
> +const char *vhost_scsi_get_id(VHostSCSI *vs)
> +{
> + return vs->id;
> +}
> +
> +int vhost_scsi_start(VHostSCSI *vs, VirtIODevice *vdev)
> +{
> + int ret, abi_version;
> + struct vhost_scsi_target backend;
> +
> + if (!vhost_dev_query(&vs->dev, vdev)) {
> + return -ENOTSUP;
> + }
> +
> + vs->dev.nvqs = 3;
> + vs->dev.vqs = vs->vqs;
> +
> + ret = vhost_dev_enable_notifiers(&vs->dev, vdev);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + ret = vhost_dev_start(&vs->dev, vdev);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + memset(&backend, 0, sizeof(backend));
> + ret = ioctl(vs->dev.control, VHOST_SCSI_GET_ABI_VERSION, &abi_version);
> + if (ret < 0) {
> + ret = -errno;
> + vhost_dev_stop(&vs->dev, vdev);
> + return ret;
> + }
> + if (abi_version > VHOST_SCSI_ABI_VERSION) {
> + fprintf(stderr, "The running tcm_vhost kernel abi_version: %d is greater"
> + " than vhost_scsi userspace supports: %d\n", abi_version,
> + VHOST_SCSI_ABI_VERSION);
> + ret = -ENOSYS;
> + vhost_dev_stop(&vs->dev, vdev);
> + return ret;
> + }
> + fprintf(stdout, "TCM_vHost ABI version: %d\n", abi_version);
> +
> + pstrcpy((char *)backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->wwpn);
Please change vhost_wwpn to plain char *, then the cast can be removed.
> + backend.vhost_tpgt = vs->tpgt;
> + ret = ioctl(vs->dev.control, VHOST_SCSI_SET_ENDPOINT, &backend);
> + if (ret < 0) {
> + ret = -errno;
> + vhost_dev_stop(&vs->dev, vdev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +void vhost_scsi_stop(VHostSCSI *vs, VirtIODevice *vdev)
> +{
> + int ret;
> + struct vhost_scsi_target backend;
> +
> + pstrcpy((char *)backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->wwpn);
Also here.
> + backend.vhost_tpgt = vs->tpgt;
> + ret = ioctl(vs->dev.control, VHOST_SCSI_CLEAR_ENDPOINT, &backend);
> + if (ret < 0) {
> + fprintf(stderr, "Failed to clear endpoint\n");
> + }
> +
> + vhost_dev_stop(&vs->dev, vdev);
> +}
> +
> +static VHostSCSI *vhost_scsi_add(const char *id, const char *wwpn,
> + uint16_t tpgt)
> +{
> + VHostSCSI *vs = g_malloc0(sizeof(*vs));
> + int ret;
> +
> + /* TODO set up vhost-scsi device and bind to tcm_vhost/$wwpm/tpgt_$tpgt */
> + fprintf(stderr, "wwpn = \"%s\" tpgt = \"%u\"\n", id, tpgt);
> +
> + ret = vhost_dev_init(&vs->dev, -1, "/dev/vhost-scsi", false);
> + if (ret < 0) {
> + fprintf(stderr, "vhost-scsi: vhost initialization failed: %s\n",
> + strerror(-ret));
> + return NULL;
> + }
> + vs->dev.backend_features = 0;
> + vs->dev.acked_features = 0;
> +
> + vs->id = g_strdup(id);
> + vs->wwpn = g_strdup(wwpn);
> + vs->tpgt = tpgt;
> + QLIST_INSERT_HEAD(&vhost_scsi_list, vs, list);
> +
> + return vs;
> +}
> +
> +VHostSCSI *vhost_scsi_add_opts(QemuOpts *opts)
> +{
> + const char *id;
> + const char *wwpn;
> + uint64_t tpgt;
> +
> + id = qemu_opts_id(opts);
> + if (!id) {
> + fprintf(stderr, "vhost-scsi: no id specified\n");
> + return NULL;
> + }
> + if (find_vhost_scsi(id)) {
> + fprintf(stderr, "duplicate vhost-scsi: \"%s\"\n", id);
> + return NULL;
> + }
> +
> + wwpn = qemu_opt_get(opts, "wwpn");
> + if (!wwpn) {
> + fprintf(stderr, "vhost-scsi: \"%s\" missing wwpn\n", id);
> + return NULL;
> + }
> +
> + tpgt = qemu_opt_get_number(opts, "tpgt", UINT64_MAX);
> + if (tpgt > UINT16_MAX) {
> + fprintf(stderr, "vhost-scsi: \"%s\" needs a 16-bit tpgt\n", id);
> + return NULL;
> + }
> +
> + return vhost_scsi_add(id, wwpn, tpgt);
> +}
> diff --git a/hw/vhost-scsi.h b/hw/vhost-scsi.h
> new file mode 100644
> index 0000000..f3096dc
> --- /dev/null
> +++ b/hw/vhost-scsi.h
> @@ -0,0 +1,50 @@
> +/*
> + * vhost_scsi host device
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#ifndef VHOST_SCSI_H
> +#define VHOST_SCSI_H
> +
> +#include "qemu-common.h"
> +#include "qemu-option.h"
> +
> +/*
> + * Used by QEMU userspace to ensure a consistent vhost-scsi ABI.
> + *
> + * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate +
> + * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage
> + */
> +
> +#define VHOST_SCSI_ABI_VERSION 0
> +
> +/* TODO #include <linux/vhost.h> properly */
> +/* For VHOST_SCSI_SET_ENDPOINT/VHOST_SCSI_CLEAR_ENDPOINT ioctl */
> +struct vhost_scsi_target {
> + int abi_version;
> + unsigned char vhost_wwpn[224];
> + unsigned short vhost_tpgt;
> +};
> +
> +#define VHOST_VIRTIO 0xAF
> +#define VHOST_SCSI_SET_ENDPOINT _IOW(VHOST_VIRTIO, 0x40, struct vhost_scsi_target)
> +#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
> +#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, struct vhost_scsi_target)
> +
> +VHostSCSI *find_vhost_scsi(const char *id);
> +const char *vhost_scsi_get_id(VHostSCSI *vs);
> +
> +VHostSCSI *vhost_scsi_add_opts(QemuOpts *opts);
> +
> +int vhost_scsi_start(VHostSCSI *vs, VirtIODevice *vdev);
> +void vhost_scsi_stop(VHostSCSI *vs, VirtIODevice *vdev);
> +
> +#endif
> diff --git a/qemu-common.h b/qemu-common.h
> index f9deca6..ec36002 100644
> --- a/qemu-common.h
> +++ b/qemu-common.h
> @@ -280,6 +280,7 @@ typedef struct EventNotifier EventNotifier;
> typedef struct VirtIODevice VirtIODevice;
> typedef struct QEMUSGList QEMUSGList;
> typedef struct SHPCDevice SHPCDevice;
> +typedef struct VHostSCSI VHostSCSI;
>
> typedef uint64_t pcibus_t;
>
> diff --git a/qemu-config.c b/qemu-config.c
> index 5c3296b..33399ea 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -626,6 +626,21 @@ QemuOptsList qemu_boot_opts = {
> },
> };
>
> +QemuOptsList qemu_vhost_scsi_opts = {
> + .name = "vhost-scsi",
> + .head = QTAILQ_HEAD_INITIALIZER(qemu_vhost_scsi_opts.head),
> + .desc = {
> + {
> + .name = "wwpn",
> + .type = QEMU_OPT_STRING,
> + }, {
> + .name = "tpgt",
> + .type = QEMU_OPT_NUMBER,
> + },
> + { /* end of list */ }
> + },
> +};
> +
> static QemuOptsList *vm_config_groups[32] = {
> &qemu_drive_opts,
> &qemu_chardev_opts,
> @@ -641,6 +656,7 @@ static QemuOptsList *vm_config_groups[32] = {
> &qemu_machine_opts,
> &qemu_boot_opts,
> &qemu_iscsi_opts,
> + &qemu_vhost_scsi_opts,
> NULL,
> };
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 47cb5bd..4e7a03c 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -565,6 +565,10 @@ possible drivers and properties, use @code{-device ?} and
> ETEXI
>
> DEFHEADING()
> +DEF("vhost-scsi", HAS_ARG, QEMU_OPTION_vhost_scsi,
> + "-vhost-scsi wwpn=string0,tpgt=number0\n"
> + " add vhost-scsi device\n",
> + QEMU_ARCH_ALL)
>
> DEFHEADING(File system options:)
>
> diff --git a/vl.c b/vl.c
> index 91076f0..61c8284 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -144,6 +144,7 @@ int main(int argc, char **argv)
> #include "qemu-options.h"
> #include "qmp-commands.h"
> #include "main-loop.h"
> +#include "hw/vhost-scsi.h"
> #ifdef CONFIG_VIRTFS
> #include "fsdev/qemu-fsdev.h"
> #endif
> @@ -1858,6 +1859,14 @@ static int fsdev_init_func(QemuOpts *opts, void *opaque)
> }
> #endif
>
> +static int vhost_scsi_init_func(QemuOpts *opts, void *opaque)
> +{
> + if (!vhost_scsi_add_opts(opts)) {
> + return -1;
> + }
> + return 0;
> +}
> +
> static int mon_init_func(QemuOpts *opts, void *opaque)
> {
> CharDriverState *chr;
> @@ -2617,6 +2626,11 @@ int main(int argc, char **argv, char **envp)
> }
> break;
> #endif
> + case QEMU_OPTION_vhost_scsi:
> + if (!qemu_opts_parse(qemu_find_opts("vhost-scsi"), optarg, 0)) {
> + exit(1);
> + }
> + break;
> #ifdef CONFIG_SLIRP
> case QEMU_OPTION_tftp:
> legacy_tftp_prefix = optarg;
> @@ -3337,6 +3351,10 @@ int main(int argc, char **argv, char **envp)
> exit(1);
> }
> #endif
> + if (qemu_opts_foreach(qemu_find_opts("vhost-scsi"),
> + vhost_scsi_init_func, NULL, 1)) {
> + exit(1);
> + }
>
> os_daemonize();
>
> --
> 1.7.2.5
>
>
^ permalink raw reply
* [ 01/82] virtio-blk: Call del_gendisk() before disable guest kick
From: Greg Kroah-Hartman @ 2012-08-13 20:18 UTC (permalink / raw)
To: linux-kernel, stable
Cc: kvm, Michael S. Tsirkin, Greg KH, virtualization, akpm, torvalds,
alan
In-Reply-To: <20120813201746.448504360@linuxfoundation.org>
From: Greg KH <gregkh@linuxfoundation.org>
3.5-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asias He <asias@redhat.com>
commit 02e2b124943648fba0a2ccee5c3656a5653e0151 upstream.
del_gendisk() might not return due to failing to remove the
/sys/block/vda/serial sysfs entry when another thread (udev) is
trying to read it.
virtblk_remove()
vdev->config->reset() : guest will not kick us through interrupt
del_gendisk()
device_del()
kobject_del(): got stuck, sysfs entry ref count non zero
sysfs_open_file(): user space process read /sys/block/vda/serial
sysfs_get_active() : got sysfs entry ref count
dev_attr_show()
virtblk_serial_show()
blk_execute_rq() : got stuck, interrupt is disabled
request cannot be finished
This patch fixes it by calling del_gendisk() before we disable guest's
interrupt so that the request sent in virtblk_serial_show() will be
finished and del_gendisk() will success.
This fixes another race in hot-unplug process.
It is save to call del_gendisk(vblk->disk) before
flush_work(&vblk->config_work) which might access vblk->disk, because
vblk->disk is not freed until put_disk(vblk->disk).
Signed-off-by: Asias He <asias@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Cc: kvm@vger.kernel.org
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/block/virtio_blk.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -584,13 +584,13 @@ static void __devexit virtblk_remove(str
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
+ del_gendisk(vblk->disk);
+
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
flush_work(&vblk->config_work);
- del_gendisk(vblk->disk);
-
/* Abort requests dispatched to driver. */
spin_lock_irqsave(&vblk->lock, flags);
while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) {
^ permalink raw reply
* [ 02/82] virtio-blk: Reset device after blk_cleanup_queue()
From: Greg Kroah-Hartman @ 2012-08-13 20:18 UTC (permalink / raw)
To: linux-kernel, stable
Cc: kvm, Michael S. Tsirkin, Greg KH, virtualization, akpm, torvalds,
alan
In-Reply-To: <20120813201746.448504360@linuxfoundation.org>
From: Greg KH <gregkh@linuxfoundation.org>
3.5-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asias He <asias@redhat.com>
commit 483001c765af6892b3fc3726576cb42f17d1d6b5 upstream.
blk_cleanup_queue() will call blk_drian_queue() to drain all the
requests before queue DEAD marking. If we reset the device before
blk_cleanup_queue() the drain would fail.
1) if the queue is stopped in do_virtblk_request() because device is
full, the q->request_fn() will not be called.
blk_drain_queue() {
while(true) {
...
if (!list_empty(&q->queue_head))
__blk_run_queue(q) {
if (queue is not stoped)
q->request_fn()
}
...
}
}
Do no reset the device before blk_cleanup_queue() gives the chance to
start the queue in interrupt handler blk_done().
2) In commit b79d866c8b7014a51f611a64c40546109beaf24a, We abort requests
dispatched to driver before blk_cleanup_queue(). There is a race if
requests are dispatched to driver after the abort and before the queue
DEAD mark. To fix this, instead of aborting the requests explicitly, we
can just reset the device after after blk_cleanup_queue so that the
device can complete all the requests before queue DEAD marking in the
drain process.
Signed-off-by: Asias He <asias@redhat.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: virtualization@lists.linux-foundation.org
Cc: kvm@vger.kernel.org
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/block/virtio_blk.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -576,8 +576,6 @@ static void __devexit virtblk_remove(str
{
struct virtio_blk *vblk = vdev->priv;
int index = vblk->index;
- struct virtblk_req *vbr;
- unsigned long flags;
/* Prevent config work handler from accessing the device. */
mutex_lock(&vblk->config_lock);
@@ -585,21 +583,13 @@ static void __devexit virtblk_remove(str
mutex_unlock(&vblk->config_lock);
del_gendisk(vblk->disk);
+ blk_cleanup_queue(vblk->disk->queue);
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
flush_work(&vblk->config_work);
- /* Abort requests dispatched to driver. */
- spin_lock_irqsave(&vblk->lock, flags);
- while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) {
- __blk_end_request_all(vbr->req, -EIO);
- mempool_free(vbr, vblk->pool);
- }
- spin_unlock_irqrestore(&vblk->lock, flags);
-
- blk_cleanup_queue(vblk->disk->queue);
put_disk(vblk->disk);
mempool_destroy(vblk->pool);
vdev->config->del_vqs(vdev);
^ permalink raw reply
* [ 03/82] virtio-blk: Use block layer provided spinlock
From: Greg Kroah-Hartman @ 2012-08-13 20:18 UTC (permalink / raw)
To: linux-kernel, stable
Cc: kvm, Michael S. Tsirkin, Greg KH, virtualization, akpm, torvalds,
alan
In-Reply-To: <20120813201746.448504360@linuxfoundation.org>
From: Greg KH <gregkh@linuxfoundation.org>
3.5-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asias He <asias@redhat.com>
commit 2c95a3290919541b846bee3e0fbaa75860929f53 upstream.
Block layer will allocate a spinlock for the queue if the driver does
not provide one in blk_init_queue().
The reason to use the internal spinlock is that blk_cleanup_queue() will
switch to use the internal spinlock in the cleanup code path.
if (q->queue_lock != &q->__queue_lock)
q->queue_lock = &q->__queue_lock;
However, processes which are in D state might have taken the driver
provided spinlock, when the processes wake up, they would release the
block provided spinlock.
=====================================
[ BUG: bad unlock balance detected! ]
3.4.0-rc7+ #238 Not tainted
-------------------------------------
fio/3587 is trying to release lock (&(&q->__queue_lock)->rlock) at:
[<ffffffff813274d2>] blk_queue_bio+0x2a2/0x380
but there are no more locks to release!
other info that might help us debug this:
1 lock held by fio/3587:
#0: (&(&vblk->lock)->rlock){......}, at:
[<ffffffff8132661a>] get_request_wait+0x19a/0x250
Other drivers use block layer provided spinlock as well, e.g. SCSI.
Switching to the block layer provided spinlock saves a bit of memory and
does not increase lock contention. Performance test shows no real
difference is observed before and after this patch.
Changes in v2: Improve commit log as Michael suggested.
Signed-off-by: Asias He <asias@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Cc: kvm@vger.kernel.org
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/block/virtio_blk.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -21,8 +21,6 @@ struct workqueue_struct *virtblk_wq;
struct virtio_blk
{
- spinlock_t lock;
-
struct virtio_device *vdev;
struct virtqueue *vq;
@@ -65,7 +63,7 @@ static void blk_done(struct virtqueue *v
unsigned int len;
unsigned long flags;
- spin_lock_irqsave(&vblk->lock, flags);
+ spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
int error;
@@ -99,7 +97,7 @@ static void blk_done(struct virtqueue *v
}
/* In case queue is stopped waiting for more buffers. */
blk_start_queue(vblk->disk->queue);
- spin_unlock_irqrestore(&vblk->lock, flags);
+ spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags);
}
static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
@@ -431,7 +429,6 @@ static int __devinit virtblk_probe(struc
goto out_free_index;
}
- spin_lock_init(&vblk->lock);
vblk->vdev = vdev;
vblk->sg_elems = sg_elems;
sg_init_table(vblk->sg, vblk->sg_elems);
@@ -456,7 +453,7 @@ static int __devinit virtblk_probe(struc
goto out_mempool;
}
- q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock);
+ q = vblk->disk->queue = blk_init_queue(do_virtblk_request, NULL);
if (!q) {
err = -ENOMEM;
goto out_put_disk;
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox