diff --git a/cmirror/src/compat.c b/cmirror/src/compat.c index 3639d99..da104a1 100644 --- a/cmirror/src/compat.c +++ b/cmirror/src/compat.c @@ -3,6 +3,7 @@ * * This file is released under the GPL. */ +#include <stdlib.h> #include <errno.h> #include <stdint.h> #include <string.h> @@ -339,6 +340,124 @@ static int v4_to_v5(struct clog_tfr_v4 *v4, struct clog_request *rq) return COMPAT_OFFSET + sizeof(*rq) + u_rq->data_size; } +#define switch_64(x, to_wire) \ + (to_wire) ? le64_to_cpu(x) : cpu_to_le64(x) + +void v5_data_endian_switch(struct clog_request *rq, int to_wire) +{ + int i, end; + int64_t *pi64; + uint64_t *pu64; + uint32_t rq_type = rq->u_rq.request_type & ~DM_CLOG_RESPONSE; + + if (rq->u_rq.request_type & DM_CLOG_RESPONSE) { + switch (rq_type) { + case DM_CLOG_CTR: + case DM_CLOG_DTR: + LOG_ERROR("Invalid response type in endian switch"); + exit(EXIT_FAILURE); + + case DM_CLOG_PRESUSPEND: + case DM_CLOG_POSTSUSPEND: + case DM_CLOG_RESUME: + case DM_CLOG_FLUSH: + case DM_CLOG_MARK_REGION: + case DM_CLOG_CLEAR_REGION: + case DM_CLOG_SET_REGION_SYNC: + case DM_CLOG_CHECKPOINT_READY: + case DM_CLOG_MEMBER_JOIN: + case DM_CLOG_STATUS_INFO: + case DM_CLOG_STATUS_TABLE: + /* No outbound data */ + break; + + case DM_CLOG_GET_REGION_SIZE: + case DM_CLOG_GET_SYNC_COUNT: + pu64 = (uint64_t *)rq->u_rq.data; + *pu64 = switch_64(*pu64, to_wire); + break; + case DM_CLOG_IS_CLEAN: + case DM_CLOG_IN_SYNC: + pi64 = (int64_t *)rq->u_rq.data; + *pi64 = switch_64(*pi64, to_wire); + break; + case DM_CLOG_GET_RESYNC_WORK: + case DM_CLOG_IS_REMOTE_RECOVERING: + pi64 = (int64_t *)rq->u_rq.data; + pu64 = ((uint64_t *)rq->u_rq.data) + 1; + *pi64 = switch_64(*pi64, to_wire); + *pu64 = switch_64(*pu64, to_wire); + break; + default: + LOG_ERROR("Unknown request type, %u", rq_type); + return; + } + } else { + switch (rq_type) { + case DM_CLOG_CTR: + case DM_CLOG_DTR: + LOG_ERROR("Invalid request type in endian switch"); + exit(EXIT_FAILURE); + + case DM_CLOG_PRESUSPEND: + case DM_CLOG_POSTSUSPEND: + case DM_CLOG_RESUME: + case DM_CLOG_GET_REGION_SIZE: + case DM_CLOG_FLUSH: + case DM_CLOG_GET_RESYNC_WORK: + case DM_CLOG_GET_SYNC_COUNT: + case DM_CLOG_STATUS_INFO: + case DM_CLOG_STATUS_TABLE: + case DM_CLOG_CHECKPOINT_READY: + case DM_CLOG_MEMBER_JOIN: + /* No incoming data */ + break; + case DM_CLOG_IS_CLEAN: + case DM_CLOG_IN_SYNC: + case DM_CLOG_IS_REMOTE_RECOVERING: + pu64 = (uint64_t *)rq->u_rq.data; + *pu64 = switch_64(*pu64, to_wire); + break; + case DM_CLOG_MARK_REGION: + case DM_CLOG_CLEAR_REGION: + end = rq->u_rq.data_size/sizeof(uint64_t); + + pu64 = (uint64_t *)rq->u_rq.data; + for (i = 0; i < end; i++) + pu64[i] = switch_64(pu64[i], to_wire); + break; + case DM_CLOG_SET_REGION_SYNC: + pu64 = (uint64_t *)rq->u_rq.data; + pi64 = ((int64_t *)rq->u_rq.data) + 1; + *pu64 = switch_64(*pu64, to_wire); + *pi64 = switch_64(*pi64, to_wire); + break; + default: + LOG_ERROR("Unknown request type, %u", rq_type); + exit(EXIT_FAILURE); + } + } +} + +int v5_endian_to_wire(struct clog_request *rq) +{ + int size; + struct dm_ulog_request *u_rq = &rq->u_rq; + + size = sizeof(*rq) + u_rq->data_size; + + u_rq->error = cpu_to_le32(u_rq->error); + u_rq->seq = cpu_to_le32(u_rq->seq); + u_rq->request_type = cpu_to_le32(u_rq->request_type); + u_rq->data_size = cpu_to_le64(u_rq->data_size); + + rq->originator = cpu_to_le32(rq->originator); + + v5_data_endian_switch(rq, 1); + + return size; +} + int add_compatibility_layer(void *from, void **compat_version, unsigned from_version) { @@ -357,6 +476,8 @@ int add_compatibility_layer(void *from, void **compat_version, case 4: from_size = sizeof(*v4) + v4->data_size; break; + case 5: + return v5_endian_to_wire(from); default: LOG_ERROR("Invalid 'from_version'"); return -EINVAL; @@ -396,16 +517,196 @@ static int v5_to_v3(struct clog_request *rq, struct clog_tfr_v3 *v3) return -EINVAL; } +static int v5_to_v4_copy_data(struct clog_request *rq, struct clog_tfr_v4 *v4) +{ + int data_size = 0; + struct dm_ulog_request *u_rq = &rq->u_rq; + uint32_t rq_type = le32_to_cpu(u_rq->request_type) & ~DM_CLOG_RESPONSE; + + if (le32_to_cpu(u_rq->request_type) & DM_CLOG_RESPONSE) { + switch (rq_type) { + case DM_CLOG_CTR: + case DM_CLOG_DTR: + case DM_CLOG_PRESUSPEND: + case DM_CLOG_POSTSUSPEND: + case DM_CLOG_RESUME: + case DM_CLOG_FLUSH: + case DM_CLOG_MARK_REGION: + case DM_CLOG_CLEAR_REGION: + case DM_CLOG_SET_REGION_SYNC: + case DM_CLOG_CHECKPOINT_READY: + case DM_CLOG_MEMBER_JOIN: + /* No outbound data */ + break; + + case DM_CLOG_GET_REGION_SIZE: + data_size = sizeof(uint64_t); + if (v4) { + uint64_t *a64, *b64; + + a64 = (uint64_t *)rq->u_rq.data; + b64 = (uint64_t *)v4->data; + *b64 = le64_to_cpu(*a64); + } + break; + case DM_CLOG_IS_CLEAN: + case DM_CLOG_IN_SYNC: + data_size = sizeof(int); + if (v4) { + int *p; + int64_t *p64; + + p = (int *)v4->data; + p64 = (int64_t *)rq->u_rq.data; + *p = (int)(le64_to_cpu(*p64)); + } + break; + case DM_CLOG_GET_RESYNC_WORK: + data_size = sizeof(int32_t) + + sizeof(uint32_t) + sizeof(uint64_t); + if (v4) { + int32_t *pi32; + int64_t *pi64; + uint64_t *pu64, u64; + + pi32 = (int32_t *)v4->data; + pu64 = ((uint64_t *)v4->data) + 1; + + pi64 = (int64_t *)rq->u_rq.data; + u64 = ((uint64_t *)rq->u_rq.data)[1]; + + *pi32 = (int32_t)(le64_to_cpu(*pi64)); + *pu64 = (uint64_t)le64_to_cpu(u64); + } + break; + case DM_CLOG_GET_SYNC_COUNT: + data_size = sizeof(uint64_t); + if (v4) { + uint64_t *a64, *b64; + + a64 = (uint64_t *)rq->u_rq.data; + b64 = (uint64_t *)v4->data; + *b64 = le64_to_cpu(*a64); + } + break; + case DM_CLOG_STATUS_INFO: + case DM_CLOG_STATUS_TABLE: + data_size = u_rq->data_size; + if (v4) + memcpy(v4->data, rq->u_rq.data, data_size); + break; + case DM_CLOG_IS_REMOTE_RECOVERING: + data_size = sizeof(int32_t) + + sizeof(uint32_t) + sizeof(uint64_t); + if (v4) { + int32_t *pi32; + uint64_t *pu64, u64; + int64_t i64; + + i64 = ((int64_t *)rq->u_rq.data)[0]; + u64 = ((uint64_t *)rq->u_rq.data)[1]; + + pi32 = (int32_t *)v4->data; + pu64 = ((uint64_t *)v4->data) + 1; + + *pi32 = (int32_t)(le64_to_cpu(i64)); + *pu64 = le64_to_cpu(u64); + } + break; + default: + LOG_ERROR("Unknown request type, %u", rq_type); + return -EINVAL; + } + } else { + switch (rq_type) { + case DM_CLOG_CTR: + case DM_CLOG_DTR: + case DM_CLOG_PRESUSPEND: + case DM_CLOG_POSTSUSPEND: + case DM_CLOG_RESUME: + case DM_CLOG_GET_REGION_SIZE: + case DM_CLOG_FLUSH: + case DM_CLOG_GET_RESYNC_WORK: + case DM_CLOG_GET_SYNC_COUNT: + case DM_CLOG_STATUS_INFO: + case DM_CLOG_STATUS_TABLE: + case DM_CLOG_CHECKPOINT_READY: + case DM_CLOG_MEMBER_JOIN: + /* No incoming data */ + break; + case DM_CLOG_IS_CLEAN: + case DM_CLOG_IN_SYNC: + data_size = sizeof(uint64_t); + if (v4) { + uint64_t *a64, *b64; + + a64 = (uint64_t *)rq->u_rq.data; + b64 = (uint64_t *)v4->data; + *b64 = le64_to_cpu(*a64); + } + break; + case DM_CLOG_MARK_REGION: + case DM_CLOG_CLEAR_REGION: + data_size = le32_to_cpu(u_rq->data_size); + if (v4) { + int i, end = data_size/sizeof(uint64_t); + uint64_t *a64, *b64; + + a64 = (uint64_t *)rq->u_rq.data; + b64 = (uint64_t *)v4->data; + for (i = 0; i < end; i++) + b64[i] = le64_to_cpu(a64[i]); + } + break; + case DM_CLOG_SET_REGION_SYNC: + data_size = sizeof(uint64_t) + + sizeof(uint32_t) + sizeof(int32_t); + if (v4) { + uint64_t u64, *pu64; + int64_t i64; + int32_t *pi32; + + pu64 = (uint64_t *)v4->data; + pi32 = ((int32_t *)v4->data) + 3; + + u64 = ((uint64_t *)rq->u_rq.data)[0]; + i64 = ((int64_t *)rq->u_rq.data)[1]; + + *pu64 = le64_to_cpu(u64); + *pi32 = (uint32_t)(le64_to_cpu(i64)); + } + break; + case DM_CLOG_IS_REMOTE_RECOVERING: + data_size = sizeof(uint64_t); + if (v4) { + uint64_t *a64, *b64; + + a64 = (uint64_t *)rq->u_rq.data; + b64 = (uint64_t *)v4->data; + *b64 = le64_to_cpu(*a64); + } + break; + default: + LOG_ERROR("Unknown request type, %u", rq_type); + return -EINVAL; + } + } + + return data_size; +} + static int v5_to_v4(struct clog_request *rq, struct clog_tfr_v4 *v4) { + int size; struct dm_ulog_request *u_rq = &rq->u_rq; - if ((sizeof(*v4) + u_rq->data_size) > (COMPAT_SIZE - COMPAT_OFFSET)) { + size = sizeof(*v4) + v5_to_v4_copy_data(rq, NULL); + + if (size > (COMPAT_SIZE - COMPAT_OFFSET)) { LOG_ERROR("Not enough space for compatibility data (v5->v4)"); return -EINVAL; } - LOG_ERROR("Stripping compatibility layer (v5->v4)"); v4->uuid_instance = (uint32_t)u_rq->luid; memcpy(v4->uuid, u_rq->uuid, DM_UUID_LEN); @@ -416,11 +717,30 @@ static int v5_to_v4(struct clog_request *rq, struct clog_tfr_v4 *v4) v4->originator = rq->originator; - memcpy(v4->data, u_rq->data, u_rq->data_size); + v5_to_v4_copy_data(rq, v4); return 0; } +int v5_endian_from_wire(struct clog_request *rq) +{ + int size; + struct dm_ulog_request *u_rq = &rq->u_rq; + + u_rq->error = le32_to_cpu(u_rq->error); + u_rq->seq = le32_to_cpu(u_rq->seq); + u_rq->request_type = le32_to_cpu(u_rq->request_type); + u_rq->data_size = le64_to_cpu(u_rq->data_size); + + rq->originator = le32_to_cpu(rq->originator); + + size = sizeof(*rq) + u_rq->data_size; + + v5_data_endian_switch(rq, 0); + + return size; +} + int strip_compatibility_layer(void *from, void **to, unsigned to_version) { @@ -462,6 +782,8 @@ int strip_compatibility_layer(void *from, void **to, return v5_to_v3(from, (void *)buffer); case 4: return v5_to_v4(from, (void *)buffer); + case 5: + return v5_endian_from_wire(from); } return -EINVAL; } @@ -473,7 +795,6 @@ int strip_compatibility_layer(void *from, void **to, * * IOW, assume (version == to_version) */ - if (report) { LOG_ERROR("Unknown version for communication struct - forced to assume version %d", to_version); report = 0;