Skip to content

Commit

Permalink
Drivers: hv: kvp: Cleanup error handling in KVP
Browse files Browse the repository at this point in the history
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
K. Y. Srinivasan authored and Greg Kroah-Hartman committed Aug 16, 2012
1 parent 9b59578 commit b47a81d
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 62 deletions.
114 changes: 91 additions & 23 deletions drivers/hv/hv_kvp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -68,7 +79,7 @@ static u8 *recv_buffer;
*/

static void
kvp_register(void)
kvp_register(int reg_value)
{

struct cn_msg *msg;
Expand All @@ -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);
Expand All @@ -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.
*/
Expand All @@ -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
Expand Down Expand Up @@ -287,22 +357,20 @@ 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
* on this pool.
*/
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)];
Expand Down
17 changes: 11 additions & 6 deletions include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
66 changes: 33 additions & 33 deletions tools/hv/hv_kvp_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down Expand Up @@ -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;
Expand All @@ -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;
}


Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand All @@ -737,58 +745,54 @@ 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;

/*
* If the pool is KVP_POOL_AUTO, dynamically generate
* 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;
}

Expand Down Expand Up @@ -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;
}
/*
Expand Down

0 comments on commit b47a81d

Please sign in to comment.