diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hv/hv_kvp.c | 114 |
1 files changed, 91 insertions, 23 deletions
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0012eed6d872..eb4d0730f44d 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c | |||
@@ -48,13 +48,24 @@ static struct { | |||
48 | void *kvp_context; /* for the channel callback */ | 48 | void *kvp_context; /* for the channel callback */ |
49 | } kvp_transaction; | 49 | } kvp_transaction; |
50 | 50 | ||
51 | /* | ||
52 | * Before we can accept KVP messages from the host, we need | ||
53 | * to handshake with the user level daemon. This state tracks | ||
54 | * if we are in the handshake phase. | ||
55 | */ | ||
56 | static bool in_hand_shake = true; | ||
57 | |||
58 | /* | ||
59 | * This state maintains the version number registered by the daemon. | ||
60 | */ | ||
61 | static int dm_reg_value; | ||
62 | |||
51 | static void kvp_send_key(struct work_struct *dummy); | 63 | static void kvp_send_key(struct work_struct *dummy); |
52 | 64 | ||
53 | #define TIMEOUT_FIRED 1 | ||
54 | 65 | ||
55 | static void kvp_respond_to_host(char *key, char *value, int error); | 66 | static void kvp_respond_to_host(char *key, char *value, int error); |
56 | static void kvp_work_func(struct work_struct *dummy); | 67 | static void kvp_work_func(struct work_struct *dummy); |
57 | static void kvp_register(void); | 68 | static void kvp_register(int); |
58 | 69 | ||
59 | static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); | 70 | static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); |
60 | static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); | 71 | static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); |
@@ -68,7 +79,7 @@ static u8 *recv_buffer; | |||
68 | */ | 79 | */ |
69 | 80 | ||
70 | static void | 81 | static void |
71 | kvp_register(void) | 82 | kvp_register(int reg_value) |
72 | { | 83 | { |
73 | 84 | ||
74 | struct cn_msg *msg; | 85 | struct cn_msg *msg; |
@@ -83,7 +94,7 @@ kvp_register(void) | |||
83 | msg->id.idx = CN_KVP_IDX; | 94 | msg->id.idx = CN_KVP_IDX; |
84 | msg->id.val = CN_KVP_VAL; | 95 | msg->id.val = CN_KVP_VAL; |
85 | 96 | ||
86 | kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; | 97 | kvp_msg->kvp_hdr.operation = reg_value; |
87 | strcpy(version, HV_DRV_VERSION); | 98 | strcpy(version, HV_DRV_VERSION); |
88 | msg->len = sizeof(struct hv_kvp_msg); | 99 | msg->len = sizeof(struct hv_kvp_msg); |
89 | cn_netlink_send(msg, 0, GFP_ATOMIC); | 100 | cn_netlink_send(msg, 0, GFP_ATOMIC); |
@@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy) | |||
97 | * If the timer fires, the user-mode component has not responded; | 108 | * If the timer fires, the user-mode component has not responded; |
98 | * process the pending transaction. | 109 | * process the pending transaction. |
99 | */ | 110 | */ |
100 | kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); | 111 | kvp_respond_to_host("Unknown key", "Guest timed out", HV_E_FAIL); |
112 | } | ||
113 | |||
114 | static int kvp_handle_handshake(struct hv_kvp_msg *msg) | ||
115 | { | ||
116 | int ret = 1; | ||
117 | |||
118 | switch (msg->kvp_hdr.operation) { | ||
119 | case KVP_OP_REGISTER: | ||
120 | dm_reg_value = KVP_OP_REGISTER; | ||
121 | pr_info("KVP: IP injection functionality not available\n"); | ||
122 | pr_info("KVP: Upgrade the KVP daemon\n"); | ||
123 | break; | ||
124 | case KVP_OP_REGISTER1: | ||
125 | dm_reg_value = KVP_OP_REGISTER1; | ||
126 | break; | ||
127 | default: | ||
128 | pr_info("KVP: incompatible daemon\n"); | ||
129 | pr_info("KVP: KVP version: %d, Daemon version: %d\n", | ||
130 | KVP_OP_REGISTER1, msg->kvp_hdr.operation); | ||
131 | ret = 0; | ||
132 | } | ||
133 | |||
134 | if (ret) { | ||
135 | /* | ||
136 | * We have a compatible daemon; complete the handshake. | ||
137 | */ | ||
138 | pr_info("KVP: user-mode registering done.\n"); | ||
139 | kvp_register(dm_reg_value); | ||
140 | kvp_transaction.active = false; | ||
141 | if (kvp_transaction.kvp_context) | ||
142 | hv_kvp_onchannelcallback(kvp_transaction.kvp_context); | ||
143 | } | ||
144 | return ret; | ||
101 | } | 145 | } |
102 | 146 | ||
147 | |||
103 | /* | 148 | /* |
104 | * Callback when data is received from user mode. | 149 | * Callback when data is received from user mode. |
105 | */ | 150 | */ |
@@ -109,27 +154,52 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) | |||
109 | { | 154 | { |
110 | struct hv_kvp_msg *message; | 155 | struct hv_kvp_msg *message; |
111 | struct hv_kvp_msg_enumerate *data; | 156 | struct hv_kvp_msg_enumerate *data; |
157 | int error = 0; | ||
112 | 158 | ||
113 | message = (struct hv_kvp_msg *)msg->data; | 159 | message = (struct hv_kvp_msg *)msg->data; |
114 | switch (message->kvp_hdr.operation) { | 160 | |
161 | /* | ||
162 | * If we are negotiating the version information | ||
163 | * with the daemon; handle that first. | ||
164 | */ | ||
165 | |||
166 | if (in_hand_shake) { | ||
167 | if (kvp_handle_handshake(message)) | ||
168 | in_hand_shake = false; | ||
169 | return; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * Based on the version of the daemon, we propagate errors from the | ||
174 | * daemon differently. | ||
175 | */ | ||
176 | |||
177 | data = &message->body.kvp_enum_data; | ||
178 | |||
179 | switch (dm_reg_value) { | ||
115 | case KVP_OP_REGISTER: | 180 | case KVP_OP_REGISTER: |
116 | pr_info("KVP: user-mode registering done.\n"); | 181 | /* |
117 | kvp_register(); | 182 | * Null string is used to pass back error condition. |
118 | kvp_transaction.active = false; | 183 | */ |
119 | hv_kvp_onchannelcallback(kvp_transaction.kvp_context); | 184 | if (data->data.key[0] == 0) |
185 | error = HV_S_CONT; | ||
120 | break; | 186 | break; |
121 | 187 | ||
122 | default: | 188 | case KVP_OP_REGISTER1: |
123 | data = &message->body.kvp_enum_data; | ||
124 | /* | 189 | /* |
125 | * Complete the transaction by forwarding the key value | 190 | * We use the message header information from |
126 | * to the host. But first, cancel the timeout. | 191 | * the user level daemon to transmit errors. |
127 | */ | 192 | */ |
128 | if (cancel_delayed_work_sync(&kvp_work)) | 193 | error = message->error; |
129 | kvp_respond_to_host(data->data.key, | 194 | break; |
130 | data->data.value, | ||
131 | !strlen(data->data.key)); | ||
132 | } | 195 | } |
196 | |||
197 | /* | ||
198 | * Complete the transaction by forwarding the key value | ||
199 | * to the host. But first, cancel the timeout. | ||
200 | */ | ||
201 | if (cancel_delayed_work_sync(&kvp_work)) | ||
202 | kvp_respond_to_host(data->data.key, data->data.value, error); | ||
133 | } | 203 | } |
134 | 204 | ||
135 | static void | 205 | static void |
@@ -287,6 +357,7 @@ kvp_respond_to_host(char *key, char *value, int error) | |||
287 | */ | 357 | */ |
288 | return; | 358 | return; |
289 | 359 | ||
360 | icmsghdrp->status = error; | ||
290 | 361 | ||
291 | /* | 362 | /* |
292 | * If the error parameter is set, terminate the host's enumeration | 363 | * 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) | |||
294 | */ | 365 | */ |
295 | if (error) { | 366 | if (error) { |
296 | /* | 367 | /* |
297 | * Something failed or the we have timedout; | 368 | * Something failed or we have timedout; |
298 | * terminate the current host-side iteration. | 369 | * terminate the current host-side iteration. |
299 | */ | 370 | */ |
300 | icmsghdrp->status = HV_S_CONT; | ||
301 | goto response_done; | 371 | goto response_done; |
302 | } | 372 | } |
303 | 373 | ||
304 | icmsghdrp->status = HV_S_OK; | ||
305 | |||
306 | kvp_msg = (struct hv_kvp_msg *) | 374 | kvp_msg = (struct hv_kvp_msg *) |
307 | &recv_buffer[sizeof(struct vmbuspipe_hdr) + | 375 | &recv_buffer[sizeof(struct vmbuspipe_hdr) + |
308 | sizeof(struct icmsg_hdr)]; | 376 | sizeof(struct icmsg_hdr)]; |