diff options
| author | K. Y. Srinivasan <kys@microsoft.com> | 2012-03-16 11:02:26 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-03-16 16:36:04 -0400 |
| commit | db425334e5bb7fa65bbbd7bea9d79842f65bcf45 (patch) | |
| tree | 0212936fd1e93ae8ad009cded3b783b0f7b0711e /tools/hv | |
| parent | fa3d5b85c681518b6e4ec515814dcb2d5b702b89 (diff) | |
Tools: hv: Fully support the new KVP verbs in the user level daemon
Now fully support the new KVP messages in the user level daemon. Hyper-V defines
multiple persistent pools to which the host can write/read/modify KVP tuples.
In this patch we implement a file for each specified pool, where the KVP tuples
will be stored in the guest.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/hv')
| -rw-r--r-- | tools/hv/hv_kvp_daemon.c | 281 |
1 files changed, 280 insertions, 1 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index a98878c874be..2fb9c3d09d7f 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c | |||
| @@ -39,7 +39,8 @@ | |||
| 39 | #include <ifaddrs.h> | 39 | #include <ifaddrs.h> |
| 40 | #include <netdb.h> | 40 | #include <netdb.h> |
| 41 | #include <syslog.h> | 41 | #include <syslog.h> |
| 42 | 42 | #include <sys/stat.h> | |
| 43 | #include <fcntl.h> | ||
| 43 | 44 | ||
| 44 | /* | 45 | /* |
| 45 | * KVP protocol: The user mode component first registers with the | 46 | * KVP protocol: The user mode component first registers with the |
| @@ -79,6 +80,250 @@ static char *os_build; | |||
| 79 | static char *lic_version; | 80 | static char *lic_version; |
| 80 | static struct utsname uts_buf; | 81 | static struct utsname uts_buf; |
| 81 | 82 | ||
| 83 | |||
| 84 | #define MAX_FILE_NAME 100 | ||
| 85 | #define ENTRIES_PER_BLOCK 50 | ||
| 86 | |||
| 87 | struct kvp_record { | ||
| 88 | __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; | ||
| 89 | __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; | ||
| 90 | }; | ||
| 91 | |||
| 92 | struct kvp_file_state { | ||
| 93 | int fd; | ||
| 94 | int num_blocks; | ||
| 95 | struct kvp_record *records; | ||
| 96 | int num_records; | ||
| 97 | __u8 fname[MAX_FILE_NAME]; | ||
| 98 | }; | ||
| 99 | |||
| 100 | static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; | ||
| 101 | |||
| 102 | static void kvp_acquire_lock(int pool) | ||
| 103 | { | ||
| 104 | struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; | ||
| 105 | fl.l_pid = getpid(); | ||
| 106 | |||
| 107 | if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { | ||
| 108 | syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); | ||
| 109 | exit(-1); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | static void kvp_release_lock(int pool) | ||
| 114 | { | ||
| 115 | struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; | ||
| 116 | fl.l_pid = getpid(); | ||
| 117 | |||
| 118 | if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { | ||
| 119 | perror("fcntl"); | ||
| 120 | syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); | ||
| 121 | exit(-1); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | static void kvp_update_file(int pool) | ||
| 126 | { | ||
| 127 | FILE *filep; | ||
| 128 | size_t bytes_written; | ||
| 129 | |||
| 130 | /* | ||
| 131 | * We are going to write our in-memory registry out to | ||
| 132 | * disk; acquire the lock first. | ||
| 133 | */ | ||
| 134 | kvp_acquire_lock(pool); | ||
| 135 | |||
| 136 | filep = fopen(kvp_file_info[pool].fname, "w"); | ||
| 137 | if (!filep) { | ||
| 138 | kvp_release_lock(pool); | ||
| 139 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); | ||
| 140 | exit(-1); | ||
| 141 | } | ||
| 142 | |||
| 143 | bytes_written = fwrite(kvp_file_info[pool].records, | ||
| 144 | sizeof(struct kvp_record), | ||
| 145 | kvp_file_info[pool].num_records, filep); | ||
| 146 | |||
| 147 | fflush(filep); | ||
| 148 | kvp_release_lock(pool); | ||
| 149 | } | ||
| 150 | |||
| 151 | static int kvp_file_init(void) | ||
| 152 | { | ||
| 153 | int ret, fd; | ||
| 154 | FILE *filep; | ||
| 155 | size_t records_read; | ||
| 156 | __u8 *fname; | ||
| 157 | struct kvp_record *record; | ||
| 158 | struct kvp_record *readp; | ||
| 159 | int num_blocks; | ||
| 160 | int i; | ||
| 161 | int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; | ||
| 162 | |||
| 163 | if (access("/var/opt/hyperv", F_OK)) { | ||
| 164 | if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { | ||
| 165 | syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); | ||
| 166 | exit(-1); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | for (i = 0; i < KVP_POOL_COUNT; i++) { | ||
| 171 | fname = kvp_file_info[i].fname; | ||
| 172 | records_read = 0; | ||
| 173 | num_blocks = 1; | ||
| 174 | sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); | ||
| 175 | fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); | ||
| 176 | |||
| 177 | if (fd == -1) | ||
| 178 | return 1; | ||
| 179 | |||
| 180 | |||
| 181 | filep = fopen(fname, "r"); | ||
| 182 | if (!filep) | ||
| 183 | return 1; | ||
| 184 | |||
| 185 | record = malloc(alloc_unit * num_blocks); | ||
| 186 | if (record == NULL) { | ||
| 187 | fclose(filep); | ||
| 188 | return 1; | ||
| 189 | } | ||
| 190 | while (!feof(filep)) { | ||
| 191 | readp = &record[records_read]; | ||
| 192 | records_read += fread(readp, sizeof(struct kvp_record), | ||
| 193 | ENTRIES_PER_BLOCK, | ||
| 194 | filep); | ||
| 195 | |||
| 196 | if (!feof(filep)) { | ||
| 197 | /* | ||
| 198 | * We have more data to read. | ||
| 199 | */ | ||
| 200 | num_blocks++; | ||
| 201 | record = realloc(record, alloc_unit * | ||
| 202 | num_blocks); | ||
| 203 | if (record == NULL) { | ||
| 204 | fclose(filep); | ||
| 205 | return 1; | ||
| 206 | } | ||
| 207 | continue; | ||
| 208 | } | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | kvp_file_info[i].fd = fd; | ||
| 212 | kvp_file_info[i].num_blocks = num_blocks; | ||
| 213 | kvp_file_info[i].records = record; | ||
| 214 | kvp_file_info[i].num_records = records_read; | ||
| 215 | fclose(filep); | ||
| 216 | |||
| 217 | } | ||
| 218 | |||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | static int kvp_key_delete(int pool, __u8 *key, int key_size) | ||
| 223 | { | ||
| 224 | int i; | ||
| 225 | int j, k; | ||
| 226 | int num_records = kvp_file_info[pool].num_records; | ||
| 227 | struct kvp_record *record = kvp_file_info[pool].records; | ||
| 228 | |||
| 229 | for (i = 0; i < num_records; i++) { | ||
| 230 | if (memcmp(key, record[i].key, key_size)) | ||
| 231 | continue; | ||
| 232 | /* | ||
| 233 | * Found a match; just move the remaining | ||
| 234 | * entries up. | ||
| 235 | */ | ||
| 236 | if (i == num_records) { | ||
| 237 | kvp_file_info[pool].num_records--; | ||
| 238 | kvp_update_file(pool); | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | j = i; | ||
| 243 | k = j + 1; | ||
| 244 | for (; k < num_records; k++) { | ||
| 245 | strcpy(record[j].key, record[k].key); | ||
| 246 | strcpy(record[j].value, record[k].value); | ||
| 247 | j++; | ||
| 248 | } | ||
| 249 | |||
| 250 | kvp_file_info[pool].num_records--; | ||
| 251 | kvp_update_file(pool); | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | return 1; | ||
| 255 | } | ||
| 256 | |||
| 257 | static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, | ||
| 258 | int value_size) | ||
| 259 | { | ||
| 260 | int i; | ||
| 261 | int j, k; | ||
| 262 | int num_records = kvp_file_info[pool].num_records; | ||
| 263 | struct kvp_record *record = kvp_file_info[pool].records; | ||
| 264 | int num_blocks = kvp_file_info[pool].num_blocks; | ||
| 265 | |||
| 266 | if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || | ||
| 267 | (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) | ||
| 268 | return 1; | ||
| 269 | |||
| 270 | for (i = 0; i < num_records; i++) { | ||
| 271 | if (memcmp(key, record[i].key, key_size)) | ||
| 272 | continue; | ||
| 273 | /* | ||
| 274 | * Found a match; just update the value - | ||
| 275 | * this is the modify case. | ||
| 276 | */ | ||
| 277 | memcpy(record[i].value, value, value_size); | ||
| 278 | kvp_update_file(pool); | ||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | /* | ||
| 283 | * Need to add a new entry; | ||
| 284 | */ | ||
| 285 | if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { | ||
| 286 | /* Need to allocate a larger array for reg entries. */ | ||
| 287 | record = realloc(record, sizeof(struct kvp_record) * | ||
| 288 | ENTRIES_PER_BLOCK * (num_blocks + 1)); | ||
| 289 | |||
| 290 | if (record == NULL) | ||
| 291 | return 1; | ||
| 292 | kvp_file_info[pool].num_blocks++; | ||
| 293 | |||
| 294 | } | ||
| 295 | memcpy(record[i].value, value, value_size); | ||
| 296 | memcpy(record[i].key, key, key_size); | ||
| 297 | kvp_file_info[pool].records = record; | ||
| 298 | kvp_file_info[pool].num_records++; | ||
| 299 | kvp_update_file(pool); | ||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, | ||
| 304 | int value_size) | ||
| 305 | { | ||
| 306 | int i; | ||
| 307 | int num_records = kvp_file_info[pool].num_records; | ||
| 308 | struct kvp_record *record = kvp_file_info[pool].records; | ||
| 309 | |||
| 310 | if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || | ||
| 311 | (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) | ||
| 312 | return 1; | ||
| 313 | |||
| 314 | for (i = 0; i < num_records; i++) { | ||
| 315 | if (memcmp(key, record[i].key, key_size)) | ||
| 316 | continue; | ||
| 317 | /* | ||
| 318 | * Found a match; just copy the value out. | ||
| 319 | */ | ||
| 320 | memcpy(value, record[i].value, value_size); | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | return 1; | ||
| 325 | } | ||
| 326 | |||
| 82 | void kvp_get_os_info(void) | 327 | void kvp_get_os_info(void) |
| 83 | { | 328 | { |
| 84 | FILE *file; | 329 | FILE *file; |
| @@ -315,6 +560,11 @@ int main(void) | |||
| 315 | */ | 560 | */ |
| 316 | kvp_get_os_info(); | 561 | kvp_get_os_info(); |
| 317 | 562 | ||
| 563 | if (kvp_file_init()) { | ||
| 564 | syslog(LOG_ERR, "Failed to initialize the pools"); | ||
| 565 | exit(-1); | ||
| 566 | } | ||
| 567 | |||
| 318 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); | 568 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); |
| 319 | if (fd < 0) { | 569 | if (fd < 0) { |
| 320 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); | 570 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); |
| @@ -389,9 +639,38 @@ int main(void) | |||
| 389 | } | 639 | } |
| 390 | continue; | 640 | continue; |
| 391 | 641 | ||
| 642 | /* | ||
| 643 | * The current protocol with the kernel component uses a | ||
| 644 | * NULL key name to pass an error condition. | ||
| 645 | * For the SET, GET and DELETE operations, | ||
| 646 | * use the existing protocol to pass back error. | ||
| 647 | */ | ||
| 648 | |||
| 392 | case KVP_OP_SET: | 649 | case KVP_OP_SET: |
| 650 | if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, | ||
| 651 | hv_msg->body.kvp_set.data.key, | ||
| 652 | hv_msg->body.kvp_set.data.key_size, | ||
| 653 | hv_msg->body.kvp_set.data.value, | ||
| 654 | hv_msg->body.kvp_set.data.value_size)) | ||
| 655 | strcpy(hv_msg->body.kvp_set.data.key, ""); | ||
| 656 | break; | ||
| 657 | |||
| 393 | case KVP_OP_GET: | 658 | case KVP_OP_GET: |
| 659 | if (kvp_get_value(hv_msg->kvp_hdr.pool, | ||
| 660 | hv_msg->body.kvp_set.data.key, | ||
| 661 | hv_msg->body.kvp_set.data.key_size, | ||
| 662 | hv_msg->body.kvp_set.data.value, | ||
| 663 | hv_msg->body.kvp_set.data.value_size)) | ||
| 664 | strcpy(hv_msg->body.kvp_set.data.key, ""); | ||
| 665 | break; | ||
| 666 | |||
| 394 | case KVP_OP_DELETE: | 667 | case KVP_OP_DELETE: |
| 668 | if (kvp_key_delete(hv_msg->kvp_hdr.pool, | ||
| 669 | hv_msg->body.kvp_delete.key, | ||
| 670 | hv_msg->body.kvp_delete.key_size)) | ||
| 671 | strcpy(hv_msg->body.kvp_delete.key, ""); | ||
| 672 | break; | ||
| 673 | |||
| 395 | default: | 674 | default: |
| 396 | break; | 675 | break; |
| 397 | } | 676 | } |
