lkml.org 
[lkml]   [2012]   [Mar]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3/4] Tools: hv: Fully support the new KVP verbs in the user level daemon
    Date
    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 stored in the guest.

    Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
    Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
    ---
    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 a98878c..2fb9c3d 100644
    --- a/tools/hv/hv_kvp_daemon.c
    +++ b/tools/hv/hv_kvp_daemon.c
    @@ -39,7 +39,8 @@
    #include <ifaddrs.h>
    #include <netdb.h>
    #include <syslog.h>
    -
    +#include <sys/stat.h>
    +#include <fcntl.h>

    /*
    * KVP protocol: The user mode component first registers with the
    @@ -79,6 +80,250 @@ static char *os_build;
    static char *lic_version;
    static struct utsname uts_buf;

    +
    +#define MAX_FILE_NAME 100
    +#define ENTRIES_PER_BLOCK 50
    +
    +struct kvp_record {
    + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
    +};
    +
    +struct kvp_file_state {
    + int fd;
    + int num_blocks;
    + struct kvp_record *records;
    + int num_records;
    + __u8 fname[MAX_FILE_NAME];
    +};
    +
    +static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
    +
    +static void kvp_acquire_lock(int pool)
    +{
    + struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    + fl.l_pid = getpid();
    +
    + if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
    + syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
    + exit(-1);
    + }
    +}
    +
    +static void kvp_release_lock(int pool)
    +{
    + struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    + fl.l_pid = getpid();
    +
    + if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
    + perror("fcntl");
    + syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
    + exit(-1);
    + }
    +}
    +
    +static void kvp_update_file(int pool)
    +{
    + FILE *filep;
    + size_t bytes_written;
    +
    + /*
    + * We are going to write our in-memory registry out to
    + * disk; acquire the lock first.
    + */
    + kvp_acquire_lock(pool);
    +
    + filep = fopen(kvp_file_info[pool].fname, "w");
    + if (!filep) {
    + kvp_release_lock(pool);
    + syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
    + exit(-1);
    + }
    +
    + bytes_written = fwrite(kvp_file_info[pool].records,
    + sizeof(struct kvp_record),
    + kvp_file_info[pool].num_records, filep);
    +
    + fflush(filep);
    + kvp_release_lock(pool);
    +}
    +
    +static int kvp_file_init(void)
    +{
    + int ret, fd;
    + FILE *filep;
    + size_t records_read;
    + __u8 *fname;
    + struct kvp_record *record;
    + struct kvp_record *readp;
    + int num_blocks;
    + int i;
    + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
    +
    + if (access("/var/opt/hyperv", F_OK)) {
    + if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
    + syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
    + exit(-1);
    + }
    + }
    +
    + for (i = 0; i < KVP_POOL_COUNT; i++) {
    + fname = kvp_file_info[i].fname;
    + records_read = 0;
    + num_blocks = 1;
    + sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
    + fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
    +
    + if (fd == -1)
    + return 1;
    +
    +
    + filep = fopen(fname, "r");
    + if (!filep)
    + return 1;
    +
    + record = malloc(alloc_unit * num_blocks);
    + if (record == NULL) {
    + fclose(filep);
    + return 1;
    + }
    + while (!feof(filep)) {
    + readp = &record[records_read];
    + records_read += fread(readp, sizeof(struct kvp_record),
    + ENTRIES_PER_BLOCK,
    + filep);
    +
    + if (!feof(filep)) {
    + /*
    + * We have more data to read.
    + */
    + num_blocks++;
    + record = realloc(record, alloc_unit *
    + num_blocks);
    + if (record == NULL) {
    + fclose(filep);
    + return 1;
    + }
    + continue;
    + }
    + break;
    + }
    + kvp_file_info[i].fd = fd;
    + kvp_file_info[i].num_blocks = num_blocks;
    + kvp_file_info[i].records = record;
    + kvp_file_info[i].num_records = records_read;
    + fclose(filep);
    +
    + }
    +
    + return 0;
    +}
    +
    +static int kvp_key_delete(int pool, __u8 *key, int key_size)
    +{
    + int i;
    + int j, k;
    + int num_records = kvp_file_info[pool].num_records;
    + struct kvp_record *record = kvp_file_info[pool].records;
    +
    + for (i = 0; i < num_records; i++) {
    + if (memcmp(key, record[i].key, key_size))
    + continue;
    + /*
    + * Found a match; just move the remaining
    + * entries up.
    + */
    + if (i == num_records) {
    + kvp_file_info[pool].num_records--;
    + kvp_update_file(pool);
    + return 0;
    + }
    +
    + j = i;
    + k = j + 1;
    + for (; k < num_records; k++) {
    + strcpy(record[j].key, record[k].key);
    + strcpy(record[j].value, record[k].value);
    + j++;
    + }
    +
    + kvp_file_info[pool].num_records--;
    + kvp_update_file(pool);
    + return 0;
    + }
    + return 1;
    +}
    +
    +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 = kvp_file_info[pool].num_records;
    + struct kvp_record *record = kvp_file_info[pool].records;
    + int num_blocks = kvp_file_info[pool].num_blocks;
    +
    + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
    + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
    + return 1;
    +
    + for (i = 0; i < num_records; i++) {
    + if (memcmp(key, record[i].key, key_size))
    + continue;
    + /*
    + * Found a match; just update the value -
    + * this is the modify case.
    + */
    + memcpy(record[i].value, value, value_size);
    + kvp_update_file(pool);
    + return 0;
    + }
    +
    + /*
    + * Need to add a new entry;
    + */
    + if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
    + /* Need to allocate a larger array for reg entries. */
    + record = realloc(record, sizeof(struct kvp_record) *
    + ENTRIES_PER_BLOCK * (num_blocks + 1));
    +
    + if (record == NULL)
    + return 1;
    + kvp_file_info[pool].num_blocks++;
    +
    + }
    + memcpy(record[i].value, value, value_size);
    + memcpy(record[i].key, key, key_size);
    + kvp_file_info[pool].records = record;
    + kvp_file_info[pool].num_records++;
    + kvp_update_file(pool);
    + return 0;
    +}
    +
    +static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
    + int value_size)
    +{
    + int i;
    + int num_records = kvp_file_info[pool].num_records;
    + struct kvp_record *record = kvp_file_info[pool].records;
    +
    + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
    + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
    + return 1;
    +
    + for (i = 0; i < num_records; i++) {
    + if (memcmp(key, record[i].key, key_size))
    + continue;
    + /*
    + * Found a match; just copy the value out.
    + */
    + memcpy(value, record[i].value, value_size);
    + return 0;
    + }
    +
    + return 1;
    +}
    +
    void kvp_get_os_info(void)
    {
    FILE *file;
    @@ -315,6 +560,11 @@ int main(void)
    */
    kvp_get_os_info();

    + if (kvp_file_init()) {
    + syslog(LOG_ERR, "Failed to initialize the pools");
    + exit(-1);
    + }
    +
    fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
    if (fd < 0) {
    syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
    @@ -389,9 +639,38 @@ int main(void)
    }
    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.
    + */
    +
    case KVP_OP_SET:
    + if (kvp_key_add_or_modify(hv_msg->kvp_hdr.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, "");
    + break;
    +
    case KVP_OP_GET:
    + if (kvp_get_value(hv_msg->kvp_hdr.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, "");
    + break;
    +
    case KVP_OP_DELETE:
    + if (kvp_key_delete(hv_msg->kvp_hdr.pool,
    + hv_msg->body.kvp_delete.key,
    + hv_msg->body.kvp_delete.key_size))
    + strcpy(hv_msg->body.kvp_delete.key, "");
    + break;
    +
    default:
    break;
    }
    --
    1.7.4.1


    \
     
     \ /
      Last update: 2012-03-11 00:29    [W:4.351 / U:0.296 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site