Viewing: liblustreapi_layout.c
// SPDX-License-Identifier: LGPL-2.1+
/*
* Copyright (c) 2016, 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustreapi library for layout calls for interacting with the layout of
* Lustre files while hiding details of the internal data structures
* from the user.
*
* Author: Ned Bass <bass6@llnl.gov>
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/xattr.h>
#include <time.h>
#include <libcfs/util/list.h>
#include <lustre/lustreapi.h>
#include <linux/lustre/erasure_code.h>
#include <linux/lustre/lustre_idl.h>
#include "lstddef.h"
#include "lustreapi_internal.h"
/**
* Data structure when computing/verifying parities holding a list
* of all regions in the parity stripe that depends on data from
* the data stripes.
* This allows us to only verify/write the parts of the parity stripes
* that are actually used for protecting the data stripes and to ignore
* those regions that do not need parity data.
* For example regions that are covered by a "hole" in all the corresponding
* data stripes of are beyond eof for the data mirror.
*/
struct ec_parity_coverage {
struct ec_parity_coverage *next;
uint64_t pos;
uint64_t len;
};
/**
* Layout component, which contains all attributes of a plain
* V1/V3/FOREIGN(HSM) layout.
*/
struct llapi_layout_comp {
uint64_t llc_pattern;
union {
struct { /* For plain layout. */
uint64_t llc_stripe_size;
uint64_t llc_stripe_count;
uint64_t llc_stripe_offset;
/**
* Add 1 so user always gets back a null terminated
* string.
*/
char llc_pool_name[LOV_MAXPOOLNAME + 1];
/**
* Number of objects in llc_objects array if was
* initialized.
*/
uint32_t llc_objects_count;
/**
* EC parity comp specific fields.
*/
uint8_t llc_dstripe_count;
uint8_t llc_cstripe_count;
struct lov_user_ost_data_v1 *llc_objects;
};
struct { /* For FOREIGN/HSM layout. */
uint32_t llc_length;
uint32_t llc_type;
uint32_t llc_hsm_flags;
union {
struct lov_hsm_base llc_hsm;
char *llc_value;
};
};
};
/* fields used only for composite layouts */
struct lu_extent llc_extent; /* [start, end) of component */
uint32_t llc_id; /* unique ID of component */
/* mirror ID this component belongs to */
uint32_t llc_mirror_id;
/* mirror link id for data and parity components */
uint16_t llc_mirror_link_id;
uint32_t llc_flags; /* LCME_FL_* flags */
uint64_t llc_timestamp; /* snapshot timestamp */
/* linked to the llapi_layout components list */
struct list_head llc_list;
bool llc_ondisk;
};
#define llc_archive_id llc_hsm.lhb_archive_id
#define llc_archive_ver llc_hsm.lhb_archive_ver
#define llc_uuid llc_hsm.lhb_uuid
/*
* An Opaque data type abstracting the layout of a Lustre file.
*/
struct llapi_layout {
uint32_t llot_magic; /* LLAPI_LAYOUT_MAGIC */
uint32_t llot_gen;
uint32_t llot_flags;
bool llot_is_composite;
uint16_t llot_mirror_count;
uint16_t llot_curr_link_id;
uint32_t llot_curr_mirror_id;
/* Cursor pointing to one of the components in llot_comp_list */
struct llapi_layout_comp *llot_cur_comp;
struct list_head llot_comp_list;
};
/**
* llapi_layout_objects_in_lum() - Compute the number of elements in the
* lmm_objects array of @lum with size @lum_size.
* @lum: the struct lov_user_md to check
* @lum_size: the number of bytes in @lum
*
* Return number of elements in array lum->lmm_objects
*/
static int llapi_layout_objects_in_lum(struct lov_user_md *lum, size_t lum_size)
{
uint32_t magic;
size_t base_size;
if (lum->lmm_magic == __swab32(LOV_MAGIC_FOREIGN))
return 0;
if (lum_size < lov_user_md_size(0, LOV_MAGIC_V1))
return 0;
if (lum->lmm_magic == __swab32(LOV_MAGIC_V1) ||
lum->lmm_magic == __swab32(LOV_MAGIC_V3))
magic = __swab32(lum->lmm_magic);
else
magic = lum->lmm_magic;
base_size = lov_user_md_size(0, magic);
if (lum_size <= base_size)
return 0;
else
return (lum_size - base_size) / sizeof(lum->lmm_objects[0]);
}
/*
* Byte-swap the fields of struct lov_user_md.
*
* XXX Rather than duplicating swabbing code here, we should eventually
* refactor the needed functions in lustre/ptlrpc/pack_generic.c
* into a library that can be shared between kernel and user code.
*/
static void
llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
{
int i, j, ent_count, obj_count;
struct lov_comp_md_v1 *comp_v1 = NULL;
struct lov_comp_md_entry_v1 *ent;
struct lov_user_ost_data *lod;
if (lum->lmm_magic != __swab32(LOV_MAGIC_V1) &&
lum->lmm_magic != __swab32(LOV_MAGIC_V3) &&
lum->lmm_magic != __swab32(LOV_MAGIC_COMP_V1))
return;
if (lum->lmm_magic == __swab32(LOV_MAGIC_COMP_V1))
comp_v1 = (struct lov_comp_md_v1 *)lum;
if (comp_v1 != NULL) {
comp_v1->lcm_magic = __swab32(comp_v1->lcm_magic);
comp_v1->lcm_size = __swab32(comp_v1->lcm_size);
comp_v1->lcm_layout_gen = __swab32(comp_v1->lcm_layout_gen);
comp_v1->lcm_flags = __swab16(comp_v1->lcm_flags);
comp_v1->lcm_entry_count = __swab16(comp_v1->lcm_entry_count);
ent_count = comp_v1->lcm_entry_count;
} else {
ent_count = 1;
}
for (i = 0; i < ent_count; i++) {
if (comp_v1 != NULL) {
ent = &comp_v1->lcm_entries[i];
ent->lcme_id = __swab32(ent->lcme_id);
ent->lcme_flags = __swab32(ent->lcme_flags);
ent->lcme_time_and_id = __swab64(ent->lcme_time_and_id);
ent->lcme_extent.e_start = __swab64(ent->lcme_extent.e_start);
ent->lcme_extent.e_end = __swab64(ent->lcme_extent.e_end);
ent->lcme_offset = __swab32(ent->lcme_offset);
ent->lcme_size = __swab32(ent->lcme_size);
lum = (struct lov_user_md *)((char *)comp_v1 +
ent->lcme_offset);
lum_size = ent->lcme_size;
}
lum->lmm_magic = __swab32(lum->lmm_magic);
if (lum->lmm_magic == LOV_MAGIC_FOREIGN) {
struct lov_hsm_md *lhm;
lhm = (struct lov_hsm_md *)lum;
lhm->lhm_length = __swab32(lhm->lhm_length);
lhm->lhm_type = __swab32(lhm->lhm_type);
lhm->lhm_flags = __swab32(lhm->lhm_flags);
if (!lov_hsm_type_supported(lhm->lhm_type))
continue;
lhm->lhm_archive_id = __swab64(lhm->lhm_archive_id);
lhm->lhm_archive_ver = __swab64(lhm->lhm_archive_ver);
} else {
obj_count = llapi_layout_objects_in_lum(lum, lum_size);
lum->lmm_pattern = __swab32(lum->lmm_pattern);
lum->lmm_stripe_size = __swab32(lum->lmm_stripe_size);
lum->lmm_stripe_count = __swab16(lum->lmm_stripe_count);
lum->lmm_stripe_offset =
__swab16(lum->lmm_stripe_offset);
if (lum->lmm_magic != LOV_MAGIC_V1) {
struct lov_user_md_v3 *v3;
v3 = (struct lov_user_md_v3 *)lum;
lod = v3->lmm_objects;
} else {
lod = lum->lmm_objects;
}
for (j = 0; j < obj_count; j++)
lod[j].l_ost_idx = __swab32(lod[j].l_ost_idx);
}
}
}
/**
* __llapi_comp_objects_realloc() - copy existing object to new object
* @comp: existing layout to be modified
* @new_stripes: number of stripes in new layout
*
* (Re-)allocate llc_objects[] to @num_stripes stripes.
* Copy over existing llc_objects[], if any, to the new llc_objects[].
*
* Return:
* * %0 if the objects are re-allocated successfully
* * %negative on error with errno set
*/
static int __llapi_comp_objects_realloc(struct llapi_layout_comp *comp,
unsigned int new_stripes)
{
struct lov_user_ost_data_v1 *new_objects;
unsigned int i;
if (new_stripes > LOV_MAX_STRIPE_COUNT) {
errno = EINVAL;
return -1;
}
if (new_stripes == comp->llc_objects_count)
return 0;
if (new_stripes != 0 && new_stripes <= comp->llc_objects_count)
return 0;
new_objects = realloc(comp->llc_objects,
sizeof(*new_objects) * new_stripes);
if (new_objects == NULL && new_stripes != 0) {
errno = ENOMEM;
return -1;
}
for (i = comp->llc_objects_count; i < new_stripes; i++)
new_objects[i].l_ost_idx = LLAPI_LAYOUT_IDX_MAX;
comp->llc_objects = new_objects;
comp->llc_objects_count = new_stripes;
return 0;
}
/**
* __llapi_comp_alloc() - Allocate storage for a llapi_layout_comp with
* @num_stripes stripes.
* @num_stripes: number of stripes in new layout
*
* Return valid pointer if allocation succeeds or NULL if allocation fails
*/
static struct llapi_layout_comp *__llapi_comp_alloc(unsigned int num_stripes)
{
struct llapi_layout_comp *comp;
if (num_stripes > LOV_MAX_STRIPE_COUNT) {
errno = EINVAL;
return NULL;
}
comp = calloc(1, sizeof(*comp));
if (comp == NULL) {
errno = ENOMEM;
return NULL;
}
comp->llc_objects = NULL;
comp->llc_objects_count = 0;
if (__llapi_comp_objects_realloc(comp, num_stripes) < 0) {
free(comp);
return NULL;
}
/* Set defaults. */
comp->llc_pattern = LLAPI_LAYOUT_DEFAULT;
comp->llc_stripe_size = LLAPI_LAYOUT_DEFAULT;
comp->llc_stripe_count = LLAPI_LAYOUT_DEFAULT;
comp->llc_stripe_offset = LLAPI_LAYOUT_DEFAULT;
comp->llc_pool_name[0] = '\0';
comp->llc_extent.e_start = 0;
comp->llc_extent.e_end = LUSTRE_EOF;
comp->llc_flags = 0;
comp->llc_id = 0;
comp->llc_mirror_id = 0;
comp->llc_mirror_link_id = LLAPI_MIRROR_LINK_NONE;
INIT_LIST_HEAD(&comp->llc_list);
return comp;
}
/**
* __llapi_comp_hsm_alloc() - Alloc storage for HSM component with @length buffer.
* @length: size of allocation
*
* Return valid pointer if allocation succeeds or NULL if allocate fails
*/
static struct llapi_layout_comp *__llapi_comp_hsm_alloc(uint32_t length)
{
struct llapi_layout_comp *comp;
if (lov_foreign_md_size(length) > XATTR_SIZE_MAX) {
errno = EINVAL;
return NULL;
}
comp = calloc(1, sizeof(*comp));
if (comp == NULL) {
errno = ENOMEM;
return NULL;
}
comp->llc_pattern = LLAPI_LAYOUT_FOREIGN;
comp->llc_length = length;
comp->llc_type = LU_FOREIGN_TYPE_UNKNOWN;
comp->llc_hsm_flags = 0;
comp->llc_archive_id = 0;
comp->llc_archive_ver = 0;
comp->llc_extent.e_start = 0;
comp->llc_extent.e_end = LUSTRE_EOF;
comp->llc_flags = 0;
comp->llc_id = 0;
INIT_LIST_HEAD(&comp->llc_list);
return comp;
}
/**
* __llapi_comp_free() - Free memory allocated for @comp
* @comp: previously allocated by __llapi_comp_alloc()
*/
static void __llapi_comp_free(struct llapi_layout_comp *comp)
{
if (comp->llc_pattern != LLAPI_LAYOUT_FOREIGN &&
comp->llc_objects != NULL) {
free(comp->llc_objects);
}
free(comp);
}
/**
* llapi_layout_free() - Free memory allocated for @layout.
* @layout: previously allocated by llapi_layout_alloc()
*/
void llapi_layout_free(struct llapi_layout *layout)
{
struct llapi_layout_comp *comp, *n;
if (layout == NULL)
return;
list_for_each_entry_safe(comp, n, &layout->llot_comp_list, llc_list) {
list_del_init(&comp->llc_list);
__llapi_comp_free(comp);
}
free(layout);
}
/**
* __llapi_layout_alloc() - Allocate and initialize a llapi_layout structure.
*
* Returns valid llapi_layout pointer on success or NULL if memory allocation
* fails
*/
static struct llapi_layout *__llapi_layout_alloc(void)
{
struct llapi_layout *layout;
layout = calloc(1, sizeof(*layout));
if (layout == NULL) {
errno = ENOMEM;
return NULL;
}
/* Set defaults. */
layout->llot_magic = LLAPI_LAYOUT_MAGIC;
layout->llot_gen = 0;
layout->llot_flags = 0;
layout->llot_is_composite = false;
layout->llot_mirror_count = 1;
layout->llot_curr_link_id = 1;
layout->llot_curr_mirror_id = 1;
layout->llot_cur_comp = NULL;
INIT_LIST_HEAD(&layout->llot_comp_list);
return layout;
}
/**
* llapi_layout_alloc() - Allocate and initialize a new plain layout.
*
* Return valid llapi_layout pointer on success or NULL if memory allocation
* fails
*/
struct llapi_layout *llapi_layout_alloc(void)
{
struct llapi_layout_comp *comp;
struct llapi_layout *layout;
layout = __llapi_layout_alloc();
if (layout == NULL)
return NULL;
comp = __llapi_comp_alloc(0);
if (comp == NULL) {
free(layout);
return NULL;
}
list_add_tail(&comp->llc_list, &layout->llot_comp_list);
layout->llot_cur_comp = comp;
return layout;
}
/**
* llapi_layout_lum_truncated() - Check if the given @lum_size is large enough
* to hold the required fields in @lum.
* @lum: the struct lov_user_md to check
* @lum_size: the number of bytes in @lum
*
* Return:
* * %true the @lum_size is too small
* * %false the @lum_size is large enough
*/
static bool llapi_layout_lum_truncated(struct lov_user_md *lum, size_t lum_size)
{
uint32_t magic;
if (lum_size < sizeof(lum->lmm_magic))
return true;
if (lum->lmm_magic == LOV_MAGIC_V1 ||
lum->lmm_magic == __swab32(LOV_MAGIC_V1))
magic = LOV_MAGIC_V1;
else if (lum->lmm_magic == LOV_MAGIC_V3 ||
lum->lmm_magic == __swab32(LOV_MAGIC_V3))
magic = LOV_MAGIC_V3;
else if (lum->lmm_magic == LOV_MAGIC_SPECIFIC ||
lum->lmm_magic == __swab32(LOV_MAGIC_SPECIFIC))
magic = LOV_MAGIC_V3;
else if (lum->lmm_magic == LOV_MAGIC_COMP_V1 ||
lum->lmm_magic == __swab32(LOV_MAGIC_COMP_V1))
magic = LOV_MAGIC_COMP_V1;
else
return true;
if (magic == LOV_MAGIC_V1 || magic == LOV_MAGIC_V3)
return lum_size < lov_user_md_size(0, magic);
else
return lum_size < sizeof(struct lov_comp_md_v1);
}
/* Verify if the objects count in lum is consistent with the
* stripe count in lum. It applies to regular file only.
*/
static bool llapi_layout_lum_valid(struct lov_user_md *lum, int lum_size)
{
struct lov_comp_md_v1 *comp_v1 = NULL;
int i, ent_count, obj_count;
if (lum->lmm_magic == LOV_MAGIC_COMP_V1) {
comp_v1 = (struct lov_comp_md_v1 *)lum;
ent_count = comp_v1->lcm_entry_count;
} else if (lum->lmm_magic == LOV_MAGIC_V1 ||
lum->lmm_magic == LOV_MAGIC_V3) {
ent_count = 1;
} else {
return false;
}
for (i = 0; i < ent_count; i++) {
if (comp_v1) {
lum = (struct lov_user_md *)((char *)comp_v1 +
comp_v1->lcm_entries[i].lcme_offset);
lum_size = comp_v1->lcm_entries[i].lcme_size;
if (lum->lmm_magic == LOV_MAGIC_FOREIGN)
continue;
}
obj_count = llapi_layout_objects_in_lum(lum, lum_size);
if (comp_v1) {
if (!(comp_v1->lcm_entries[i].lcme_flags &
LCME_FL_INIT) && obj_count != 0)
return false;
} else if (obj_count != lum->lmm_stripe_count) {
return false;
}
}
return true;
}
/**
* llapi_layout_get_by_xattr() - Convert data from lov_user_md to llapi_layout
* @lov_xattr: LOV user metadata xattr to copy data from
* @lov_xattr_size: size the lov_xattr_size passed in
* @flags: flags to control how layout is retrieved
*
* Convert the data from a lov_user_md to a newly allocated llapi_layout. The
* caller is responsible for freeing the returned pointer.
*
* Return valid llapi_layout pointer on success or NULL if memory allocation
* fails
*/
struct llapi_layout *llapi_layout_get_by_xattr(void *lov_xattr,
ssize_t lov_xattr_size,
enum llapi_layout_get_flags flags)
{
struct lov_user_md *lum = lov_xattr;
struct lov_comp_md_v1 *comp_v1 = NULL;
struct lov_comp_md_entry_v1 *ent;
struct lov_user_md *v1;
struct llapi_layout *layout = NULL;
struct llapi_layout_comp *comp;
int i, ent_count = 0, obj_count = 0;
if (lov_xattr == NULL || lov_xattr_size <= 0) {
errno = EINVAL;
return NULL;
}
/* Return an error if we got back a partial layout. */
if (llapi_layout_lum_truncated(lov_xattr, lov_xattr_size)) {
errno = ERANGE;
return NULL;
}
#if __BYTE_ORDER == __BIG_ENDIAN
if (flags & LLAPI_LAYOUT_GET_COPY) {
lum = malloc(lov_xattr_size);
if (lum == NULL) {
errno = ENOMEM;
return NULL;
}
memcpy(lum, lov_xattr, lov_xattr_size);
}
#endif
llapi_layout_swab_lov_user_md(lum, lov_xattr_size);
#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 16, 53, 0)
#define LLAPI_LXF_CHECK_OLD 0x0001
if (flags & LLAPI_LXF_CHECK_OLD)
flags = (flags & ~LLAPI_LXF_CHECK_OLD) | LLAPI_LAYOUT_GET_CHECK;
#endif
if ((flags & LLAPI_LAYOUT_GET_CHECK) &&
!llapi_layout_lum_valid(lum, lov_xattr_size)) {
errno = EBADSLT;
goto out;
}
layout = __llapi_layout_alloc();
if (layout == NULL) {
errno = ENOMEM;
goto out;
}
if (lum->lmm_magic == LOV_MAGIC_COMP_V1) {
comp_v1 = (struct lov_comp_md_v1 *)lum;
ent_count = comp_v1->lcm_entry_count;
layout->llot_gen = comp_v1->lcm_layout_gen;
layout->llot_is_composite = true;
layout->llot_mirror_count = comp_v1->lcm_mirror_count + 1;
layout->llot_gen = comp_v1->lcm_layout_gen;
layout->llot_flags = comp_v1->lcm_flags;
} else if (lum->lmm_magic == LOV_MAGIC_V1 ||
lum->lmm_magic == LOV_MAGIC_V3 ||
lum->lmm_magic == LOV_MAGIC_SPECIFIC) {
ent_count = 1;
layout->llot_is_composite = false;
if (lov_xattr_size <= 0) {
errno = EINVAL;
goto out_layout;
}
} else {
errno = EOPNOTSUPP;
goto out_layout;
}
if (ent_count == 0) {
errno = EINVAL;
goto out_layout;
}
v1 = (struct lov_user_md *)lum;
for (i = 0; i < ent_count; i++) {
if (comp_v1 != NULL) {
ent = &comp_v1->lcm_entries[i];
v1 = (struct lov_user_md *)((char *)comp_v1 +
ent->lcme_offset);
lov_xattr_size = ent->lcme_size;
} else {
ent = NULL;
}
if (v1->lmm_magic == LOV_MAGIC_FOREIGN) {
struct lov_hsm_md *lhm;
lhm = (struct lov_hsm_md *)v1;
if (!lov_hsm_type_supported(lhm->lhm_type))
goto out_layout;
if (lhm->lhm_length != sizeof(struct lov_hsm_base))
goto out_layout;
comp = __llapi_comp_hsm_alloc(lhm->lhm_length);
if (comp == NULL)
goto out_layout;
comp->llc_length = lhm->lhm_length;
comp->llc_type = lhm->lhm_type;
comp->llc_hsm_flags = lhm->lhm_flags;
comp->llc_archive_id = lhm->lhm_archive_id;
comp->llc_archive_ver = lhm->lhm_archive_ver;
memcpy(comp->llc_uuid, lhm->lhm_archive_uuid,
sizeof(comp->llc_uuid));
} else {
obj_count = llapi_layout_objects_in_lum(v1,
lov_xattr_size);
comp = __llapi_comp_alloc(obj_count);
if (comp == NULL)
goto out_layout;
}
if (ent != NULL) {
comp->llc_extent.e_start = ent->lcme_extent.e_start;
comp->llc_extent.e_end = ent->lcme_extent.e_end;
comp->llc_id = ent->lcme_id;
comp->llc_mirror_id = mirror_id_of(ent->lcme_id);
comp->llc_flags = ent->lcme_flags;
comp->llc_timestamp = lcme_timestamp_time_unpack(
ent->lcme_time_and_id);
comp->llc_mirror_link_id =
lcme_timestamp_id_unpack(ent->lcme_time_and_id);
comp->llc_dstripe_count = ent->lcme_dstripe_count;
comp->llc_cstripe_count = ent->lcme_cstripe_count;
} else {
comp->llc_extent.e_start = 0;
comp->llc_extent.e_end = LUSTRE_EOF;
comp->llc_id = 0;
comp->llc_flags = 0;
}
if (v1->lmm_magic == LOV_MAGIC_FOREIGN) {
comp->llc_pattern = LLAPI_LAYOUT_FOREIGN;
goto comp_add;
}
if (v1->lmm_pattern == LOV_PATTERN_RAID0)
comp->llc_pattern = LLAPI_LAYOUT_RAID0;
else if (v1->lmm_pattern == (LOV_PATTERN_RAID0 |
LOV_PATTERN_OVERSTRIPING))
comp->llc_pattern = LLAPI_LAYOUT_OVERSTRIPING;
else if (v1->lmm_pattern & LOV_PATTERN_MDT)
comp->llc_pattern = LLAPI_LAYOUT_MDT;
else
/* Lustre only supports RAID0, overstripping,
* DoM and FOREIGN/HSM for now.
*/
comp->llc_pattern = v1->lmm_pattern;
if (v1->lmm_stripe_size == 0)
comp->llc_stripe_size = LLAPI_LAYOUT_DEFAULT;
else
comp->llc_stripe_size = v1->lmm_stripe_size;
if (v1->lmm_stripe_count >= LOV_ALL_STRIPES_WIDE &&
v1->lmm_stripe_count <= LOV_ALL_STRIPES)
comp->llc_stripe_count = LLAPI_LAYOUT_WIDE_MIN +
(LOV_ALL_STRIPES -
v1->lmm_stripe_count);
else if (v1->lmm_stripe_count == 0)
comp->llc_stripe_count = LLAPI_LAYOUT_DEFAULT;
else
comp->llc_stripe_count = v1->lmm_stripe_count;
if (v1->lmm_stripe_offset ==
(typeof(v1->lmm_stripe_offset))-1)
comp->llc_stripe_offset = LLAPI_LAYOUT_DEFAULT;
else
comp->llc_stripe_offset = v1->lmm_stripe_offset;
if (v1->lmm_magic != LOV_USER_MAGIC_V1) {
const struct lov_user_md_v3 *lumv3;
size_t size = sizeof(comp->llc_pool_name);
int rc;
lumv3 = (struct lov_user_md_v3 *)v1;
rc = snprintf(comp->llc_pool_name, size,
"%s", lumv3->lmm_pool_name);
/* Avoid GCC 7 format-truncation warning. */
if (rc > size)
comp->llc_pool_name[size - 1] = 0;
memcpy(comp->llc_objects, lumv3->lmm_objects,
obj_count * sizeof(lumv3->lmm_objects[0]));
} else {
const struct lov_user_md_v1 *lumv1;
lumv1 = (struct lov_user_md_v1 *)v1;
memcpy(comp->llc_objects, lumv1->lmm_objects,
obj_count * sizeof(lumv1->lmm_objects[0]));
}
if (obj_count != 0)
comp->llc_stripe_offset =
comp->llc_objects[0].l_ost_idx;
comp_add:
comp->llc_ondisk = true;
list_add_tail(&comp->llc_list, &layout->llot_comp_list);
layout->llot_cur_comp = comp;
}
out:
if (lum != lov_xattr)
free(lum);
return layout;
out_layout:
llapi_layout_free(layout);
layout = NULL;
goto out;
}
/**
* get_lum_size() - Get @lum_size from a lov_user_md @lum
* @lum: lov_user_md to get lum_size
*
* Returns -1 on error and lum_size on success
*/
static size_t get_lum_size(struct lov_user_md *lum)
{
size_t lum_size;
if (lum == NULL) {
errno = EINVAL;
return -1;
}
if (lum->lmm_magic == LOV_USER_MAGIC_COMP_V1)
lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
else if (lum->lmm_magic == LOV_USER_MAGIC_SPECIFIC)
lum_size = lov_user_md_size(lum->lmm_stripe_count,
lum->lmm_magic);
else
lum_size = lov_user_md_size(0, lum->lmm_magic);
return lum_size;
}
/**
* llapi_layout_set_by_xattr() - Set @lum on the file descriptor @fd and write
* the lum on the file referenced by the file descriptor
* @fd: open file descriptor
* @lum: lov_user_md to write on the file
*
* Returns -1 on error and set errno
*/
int llapi_layout_set_by_xattr(int fd, struct lov_user_md *lum)
{
int rc;
ssize_t lum_size;
lum_size = get_lum_size(lum);
if (lum_size < 0) {
errno = EINVAL;
return -1;
}
rc = fsetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_size, 0);
return rc;
}
enum lov_pattern llapi_pattern_to_lov(uint64_t llapi_pattern)
{
enum lov_pattern lov_pattern;
switch (llapi_pattern) {
case LLAPI_LAYOUT_DEFAULT:
lov_pattern = LOV_PATTERN_RAID0;
break;
case LLAPI_LAYOUT_RAID0:
lov_pattern = LOV_PATTERN_RAID0;
break;
case LLAPI_LAYOUT_MDT:
lov_pattern = LOV_PATTERN_MDT;
break;
case LLAPI_LAYOUT_FOREIGN:
lov_pattern = LOV_PATTERN_FOREIGN;
break;
case LLAPI_LAYOUT_OVERSTRIPING:
lov_pattern = LOV_PATTERN_OVERSTRIPING | LOV_PATTERN_RAID0;
break;
default:
lov_pattern = EINVAL;
}
return lov_pattern;
}
/**
* llapi_layout_to_lum() - Convert data from llapi_layout to lov_user_md.
* @layout: the layout to copy from
*
* Convert the data from a llapi_layout to a newly allocated lov_user_md.
* The caller is responsible for freeing the returned pointer.
*
* Return valid lov_user_md pointer on success or NULL if memory allocation
* fails or the layout is invalid
*/
static struct lov_user_md *
llapi_layout_to_lum(const struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
struct lov_comp_md_v1 *comp_v1 = NULL;
struct lov_comp_md_entry_v1 *ent;
struct lov_user_md *lum = NULL;
size_t lum_size = 0;
int ent_idx = 0;
uint32_t offset = 0;
if (layout == NULL ||
list_empty((struct list_head *)&layout->llot_comp_list)) {
errno = EINVAL;
return NULL;
}
/* Allocate header of lov_comp_md_v1 if necessary */
if (layout->llot_is_composite) {
int comp_cnt = 0;
list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
comp_cnt++;
lum_size = sizeof(*comp_v1) + comp_cnt * sizeof(*ent);
lum = calloc(lum_size, 1);
if (lum == NULL) {
errno = ENOMEM;
return NULL;
}
comp_v1 = (struct lov_comp_md_v1 *)lum;
comp_v1->lcm_magic = LOV_USER_MAGIC_COMP_V1;
comp_v1->lcm_size = lum_size;
comp_v1->lcm_layout_gen = 0;
comp_v1->lcm_flags = layout->llot_flags;
comp_v1->lcm_entry_count = comp_cnt;
comp_v1->lcm_mirror_count = layout->llot_mirror_count - 1;
offset += lum_size;
}
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
struct lov_user_md *blob;
size_t blob_size;
uint32_t magic;
int i, obj_count = 0;
struct lov_user_ost_data *lmm_objects;
uint64_t pattern = comp->llc_pattern;
if ((pattern & LLAPI_LAYOUT_SPECIFIC) != 0) {
if (comp->llc_objects_count <
comp->llc_stripe_count) {
errno = EINVAL;
goto error;
}
magic = LOV_USER_MAGIC_SPECIFIC;
obj_count = comp->llc_stripe_count;
pattern &= ~LLAPI_LAYOUT_SPECIFIC;
} else if (strlen(comp->llc_pool_name) != 0) {
magic = LOV_USER_MAGIC_V3;
} else {
magic = LOV_USER_MAGIC_V1;
}
/* All stripes must be specified when the pattern contains
* LLAPI_LAYOUT_SPECIFIC */
for (i = 0; i < obj_count; i++) {
if (comp->llc_objects[i].l_ost_idx ==
LLAPI_LAYOUT_IDX_MAX) {
errno = EINVAL;
goto error;
}
}
blob_size = lov_user_md_size(obj_count, magic);
blob = realloc(lum, lum_size + blob_size);
if (blob == NULL) {
errno = ENOMEM;
goto error;
} else {
lum = blob;
comp_v1 = (struct lov_comp_md_v1 *)lum;
blob = (struct lov_user_md *)((char *)lum + lum_size);
lum_size += blob_size;
}
blob->lmm_magic = magic;
blob->lmm_pattern = llapi_pattern_to_lov(pattern);
if (blob->lmm_pattern == EINVAL) {
errno = EINVAL;
goto error;
}
if (comp->llc_stripe_size == LLAPI_LAYOUT_DEFAULT)
blob->lmm_stripe_size = 0;
else
blob->lmm_stripe_size = comp->llc_stripe_size;
if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT)
blob->lmm_stripe_count = 0;
else if (comp->llc_stripe_count >= LLAPI_LAYOUT_WIDE_MIN &&
comp->llc_stripe_count <= LLAPI_LAYOUT_WIDE_MAX) {
blob->lmm_stripe_count = LOV_ALL_STRIPES -
(comp->llc_stripe_count -
LLAPI_LAYOUT_WIDE_MIN);
}
else
blob->lmm_stripe_count = comp->llc_stripe_count;
if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
blob->lmm_stripe_offset = -1;
else
blob->lmm_stripe_offset = comp->llc_stripe_offset;
if (magic == LOV_USER_MAGIC_V3 ||
magic == LOV_USER_MAGIC_SPECIFIC) {
struct lov_user_md_v3 *lumv3 =
(struct lov_user_md_v3 *)blob;
if (comp->llc_pool_name[0] != '\0') {
snprintf(lumv3->lmm_pool_name,
sizeof(lumv3->lmm_pool_name), "%s",
comp->llc_pool_name);
} else {
memset(lumv3->lmm_pool_name, 0,
sizeof(lumv3->lmm_pool_name));
}
lmm_objects = lumv3->lmm_objects;
} else {
lmm_objects = blob->lmm_objects;
}
for (i = 0; i < obj_count; i++)
lmm_objects[i].l_ost_idx =
comp->llc_objects[i].l_ost_idx;
if (layout->llot_is_composite) {
ent = &comp_v1->lcm_entries[ent_idx];
ent->lcme_id = comp->llc_id;
ent->lcme_flags = comp->llc_flags;
ent->lcme_time_and_id =
lcme_timestamp_and_id_pack(comp->llc_timestamp,
comp->llc_mirror_link_id);
ent->lcme_dstripe_count = comp->llc_dstripe_count;
ent->lcme_cstripe_count = comp->llc_cstripe_count;
ent->lcme_extent.e_start = comp->llc_extent.e_start;
ent->lcme_extent.e_end = comp->llc_extent.e_end;
ent->lcme_size = blob_size;
ent->lcme_offset = offset;
offset += blob_size;
comp_v1->lcm_size += blob_size;
ent_idx++;
} else {
break;
}
}
return lum;
error:
free(lum);
return NULL;
}
/**
* get_parent_dir() - Get the parent directory of a path.
* @path: path to get parent of
* @buf: buffer in which to store parent path [out]
* @size: size in bytes of buffer @buf
*/
static void get_parent_dir(const char *path, char *buf, size_t size)
{
char *p;
strncpy(buf, path, size - 1);
p = strrchr(buf, '/');
if (p != NULL) {
*p = '\0';
} else if (size >= 2) {
strncpy(buf, ".", 2);
buf[size - 1] = '\0';
}
}
/**
* inherit_sys_attributes() - Substitute unspecified attribute values in layout
* @layout: layout to inherit values from
* @path: file path of the filesystem
*
* Substitute unspecified attribute values in @layout with values
* from fs global settings. (lov.stripesize, lov.stripecount,
* lov.stripeoffset)
*/
static void inherit_sys_attributes(struct llapi_layout *layout,
const char *path)
{
struct llapi_layout_comp *comp;
unsigned int ssize, scount, soffset;
int rc;
rc = sattr_cache_get_defaults(NULL, path, &scount, &ssize, &soffset);
if (rc)
return;
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
if (comp->llc_pattern == LLAPI_LAYOUT_DEFAULT)
comp->llc_pattern = LLAPI_LAYOUT_RAID0;
if (comp->llc_stripe_size == LLAPI_LAYOUT_DEFAULT)
comp->llc_stripe_size = ssize;
if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT)
comp->llc_stripe_count = scount;
if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
comp->llc_stripe_offset = soffset;
}
}
/**
* __llapi_layout_cur_comp() - Get the current component of @layout.
* @layout: layout to get current component
*
* Return valid llapi_layout_comp pointer on success or NULL on error
*/
static struct llapi_layout_comp *
__llapi_layout_cur_comp(const struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return NULL;
}
if (layout->llot_cur_comp == NULL) {
errno = EINVAL;
return NULL;
}
/* Verify data consistency */
list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
if (comp == layout->llot_cur_comp)
return comp;
errno = EFAULT;
return NULL;
}
/**
* is_any_specified() - Test if any attributes of @layout are specified.
* @layout: the layout to check
*
* Return:
* * %true any attributes are specified
* * %false all attributes are unspecified
*/
static bool is_any_specified(const struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return false;
if (layout->llot_is_composite || layout->llot_mirror_count != 1)
return true;
return comp->llc_pattern != LLAPI_LAYOUT_DEFAULT ||
comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT ||
comp->llc_stripe_count != LLAPI_LAYOUT_DEFAULT ||
comp->llc_stripe_offset != LLAPI_LAYOUT_DEFAULT ||
strlen(comp->llc_pool_name);
}
/**
* llapi_layout_get_by_fd() - Get the striping layout for the file referenced
* by file descriptor @fd.
* @fd: open file descriptor
* @flags: open file descriptor
*
* If the filesystem does not support the "lustre." xattr namespace, the
* file must be on a non-Lustre filesystem, so set errno to ENOTTY per
* convention. If the file has no "lustre.lov" data, the file will
* inherit default values, so return a default layout.
*
* If the kernel gives us back less than the expected amount of data,
* we fail with errno set to EINTR.
*
* Return valid llapi_layout pointer on success or NULL if an error occurs
*/
struct llapi_layout *llapi_layout_get_by_fd(int fd,
enum llapi_layout_get_flags flags)
{
size_t lum_len;
struct lov_user_md *lum;
struct llapi_layout *layout = NULL;
ssize_t bytes_read;
struct stat st;
lum_len = XATTR_SIZE_MAX;
lum = malloc(lum_len);
if (lum == NULL)
return NULL;
bytes_read = fgetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_len);
if (bytes_read < 0) {
if (errno == EOPNOTSUPP)
errno = ENOTTY;
else if (errno == ENODATA)
layout = llapi_layout_alloc();
goto out;
}
/* Directories may have a positive non-zero lum->lmm_stripe_count
* yet have an empty lum->lmm_objects array. For non-directories the
* amount of data returned from the kernel must be consistent
* with the stripe count. */
if (fstat(fd, &st) < 0)
goto out;
layout = llapi_layout_get_by_xattr(lum, bytes_read,
S_ISDIR(st.st_mode) ? 0 : LLAPI_LAYOUT_GET_CHECK);
out:
free(lum);
return layout;
}
/**
* llapi_layout_set_by_fd() - Set @layout on the file descriptor @fd
* @fd: open file descriptor
* @layout: layout to write on the file
*
* Set @layout on the file descriptor @fd and write the layout on the
* file referenced by the file descriptor
*
* Return %0 on success or -1 on error and set errno
*/
int llapi_layout_set_by_fd(int fd, struct llapi_layout *layout)
{
struct lov_user_md *lum;
int rc;
int tmp_errno;
if (layout == NULL) {
errno = EINVAL;
return -1;
}
rc = llapi_layout_v2_sanity(layout, false, false, NULL);
if (rc) {
errno = EINVAL;
return -1;
}
lum = llapi_layout_to_lum(layout);
if (!lum) {
errno = EINVAL;
return -1;
}
rc = llapi_layout_set_by_xattr(fd, lum);
if (rc < 0) {
tmp_errno = errno;
free(lum);
errno = tmp_errno;
return rc;
}
free(lum);
return 0;
}
/**
* llapi_layout_expected() - Get expected striping layout for a file at @path.
* @path: Path to get striping info
*
* Substitute expected inherited attribute values for unspecified
* attributes. Unspecified attributes may belong to directories and
* never-written-to files, and indicate that default values will be
* assigned when files are created or first written to. A default value
* is inherited from the parent directory if the attribute is specified
* there, otherwise it is inherited from the filesystem root.
* Unspecified attributes normally have the value LLAPI_LAYOUT_DEFAULT.
*
* The complete @path need not refer to an existing file or directory,
* but some leading portion of it must reside within a lustre filesystem.
* A use case for this interface would be to obtain the literal striping
* values that would be assigned to a new file in a given directory.
*
* Return valid llapi_layout pointer on success or NULL if an error occurs
*/
static struct llapi_layout *llapi_layout_expected(const char *path)
{
struct llapi_layout *path_layout = NULL;
char donor_path[PATH_MAX];
struct stat st;
int fd;
int rc;
fd = open(path, O_RDONLY);
if (fd < 0 && errno != ENOENT)
return NULL;
if (fd >= 0) {
int tmp;
path_layout = llapi_layout_get_by_fd(fd, 0);
tmp = errno;
close(fd);
errno = tmp;
}
if (path_layout == NULL) {
if (errno != ENODATA && errno != ENOENT)
return NULL;
path_layout = llapi_layout_alloc();
if (path_layout == NULL)
return NULL;
}
if (is_any_specified(path_layout)) {
inherit_sys_attributes(path_layout, path);
return path_layout;
}
llapi_layout_free(path_layout);
rc = stat(path, &st);
if (rc < 0 && errno != ENOENT)
return NULL;
/* If path is a not a directory or doesn't exist, inherit layout
* from parent directory. */
if ((rc == 0 && !S_ISDIR(st.st_mode)) ||
(rc < 0 && errno == ENOENT)) {
get_parent_dir(path, donor_path, sizeof(donor_path));
path_layout = llapi_layout_get_by_path(donor_path, 0);
if (path_layout != NULL) {
if (is_any_specified(path_layout)) {
inherit_sys_attributes(path_layout, donor_path);
return path_layout;
}
llapi_layout_free(path_layout);
}
}
/* Inherit layout from the filesystem root. */
rc = llapi_search_mounts(path, 0, donor_path, NULL);
if (rc < 0)
return NULL;
path_layout = llapi_layout_get_by_path(donor_path, 0);
if (path_layout == NULL)
return NULL;
inherit_sys_attributes(path_layout, donor_path);
return path_layout;
}
/**
* llapi_layout_get_by_path() - Get the striping layout for the file at @path
* @path: path for which to get the layout
* @flags: flags to control how layout is retrieved
*
* If @flags contains LLAPI_LAYOUT_GET_EXPECTED, substitute expected inherited
* attribute values for unspecified attributes. See llapi_layout_expected().
*
* Return valid llapi_layout pointer on success or NULL on error
*/
struct llapi_layout *llapi_layout_get_by_path(const char *path,
enum llapi_layout_get_flags flags)
{
struct llapi_layout *layout = NULL;
bool failed = false;
int open_flags;
int fd;
int tmp;
if (flags & LLAPI_LAYOUT_GET_EXPECTED)
return llapi_layout_expected(path);
/* Always get layout in O_DIRECT */
/* Allow fetching layout even without the key on encrypted files */
open_flags = O_RDONLY | O_DIRECT | O_CIPHERTEXT;
do_open:
fd = open(path, open_flags);
if (fd < 0) {
if (errno != EINVAL || failed)
return layout;
/* EINVAL is because a directory cannot be opened in O_DIRECT */
open_flags = O_RDONLY | O_CIPHERTEXT;
failed = true;
goto do_open;
}
layout = llapi_layout_get_by_fd(fd, flags);
tmp = errno;
close(fd);
errno = tmp;
return layout;
}
/**
* llapi_layout_get_by_fid() - Get the layout for the file with @fid in
* filesystem @lustre_dir.
* @lustre_dir: path within Lustre filesystem containing @fid
* @fid: Lustre identifier of file to get layout for
* @flags: open file descriptor
*
* Return valid llapi_layout pointer on success or NULL if an error occurs
*/
struct llapi_layout *llapi_layout_get_by_fid(const char *lustre_dir,
const struct lu_fid *fid,
enum llapi_layout_get_flags flags)
{
int fd;
int tmp;
int saved_msg_level = llapi_msg_get_level();
struct llapi_layout *layout = NULL;
/* Prevent llapi internal routines from writing to console
* while executing this function, then restore previous message
* level. */
llapi_msg_set_level(LLAPI_MSG_OFF);
fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY);
llapi_msg_set_level(saved_msg_level);
if (fd < 0)
return NULL;
layout = llapi_layout_get_by_fd(fd, flags);
tmp = errno;
close(fd);
errno = tmp;
return layout;
}
/**
* llapi_layout_stripe_count_get() - Get the stripe count of @layout.
* @layout: layout to get stripe count from
* @count: integer to store stripe count in [out]
*
* Return
* * %0 on success
* * %negative if arguments are invalid
*/
int llapi_layout_stripe_count_get(const struct llapi_layout *layout,
uint64_t *count)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (count == NULL) {
errno = EINVAL;
return -1;
}
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN)
*count = 0;
else
*count = comp->llc_stripe_count;
return 0;
}
/*
* The llapi_layout API functions have these extra validity checks since
* they use intuitively named macros to denote special behavior, whereas
* the old API uses 0 and -1.
*/
bool llapi_layout_stripe_count_is_valid(int64_t stripe_count)
{
return stripe_count == LLAPI_LAYOUT_DEFAULT ||
(stripe_count >= LLAPI_LAYOUT_WIDE_MIN &&
stripe_count <= LLAPI_LAYOUT_WIDE_MAX) ||
(stripe_count > 0 &&
llapi_stripe_count_is_valid(stripe_count));
}
static bool llapi_layout_extension_size_is_valid(uint64_t ext_size)
{
return (ext_size != 0 &&
llapi_stripe_size_is_aligned(ext_size) &&
!llapi_stripe_size_is_too_big(ext_size));
}
static bool llapi_layout_stripe_size_is_valid(uint64_t stripe_size)
{
return stripe_size == LLAPI_LAYOUT_DEFAULT ||
(stripe_size != 0 &&
llapi_stripe_size_is_aligned(stripe_size) &&
!llapi_stripe_size_is_too_big(stripe_size));
}
static bool llapi_layout_stripe_index_is_valid(int64_t stripe_index)
{
return stripe_index == LLAPI_LAYOUT_DEFAULT ||
(stripe_index >= 0 &&
llapi_stripe_index_is_valid(stripe_index));
}
/**
* llapi_layout_stripe_count_set() - Set the stripe count of @layout.
* @layout: layout to set stripe count in
* @count: value to be set
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
int llapi_layout_stripe_count_set(struct llapi_layout *layout,
uint64_t count)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (comp->llc_flags & LCME_FL_PARITY) {
errno = EINVAL;
return -1;
}
if (!llapi_layout_stripe_count_is_valid(count)) {
errno = EINVAL;
return -1;
}
comp->llc_stripe_count = count;
return 0;
}
/**
* layout_stripe_size_get() - Get the stripe/extension size of @layout.
* @layout: layout to get stripe size from
* @size: integer to store stripe size in [out]
* @extension: flag if extenion size is requested
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
static int layout_stripe_size_get(const struct llapi_layout *layout,
uint64_t *size, bool extension)
{
struct llapi_layout_comp *comp;
int comp_ext;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (size == NULL) {
errno = EINVAL;
return -1;
}
/* FIXME: return a component rather than FOREIGN/HSM component. */
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
errno = EINVAL;
return -1;
}
comp_ext = comp->llc_flags & LCME_FL_EXTENSION;
if ((comp_ext && !extension) || (!comp_ext && extension)) {
errno = EINVAL;
return -1;
}
*size = comp->llc_stripe_size;
if (comp->llc_flags & LCME_FL_EXTENSION)
*size *= SEL_UNIT_SIZE;
return 0;
}
int llapi_layout_stripe_size_get(const struct llapi_layout *layout,
uint64_t *size)
{
return layout_stripe_size_get(layout, size, false);
}
int llapi_layout_extension_size_get(const struct llapi_layout *layout,
uint64_t *size)
{
return layout_stripe_size_get(layout, size, true);
}
/**
* layout_stripe_size_set() - Set the stripe/extension size of @layout.
* @layout: layout to set stripe size in
* @size: value to be set
* @extension: flag if extenion size is passed
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
static int layout_stripe_size_set(struct llapi_layout *layout,
uint64_t size, bool extension)
{
struct llapi_layout_comp *comp;
int comp_ext;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN ||
comp->llc_flags & LCME_FL_PARITY) {
errno = EINVAL;
return -1;
}
comp_ext = comp->llc_flags & LCME_FL_EXTENSION;
if ((comp_ext && !extension) || (!comp_ext && extension)) {
errno = EINVAL;
return -1;
}
if (comp_ext)
size /= SEL_UNIT_SIZE;
if ((comp_ext && !llapi_layout_extension_size_is_valid(size)) ||
(!comp_ext && !llapi_layout_stripe_size_is_valid(size))) {
errno = EINVAL;
return -1;
}
comp->llc_stripe_size = size;
return 0;
}
int llapi_layout_stripe_size_set(struct llapi_layout *layout,
uint64_t size)
{
return layout_stripe_size_set(layout, size, false);
}
int llapi_layout_extension_size_set(struct llapi_layout *layout,
uint64_t size)
{
return layout_stripe_size_set(layout, size, true);
}
/**
* llapi_layout_pattern_get() - Get the RAID pattern of @layout.
* @layout: layout to get pattern from
* @pattern: integer to store pattern in [out]
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
int llapi_layout_pattern_get(const struct llapi_layout *layout,
uint64_t *pattern)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (pattern == NULL) {
errno = EINVAL;
return -1;
}
*pattern = comp->llc_pattern;
return 0;
}
/**
* llapi_layout_pattern_set() - Set the pattern of @layout.
* @layout: layout to set pattern in
* @pattern: value to be set
*
* Return:
* * %0 on success
* * %negative if arguments are invalid or RAID pattern is unsupported
*/
int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (pattern != LLAPI_LAYOUT_DEFAULT &&
pattern != LLAPI_LAYOUT_RAID0 && pattern != LLAPI_LAYOUT_MDT
&& pattern != LLAPI_LAYOUT_OVERSTRIPING &&
pattern != LLAPI_LAYOUT_FOREIGN) {
errno = EOPNOTSUPP;
return -1;
}
comp->llc_pattern = pattern |
(comp->llc_pattern & LLAPI_LAYOUT_SPECIFIC);
return 0;
}
static inline int stripe_number_roundup(int stripe_number)
{
unsigned int round_up = (stripe_number + 8) & ~7;
return round_up > LOV_MAX_STRIPE_COUNT ?
LOV_MAX_STRIPE_COUNT : round_up;
}
/**
* llapi_layout_ost_index_set() - Set the OST index of stripe number
* @stripe_number to @ost_index.
* @layout: layout to set OST index in
* @stripe_number: stripe number to set index for
* @ost_index: the index to set
*
* If only the starting stripe's OST index is specified, then this can use
* the normal LOV_MAGIC_{V1,V3} layout type. If multiple OST indices are
* given, then allocate an array to hold the list of indices and ensure that
* the LOV_USER_MAGIC_SPECIFIC layout is used when creating the file.
*
* Return:
* * %0 on success
* * %negative if arguments are invalid or an unsupported stripe number was
* specified, error returned in errno
*/
int llapi_layout_ost_index_set(struct llapi_layout *layout, int stripe_number,
uint64_t ost_index)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
errno = EINVAL;
return -1;
}
if (!llapi_layout_stripe_index_is_valid(ost_index)) {
errno = EINVAL;
return -1;
}
if (stripe_number == 0 && ost_index == LLAPI_LAYOUT_DEFAULT) {
comp->llc_stripe_offset = ost_index;
comp->llc_pattern &= ~LLAPI_LAYOUT_SPECIFIC;
__llapi_comp_objects_realloc(comp, 0);
} else if (stripe_number >= 0 &&
stripe_number < LOV_MAX_STRIPE_COUNT) {
if (ost_index >= LLAPI_LAYOUT_IDX_MAX) {
errno = EINVAL;
return -1;
}
/* Preallocate a few more stripes to avoid realloc() overhead.*/
if (__llapi_comp_objects_realloc(comp,
stripe_number_roundup(stripe_number)) < 0)
return -1;
comp->llc_objects[stripe_number].l_ost_idx = ost_index;
if (stripe_number == 0)
comp->llc_stripe_offset = ost_index;
else
comp->llc_pattern |= LLAPI_LAYOUT_SPECIFIC;
if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT ||
comp->llc_stripe_count <= stripe_number)
comp->llc_stripe_count = stripe_number + 1;
} else {
errno = EINVAL;
return -1;
}
return 0;
}
static int reset_index_cb(struct llapi_layout *layout, void *cbdata)
{
int *save_errno = (int *)cbdata;
int rc;
rc = llapi_layout_ost_index_set(layout, 0, LLAPI_LAYOUT_DEFAULT);
/* save the first error returned, but try to reset all components */
if (rc && !*save_errno)
*save_errno = errno;
return LLAPI_LAYOUT_ITER_CONT;
}
/**
* llapi_layout_ost_index_reset() - Reset the OST index on all components in
* @layout to LLAPI_LAYOUT_DEFAULT.
* @layout: layout to reset OST index in
*
* This is useful when reusing a file layout that was copied from an existing
* file and to be used for a new file (e.g. when mirroring or migrating or
* copying a file), so the objects are allocated on different OSTs.
*
* Return:
* * %0 Success.
* * %negative errno Error with errno set to non-zero value.
*/
int llapi_layout_ost_index_reset(struct llapi_layout *layout)
{
int save_errno = 0;
int rc;
rc = llapi_layout_comp_iterate(layout, reset_index_cb, &save_errno);
if (save_errno)
errno = save_errno;
return save_errno ? -save_errno : (rc < 0 ? -errno : 0);
}
/**
* llapi_layout_ost_index_get() - Get OST index associated with @stripe_number.
* @layout: layout to get index from
* @stripe_number: stripe number to get index for
* @index: integer to store index in [out]
*
* Stripes are indexed starting from zero.
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
int llapi_layout_ost_index_get(const struct llapi_layout *layout,
uint64_t stripe_number, uint64_t *index)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
errno = EINVAL;
return -1;
}
if (index == NULL) {
errno = EINVAL;
return -1;
}
if (stripe_number >= comp->llc_stripe_count ||
stripe_number >= comp->llc_objects_count) {
errno = EINVAL;
return -1;
}
if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
*index = LLAPI_LAYOUT_DEFAULT;
else
*index = comp->llc_objects[stripe_number].l_ost_idx;
return 0;
}
/**
* llapi_layout_pool_name_get() - Get the pool name of layout @layout.
* @layout: layout to get pool name from
* @dest: buffer to store pool name in [out]
* @n: size in bytes of buffer @dest
*
* Return:
* * %0 on success
* * %negative if arguments are invalid
*/
int llapi_layout_pool_name_get(const struct llapi_layout *layout, char *dest,
size_t n)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (dest == NULL) {
errno = EINVAL;
return -1;
}
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
errno = EINVAL;
return -1;
}
snprintf(dest, n, "%s", comp->llc_pool_name);
return 0;
}
/**
* llapi_layout_pool_name_set() - Set the name of the pool of layout @layout.
* @layout: layout to set pool name in
* @pool_name: pool name to set
*
* Return:
* * %0 on success
* * %negative if arguments are invalid or pool name is too long
*/
int llapi_layout_pool_name_set(struct llapi_layout *layout,
const char *pool_name)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (!llapi_pool_name_is_valid(&pool_name)) {
errno = EINVAL;
return -1;
}
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
errno = EINVAL;
return -1;
}
snprintf(comp->llc_pool_name, sizeof(comp->llc_pool_name), "%s",
pool_name);
return 0;
}
/**
* llapi_layout_file_open() - Open and possibly create file with given @layout.
* @path: name of the file to open
* @open_flags: open() flags
* @mode: permissions to create file, filtered by umask
* @layout: layout to create new file with
*
* If @layout is NULL this function acts as a simple wrapper for
* open(). By convention, ENOTTY is returned in errno if @path
* refers to a non-Lustre file.
*
* Return:
* * %non-negative file descriptor on successful open
* * %negative if an error occurred
*/
int llapi_layout_file_open(const char *path, int open_flags, mode_t mode,
const struct llapi_layout *layout)
{
char fsname[MAX_OBD_NAME + 1] = { 0 };
struct llapi_layout_comp *comp;
int fd;
int rc;
comp = __llapi_layout_cur_comp(layout);
if (path == NULL ||
(layout != NULL && layout->llot_magic != LLAPI_LAYOUT_MAGIC)) {
errno = EINVAL;
return -1;
}
if (layout) {
/* Make sure we are on a Lustre file system */
if (comp != NULL && comp->llc_pool_name[0] != '\0' &&
!lov_pool_is_ignored(comp->llc_pool_name)) {
rc = llapi_search_fsname(path, fsname);
if (rc) {
errno = ENOTTY;
return -1;
}
}
rc = llapi_layout_v2_sanity((struct llapi_layout *)layout,
false,
!!(layout->llot_mirror_count > 1),
fsname);
if (rc) {
llapi_layout_sanity_perror(rc);
return -1;
}
}
/* Object creation must be postponed until after layout
* attributes have been applied.
*/
if (open_flags & O_CREAT)
open_flags |= O_LOV_DELAY_CREATE;
fd = open(path, open_flags, mode);
if (layout == NULL || fd < 0)
return fd;
rc = llapi_layout_set_by_fd(fd, (struct llapi_layout *)layout);
if (rc < 0) {
struct lov_user_md *lum;
size_t lum_size;
int tmp = errno;
lum = llapi_layout_to_lum(layout);
lum_size = get_lum_size(lum);
/* caller usually prints error, but doesn't know xattr size */
if (errno == ENOSPC)
llapi_error(LLAPI_MSG_ERROR, errno,
"error setting %zd-byte layout on '%s'\n",
lum_size, path);
free(lum);
close(fd);
errno = tmp;
fd = -1;
}
errno = errno == EOPNOTSUPP ? ENOTTY : errno;
return fd;
}
/**
* llapi_layout_file_create() - Create a file with a given @layout.
* @path: name of the file to open
* @open_flags: open() flags
* @mode: permissions to create new file with
* @layout: layout to create new file with
*
* Force O_CREAT and O_EXCL flags on so caller is assured that file was
* created with the given @layout on successful function return.
*
* Return:
* * %non-negative file descriptor on successful open
* * %negative if an error occurred
*/
int llapi_layout_file_create(const char *path, int open_flags, int mode,
const struct llapi_layout *layout)
{
return llapi_layout_file_open(path, open_flags|O_CREAT|O_EXCL, mode,
layout);
}
int llapi_layout_flags_get(struct llapi_layout *layout, uint32_t *flags)
{
if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
*flags = layout->llot_flags;
return 0;
}
/* Set flags to the header of a component layout */
int llapi_layout_flags_set(struct llapi_layout *layout, uint32_t flags)
{
if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
layout->llot_flags = flags;
return 0;
}
const char *llapi_layout_flags_string(uint32_t flags)
{
switch (flags & LCM_FL_FLR_MASK) {
case LCM_FL_RDONLY:
return "ro";
case LCM_FL_WRITE_PENDING:
return "wp";
case LCM_FL_SYNC_PENDING:
return "sp";
case LCM_FL_RDONLY | LCM_FL_PCC_RDONLY:
return "ro,pccro";
case LCM_FL_WRITE_PENDING | LCM_FL_PCC_RDONLY:
return "wp,pccro";
case LCM_FL_SYNC_PENDING | LCM_FL_PCC_RDONLY:
return "sp,pccro";
}
return "0";
}
__u16 llapi_layout_string_flags(char *string)
{
if (strncmp(string, "ro", strlen(string)) == 0)
return LCM_FL_RDONLY;
if (strncmp(string, "wp", strlen(string)) == 0)
return LCM_FL_WRITE_PENDING;
if (strncmp(string, "sp", strlen(string)) == 0)
return LCM_FL_SYNC_PENDING;
return 0;
}
static struct {
enum lov_pattern llpn_pattern;
const char *llpn_pattern_name;
} lov_pattern_names[] = {
{ LOV_PATTERN_BAD, "bad" },
{ LOV_PATTERN_RAID0, "raid0" },
{ LOV_PATTERN_RAID1, "raid1" },
{ LOV_PATTERN_PARITY, "parity" },
{ LOV_PATTERN_MDT, "mdt" },
{ LOV_PATTERN_OVERSTRIPING, "overstriped" }, /* getstripe */
{ LOV_PATTERN_OVERSTRIPING, "overstriping" }, /* setstripe compat */
{ LOV_PATTERN_FOREIGN, "foreign" },
{ LOV_PATTERN_COMPRESS, "compress" },
{ LOV_PATTERN_F_HOLE, "hole" },
{ LOV_PATTERN_F_RELEASED, "released" },
{ LOV_PATTERN_DEFAULT, "default" },
{ 0, NULL }
};
int llapi_lov_string_pattern(const char *string, enum lov_pattern *pattern)
{
const char *p;
*pattern = 0;
for (p = string; p != NULL; p = strchr(p, ',')) {
int i;
while (*p == ',')
p++;
if (*p == '\0')
break;
for (i = 0; lov_pattern_names[i].llpn_pattern != 0; i++) {
int l;
l = strlen(lov_pattern_names[i].llpn_pattern_name);
if (strncmp(p, lov_pattern_names[i].llpn_pattern_name,
l))
continue;
*pattern |= lov_pattern_names[i].llpn_pattern;
break;
}
if (lov_pattern_names[i].llpn_pattern == 0) {
errno = EINVAL;
return -errno;
}
}
return 0;
}
char *llapi_lov_pattern_string(enum lov_pattern pattern, char *buf,
size_t buflen)
{
char *p = buf;
int l = 0;
int i;
p[0] = '\0';
for (i = 0; lov_pattern_names[i].llpn_pattern && pattern; i++) {
if ((lov_pattern_names[i].llpn_pattern & pattern) ==
lov_pattern_names[i].llpn_pattern) {
l += snprintf(p, buflen - l, "%s%s", buf[0] ? "," : "",
lov_pattern_names[i].llpn_pattern_name);
pattern &= ~lov_pattern_names[i].llpn_pattern;
if (l >= buflen) {
errno = EOVERFLOW;
return NULL;
}
p = buf + l;
}
}
if (pattern)
snprintf(buf, buflen, "%sunknown_%x", buf[0] ? "," : "",
pattern);
return buf;
}
/**
* llapi_layout_mirror_count_is_valid() - Check the validity of mirror count.
* @count: Mirror count value to be checked.
*
* This function checks the validity of mirror count.
*
* Return: true on success or false on failure.
*/
static bool llapi_layout_mirror_count_is_valid(uint16_t count)
{
return count <= LUSTRE_MIRROR_COUNT_MAX;
}
/**
* llapi_layout_mirror_count_get() - Get mirror count from header of a layout.
* @layout: Layout to get mirror count from.
* @count: Returned mirror count value.
*
* This function gets mirror count from the header of a layout.
*
* Return: 0 on success or %negative on failure.
*/
int llapi_layout_mirror_count_get(struct llapi_layout *layout,
uint16_t *count)
{
if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
*count = layout->llot_mirror_count;
return 0;
}
/**
* llapi_layout_mirror_count_set() - Set mirror count to the header of a layout.
* @layout: Layout to set mirror count in.
* @count: Mirror count value to be set.
*
* This function sets mirror count to the header of a layout.
*
* Return: 0 on success or %negative on failure.
*/
int llapi_layout_mirror_count_set(struct llapi_layout *layout,
uint16_t count)
{
if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
if (!llapi_layout_mirror_count_is_valid(count)) {
errno = EINVAL;
return -1;
}
layout->llot_mirror_count = count;
return 0;
}
/**
* llapi_layout_mirror_count_sync() - Synchronize mirror count from components
* @layout: layout to synchronize
*
* Iterates over all components and counts mirror boundaries (components with
* extent start == 0). Updates llot_mirror_count and each component's
* llc_mirror_id accordingly. Note, mirror IDs are decoupled from mirror count
* and need to be tracked separately.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_mirror_count_sync(struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
uint16_t mirror_count = 0;
uint32_t mirror_id = 0;
if (!layout || layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
if (comp->llc_extent.e_start == 0) {
if (!llapi_layout_mirror_count_is_valid(mirror_count +
1)) {
errno = EINVAL;
return -1;
}
mirror_count++;
mirror_id++;
}
/* Only count/assign for components without a mirror ID set */
if (comp->llc_mirror_id == 0) {
comp->llc_mirror_id = mirror_id;
} else {
/* Track existing mirror IDs */
if (comp->llc_mirror_id > mirror_id)
mirror_id = comp->llc_mirror_id;
}
}
layout->llot_curr_mirror_id = mirror_id + 1;
layout->llot_mirror_count = mirror_count;
return 0;
}
/**
* llapi_layout_comp_extent_get() - Fetch the start and end offset of the
* current layout component.
* @layout: the layout component
* @start: extent start, inclusive [out]
* @end: extent end, exclusive [out]
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_extent_get(const struct llapi_layout *layout,
uint64_t *start, uint64_t *end)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (start == NULL || end == NULL) {
errno = EINVAL;
return -1;
}
*start = comp->llc_extent.e_start;
*end = comp->llc_extent.e_end;
return 0;
}
/**
* llapi_layout_comp_extent_set() - Set the layout extent of a layout.
* @layout: the layout to be set
* @start: extent start, inclusive
* @end: extent end, exclusive
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_extent_set(struct llapi_layout *layout,
uint64_t start, uint64_t end)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (start > end) {
errno = EINVAL;
return -1;
}
comp->llc_extent.e_start = start;
comp->llc_extent.e_end = end;
layout->llot_is_composite = true;
return 0;
}
/**
* llapi_layout_comp_flags_get() - Gets the attribute flags of the current
* component.
* @layout: the layout component
* @flags: stored the returned component flags [out]
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_flags_get(const struct llapi_layout *layout,
uint32_t *flags)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (flags == NULL) {
errno = EINVAL;
return -1;
}
*flags = comp->llc_flags;
return 0;
}
/**
* llapi_layout_comp_flags_set() - Sets the specified flags of the current
* component leaving other flags as-is.
* @layout: the layout component
* @flags: component flags to be set
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_flags_set(struct llapi_layout *layout, uint32_t flags)
{
struct llapi_layout_comp *comp;
/* LCME_FL_PARITY cannot be set with this function */
if (flags & LCME_FL_PARITY) {
errno = EINVAL;
return -1;
}
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
comp->llc_flags |= flags;
return 0;
}
/**
* llapi_layout_comp_flags_clear() - Clears the flags specified in the flags
* leaving other flags as-is.
* @layout: the layout component
* @flags: component flags to be cleared
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_flags_clear(struct llapi_layout *layout,
uint32_t flags)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
comp->llc_flags &= ~flags;
return 0;
}
/**
* llapi_layout_comp_id_get() - Fetches the file-unique component ID of the
* current layout component.
* @layout: the layout component
* @id: stored the returned component ID [out]
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_id_get(const struct llapi_layout *layout, uint32_t *id)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (id == NULL) {
errno = EINVAL;
return -1;
}
*id = comp->llc_id;
return 0;
}
/**
* llapi_layout_mirror_id_get() - Return mirror id of current layout component.
* @layout: the layout component
* @id: stored the returned mirror ID [out]
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_mirror_id_get(const struct llapi_layout *layout, uint32_t *id)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (!comp)
return -1;
if (!id) {
errno = EINVAL;
return -1;
}
*id = comp->llc_mirror_id;
return 0;
}
/**
* llapi_layout_comp_mirror_link_id_get() - Return mirror link ID of current
* layout component.
* @layout: the layout component
* @id: stored the returned mirror link ID [out]
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_mirror_link_id_get(const struct llapi_layout *layout,
uint16_t *id)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (!comp)
return -1;
if (!id) {
errno = EINVAL;
return -1;
}
*id = comp->llc_mirror_link_id;
return 0;
}
/**
* llapi_layout_comp_add() - Adds a component to @layout
* @layout: existing composite or plain layout
*
* Adds a component to @layout, the new component will be added to
* the tail of components list and it'll inherit attributes of existing
* ones. The @layout will change it's current component pointer to
* the newly added component, and it'll be turned into a composite
* layout if it was not before the adding.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_add(struct llapi_layout *layout)
{
return llapi_layout_comp_add_extent(layout, 0, 0);
}
int llapi_layout_comp_add_extent(struct llapi_layout *layout,
uint64_t start, uint64_t end)
{
struct llapi_layout_comp *last, *comp, *new;
bool save_composite;
/* Validate input parameters */
if (!layout) {
errno = EINVAL;
return -1;
}
/* extent start should be less than extent end */
if (start != 0 && start >= end) {
errno = EINVAL;
return -1;
}
save_composite = layout->llot_is_composite;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
new = __llapi_comp_alloc(0);
if (new == NULL)
return -1;
last = list_last_entry(&layout->llot_comp_list, typeof(*last),
llc_list);
list_add_tail(&new->llc_list, &layout->llot_comp_list);
layout->llot_is_composite = true;
layout->llot_cur_comp = new;
/* If no extent was provided:
* We need to set a temporary non-zero value for "end" when we call
* comp_extent_set, so we use LUSTRE_EOF-1, which is > all allowed
* for the end of the previous component. (If we're adding this
* component, the end of the previous component cannot be EOF.)
*/
if (start == 0 && end == 0) {
start = last->llc_extent.e_end;
end = LUSTRE_EOF - 1;
}
if (llapi_layout_comp_extent_set(layout, start, end)) {
(void)llapi_layout_comp_del(layout);
layout->llot_is_composite = save_composite;
return -1;
}
return 0;
}
static int layout_ec_verify_stripes(__u64 stripe_count, __u8 k, __u8 p)
{
struct ec_split_comp sc;
/* Validate stripe counts */
if (p == 0 || k == 0)
return -EINVAL;
/*
* The total number of parities we will need across all the raid
* sets can not exceed the number of stripes in the data comp
* it protects.
*/
ec_split_stripes(stripe_count, k, &sc);
if (sc.esc_n0 * sc.esc_k0 + sc.esc_n1 * sc.esc_k1 != stripe_count ||
(sc.esc_n0 + sc.esc_n1) * p > stripe_count)
return -EINVAL;
return 0;
}
/**
* llapi_layout_comp_add_ec() - Adds a EC component to @layout
* @layout: existing composite or plain layout
* @mirror_id: mirror id of the data component to be protected by the EC
* component, a mirror id of 0 is invalid
* @start: start offset of the extent
* @end: end offset of the extent
* @dstripe_count: number of data stripes
* @cstripe_count: number of coding stripes
*
* Adds a EC component to @layout at the tail of components list and if
* @comp_id is specified, the EC component will protect the data component for
* the specified mirror id. A data component can only protected by one EC
* component. The @layout will change it's current component pointer to the
* newly added EC component, and it'll be turned into a composite layout if it
* was not before the adding.
*
* Before (and after) adding the EC component the mirror count and IDs are
* synced to ensure the EC component is added to the correct mirror.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_add_ec(struct llapi_layout *layout, uint32_t mirror_id,
uint64_t start, uint64_t end,
uint8_t dstripe_count, uint8_t cstripe_count)
{
struct llapi_layout_comp *parity_comp, *comp;
bool found = false;
int rc;
/* Validate input parameters */
if (!layout) {
errno = EINVAL;
return -1;
}
/* Validate extent parameters */
if (mirror_id == 0 || start >= end) {
errno = EINVAL;
return -1;
}
/* Validate EC stripe count limits */
if (dstripe_count > LOV_EC_MAX_DATA_STRIPES) {
errno = EINVAL;
return -1;
}
if (cstripe_count > LOV_EC_MAX_CODING_STRIPES) {
errno = EINVAL;
return -1;
}
if (dstripe_count + cstripe_count > LOV_EC_MAX_TOTAL_STRIPES) {
errno = EINVAL;
return -1;
}
/* Sync mirror count and IDs so that the data component can be found */
rc = llapi_layout_mirror_count_sync(layout);
if (rc)
return rc;
/*
* Find the matching data component. A data component with the same
* extent must exist before adding an EC component.
*/
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
if (comp->llc_mirror_id != mirror_id)
continue;
/* component to protect should not be a parity component */
if (comp->llc_flags & LCME_FL_PARITY) {
errno = EINVAL;
return -1;
}
if (comp->llc_extent.e_start == start &&
comp->llc_extent.e_end == end) {
/* Data comp is already protected by a parity comp */
if (comp->llc_mirror_link_id !=
LLAPI_MIRROR_LINK_NONE) {
errno = EINVAL;
return -1;
}
found = true;
break;
}
}
if (!found) {
errno = ENOENT;
return -1;
}
/*
* If the data component's stripe count is still LLAPI_LAYOUT_DEFAULT,
* set it to dstripe_count (the number of data stripes in the EC layout)
*/
if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT ||
comp->llc_stripe_count == LLAPI_LAYOUT_WIDE)
comp->llc_stripe_count = dstripe_count;
/* Parity components require non-zero cstripe and dstripe */
if (layout_ec_verify_stripes(comp->llc_stripe_count,
dstripe_count, cstripe_count)) {
errno = EINVAL;
return -1;
}
rc = llapi_layout_comp_add_extent(layout, start, end);
if (rc)
return rc;
/* Sync mirror count and IDs to generate the mirror ID for the new
* parity component, which is needed for the bi-directional link.
*/
rc = llapi_layout_mirror_count_sync(layout);
if (rc) {
(void)llapi_layout_comp_del(layout);
return rc;
}
parity_comp = __llapi_layout_cur_comp(layout);
if (!parity_comp) {
(void)llapi_layout_comp_del(layout);
return -1;
}
parity_comp->llc_flags |= LCME_FL_PARITY;
parity_comp->llc_cstripe_count = cstripe_count;
parity_comp->llc_dstripe_count = dstripe_count;
/* mark the data component as protected indirectly */
comp->llc_cstripe_count = cstripe_count;
comp->llc_dstripe_count = dstripe_count;
/*
* Copy stripe size from the matching data component.
* EC/parity components must have the same stripe size as their
* corresponding data components.
*/
parity_comp->llc_stripe_size = comp->llc_stripe_size;
/*
* Copy pool name from the matching data component so that
* parity components inherit the same pool placement.
*/
if (comp->llc_pool_name[0] != '\0')
snprintf(parity_comp->llc_pool_name,
sizeof(parity_comp->llc_pool_name), "%s",
comp->llc_pool_name);
/* bi-directional link for data and parity components */
parity_comp->llc_mirror_link_id = layout->llot_curr_link_id;
comp->llc_mirror_link_id = layout->llot_curr_link_id;
parity_comp->llc_flags |= LCME_FL_IS_LINK_ID;
comp->llc_flags |= LCME_FL_IS_LINK_ID;
layout->llot_curr_link_id++;
return 0;
}
/**
* Get EC coding stripe count from the current component.
*
* \param[in] layout existing layout
* \param[out] cstripe_count EC coding stripe count
*
* \retval 0 on success
* \retval <0 if error occurs
*/
int llapi_layout_ec_cstripe_count_get(const struct llapi_layout *layout,
uint8_t *cstripe_count)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (!comp)
return -1;
if (!cstripe_count) {
errno = EINVAL;
return -1;
}
if (!(comp->llc_flags & LCME_FL_PARITY)) {
errno = EINVAL;
return -1;
}
*cstripe_count = comp->llc_cstripe_count;
return 0;
}
/**
* Get EC data stripe count from the current component.
*
* \param[in] layout existing layout
* \param[out] dstripe_count EC data stripe count
*
* \retval 0 on success
* \retval <0 if error occurs
*/
int llapi_layout_ec_dstripe_count_get(const struct llapi_layout *layout,
uint8_t *dstripe_count)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (!comp)
return -1;
if (!dstripe_count) {
errno = EINVAL;
return -1;
}
if (!(comp->llc_flags & LCME_FL_PARITY)) {
errno = EINVAL;
return -1;
}
*dstripe_count = comp->llc_dstripe_count;
return 0;
}
/**
* Adds a first component of a mirror to \a layout.
* The \a layout will change it's current component pointer to
* the newly added component, and it'll be turned into a composite
* layout if it was not before the adding.
*
* The @layout will change it's current component pointer to the newly added
* component, and it'll be turned into a composite layout if it was not before
* the adding.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_add_first_comp(struct llapi_layout *layout)
{
struct llapi_layout_comp *comp, *new;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
new = __llapi_comp_alloc(0);
if (new == NULL)
return -1;
new->llc_extent.e_start = 0;
list_add_tail(&new->llc_list, &layout->llot_comp_list);
layout->llot_cur_comp = new;
layout->llot_is_composite = true;
return 0;
}
/**
* llapi_layout_comp_del() - Deletes current component from the composite layout
* @layout: composite layout
*
* Deletes current component from the composite layout. The component to be
* deleted must be the tail of components list, and it can't be the only
* component in the layout.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_del(struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
struct llapi_layout_comp *data_comp = NULL;
uint32_t comp_mirror_link_id = 0;
bool is_parity_comp = false;
uint64_t comp_start = 0;
uint64_t comp_end = 0;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (!layout->llot_is_composite) {
errno = EINVAL;
return -1;
}
if (comp->llc_flags & LCME_FL_PARITY) {
bool found = false;
/* Save protected comp info to clear link later on data comp */
is_parity_comp = true;
comp_start = comp->llc_extent.e_start;
comp_end = comp->llc_extent.e_end;
comp_mirror_link_id = comp->llc_flags & LCME_FL_IS_LINK_ID ?
comp->llc_mirror_link_id :
comp->llc_mirror_id;
/* find the protected data component */
list_for_each_entry(data_comp, &layout->llot_comp_list,
llc_list) {
if (data_comp->llc_mirror_link_id ==
comp_mirror_link_id &&
data_comp->llc_extent.e_start == comp_start &&
data_comp->llc_extent.e_end == comp_end) {
found = true;
break;
}
}
/* data component must exist; otherwise layout is invalid */
if (!found) {
errno = ENOENT;
return -1;
}
} else {
/* A data comp that is protected by a parity comp can't be
* deleted until the parity comp is deleted.
*/
if (comp->llc_mirror_link_id != LLAPI_MIRROR_LINK_NONE) {
errno = EINVAL;
return -1;
}
}
/* It must be the tail of the list (for PFL, can be relaxed
* once we get mirrored components) */
if (comp->llc_list.next != &layout->llot_comp_list) {
errno = EINVAL;
return -1;
}
layout->llot_cur_comp =
list_last_entry(&comp->llc_list, typeof(*comp), llc_list);
if (comp->llc_list.prev == &layout->llot_comp_list)
layout->llot_cur_comp = NULL;
list_del_init(&comp->llc_list);
__llapi_comp_free(comp);
if (!is_parity_comp)
return 0;
/* Clear link on protected data_component */
data_comp->llc_mirror_link_id = LLAPI_MIRROR_LINK_NONE;
data_comp->llc_flags &= ~LCME_FL_IS_LINK_ID;
return 0;
}
/**
* llapi_layout_comp_use_id() - Move the current component pointer to the
* component with specified component ID.
* @layout: composite layout
* @comp_id: component ID
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_comp_use_id(struct llapi_layout *layout, uint32_t comp_id)
{
struct llapi_layout_comp *comp;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1; /* use previously set errno */
if (!layout->llot_is_composite) {
errno = EINVAL;
return -1;
}
if (comp_id == LCME_ID_INVAL) {
errno = EINVAL;
return -1;
}
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
if (comp->llc_id == comp_id) {
layout->llot_cur_comp = comp;
return 0;
}
}
errno = ENOENT;
return -1;
}
/**
* __llapi_layout_find_comp_by_id() - Find component by ID
* @layout: layout structure
* @comp_id: component ID
*
* Return:
* * pointer to component on success
* * NULL if not found
*/
static struct llapi_layout_comp *
__llapi_layout_find_comp_by_id(struct llapi_layout *layout, uint32_t comp_id)
{
struct llapi_layout_comp *comp;
list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
if (comp->llc_id == comp_id)
return comp;
return NULL;
}
/**
* llapi_layout_find_data_comp_by_parity() - Find data component for parity comp
* @layout: layout structure
* @parity_comp: parity component
*
* Return:
* * pointer to data component on success
* * NULL if not found
*/
static struct llapi_layout_comp *
__llapi_layout_find_data_comp_by_parity(struct llapi_layout *layout,
struct llapi_layout_comp *parity_comp)
{
struct llapi_layout_comp *comp;
bool is_link_id = parity_comp->llc_flags & LCME_FL_IS_LINK_ID;
list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
uint16_t comp_link_id;
if (comp->llc_flags & LCME_FL_PARITY)
continue;
comp_link_id = is_link_id ? comp->llc_mirror_link_id :
comp->llc_mirror_id;
/* Check if link ids match and extents match */
if (comp_link_id == parity_comp->llc_mirror_link_id &&
comp->llc_extent.e_start ==
parity_comp->llc_extent.e_start &&
comp->llc_extent.e_end == parity_comp->llc_extent.e_end)
return comp;
}
return NULL;
}
/**
* llapi_layout_comp_use() - Move the current component pointer to a specified
* position.
*
* @layout: composite layout
* @pos: the position to be moved, it can be:
* LLAPI_LAYOUT_COMP_USE_FIRST: use first component
* LLAPI_LAYOUT_COMP_USE_LAST: use last component
* LLAPI_LAYOUT_COMP_USE_NEXT: use component after current
* LLAPI_LAYOUT_COMP_USE_PREV: use component before current
*
* Return:
* * %0 moved successfully
* * %1 at last component with NEXT, at first component with PREV
* * %negative if error occurs
*/
int llapi_layout_comp_use(struct llapi_layout *layout,
enum llapi_layout_comp_use pos)
{
struct llapi_layout_comp *comp, *head, *tail;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL)
return -1;
if (!layout->llot_is_composite) {
if (pos == LLAPI_LAYOUT_COMP_USE_FIRST ||
pos == LLAPI_LAYOUT_COMP_USE_LAST)
return 0;
errno = ENOENT;
return 1;
}
head = list_first_entry(&layout->llot_comp_list, typeof(*head),
llc_list);
tail = list_last_entry(&layout->llot_comp_list, typeof(*tail),
llc_list);
switch (pos) {
case LLAPI_LAYOUT_COMP_USE_FIRST:
layout->llot_cur_comp = head;
break;
case LLAPI_LAYOUT_COMP_USE_NEXT:
if (comp == tail) {
errno = ENOENT;
return 1;
}
layout->llot_cur_comp = list_first_entry(&comp->llc_list,
typeof(*comp),
llc_list);
break;
case LLAPI_LAYOUT_COMP_USE_LAST:
layout->llot_cur_comp = tail;
break;
case LLAPI_LAYOUT_COMP_USE_PREV:
if (comp == head) {
errno = ENOENT;
return 1;
}
layout->llot_cur_comp = list_last_entry(&comp->llc_list,
typeof(*comp),
llc_list);
break;
default:
errno = EINVAL;
return -1;
}
return 0;
}
/**
* llapi_layout_file_comp_add() - Add layout component(s) to an existing file.
* @path: The path name of the file
* @layout: The layout component(s) to be added
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_file_comp_add(const char *path,
const struct llapi_layout *layout)
{
int rc, fd = -1, lum_size, tmp_errno = 0;
struct llapi_layout *existing_layout = NULL;
struct lov_user_md *lum = NULL;
char fsname[MAX_OBD_NAME + 1] = { 0 };
struct llapi_layout_comp *comp;
if (path == NULL || layout == NULL ||
layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
errno = EINVAL;
return -1;
}
fd = open(path, O_RDWR);
if (fd < 0) {
tmp_errno = errno;
rc = -1;
goto out;
}
existing_layout = llapi_layout_get_by_fd(fd, 0);
if (existing_layout == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
rc = llapi_layout_merge(&existing_layout, layout);
if (rc) {
tmp_errno = errno;
rc = -1;
goto out;
}
comp = __llapi_layout_cur_comp(layout);
if (comp != NULL && comp->llc_pool_name[0] != '\0' &&
!lov_pool_is_ignored(comp->llc_pool_name)) {
rc = llapi_search_fsname(path, fsname);
if (rc) {
tmp_errno = -rc;
rc = -1;
goto out;
}
}
rc = llapi_layout_v2_sanity(existing_layout, false, false, fsname);
if (rc) {
tmp_errno = errno;
llapi_layout_sanity_perror(rc);
rc = -1;
goto out;
}
lum = llapi_layout_to_lum(layout);
if (lum == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) {
tmp_errno = EINVAL;
rc = -1;
goto out;
}
lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
rc = fsetxattr(fd, XATTR_LUSTRE_LOV".add", lum, lum_size, 0);
if (rc < 0) {
tmp_errno = errno;
rc = -1;
goto out;
}
out:
if (fd >= 0)
close(fd);
free(lum);
llapi_layout_free(existing_layout);
errno = tmp_errno;
return rc;
}
/**
* llapi_layout_file_comp_del() - Delete component(s) by component id
* @path: path name of the file
* @id: unique component ID
* @flags: flags: LCME_FL_* or; negative flags: (LCME_FL_NEG|LCME_FL_*)
*
* Delete component(s) by the specified component id or component flags
* from an existing file.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags)
{
int rc = 0, fd = -1, lum_size, tmp_errno = 0;
struct llapi_layout *layout;
struct llapi_layout_comp *comp, *next;
struct llapi_layout *existing_layout = NULL;
struct lov_user_md *lum = NULL;
if (path == NULL || id > LCME_ID_MAX || (flags & ~LCME_KNOWN_FLAGS)) {
errno = EINVAL;
return -1;
}
/* Can only specify ID or flags, not both, not none. */
if ((id != LCME_ID_INVAL && flags != 0) ||
(id == LCME_ID_INVAL && flags == 0)) {
errno = EINVAL;
return -1;
}
layout = llapi_layout_alloc();
if (layout == NULL)
return -1;
llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
comp->llc_id = id;
comp->llc_flags = flags;
lum = llapi_layout_to_lum(layout);
if (lum == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
fd = open(path, O_RDWR);
if (fd < 0) {
tmp_errno = errno;
rc = -1;
goto out;
}
existing_layout = llapi_layout_get_by_fd(fd, 0);
if (existing_layout == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
comp = NULL;
next = NULL;
while (rc == 0 && existing_layout->llot_cur_comp != NULL) {
rc = llapi_layout_comp_use(existing_layout, comp ?
LLAPI_LAYOUT_COMP_USE_PREV :
LLAPI_LAYOUT_COMP_USE_LAST);
if (rc != 0)
break;
next = comp;
comp = __llapi_layout_cur_comp(existing_layout);
if (comp == NULL) {
rc = -1;
break;
}
if (id != LCME_ID_INVAL && id != comp->llc_id)
continue;
else if ((flags & LCME_FL_NEG) && (flags & comp->llc_flags))
continue;
else if (flags && !(flags & comp->llc_flags))
continue;
rc = llapi_layout_comp_del(existing_layout);
/* the layout position is moved to previous one, adjust */
comp = next;
}
if (rc < 0) {
tmp_errno = errno;
goto out;
}
rc = llapi_layout_sanity(existing_layout, false, false);
if (rc) {
tmp_errno = errno;
llapi_layout_sanity_perror(rc);
rc = -1;
goto out;
}
rc = fsetxattr(fd, XATTR_LUSTRE_LOV".del", lum, lum_size, 0);
if (rc < 0) {
tmp_errno = errno;
rc = -1;
goto out;
}
out:
if (fd >= 0)
close(fd);
free(lum);
llapi_layout_free(layout);
llapi_layout_free(existing_layout);
errno = tmp_errno;
return rc;
}
/* Internal utility function to apply flags for sanity checking */
static void llapi_layout_comp_apply_flags(struct llapi_layout_comp *comp,
uint32_t flags)
{
if (flags & LCME_FL_NEG)
comp->llc_flags &= ~flags;
else
comp->llc_flags |= flags;
}
struct llapi_layout_apply_flags_args {
uint32_t *lfa_ids;
uint32_t *lfa_flags;
int lfa_count;
int lfa_rc;
};
static int llapi_layout_apply_flags_cb(struct llapi_layout *layout,
void *arg)
{
struct llapi_layout_apply_flags_args *args = arg;
struct llapi_layout_comp *comp;
int i = 0;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL) {
args->lfa_rc = -1;
return LLAPI_LAYOUT_ITER_STOP;
}
for (i = 0; i < args->lfa_count; i++) {
if (comp->llc_id == args->lfa_ids[i])
llapi_layout_comp_apply_flags(comp, args->lfa_flags[i]);
}
return LLAPI_LAYOUT_ITER_CONT;
}
/* Apply flags to the layout for sanity checking */
static int llapi_layout_apply_flags(struct llapi_layout *layout, uint32_t *ids,
uint32_t *flags, int count)
{
struct llapi_layout_apply_flags_args args;
int rc = 0;
if (!ids || !flags || count == 0) {
errno = EINVAL;
return -1;
}
args.lfa_ids = ids;
args.lfa_flags = flags;
args.lfa_count = count;
args.lfa_rc = 0;
rc = llapi_layout_comp_iterate(layout,
llapi_layout_apply_flags_cb,
&args);
if (errno == ENOENT)
errno = 0;
if (rc != LLAPI_LAYOUT_ITER_CONT)
rc = args.lfa_rc;
return rc;
}
/**
* llapi_layout_file_comp_set() - Change flags by component ID of components
* @path: path name of the file
* @ids: An array of component IDs
* @flags: flags: LCME_FL_* or; negative flags: (LCME_FL_NEG|LCME_FL_*)
* @count: Number of elements in ids and flags array
*
* Change flags by component ID of components of an existing file.
* The component to be modified is specified by the comp->lcme_id value,
* which must be a unique component ID.
*
* Return:
* * %0 on success
* * %negative if error occurs
*/
int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
size_t count)
{
int rc = -1, fd = -1, i, tmp_errno = 0;
size_t lum_size;
struct llapi_layout *existing_layout = NULL;
struct llapi_layout *layout = NULL;
struct llapi_layout_comp *comp;
struct lov_user_md *lum = NULL;
char fsname[MAX_OBD_NAME + 1] = { 0 };
if (path == NULL) {
errno = EINVAL;
return -1;
}
if (!count)
return 0;
for (i = 0; i < count; i++) {
if (!ids[i] || !flags[i]) {
errno = EINVAL;
return -1;
}
if (ids[i] > LCME_ID_MAX || (flags[i] & ~LCME_KNOWN_FLAGS)) {
errno = EINVAL;
return -1;
}
/* do not allow to set or clear INIT flag */
if (flags[i] & LCME_FL_INIT) {
errno = EINVAL;
return -1;
}
}
fd = open(path, O_RDWR);
if (fd < 0) {
tmp_errno = errno;
rc = -1;
goto out;
}
existing_layout = llapi_layout_get_by_fd(fd, 0);
if (existing_layout == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
if (llapi_layout_apply_flags(existing_layout, ids, flags, count)) {
tmp_errno = errno;
rc = -1;
goto out;
}
comp = __llapi_layout_cur_comp(layout);
if (comp && comp->llc_pool_name[0] != '\0' &&
!lov_pool_is_ignored(comp->llc_pool_name)) {
rc = llapi_search_fsname(path, fsname);
if (rc) {
tmp_errno = -rc;
rc = -1;
goto out;
}
}
rc = llapi_layout_v2_sanity(existing_layout, false, false, fsname);
if (rc) {
tmp_errno = errno;
llapi_layout_sanity_perror(rc);
rc = -1;
goto out;
}
layout = __llapi_layout_alloc();
if (layout == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
layout->llot_is_composite = true;
for (i = 0; i < count; i++) {
comp = __llapi_comp_alloc(0);
if (comp == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
comp->llc_id = ids[i];
comp->llc_flags = flags[i];
list_add_tail(&comp->llc_list, &layout->llot_comp_list);
layout->llot_cur_comp = comp;
}
lum = llapi_layout_to_lum(layout);
if (lum == NULL) {
tmp_errno = errno;
rc = -1;
goto out;
}
lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
/* flush cached pages from clients */
rc = llapi_file_flush(fd);
if (rc) {
tmp_errno = -rc;
rc = -1;
goto out;
}
rc = fsetxattr(fd, XATTR_LUSTRE_LOV".set.flags", lum, lum_size, 0);
if (rc < 0) {
tmp_errno = errno;
goto out;
}
rc = 0;
out:
if (fd >= 0)
close(fd);
free(lum);
llapi_layout_free(existing_layout);
llapi_layout_free(layout);
errno = tmp_errno;
return rc;
}
/**
* llapi_layout_is_composite() - Check if the file layout is composite.
* @layout: the file layout to check
*
* Return:
* * %true composite
* * %false not composite
*/
bool llapi_layout_is_composite(struct llapi_layout *layout)
{
return layout->llot_is_composite;
}
/**
* llapi_layout_comp_iterate() - Iterate every components in the @layout and
* call callback function @cb.
* @layout: component layout list.
* @cb: callback function called for each component
* @cbdata: callback data passed to the callback function
*
* Return:
* * %negative error happens during the iteration
* * %LLAPI_LAYOUT_ITER_CONT finished the iteration w/o error
* * %LLAPI_LAYOUT_ITER_STOP got something, stop the iteration
*/
int llapi_layout_comp_iterate(struct llapi_layout *layout,
llapi_layout_iter_cb cb, void *cbdata)
{
int rc;
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
if (rc < 0)
return rc;
/**
* make sure on success llapi_layout_comp_use() API returns 0 with
* USE_FIRST.
*/
assert(rc == 0);
while (1) {
rc = cb(layout, cbdata);
if (rc != LLAPI_LAYOUT_ITER_CONT)
break;
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
if (rc < 0)
return rc;
else if (rc == 1) /* reached the last comp */
return LLAPI_LAYOUT_ITER_CONT;
}
return rc;
}
/**
* layout_convert_mirror_to_link_ids() - Convert mirror ID bindings to temporary
* link IDs
* @layout: layout to convert
*
* When existing layouts are modified, e.g., through llapi_layout_merge(),
* mirror ids are regenerated by the server. To preserve the data-parity
* binding, the mirror id bindings are converted to temporary link id bindings
* that are later converted back when the mirror ids are set by the server.
*/
static void layout_convert_mirror_to_link_ids(struct llapi_layout *layout)
{
struct llapi_layout_comp *data_comp, *parity_comp;
uint16_t new_link_id;
if (!layout)
return;
/* find data components with mirror id bindings */
list_for_each_entry(data_comp, &layout->llot_comp_list, llc_list) {
uint16_t old_mirror_id;
if ((data_comp->llc_flags & LCME_FL_PARITY) ||
(data_comp->llc_flags & LCME_FL_IS_LINK_ID))
continue;
if (data_comp->llc_mirror_link_id == 0)
continue;
/* convert mirror id binding to link id */
old_mirror_id = data_comp->llc_mirror_link_id;
new_link_id = layout->llot_curr_link_id++;
data_comp->llc_mirror_link_id = new_link_id;
data_comp->llc_flags |= LCME_FL_IS_LINK_ID;
/* find parity components with the same mirror ID */
list_for_each_entry(parity_comp, &layout->llot_comp_list,
llc_list) {
if (!(parity_comp->llc_flags & LCME_FL_PARITY) ||
(parity_comp->llc_flags & LCME_FL_IS_LINK_ID))
continue;
if (parity_comp->llc_mirror_link_id != old_mirror_id)
continue;
parity_comp->llc_mirror_link_id = new_link_id;
parity_comp->llc_flags |= LCME_FL_IS_LINK_ID;
}
}
}
/**
* llapi_layout_merge() - Merge a composite layout into another one.
* @dst_layout: Destination composite layout.
* @src_layout: Source composite layout.
*
* This function copies all of the components from @src_layout and
* appends them to @dst_layout.
*
* Return: 0 on success or %negative on failure.
*/
int llapi_layout_merge(struct llapi_layout **dst_layout,
const struct llapi_layout *src_layout)
{
struct llapi_layout *new_layout = *dst_layout;
struct llapi_layout_comp *new = NULL;
struct llapi_layout_comp *comp = NULL;
uint16_t link_id_offset;
uint32_t mirror_id_offset;
int i = 0;
if (!src_layout ||
list_empty((struct list_head *)&src_layout->llot_comp_list))
return 0;
if (!new_layout) {
new_layout = __llapi_layout_alloc();
if (!new_layout) {
errno = ENOMEM;
return -1;
}
}
if (llapi_layout_mirror_count_sync(new_layout))
goto error;
link_id_offset = new_layout->llot_curr_link_id - 1;
mirror_id_offset = new_layout->llot_curr_mirror_id - 1;
list_for_each_entry(comp, &src_layout->llot_comp_list, llc_list) {
new = __llapi_comp_alloc(0);
if (!new) {
errno = ENOMEM;
goto error;
}
new->llc_pattern = comp->llc_pattern;
new->llc_stripe_size = comp->llc_stripe_size;
new->llc_stripe_count = comp->llc_stripe_count;
new->llc_stripe_offset = comp->llc_stripe_offset;
if (comp->llc_pool_name[0] != '\0')
snprintf(new->llc_pool_name, sizeof(new->llc_pool_name),
"%s", comp->llc_pool_name);
for (i = 0; i < comp->llc_objects_count; i++) {
if (__llapi_comp_objects_realloc(
new, stripe_number_roundup(i)) < 0) {
errno = EINVAL;
__llapi_comp_free(new);
goto error;
}
new->llc_objects[i].l_ost_idx =
comp->llc_objects[i].l_ost_idx;
}
new->llc_objects_count = comp->llc_objects_count;
new->llc_extent.e_start = comp->llc_extent.e_start;
new->llc_extent.e_end = comp->llc_extent.e_end;
new->llc_id = comp->llc_id;
new->llc_flags = comp->llc_flags;
new->llc_dstripe_count = comp->llc_dstripe_count;
new->llc_cstripe_count = comp->llc_cstripe_count;
/* offset mirror ids on the src layout */
new->llc_mirror_id = comp->llc_mirror_id + mirror_id_offset;
new_layout->llot_curr_mirror_id = new->llc_mirror_id + 1;
/* offset all mirror link ids on the src layout */
if (comp->llc_mirror_link_id != 0) {
if (comp->llc_flags & LCME_FL_IS_LINK_ID) {
new->llc_mirror_link_id =
comp->llc_mirror_link_id +
link_id_offset;
new->llc_flags |= LCME_FL_IS_LINK_ID;
new_layout->llot_curr_link_id =
new->llc_mirror_link_id + 1;
} else {
new->llc_mirror_link_id =
comp->llc_mirror_link_id +
mirror_id_offset;
}
}
list_add_tail(&new->llc_list, &new_layout->llot_comp_list);
new_layout->llot_cur_comp = new;
}
new_layout->llot_is_composite = true;
/* convert bindings to link ids since mirror ids are reset by the lod */
layout_convert_mirror_to_link_ids(new_layout);
if (llapi_layout_mirror_count_sync(new_layout))
goto error;
*dst_layout = new_layout;
return 0;
error:
llapi_layout_free(new_layout);
*dst_layout = NULL;
return -1;
}
/**
* llapi_layout_get_last_init_comp() - Get the last initialized component
* @layout: component layout list.
*
* Return:
* * %0 found
* * %-EINVAL not found
* * %-EISDIR directory layout
*/
int llapi_layout_get_last_init_comp(struct llapi_layout *layout)
{
struct llapi_layout_comp *comp = NULL, *head = NULL;
if (!layout->llot_is_composite)
return 0;
head = list_first_entry(&layout->llot_comp_list, typeof(*comp),
llc_list);
if (head == NULL)
return -EINVAL;
if (head->llc_id == 0 && !(head->llc_flags & LCME_FL_INIT))
/* a directory */
return -EISDIR;
/* traverse the components from the tail to find the last init one */
comp = list_last_entry(&layout->llot_comp_list, typeof(*comp),
llc_list);
while (comp != head) {
if (comp->llc_flags & LCME_FL_INIT)
break;
comp = list_last_entry(&comp->llc_list, typeof(*comp),
llc_list);
}
layout->llot_cur_comp = comp;
return comp->llc_flags & LCME_FL_INIT ? 0 : -EINVAL;
}
/**
* llapi_layout_mirror_inherit() - Interit stripe info from the file's component
* to the mirror
* @f_layout: file component layout list.
* @m_layout: mirro component layout list.
*
* Return:
* * %0 on success
* * %-EINVAL on error
*/
int llapi_layout_mirror_inherit(struct llapi_layout *f_layout,
struct llapi_layout *m_layout)
{
struct llapi_layout_comp *m_comp = NULL;
struct llapi_layout_comp *f_comp = NULL;
int rc = 0;
f_comp = __llapi_layout_cur_comp(f_layout);
if (f_comp == NULL)
return -EINVAL;
m_comp = __llapi_layout_cur_comp(m_layout);
if (m_comp == NULL)
return -EINVAL;
/* DoM component does not inherit stripe size */
if (m_comp->llc_pattern != LLAPI_LAYOUT_MDT)
m_comp->llc_stripe_size = f_comp->llc_stripe_size;
m_comp->llc_stripe_count = f_comp->llc_stripe_count;
return rc;
}
/**
* llapi_mirror_find_stale() - Find all stale components.
* @layout: component layout list.
* @comp: array of stale component info. [out]
* @comp_size: array size of @comp.
* @mirror_ids: array of mirror id that only components belonging to these
* mirror will be collected.
* @ids_nr: number of mirror ids array.
*
* Return number of component info collected on success or error code on failure
*/
static int _mirror_find_stale(struct llapi_layout *layout,
struct llapi_resync_comp *comp, size_t comp_size,
__u16 *mirror_ids, int ids_nr, bool find_ec)
{
int idx = 0;
int rc;
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
if (rc < 0)
goto error;
while (rc == 0) {
uint32_t id;
uint32_t mirror_id;
uint32_t flags;
uint64_t start, end;
rc = llapi_layout_comp_flags_get(layout, &flags);
if (rc < 0)
goto error;
if (!(flags & LCME_FL_STALE))
goto next;
if (find_ec && !(flags & LCME_FL_PARITY))
goto next;
if (!find_ec && (flags & LCME_FL_PARITY))
goto next;
rc = llapi_layout_mirror_id_get(layout, &mirror_id);
if (rc < 0)
goto error;
/* the caller only wants stale components from specific
* mirrors */
if (ids_nr > 0) {
int j;
for (j = 0; j < ids_nr; j++) {
if (mirror_ids[j] == mirror_id)
break;
}
/* not in the specified mirror */
if (j == ids_nr)
goto next;
} else if (flags & LCME_FL_NOSYNC) {
/* if not specified mirrors, do not resync "nosync"
* mirrors */
goto next;
}
rc = llapi_layout_comp_id_get(layout, &id);
if (rc < 0)
goto error;
rc = llapi_layout_comp_extent_get(layout, &start, &end);
if (rc < 0)
goto error;
/* pack this component into @comp array */
comp[idx].lrc_id = id;
comp[idx].lrc_mirror_id = mirror_id;
comp[idx].lrc_start = start;
comp[idx].lrc_end = end;
comp[idx].lrc_synced = true;
idx++;
if (idx >= comp_size) {
rc = -EINVAL;
goto error;
}
next:
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
if (rc < 0) {
rc = -EINVAL;
goto error;
}
}
error:
return rc < 0 ? rc : idx;
}
int llapi_mirror_find_stale(struct llapi_layout *layout,
struct llapi_resync_comp *comp, size_t comp_size,
__u16 *mirror_ids, int ids_nr)
{
return _mirror_find_stale(layout, comp, comp_size,
mirror_ids, ids_nr, 0);
}
int llapi_ec_find_stale(struct llapi_layout *layout,
struct llapi_resync_comp *comp, size_t comp_size,
__u16 *mirror_ids, int ids_nr)
{
return _mirror_find_stale(layout, comp, comp_size,
mirror_ids, ids_nr, 1);
}
/* locate @layout to a valid component covering file [file_start, file_end) */
int llapi_mirror_find(struct llapi_layout *layout, uint64_t file_start,
uint64_t file_end, uint64_t *endp)
{
uint32_t mirror_id = 0;
int rc;
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
if (rc < 0)
return rc;
*endp = 0;
while (rc == 0) {
uint64_t start, end;
uint32_t flags, id, rid;
rc = llapi_layout_comp_flags_get(layout, &flags);
if (rc < 0)
return rc;
if (flags & LCME_FL_STALE)
goto next;
if (flags & LCME_FL_PARITY)
goto next;
rc = llapi_layout_mirror_id_get(layout, &rid);
if (rc < 0)
return rc;
rc = llapi_layout_comp_id_get(layout, &id);
if (rc < 0)
return rc;
rc = llapi_layout_comp_extent_get(layout, &start, &end);
if (rc < 0)
return rc;
if (file_start >= start && file_start < end) {
if (!mirror_id)
mirror_id = rid;
else if (mirror_id != rid || *endp != start)
break;
file_start = *endp = end;
if (end >= file_end)
break;
}
next:
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
if (rc < 0)
return rc;
}
if (!mirror_id)
return -ENOENT;
return mirror_id;
}
int llapi_mirror_resync_many_params(int fd, struct llapi_layout *layout,
struct llapi_resync_comp *comp_array,
int comp_size, uint64_t start, uint64_t end,
unsigned long stats_interval_sec,
uint64_t bandwidth_bytes_sec)
{
struct stat stbuf;
size_t buflen = 64 << 20; /* 64M */
ssize_t page_size;
void *buf;
uint64_t pos = start;
uint64_t data_off = pos, data_end = pos;
uint64_t mirror_end = LUSTRE_EOF;
uint32_t src = 0;
int i;
int rc;
int rc2 = 0;
struct timespec start_time;
struct timespec now;
struct timespec last_bw_print;
uint64_t total_bytes_read = 0;
uint64_t total_bytes_written = 0;
uint64_t write_estimation_bytes = 0;
rc = fstat(fd, &stbuf);
if (rc < 0)
return -errno;
/* estimate is too big for sparse file, but good enough for % done */
if (bandwidth_bytes_sec > 0 || stats_interval_sec) {
for (i = 0; i < comp_size; i++) {
write_estimation_bytes +=
min_t(uint64_t, comp_array[i].lrc_end,
stbuf.st_size) - comp_array[i].lrc_start;
}
}
/* limit transfer size to what can be sent in one second */
if (bandwidth_bytes_sec && bandwidth_bytes_sec < buflen)
buflen = (bandwidth_bytes_sec + ONE_MB - 1) & ~(ONE_MB - 1);
page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
rc = -errno;
return rc;
}
rc = posix_memalign(&buf, page_size, buflen);
if (rc)
return -rc;
(void)mlock(buf, buflen);
clock_gettime(CLOCK_MONOTONIC, &start_time);
now = last_bw_print = start_time;
while (pos < end) {
ssize_t bytes_read;
size_t to_read;
size_t to_write;
size_t data_size;
if (pos >= data_end) {
off_t tmp_off;
if (pos >= mirror_end || !src) {
rc = llapi_mirror_find(layout, pos, end,
&mirror_end);
if (rc < 0) {
llapi_error(LLAPI_MSG_ERROR, rc,
"cannot find source mirror");
goto out_free;
}
src = rc;
/* restrict mirror end by resync end */
mirror_end = MIN(end, mirror_end);
}
tmp_off = llapi_mirror_data_seek(fd, src, pos,
&data_size);
if (tmp_off < 0) {
/* switch to full copy */
to_read = mirror_end - pos;
goto do_read;
}
data_off = tmp_off;
data_end = data_off + data_size;
data_off = MIN(data_off, mirror_end);
data_end = MIN(data_end, mirror_end);
/* align by page, if there is data block to copy */
if (data_size)
data_off &= ~(page_size - 1);
}
if (pos < data_off) {
for (i = 0; i < comp_size; i++) {
uint64_t cur_pos;
size_t to_punch;
uint32_t mid = comp_array[i].lrc_mirror_id;
/* skip non-overlapped component */
if (pos >= comp_array[i].lrc_end ||
data_off <= comp_array[i].lrc_start)
continue;
if (pos < comp_array[i].lrc_start)
cur_pos = comp_array[i].lrc_start;
else
cur_pos = pos;
if (data_off > comp_array[i].lrc_end)
to_punch = comp_array[i].lrc_end -
cur_pos;
else
to_punch = data_off - cur_pos;
if (comp_array[i].lrc_end == OBD_OBJECT_EOF)
/* the last component can be truncated
* safely
*/
rc = llapi_mirror_truncate(fd, mid,
cur_pos);
else if (to_punch)
rc = llapi_mirror_punch(fd, mid,
cur_pos, to_punch);
/**
* hole at the end of file, so just truncate up
* to set size.
*/
if (!rc && data_off == data_end && !data_size)
rc = llapi_mirror_truncate(fd,
mid, data_end);
/* if failed then read failed hole range */
if (rc < 0) {
rc = 0;
pos = cur_pos;
if (pos + to_punch == data_off)
to_read = data_end - pos;
else
to_read = to_punch;
goto do_read;
}
}
pos = data_off;
}
if (pos == mirror_end)
continue;
to_read = data_end - pos;
do_read:
if (!to_read)
break;
assert(data_end <= mirror_end);
to_read = MIN(buflen, to_read);
to_read = ((to_read - 1) | (page_size - 1)) + 1;
bytes_read = llapi_mirror_read(fd, src, buf, to_read, pos);
if (bytes_read == 0) {
/* end of file */
break;
}
if (bytes_read < 0) {
rc = bytes_read;
llapi_error(LLAPI_MSG_ERROR, rc,
"error reading bytes %ld-%ld of mirror %u",
pos, to_read, src);
break;
}
total_bytes_read += bytes_read;
/* round up to page align to make direct IO happy. */
to_write = ((bytes_read - 1) | (page_size - 1)) + 1;
for (i = 0; i < comp_size; i++) {
ssize_t written;
off_t pos2 = pos;
size_t to_write2 = to_write;
/* skip non-overlapped component */
if (pos >= comp_array[i].lrc_end ||
pos + to_write <= comp_array[i].lrc_start)
continue;
if (pos < comp_array[i].lrc_start)
pos2 = comp_array[i].lrc_start;
to_write2 -= pos2 - pos;
if ((pos + to_write) > comp_array[i].lrc_end)
to_write2 -= pos + to_write -
comp_array[i].lrc_end;
written = llapi_mirror_write(fd,
comp_array[i].lrc_mirror_id,
buf + pos2 - pos,
to_write2, pos2);
if (written < 0) {
comp_array[i].lrc_synced = false;
llapi_error(LLAPI_MSG_ERROR, written,
"component %u not synced",
comp_array[i].lrc_id);
if (rc2 == 0)
rc2 = (int)written;
continue;
}
assert(written == to_write2);
total_bytes_written += written;
if (!bandwidth_bytes_sec && !stats_interval_sec)
continue;
clock_gettime(CLOCK_MONOTONIC, &now);
llapi_bandwidth_throttle(&now, &start_time,
bandwidth_bytes_sec,
total_bytes_written);
if (stats_interval_sec &&
total_bytes_written != write_estimation_bytes)
llapi_stats_log(&now, &start_time,
&last_bw_print, stats_interval_sec,
total_bytes_read, total_bytes_written,
total_bytes_written,
write_estimation_bytes);
}
pos += bytes_read;
}
out_free:
(void)munlock(buf, buflen);
free(buf);
if (rc < 0) {
/* fatal error happens */
for (i = 0; i < comp_size; i++)
comp_array[i].lrc_synced = false;
return rc;
}
/* Output at least one log, regardless of stats_interval */
if (stats_interval_sec) {
clock_gettime(CLOCK_MONOTONIC, &now);
llapi_stats_log(&now, &start_time,
&last_bw_print, stats_interval_sec,
total_bytes_read, total_bytes_written,
write_estimation_bytes, write_estimation_bytes);
}
/**
* no fatal error happens, each lrc_synced tells whether the component
* has been resync successfully.
*/
for (i = 0; i < comp_size; i++) {
struct llapi_resync_comp *comp = comp_array + i;
if (!comp->lrc_synced)
continue;
if (pos < comp->lrc_start || pos >= comp->lrc_end)
continue;
if (pos < stbuf.st_size) {
rc = llapi_mirror_punch(fd, comp->lrc_mirror_id, pos,
comp->lrc_end - pos);
} else {
rc = llapi_mirror_truncate(fd, comp->lrc_mirror_id,
pos);
}
/* Ignore truncate error on encrypted file without the
* key if tried on LUSTRE_ENCRYPTION_UNIT_SIZE boundary.
*/
if (rc < 0 && (rc != -ENOKEY || pos & ~LUSTRE_ENCRYPTION_MASK))
comp->lrc_synced = false;
}
/**
* returns the first error code for partially successful resync if
* possible.
*/
return rc2;
}
/* Total maximum number of data stripes and parities in a raid set */
#define MAX_STRIPE_POINTERS 512
/**
* llapi_ec_compute_parities() - Compute and update parities for a single RAID
* set
* @fd: file descriptor to read data from
* @layout: layout structure containing EC configuration
* @data_pos: starting position in file to read data stripes
* @num_data_stripes: number of data stripes (k)
* @ec_pos: starting position for EC parity stripes
* @num_ec_stripes: number of EC parity stripes (p)
* @data_id: mirror id of the data component to read from
* @stripe_size: size of each stripe in bytes
* @end_pos: end position in file (exclusive)
* @stripe_ptrs: array of pointers to stripe buffers (data + parity)
* @encode_matrix: encoding matrix for erasure coding
* @g_tbls: Galois field tables for EC computation
*
* This function reads data stripes from the file starting at @data_pos and
* computes the corresponding parity stripes using erasure coding. For a single
* data stripe (num_data_stripes == 1), it performs a simple copy. For multiple
* data stripes, it generates a Cauchy matrix and uses Intel ISA-L library
* functions to compute the parities.
*
* The function reads up to @stripe_size bytes per data stripe, stopping at
* @end_pos. If the file size changes during the operation (detected by
* llapi_mirror_read() returning 0 before reaching end_pos), the operation
* fails with -EBUSY.
*
* Return:
* * %0 on success
* * %-EBUSY if file size changed during operation
* * %negative error code from llapi_mirror_read() on read failure
*/
static int llapi_ec_compute_parities(int fd, struct llapi_layout *layout,
uint64_t data_pos, int num_data_stripes,
uint64_t ec_pos, int num_ec_stripes,
int data_id, uint64_t stripe_size, uint64_t end_pos,
uint8_t *stripe_ptrs[],
uint8_t *encode_matrix, uint8_t *g_tbls)
{
uint64_t bytes_left;
int rc, i, k, p, m;
k = num_data_stripes;
p = num_ec_stripes;
m = k + p;
/*
* The buffer is reused across raid sets. Only zero the portions beyond
* end_pos because bytes before it are overwritten by pread(), including
* sparse holes that read back as zeroes.
*/
bytes_left = end_pos > data_pos ? end_pos - data_pos : 0;
for (i = 0; i < k; i++) {
if (bytes_left >= stripe_size) {
bytes_left -= stripe_size;
continue;
}
memset(stripe_ptrs[i] + bytes_left, 0,
stripe_size - bytes_left);
bytes_left = 0;
}
for (i = 0; i < k; i++) {
size_t to_read;
ssize_t bytes_read;
uint8_t *read_buf = stripe_ptrs[i];
/* End of file or end of extent reached so no more data */
if (data_pos >= end_pos)
break;
to_read = end_pos - data_pos;
if (to_read > stripe_size)
to_read = stripe_size;
while (to_read) {
bytes_read = llapi_mirror_read(fd, data_id, read_buf,
to_read, data_pos);
/*
* We are careful to not read beyond eof so we can treat
* ==0 as an unrecoverable error.
* File size must have changed while we were resyncing.
*/
if (bytes_read == 0)
bytes_read = -EBUSY;
if (bytes_read < 0) {
llapi_error(LLAPI_MSG_ERROR, bytes_read,
"could not read data to compute parities");
rc = bytes_read;
goto out;
}
read_buf += bytes_read;
data_pos += bytes_read;
to_read -= bytes_read;
}
}
if (num_data_stripes == 1) {
memcpy(stripe_ptrs[1], stripe_ptrs[0], stripe_size);
} else {
gf_gen_cauchy1_matrix(encode_matrix, m, k);
ec_init_tables(k, p, &encode_matrix[k * k], g_tbls);
ec_encode_data(stripe_size, k, p, g_tbls, &stripe_ptrs[0],
&stripe_ptrs[k]);
}
rc = 0;
out:
return rc;
}
static int
llapi_ec_write_parities(int fd, uint64_t stripe_size, int k, int p,
uint64_t ec_pos, int ec_id, uint8_t *stripe_ptrs[],
struct ec_parity_coverage *cov)
{
int rc, i;
int m = k + p;
struct ec_parity_coverage *c;
for (i = k; i < m; i++) {
size_t to_write;
ssize_t bytes_written;
uint8_t *write_buf;
write_buf = stripe_ptrs[i];
for (c = cov; c; c = c->next) {
uint64_t off = ec_pos + (i - k) * stripe_size + c->pos;
to_write = c->len;
bytes_written = llapi_mirror_write(fd,
mirror_id_of(ec_id),
&write_buf[c->pos],
to_write, off);
if (bytes_written < 0) {
llapi_error(LLAPI_MSG_ERROR, bytes_written,
"could not write ec parities");
rc = bytes_written;
goto out;
}
assert(bytes_written == to_write);
llapi_printf(LLAPI_MSG_NORMAL,
"Wrote parity #%d range=0x%llx-0x%llx\n",
i - k,
(unsigned long long)off,
(unsigned long long)(off + c->len));
}
}
rc = 0;
out:
return rc;
}
/**
* llapi_ec_verify_parities() - Verify on-disk EC parities match the in-memory
* parities computed from the data stripes
* @fd: file descriptor of the file to verify
* @stripe_size: size of a single stripe in bytes
* @k: number of data stripes in the RAID set
* @p: number of parity stripes in the RAID set
* @ec_pos: byte offset within the EC mirror to start reading parities from
* @ec_id: mirror id of the EC parity component
* @stripe_ptrs: array of stripe buffers; stripe_ptrs[k..k+p-1] hold the
* expected (in-memory) parities to compare against
*
* Reads each on-disk parity stripe and compares it against the corresponding
* computed parity in stripe_ptrs[k + i].
*
* Note, stripe_ptrs[0] is reused as the read buffer and its contents
* are overwritten by this function. The caller must not rely on stripe_ptrs[0]
* holding the original data stripe after this call returns.
*
* Return:
* * %0 on success (all parities match)
* * %-EINVAL on a short read or parity mismatch
* * %negative error code from llapi_mirror_read() on read failure
*/
static int llapi_ec_verify_parities(int fd, uint64_t stripe_size, int k, int p,
uint64_t ec_pos, int ec_id,
uint8_t *stripe_ptrs[],
struct ec_parity_coverage *cov)
{
struct ec_parity_coverage *c;
int rc;
int i;
for (i = 0; i < p; i++) {
uint8_t *read_buf = stripe_ptrs[0];
for (c = cov; c; c = c->next) {
uint64_t off = ec_pos + i * stripe_size + c->pos;
size_t to_read = c->len;
ssize_t bytes_read;
bytes_read = llapi_mirror_read(fd, mirror_id_of(ec_id),
&read_buf[c->pos],
to_read, off);
if (bytes_read < 0) {
llapi_error(LLAPI_MSG_ERROR, bytes_read,
"could not read ec parities");
rc = bytes_read;
goto out;
}
/*
* Short read from parity component indicates the
* parity data was not written for this region.
*/
if (bytes_read != to_read) {
rc = -EINVAL;
llapi_error(
LLAPI_MSG_ERROR, rc,
"parity %d short read: got %zd expected %zu",
i, bytes_read, to_read);
goto out;
}
if (memcmp(&read_buf[c->pos],
&stripe_ptrs[k + i][c->pos],
c->len)) {
rc = -EINVAL;
llapi_error(LLAPI_MSG_ERROR, rc,
"parity mismatch at ec_pos=%llu",
(unsigned long long)off);
goto out;
}
llapi_printf(
LLAPI_MSG_NORMAL,
"Verified parity #%d range=0x%llx-0x%llx\n", i,
(unsigned long long)off,
(unsigned long long)(off + c->len));
}
}
rc = 0;
out:
return rc;
}
/**
* Compute the parity coverage for a single SEEK_DATA region
*/
static int ec_compute_parity_coverage_single(int fd, uint64_t stripe_start_pos,
uint64_t data_pos, uint64_t len,
struct ec_parity_coverage **cov)
{
size_t data_len;
off_t data_off;
struct ec_parity_coverage *c;
data_off = llapi_data_seek(fd, data_pos, &data_len);
if (data_off < 0) {
llapi_error(LLAPI_MSG_ERROR, data_off,
"failed to SEEK_DATA");
return data_off;
}
/* normalize data_off and data_pos to start of stripe */
data_off -= stripe_start_pos;
data_pos -= stripe_start_pos;
if (data_len == 0 || data_off >= data_pos + len)
return len;
/*
* Clamp data_off+data_len so that it does not extend beyond
* data_pos+len
*/
if (data_off + data_len > data_pos + len)
data_len = len - (data_off - data_pos);
/* If this will be the first region in the list */
if (*cov == NULL || data_off < (*cov)->pos) {
c = malloc(sizeof(struct ec_parity_coverage));
if (c == NULL)
return -ENOMEM;
c->next = *cov;
c->pos = data_off;
c->len = data_len;
*cov = c;
/* merge if the new head overlaps with the previous head */
c = (*cov)->next;
while (c &&
c->pos <= (*cov)->pos + (*cov)->len) {
if (c->pos + c->len > (*cov)->pos + (*cov)->len)
(*cov)->len = c->pos + c->len - (*cov)->pos;
(*cov)->next = c->next;
free(c);
c = (*cov)->next;
}
return data_off - data_pos + data_len;
}
/* Skip forward until we find which cov to insert this range behind */
while ((*cov)->next && data_off > (*cov)->next->pos)
cov = &(*cov)->next;
if (data_off <= (*cov)->pos + (*cov)->len) {
/*
* This range starts inside or immediately after the current
* cov so we can just extend its length.
*/
(*cov)->len = data_off - (*cov)->pos + data_len;
} else {
/*
* This range starts beyond the end of the current cov.
* Create a new entry. If it overlaps with the next entry
* we can merge them below.
*/
c = malloc(sizeof(struct ec_parity_coverage));
if (c == NULL)
return -ENOMEM;
c->next = (*cov)->next;
c->pos = data_off;
c->len = data_len;
(*cov)->next = c;
cov = &(*cov)->next;
}
/* merge if the new head overlaps with the previous head */
c = (*cov)->next;
while (c &&
c->pos <= (*cov)->pos + (*cov)->len) {
if (c->pos + c->len > (*cov)->pos + (*cov)->len)
(*cov)->len = c->pos + c->len - (*cov)->pos;
(*cov)->next = c->next;
free(c);
c = (*cov)->next;
}
return data_off - data_pos + data_len;
}
/**
* Compute the parity coverage for a single stripe
*/
static int
ec_compute_parity_coverage_stripe(int fd, uint64_t data_pos,
uint64_t stripe_size,
struct ec_parity_coverage **cov)
{
int rc;
uint64_t len = stripe_size;
uint64_t stripe_start_pos = data_pos;
/* If we already cover the whole stripe there is nothing more to do. */
if (*cov && (*cov)->pos == 0 && (*cov)->len == stripe_size)
return 0;
while (len) {
rc = ec_compute_parity_coverage_single(fd, stripe_start_pos,
data_pos, len, cov);
if (rc <= 0)
return rc;
if (rc > len)
return 0;
data_pos += rc;
len -= rc;
}
return 0;
}
/**
* Compute the parity coverage for an entire raid set.
*
* The parity coverate is the set of ranges in the parity stripe that are
* computed from actual file data in the corresponding data stripes.
* This takes into account both EOF as well as holes in a sparse file.
*
* All the examples are for a file with --ec 2+1. I.e. Two data stripes
* and one parity stripe.
* Stripe size is 64kb.
*
* In the examples below we also assume byte level granularity
* on the data and hole sections. This is for illustration purposes
* but is unlikely in real world as the data regions reported from
* SEEK_DATA/SEEK_HOLE will depend on the underlying filesystem of
* the OST hosting the data stripes and will most often be
* aligned to full 4kb blocks.
*
*
* Example:
* A File containing 6 bytes of data at offset 0. EOF is at offset 6.
* +--------------------------+
* | data | | Stripe #0, data in range 0-6
* +--------------------------+
* ^ EOF at offset 6
*
* +--------------------------+
* | | Stripe #1, no data.
* +--------------------------+
*
* In this case the parity stripe will contain parity data in the range 0 - 6.
* The rest of the parity stripe is undefined, a hole, as there is no data
* for the range 6 - end_of_stripe
*
*
* A File containing 6 bytes of data at offset 0. EOF is at offset 48.
* +--------------------------+
* | data | hole | | Stripe #0, data in range 0-6
* +--------------------------+
* ^ EOF at offset 48
*
* +--------------------------+
* | | Stripe #1, no data.
* +--------------------------+
*
* In this case the parity stripe will contain parity data in the range 0 - 6.
* The rest of the parity stripe is undefined, a hole, as there is no data
* for the range 6 - end_of_stripe
* While EOF is at offset 48, there range 6 - 48 is a hole and contains no data
* and thus do not contribute to partity data.
*
*
* Example:
* A File containing data in the ranged 0 - 6 and 70000 - 70006
* +--------------------------+
* | data | hole | Stripe #0, data in range 0-6
* +--------------------------+
* ^ start of stripe #0 at offset 0
*
* +--------------------------+
* | |data| | Stripe #1, data in the range 70000 - 70006
* +--------------------------+
* ^ ^ EOF at offset 70006
* | start of stripe #0 at offset 65536
*
* In this case the parity stripe will contain parity data in the two ranges
* 0 - 6 due to the data in stripe #0 range 0 - 6
* 4464 - 4470 due to the data in stripe #1, range 70000 - 70006
*
*
* Example:
* A File containing data in the ranged 10 - 2048 and 66536 - 70000
* +--------------------------+
* | | data | hole | Stripe #0, data in range 10 - 2048
* +--------------------------+
* ^ start of stripe #0 at offset 0
*
* +--------------------------+
* | | data | | Stripe #1, data in the range 66536 - 70000
* +--------------------------+
* ^ start of stripe #1 at offset 66536 - 70000
*
* In this case the parity stripe will contain parity in a single range
* that is the result of merging the two ranges of data in stripe #0 and #1
* 10 - 4464 Due to the merger of the two ranges 10 - 2048 and 1024 - 4464
*
*/
static int ec_compute_parity_coverage_raidset(int fd, uint64_t data_pos, int k,
uint64_t stripe_size,
struct ec_parity_coverage **cov)
{
int i, rc;
for (i = 0; i < k; i++) {
rc = ec_compute_parity_coverage_stripe(fd, data_pos,
stripe_size, cov);
if (rc)
goto out_free;
data_pos += stripe_size;
}
return 0;
out_free:
while (*cov) {
struct ec_parity_coverage *tmp = (*cov)->next;
free(*cov);
*cov = tmp;
}
return rc;
}
static int
llapi_ec_resync_or_verify_raidset(int fd, struct llapi_layout *layout,
struct llapi_layout_comp *data_comp,
uint64_t data_pos, int k,
struct llapi_layout_comp *ec_comp,
uint64_t ec_pos, int p,
uint8_t *stripe_ptrs[],
uint8_t *encode_matrix,
uint8_t *g_tbls,
uint64_t end_pos, int is_verify)
{
int rc = 0;
size_t data_len;
off_t data_off;
struct ec_parity_coverage *cov = NULL;
if (data_pos >= end_pos)
goto out_free;
data_off = llapi_data_seek(fd, data_pos, &data_len);
if (data_off < 0) {
rc = data_off;
llapi_error(LLAPI_MSG_ERROR, rc,
"failed to SEEK_DATA");
goto out_free;
}
if (data_off >= data_pos + k * data_comp->llc_stripe_size)
goto out_free;
if (!data_len)
goto out_free;
llapi_printf(LLAPI_MSG_NORMAL,
"Compute/verify raidset range=%llu-%llu\n",
(unsigned long long)data_pos,
(unsigned long long)(data_pos +
k * data_comp->llc_stripe_size));
rc = ec_compute_parity_coverage_raidset(fd, data_pos, k,
data_comp->llc_stripe_size, &cov);
if (rc)
goto out_free;
rc = llapi_ec_compute_parities(fd, layout,
data_pos, k,
ec_pos, p,
data_comp->llc_mirror_id,
data_comp->llc_stripe_size, end_pos,
stripe_ptrs, encode_matrix, g_tbls);
if (rc)
goto out_free;
if (is_verify) {
rc = llapi_ec_verify_parities(fd,
data_comp->llc_stripe_size, k, p, ec_pos,
ec_comp->llc_id, stripe_ptrs, cov);
if (rc)
goto out_free;
} else {
rc = llapi_ec_write_parities(fd,
data_comp->llc_stripe_size, k, p, ec_pos,
ec_comp->llc_id, stripe_ptrs, cov);
if (rc)
goto out_free;
}
out_free:
while (cov) {
struct ec_parity_coverage *tmp = cov->next;
free(cov);
cov = tmp;
}
return rc;
}
/**
* llapi_ec_resync_or_verify_comp() - Resync EC parities for a single component
* @fd: file descriptor of the file to resync
* @layout: layout structure containing the file layout
* @data_comp: data component to read from
* @ec_comp: EC parity component to update
* @is_verify: if true, compare the computed parities against the stored ones
* (verify) instead of writing them back (resync)
*
* This function resyncs the erasure coding parities for a single component
* by splitting the stripe set into smaller RAID sets and computing parities
* for each set. The data component's stripes are divided into RAID sets of
* approximately ec_comp->llc_dstripe_count stripes each, then
* llapi_ec_compute_parities() computes the parities for each RAID set, which
* are then written back (resync) or, if @is_verify is set, compared against
* the stored parities (verify).
*
* The function handles the splitting of stripes into two groups:
* - c0 RAID sets with k stripes each
* - c1 RAID sets with k-1 stripes each (c1 may be 0)
*
* Processing stops at the end of the data component extent or EOF, whichever
* comes first. If the extent starts beyond EOF, the function returns success
* without processing.
*
* Return:
* * %0 on success
* * %-errno from fstat() if file stat fails
* * %-EINVAL if total stripes exceed MAX_STRIPE_POINTERS
* * %-ENOMEM if memory allocation fails
* * %negative error code from llapi_ec_compute_parities() or
* llapi_ec_write_parities() on failure
*/
static int
llapi_ec_resync_or_verify_comp(int fd, struct llapi_layout *layout,
struct llapi_layout_comp *data_comp,
struct llapi_layout_comp *ec_comp,
int is_verify)
{
int rc = 0, i, k, p, m;
struct stat stbuf;
struct ec_split_comp sc;
uint64_t data_pos, ec_pos, end_pos;
uint64_t stripe_set_size, ec_size;
uint64_t num_hole_stripe_sets;
uint32_t ec_mirror_id = mirror_id_of(ec_comp->llc_id);
uint8_t *buf = NULL;
uint8_t *encode_matrix = NULL;
uint8_t *g_tbls = NULL;
uint8_t *stripe_ptrs[MAX_STRIPE_POINTERS];
size_t data_len, ec_len;
off_t data_off, ec_off;
ssize_t page_size;
rc = fstat(fd, &stbuf);
if (rc < 0)
return -errno;
/* This extent is past the end of the file so we can just skip it */
if (stbuf.st_size < data_comp->llc_extent.e_start)
return 0;
data_pos = data_comp->llc_extent.e_start;
data_off = data_pos;
ec_pos = ec_comp->llc_extent.e_start;
/* We only use data until end of extent or eof */
end_pos = stbuf.st_size;
if (end_pos > data_comp->llc_extent.e_end)
end_pos = data_comp->llc_extent.e_end;
/*
* We have data_comp->llc_stripe_count number of stripes in the
* in the data comp we want to split this into raid sets of
* approximately ec_comp->llc_dstripe_count stripes each.
* Call ec_split_stripes and find a mapping into smaller raidsets.
*/
ec_split_stripes(data_comp->llc_stripe_count,
ec_comp->llc_dstripe_count, &sc);
/*
* We have now split the total number of data stripes into
* c0 number of raidsets with k stripes each and
* c1 number of raidsets with k-1 stripes each.
* c1 may be 0.
* Compute and update the parities one raid set at a time.
*/
k = sc.esc_k0;
p = ec_comp->llc_cstripe_count;
m = k + p;
if (m > MAX_STRIPE_POINTERS) {
rc = -EINVAL;
goto out_free;
}
page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
rc = -errno;
goto out_free;
}
rc = posix_memalign((void **)&buf, page_size,
m * data_comp->llc_stripe_size);
if (rc) {
rc = -rc;
goto out_free;
}
memset(buf, 0, m * data_comp->llc_stripe_size);
for (i = 0; i < m; i++)
stripe_ptrs[i] = &buf[i * data_comp->llc_stripe_size];
encode_matrix = malloc((k + p) * k);
if (encode_matrix == NULL) {
rc = -ENOMEM;
goto out_free;
}
g_tbls = malloc(k * p * 32);
if (g_tbls == NULL) {
rc = -ENOMEM;
goto out_free;
}
if (data_pos >= end_pos) {
rc = 0;
goto out_free;
}
stripe_set_size = data_comp->llc_stripe_count *
data_comp->llc_stripe_size;
ec_size = (uint64_t)(sc.esc_n0 + sc.esc_n1) *
ec_comp->llc_cstripe_count *
ec_comp->llc_stripe_size;
one_more_stripeset:
data_off = llapi_mirror_data_seek(fd, data_comp->llc_mirror_id,
data_pos, &data_len);
if (data_off < 0) {
rc = data_off;
llapi_error(LLAPI_MSG_ERROR, rc, "failed to SEEK_DATA");
goto out_free;
}
if (data_off == data_pos)
goto stripeset_with_data;
/*
* Punch or verify holes for whole stripe sets only. Anything not
* covered here is left to stripeset_with_data, e.g. partial stripe set.
*
* Floor division counts full stripe sets only. For data_len == 0, it
* means a hole extends to the @end_pos; otherwise @data_off marks
* the next data and we jump to stripeset_with_data if it is still
* inside this stripe set.
*/
data_off = min_t(uint64_t, (uint64_t)data_off, end_pos);
if (data_len == 0) {
if (end_pos < data_pos + stripe_set_size)
goto stripeset_with_data;
num_hole_stripe_sets = (end_pos - data_pos) / stripe_set_size;
} else {
if (data_off < data_pos + stripe_set_size)
goto stripeset_with_data;
num_hole_stripe_sets = (data_off - data_pos) / stripe_set_size;
}
if (is_verify) {
/* skip over the same number of stripe sets as the data part */
ec_off = llapi_mirror_data_seek(fd, ec_mirror_id, ec_pos,
&ec_len);
if (ec_off < 0) {
rc = ec_off;
llapi_error(LLAPI_MSG_ERROR, rc, "failed to SEEK_EC");
goto out_free;
}
if (ec_off < ec_pos + num_hole_stripe_sets * ec_size) {
rc = -EINVAL;
llapi_error(LLAPI_MSG_ERROR, rc, "parity %d mismatch",
ec_mirror_id);
goto out_free;
}
} else {
/* punch on EC parity for the data part that is not written */
size_t punch_len = num_hole_stripe_sets * ec_size;
rc = llapi_mirror_punch(fd, ec_mirror_id, ec_pos, punch_len);
if (rc < 0)
goto out_free;
}
data_pos += num_hole_stripe_sets * stripe_set_size;
ec_pos += num_hole_stripe_sets * ec_size;
if (data_pos >= end_pos) {
rc = 0;
goto out_free;
}
stripeset_with_data:
for (i = 0, k = sc.esc_k0; i < sc.esc_n0 + sc.esc_n1; i++) {
if (i == sc.esc_n0)
k = sc.esc_k1;
/* Skip raidsets that are covered by an initial hole */
if (data_off > data_pos + k * data_comp->llc_stripe_size)
goto skip;
rc = llapi_ec_resync_or_verify_raidset(
fd, layout, data_comp, data_pos, k, ec_comp, ec_pos, p,
stripe_ptrs, encode_matrix, g_tbls, end_pos, is_verify);
if (rc)
goto out_free;
skip:
data_pos += k * data_comp->llc_stripe_size;
ec_pos += ec_comp->llc_cstripe_count * ec_comp->llc_stripe_size;
if (data_pos >= end_pos)
goto out_free;
}
if (data_pos < end_pos)
goto one_more_stripeset;
out_free:
free(g_tbls);
free(encode_matrix);
free(buf);
return rc;
}
/**
* llapi_ec_check_comp_match() - Verify an EC parity component is compatible
* with its data component
* @data_comp: data component the parities are computed from
* @ec_comp: EC parity component to validate against @data_comp
*
* The data and parity components must agree on stripe size and extent, and the
* data component must have enough stripes to hold the requested parities,
* otherwise the erasure coding would not fit in the extent.
*
* Return: 0 if the pair is consistent, -EINVAL otherwise
*/
static int llapi_ec_check_comp_match(struct llapi_layout_comp *data_comp,
struct llapi_layout_comp *ec_comp)
{
struct ec_split_comp sc;
/* data_comp and ec_comp must match in stripe size and region */
if (data_comp->llc_stripe_size != ec_comp->llc_stripe_size) {
llapi_error(LLAPI_MSG_ERROR, -EINVAL,
"data and ec stripe mismatch");
return -EINVAL;
}
if ((data_comp->llc_extent.e_start != ec_comp->llc_extent.e_start) ||
(data_comp->llc_extent.e_end != ec_comp->llc_extent.e_end)) {
llapi_error(LLAPI_MSG_ERROR, -EINVAL,
"data and ec range mismatch");
return -EINVAL;
}
/*
* If there are more parities than there are data stripes then the
* resulting erasure coding will no longer fit in the extent.
*/
ec_split_stripes(data_comp->llc_stripe_count,
ec_comp->llc_dstripe_count, &sc);
if (data_comp->llc_stripe_count <
(uint64_t)(sc.esc_n0 + sc.esc_n1) * ec_comp->llc_cstripe_count) {
llapi_error(LLAPI_MSG_ERROR, -EINVAL, "too many parities");
return -EINVAL;
}
return 0;
}
/*
* Definitions:
* Stripe set:
* These are the stripes defined for the data comp.
* The number of stripes in a stripe set is data_comp->llc_stripe_count.
* Stripe sets are repeated, one after the other, until the end of the
* comp.
* Each repetition of a stripe set is a row.
*
* Raid set:
* The stripe set is split into smaller groups over which ec parities
* are computed.
* This is the raid set.
* The raid set covers a specific range of each row.
* A raid set covers the same range for every row.
*
* The stripe set is split into raidsets so that there will be
* c0 number of raidsets with k0 stripes each and
* c1 number of raidsets with k1 stripes each.
* k1 == k0 - 1
* c1 may be 0.
*
* Raid stride:
* For a raid set, this is the number of stripes from one row in a raid
* set until the next row for that raid set.
* The raid stride is the same for all raid sets in a comp.
* The raid stride is data_comp->llc_stripe_count.
*
* Parity stride:
* This is the number of stripes for a specific parity for one row
* stripes to the same parity for the next row.
* It is the same as the number of parities.
*
*
* EC parity comps match to a single instance of a data mirror so that
* we can guarantee that the set of OSTs used in the data mirror will not
* overlap with the set of OSTs in the ec mirror.
* Thus if you delete a data mirror the EC mirror will not longer be useful
* and should be deleted as well.
*
* An EC comp must span the same region of the file as its associated data
* comp and thus the llc_extent.e_start and llc_extent.e_end must match
* between the two. Additionally the EC comp and its data comp must also have
* the same stripe size.
*
* The number of stripes in the EC comp must be equal or less than the number
* of stripes in the data comp, or else the parity data will not fit.
* I.e. there would be more parity data than would fit in the range
* llc_extent.e_start to llc_extent.e_start + llc_extent.e_end.
* Thus for example we can not do 2+8 encoding as the parities would
* take up 4 times more data than the actual data and the corresponding range
* of the file.
*
* As a special case, if there is only a single data stripe then we just store
* a copy of the data as the first (and only) parity instead of computing it
* just as if it was a normal mirror component of a single stripe.
* In this case there can only be a single "parity" stripe for the same reason
* as above.
*
* The data stripe consists of llc_stripe_count number of stripes.
* This might be a large number, much larger than what we want to compute the
* erasure code data over, so we need to split it into smaller raid sets.
*
* Example: we have a stripe set 11 data stripes:
*
* +-----------------+ \ <- mirror offset: llc_extent.e_start
* | Data stripe #0 | |
* +-----------------+ |
* | Data stripe #1 | |
* +-----------------+ |
* | Data stripe #2 | |
* +-----------------+ |
* | Data stripe #3 | |
* +-----------------+ |
* | Data stripe #4 | |
* +-----------------+ | Row #0 of the stripe set.
* | Data stripe #5 | |
* +-----------------+ |
* | Data stripe #6 | |
* +-----------------+ |
* | Data stripe #7 | |
* +-----------------+ |
* | Data stripe #8 | |
* +-----------------+ |
* | Data stripe #9 | |
* +-----------------+ |
* | Data stripe #10 | |
* +-----------------+ /
* ... Repeated until llc_extent.e_end.
*
* Assume we want to use 4+2 erasure coding.
* I.e. 2 parities for raid sets of at most 4 stripes each.
*
* Each row is then split into smaller raid sets using the
* function ec_split_stripes().
* This splits into 2 x 4 stripes + 1 x 3 stripes, like this:
* +-----------------+ \ <- mirror offset: llc_extent.e_start
* | Data stripe #0 | |
* +-----------------+ |
* | Data stripe #1 | |
* +-----------------+ | RAID set #0, row #0
* | Data stripe #2 | |
* +-----------------+ |
* | Data stripe #3 | |
* +-----------------+ /
*
* +-----------------+ \
* | Data stripe #4 | |
* +-----------------+ |
* | Data stripe #5 | |
* +-----------------+ | RAID set #1, row #0
* | Data stripe #6 | |
* +-----------------+ |
* | Data stripe #7 | |
* +-----------------+ /
*
* +-----------------+ \
* | Data stripe #8 | |
* +-----------------+ |
* | Data stripe #9 | | RAID set #2, row #0
* +-----------------+ |
* | Data stripe #10 | |
* +-----------------+ /
*
* +-----------------+ \ <- mirror offset: llc_extent.e_start
* | Data stripe #11 | | + the raid stride * stripe size
* +-----------------+ |
* | Data stripe #12 | |
* +-----------------+ | RAID set #0, row #1
* | Data stripe #13 | |
* +-----------------+ |
* | Data stripe #14 | |
* +-----------------+ /
* ...
*
* For a given data stripe ds, the row it belongs to is:
* row = ds / data_comp->llc_stripe_count
*
* and which raid set rs it belongs to is given by:
*
* _o = ds % data_comp->llc_stripe_count
* if (_o <= c0 * k0)
* rs = _o / k0
* else
* rs = c0 + (_o - c0 * k0) / k1
*
*
* For each RAID set 2 parities will be computed and they will be laid out
* sequentially in the ec comp as this:
*
* +---------------------------+ \ <- mirror offset: llc_extent.e_start
* | Parity #0 for RAID set #0 | |
* +---------------------------+ | Parity set #0, row #0
* | Parity #1 for RAID set #0 | |
* +---------------------------+ X
* | Parity #0 for RAID set #1 | |
* +---------------------------+ | Parity set #1, row #0
* | Parity #1 for RAID set #1 | |
* +---------------------------+ X
* | Parity #0 for RAID set #2 | |
* +---------------------------+ | Parity set #2, row #0
* | Parity #1 for RAID set #2 | |
* +---------------------------+ X <- mirror offset: llc_extent.e_start
* | Parity #0 for RAID set #0 | | + parity_stride * stripe size
* +---------------------------+ | Parity set #0, row #1
* | Parity #1 for RAID set #0 | |
* +---------------------------+ X
* ...
*
* For a given row, raid set and parity, the parity will be stored in the
* parity stripe:
*
* parity-stripe = row * parity-stride
* + raid-set * number-of-parities
* + parity
*
*
* If there is a hole that spans the entire raidset then we can skip
* computing the parities and leave it as a hole in the ec comp as well.
* Example, assume there is a hole spanning the entire RAID set #1 above, this
* will result in a parity layout as:
* +---------------------------+ <- file offset: start of stripe-set
* | Parity #0 for RAID set #0 |
* +-----------------+---------+
* | Parity #1 for RAID set #0 |
* +---------------------------+
* | hole |
* +---------------------------+ No data in RAID set #1 so
* | hole | no need to store the parities either.
* +-----------------+---------+
* | Parity #0 for RAID set #2 |
* +-----------------+---------+
* | Parity #1 for RAID set #2 |
* +-----------------+---------+
* | hole until e_end |
* ...
*
* The extent offsets matches between the EC comp and the data comp
* it protects.
*
* Computations are done on whole stripes at a time.
* We compute the parities for a full raid set at a time.
*
* There is no guarantee that the raid set will be fully populated.
* We could for example reach the end of the extent (llc_extent.e_end)
* partially through the raid set, or we could reach EOF.
* In both cases we pad the remainder of the raid set with 0 when we compute
* the parity.
*
* Example: the extent ends partway through the second stripe in raidset #2:
* The raid set is padded with 0 so that we have a full set of stripes to
* compute the parities over.
* +------------------------+ \
* | Data stripe #8 | |
* +------------------------+ |
* | Data stripe #9 | 00000 | | RAID set #2
* +------------------------+ |
* | 0000000000000000000000 | |
* +------------------------+ /
*
*/
int llapi_ec_resync_many_params(int fd, struct llapi_layout *layout,
struct llapi_resync_comp *comp_array,
int comp_size,
unsigned long stats_interval_sec,
uint64_t bandwidth_bytes_sec)
{
int rc, i;
struct llapi_layout_comp *ec_comp, *data_comp;
for (i = 0; i < comp_size; i++) {
/* Find the stale ec comp */
ec_comp = __llapi_layout_find_comp_by_id(layout,
comp_array[i].lrc_id);
if (!ec_comp) {
rc = -ENOENT;
llapi_error(LLAPI_MSG_ERROR, rc,
"cannot find ec comp");
goto out;
}
/* Find the data comp that matches the same region */
data_comp = __llapi_layout_find_data_comp_by_parity(layout,
ec_comp);
if (!data_comp) {
rc = -ENOENT;
llapi_error(LLAPI_MSG_ERROR, rc,
"cannot find data comp");
goto out;
}
/* Skip resyncing parity if data component has NOSYNC flag */
if (data_comp->llc_flags & LCME_FL_NOSYNC)
continue;
rc = llapi_ec_check_comp_match(data_comp, ec_comp);
if (rc)
goto out;
rc = llapi_ec_resync_or_verify_comp(fd, layout, data_comp,
ec_comp, false);
if (rc) {
llapi_error(LLAPI_MSG_ERROR, rc,
"failed to sync ec comp");
goto out;
}
}
return 0;
out:
return rc;
}
/**
* llapi_ec_verify_comps() - Verify EC parity components against their data
* @fd: File descriptor of the mirrored file.
* @layout: Mirror component list.
* @ecs: Array of EC parity component ids to verify.
* @ec_count: Number of entries in @ecs.
*
* For each EC parity component id in @ecs, locate the matching data component
* in @layout, recompute the parities from the data, and compare them against
* the on-disk parities. Returns the first failure encountered; remaining
* components in @ecs are not checked.
*
* Return: 0 on success, negative errno on failure.
*/
int llapi_ec_verify_comps(int fd, struct llapi_layout *layout, __u32 *ecs,
int ec_count)
{
struct llapi_layout_comp *data_comp;
struct llapi_layout_comp *ec_comp;
int rc = 0;
int i;
for (i = 0; i < ec_count; i++) {
ec_comp = __llapi_layout_find_comp_by_id(layout, ecs[i]);
if (!ec_comp) {
rc = -ENOENT;
llapi_error(LLAPI_MSG_ERROR, rc,
"ec component is missing");
goto out;
}
data_comp = __llapi_layout_find_data_comp_by_parity(layout,
ec_comp);
if (!data_comp) {
rc = -ENOENT;
llapi_error(
LLAPI_MSG_ERROR, rc,
"ec component does not have a matching data component");
goto out;
}
rc = llapi_ec_check_comp_match(data_comp, ec_comp);
if (rc)
goto out;
rc = llapi_ec_resync_or_verify_comp(fd, layout, data_comp,
ec_comp, true);
if (rc) {
llapi_error(LLAPI_MSG_ERROR, rc,
"ec verify failed for comp 0x%08x",
ec_comp->llc_id);
goto out;
}
}
out:
return rc;
}
int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
struct llapi_resync_comp *comp_array,
int comp_size, uint64_t start, uint64_t end)
{
return llapi_mirror_resync_many_params(fd, layout, comp_array,
comp_size, start, end, 0, 0);
}
enum llapi_layout_comp_sanity_error {
LSE_OK,
LSE_INCOMPLETE_MIRROR,
LSE_ADJACENT_EXTENSION,
LSE_INIT_EXTENSION,
LSE_FLAGS,
LSE_DOM_EXTENSION,
LSE_DOM_EXTENSION_FOLLOWING,
LSE_DOM_FIRST,
LSE_SET_COMP_START,
LSE_NOT_ZERO_LENGTH_EXTENDABLE,
LSE_END_NOT_GREATER,
LSE_ZERO_LENGTH_NORMAL,
LSE_NOT_ADJACENT_PREV,
LSE_START_GT_END,
LSE_ALIGN_END,
LSE_ALIGN_EXT,
LSE_FOREIGN_EXTENSION,
LSE_MIRROR_COUNT_INVALID,
LSE_MIRROR_COUNT_MISMATCH,
LSE_EC_PARAM,
LSE_EC_MATCH_DATA,
LSE_EC_DUP,
LSE_EC_OVERLAP,
LSE_EC_MIXED_MIRROR,
LSE_EC_DATA_COMP_UNSET_LINK_ID,
LSE_EC_UNPROTECTED_DATA,
LSE_LAST,
};
const char *const llapi_layout_strerror[] =
{
[LSE_OK] = "",
[LSE_INCOMPLETE_MIRROR] =
"Incomplete mirror - must go to EOF",
[LSE_ADJACENT_EXTENSION] =
"No adjacent extension space components",
[LSE_INIT_EXTENSION] =
"Cannot apply extension flag to init components",
[LSE_FLAGS] =
"Wrong flags",
[LSE_DOM_EXTENSION] =
"DoM components can't be extension space",
[LSE_DOM_EXTENSION_FOLLOWING] =
"DoM components cannot be followed by extension space",
[LSE_DOM_FIRST] =
"DoM component should be the first one in a file/mirror",
[LSE_SET_COMP_START] =
"Must set previous component extent before adding next",
[LSE_NOT_ZERO_LENGTH_EXTENDABLE] =
"Extendable component must start out zero-length",
[LSE_END_NOT_GREATER] =
"Component end is before end of previous component",
[LSE_ZERO_LENGTH_NORMAL] =
"Zero length components must be followed by extension",
[LSE_NOT_ADJACENT_PREV] =
"Components not adjacent (end != next->start",
[LSE_START_GT_END] =
"Component start is > end",
[LSE_ALIGN_END] =
"The component end must be aligned by the stripe size",
[LSE_ALIGN_EXT] =
"The extension size must be aligned by the stripe size",
[LSE_FOREIGN_EXTENSION] =
"FOREIGN components can't be extension space",
[LSE_MIRROR_COUNT_INVALID] =
"Mirror count is invalid",
[LSE_MIRROR_COUNT_MISMATCH] =
"Mirror count doesn't match component structure",
[LSE_EC_PARAM] =
"EC component should have valid parameters",
[LSE_EC_MATCH_DATA] =
"EC component should have matching data component",
[LSE_EC_DUP] =
"Parity component should not protect multiple data components",
[LSE_EC_OVERLAP] =
"EC components should not overlap in the same mirror",
[LSE_EC_MIXED_MIRROR] =
"Mirror contains both PARITY and non-PARITY components",
[LSE_EC_DATA_COMP_UNSET_LINK_ID] =
"Data component should link to parity component",
[LSE_EC_UNPROTECTED_DATA] =
"All data components in the mirror must be protected by parity",
};
struct llapi_layout_sanity_args {
bool lsa_incomplete;
bool lsa_flr;
bool lsa_ondisk;
int lsa_rc;
char *fsname;
uint16_t lsa_mirror_count;
/* Track parity state for mixed mirror detection */
bool lsa_in_parity;
uint64_t lsa_last_parity_end;
};
/* Inline function to verify the pool name */
static inline int verify_pool_name(char *fsname, struct llapi_layout *layout)
{
struct llapi_layout_comp *comp;
if (!fsname || fsname[0] == '\0')
return 0;
comp = __llapi_layout_cur_comp(layout);
if (!comp)
return 0;
if (comp->llc_pool_name[0] == '\0' ||
lov_pool_is_ignored(comp->llc_pool_name))
return 0;
/* check if the pool name exist */
if (llapi_search_ost(fsname, comp->llc_pool_name, NULL) < 0)
return -1;
return 0;
}
/*
* When modified, adjust llapi_stripe_param_verify() if needed as well.
*/
static int llapi_layout_sanity_cb(struct llapi_layout *layout,
void *arg)
{
struct llapi_layout_comp *comp, *next, *prev;
struct llapi_layout_sanity_args *args = arg;
bool first_comp = false;
comp = __llapi_layout_cur_comp(layout);
if (comp == NULL) {
args->lsa_rc = -1;
goto out_err;
}
if (verify_pool_name(args->fsname, layout) != 0) {
args->lsa_rc = -1;
goto out_err;
}
if (comp->llc_list.prev != &layout->llot_comp_list)
prev = list_last_entry(&comp->llc_list, typeof(*prev),
llc_list);
else
prev = NULL;
if (comp->llc_list.next != &layout->llot_comp_list)
next = list_first_entry(&comp->llc_list, typeof(*next),
llc_list);
else
next = NULL;
/*
* Start of zero implies a new mirror.
* With parity-first allowed, any component at e_start == 0 marks
* the start of a new mirror, regardless of PARITY flag.
*/
if (comp->llc_extent.e_start == 0) {
first_comp = true;
args->lsa_mirror_count++;
/* Most checks apply only within one mirror, this is an
* exception. */
if (prev && prev->llc_extent.e_end != LUSTRE_EOF) {
args->lsa_rc = LSE_INCOMPLETE_MIRROR;
goto out_err;
}
/* Reset prev at mirror boundary */
prev = NULL;
}
if (next && next->llc_extent.e_start == 0)
next = NULL;
/* Flag sanity checks */
/* No adjacent extension components */
if ((comp->llc_flags & LCME_FL_EXTENSION) && next &&
(next->llc_flags & LCME_FL_EXTENSION)) {
args->lsa_rc = LSE_ADJACENT_EXTENSION;
goto out_err;
}
/* Extension flag cannot be applied to init components and the first
* component of each mirror is automatically init */
if ((comp->llc_flags & LCME_FL_EXTENSION) &&
(comp->llc_flags & LCME_FL_INIT || first_comp)) {
args->lsa_rc = LSE_INIT_EXTENSION;
goto out_err;
}
if (comp->llc_ondisk) {
if (comp->llc_flags & LCME_FL_NEG)
args->lsa_rc = LSE_FLAGS;
} else if (!args->lsa_incomplete) {
if (args->lsa_flr) {
if (comp->llc_flags &
~(LCME_USER_COMP_FLAGS | LCME_FL_IS_LINK_ID))
args->lsa_rc = LSE_FLAGS;
} else {
if (comp->llc_flags &
~(LCME_FL_EXTENSION | LCME_FL_PREF_RW |
LCME_FL_NOCOMPR | LCME_FL_PARITY |
LCME_FL_IS_LINK_ID)) {
args->lsa_rc = LSE_FLAGS;
}
}
}
if (args->lsa_rc)
goto out_err;
/* DoM sanity checks */
if (!(comp->llc_pattern & LLAPI_LAYOUT_INVALID) &&
(comp->llc_pattern & (LLAPI_LAYOUT_MDT | LOV_PATTERN_MDT))) {
/* DoM components can't be extension components */
if (comp->llc_flags & LCME_FL_EXTENSION) {
args->lsa_rc = LSE_DOM_EXTENSION;
goto out_err;
}
/* DoM components cannot be followed by an extension comp */
if (next && (next->llc_flags & LCME_FL_EXTENSION)) {
args->lsa_rc = LSE_DOM_EXTENSION_FOLLOWING;
goto out_err;
}
/* DoM should be the first component in a mirror */
if (!first_comp) {
args->lsa_rc = LSE_DOM_FIRST;
errno = EINVAL;
goto out_err;
}
}
if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN ||
comp->llc_pattern == LOV_PATTERN_FOREIGN) {
/* FOREING/HSM components can't be extension components */
if (comp->llc_flags & LCME_FL_EXTENSION) {
args->lsa_rc = LSE_FOREIGN_EXTENSION;
goto out_err;
}
}
/* Extent sanity checks */
/* Must set previous component extent before adding another */
if (prev && prev->llc_extent.e_start == 0 &&
prev->llc_extent.e_end == 0) {
args->lsa_rc = LSE_SET_COMP_START;
goto out_err;
}
if (!args->lsa_incomplete) {
/* Components followed by extension space (extendable
* components) must be zero length before initialization.
* (Except for first comp, which will be initialized on
* creation). */
if (next && (next->llc_flags & LCME_FL_EXTENSION) &&
!first_comp && !(comp->llc_flags & LCME_FL_INIT) &&
comp->llc_extent.e_start != comp->llc_extent.e_end) {
args->lsa_rc = LSE_NOT_ZERO_LENGTH_EXTENDABLE;
goto out_err;
}
/* End must come after end of previous comp */
if (prev && comp->llc_extent.e_end < prev->llc_extent.e_end) {
args->lsa_rc = LSE_END_NOT_GREATER;
goto out_err;
}
/* Components not followed by ext space must have length > 0. */
if (comp->llc_extent.e_start == comp->llc_extent.e_end &&
(next == NULL || !(next->llc_flags & LCME_FL_EXTENSION))) {
args->lsa_rc = LSE_ZERO_LENGTH_NORMAL;
goto out_err;
}
/* The component end must be aligned by the stripe size */
if ((comp->llc_flags & LCME_FL_EXTENSION) &&
(prev->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
if (comp->llc_extent.e_end != LUSTRE_EOF &&
comp->llc_extent.e_end % prev->llc_stripe_size) {
args->lsa_rc = LSE_ALIGN_END;
goto out_err;
}
if ((comp->llc_stripe_size * SEL_UNIT_SIZE) %
prev->llc_stripe_size) {
args->lsa_rc = LSE_ALIGN_EXT;
goto out_err;
}
} else if (!(comp->llc_flags & LCME_FL_EXTENSION) &&
(comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
if (comp->llc_extent.e_end != LUSTRE_EOF &&
comp->llc_extent.e_end !=
comp->llc_extent.e_start &&
comp->llc_extent.e_end % comp->llc_stripe_size) {
args->lsa_rc = LSE_ALIGN_END;
goto out_err;
}
}
}
/* Components must have start == prev->end */
if (prev && comp->llc_extent.e_start != 0 &&
comp->llc_extent.e_start != prev->llc_extent.e_end) {
args->lsa_rc = LSE_NOT_ADJACENT_PREV;
goto out_err;
}
/* Components must have start <= end */
if (comp->llc_extent.e_start > comp->llc_extent.e_end) {
args->lsa_rc = LSE_START_GT_END;
goto out_err;
}
/* Any component should not have LCME_FL_IS_LINK_ID flag and
* llc_ondisk flag set since the former flag is transient.
*/
if (comp->llc_ondisk && (comp->llc_flags & LCME_FL_IS_LINK_ID)) {
args->lsa_rc = LSE_FLAGS;
goto out_err;
}
/* EC parity component specific validation */
if (comp->llc_flags & LCME_FL_PARITY) {
struct llapi_layout_comp *data_comp = NULL;
struct llapi_layout_comp *search_comp;
bool is_link_id = comp->llc_flags & LCME_FL_IS_LINK_ID;
/*
* Skip EC validation for components that are already on disk.
* They were validated when created, and we may not have all
* the necessary information (like stripe counts) when just
* modifying component flags.
*/
if (comp->llc_ondisk)
goto skip_ec_validation;
list_for_each_entry(search_comp, &layout->llot_comp_list,
llc_list) {
__u16 search_comp_mirror_link_id =
is_link_id ? search_comp->llc_mirror_link_id :
search_comp->llc_mirror_id;
__u16 comp_mirror_link_id =
is_link_id ? comp->llc_mirror_link_id :
comp->llc_mirror_id;
/* Skip parity components */
if (search_comp->llc_flags & LCME_FL_PARITY)
continue;
/* Comp should link to data comp to protect */
if (comp->llc_mirror_link_id !=
search_comp_mirror_link_id)
continue;
/* Check if extents match */
if (search_comp->llc_extent.e_start !=
comp->llc_extent.e_start ||
search_comp->llc_extent.e_end !=
comp->llc_extent.e_end)
continue;
/* Data comp and parity comp should have same flag */
if ((search_comp->llc_flags & LCME_FL_IS_LINK_ID) !=
(comp->llc_flags & LCME_FL_IS_LINK_ID)) {
args->lsa_rc = LSE_EC_PARAM;
goto out_err;
}
/* Data comp should link to parity component */
if (search_comp->llc_mirror_link_id !=
comp_mirror_link_id) {
args->lsa_rc = LSE_EC_DATA_COMP_UNSET_LINK_ID;
goto out_err;
}
/* Parity comp should not have multiple data comps */
if (data_comp) {
args->lsa_rc = LSE_EC_DUP;
goto out_err;
}
data_comp = search_comp;
}
if (!data_comp) {
args->lsa_rc = LSE_EC_MATCH_DATA;
goto out_err;
}
/* EC parameter validation */
if (layout_ec_verify_stripes(data_comp->llc_stripe_count,
comp->llc_dstripe_count,
comp->llc_cstripe_count)) {
args->lsa_rc = LSE_EC_PARAM;
goto out_err;
}
/*
* EC/parity components must have the same stripe size as
* their matching data components.
*/
if (comp->llc_stripe_size != data_comp->llc_stripe_size) {
args->lsa_rc = LSE_EC_PARAM;
goto out_err;
}
/*
* Check that the protected data mirror is complete, i.e., the
* extents covers [0, EOF], and all its data components are
* protected by parity.
*/
uint64_t min_data_start = LUSTRE_EOF;
uint64_t max_data_end = 0;
uint16_t data_mirror_id = data_comp->llc_mirror_id;
list_for_each_entry(search_comp, &layout->llot_comp_list,
llc_list) {
if (search_comp->llc_flags & LCME_FL_PARITY)
continue;
if (search_comp->llc_mirror_id != data_mirror_id)
continue;
/* Check extent coverage is complete */
if (search_comp->llc_extent.e_start < min_data_start)
min_data_start =
search_comp->llc_extent.e_start;
if (search_comp->llc_extent.e_end > max_data_end)
max_data_end = search_comp->llc_extent.e_end;
/* Every data component in the mirror must be protected
* by a parity component
*/
if (search_comp->llc_mirror_link_id ==
LLAPI_MIRROR_LINK_NONE) {
args->lsa_rc = LSE_EC_UNPROTECTED_DATA;
goto out_err;
}
}
/* Data mirror must be complete: extents cover [0, EOF] */
if (min_data_start != 0 || max_data_end != LUSTRE_EOF) {
args->lsa_rc = LSE_EC_PARAM;
goto out_err;
}
skip_ec_validation:
/* Continue with other validation checks */
;
}
/*
* Detect mixed parity mirrors: once we see a parity component,
* all subsequent components in that mirror must be parity until
* we hit a new mirror boundary (e_start == 0).
*/
if (comp->llc_flags & LCME_FL_PARITY) {
/* Entering or continuing parity mode */
if (!args->lsa_in_parity) {
/* First parity component in this mirror */
if (comp->llc_extent.e_start != 0) {
/* Parity can only start at mirror boundary */
args->lsa_rc = LSE_EC_MIXED_MIRROR;
goto out_err;
}
args->lsa_in_parity = true;
}
args->lsa_last_parity_end = comp->llc_extent.e_end;
} else {
/* Non-parity component */
if (args->lsa_in_parity) {
/*
* Switching from parity to non-parity within a
* mirror is not allowed. Non-parity after parity
* must start a new mirror (e_start == 0).
*/
if (comp->llc_extent.e_start != 0) {
args->lsa_rc = LSE_EC_MIXED_MIRROR;
goto out_err;
}
/* New mirror started, exit parity mode */
args->lsa_in_parity = false;
}
}
return LLAPI_LAYOUT_ITER_CONT;
out_err:
errno = errno ? errno : EINVAL;
return LLAPI_LAYOUT_ITER_STOP;
}
/* Print explanation of layout error */
void llapi_layout_sanity_perror(int error)
{
if (error >= LSE_LAST || error < 0)
llapi_err_noerrno(LLAPI_MSG_ERROR,
"Invalid layout, unrecognized error: %d\n",
error);
else
llapi_err_noerrno(LLAPI_MSG_ERROR, "Invalid layout: %s\n",
llapi_layout_strerror[error]);
}
/**
* llapi_layout_sanity() - Enforce sanity checks
* @layout: component layout list.
* @incomplete: if layout is complete or not - some checks can only be done on
* complete layouts.
* @flr: set when this is called from FLR mirror create
*
* Walk a layout and enforce sanity checks that apply to > 1 component
*
* The core idea here is that of sanity checking individual tokens vs semantic
* checking.
* We cannot check everything at the individual component level ('token'),
* instead we must check whether or not the full layout has a valid meaning.
*
* An example of a component level check is "is stripe size valid?". That is
* handled when setting stripe size.
*
* An example of a layout level check is "are the extents of these components
* valid when adjacent to one another", or "can we set these flags on adjacent
* components"?
*
* Return:
* * %0 on success
* * %negative on failure (see llapi_layout_sanity_perror)
*/
int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr)
{
return llapi_layout_v2_sanity(layout, incomplete, flr, NULL);
}
/**
* llapi_layout_v2_sanity() - Do pool name checking
* @layout: component layout list.
* @incomplete: if layout is complete or not - some checks can
* only be done on complete layouts.
* @flr: set when this is called from FLR mirror create
* @fsname: filesystem name is used to check pool name, if
* NULL no pool name check is performed
*
* This function has been introduced to do pool name checking
* on top of llapi_layout_sanity, the file name passed in this
* function is used later to verify if pool exist. The older version
* of the sanity function is passing NULL for the filename
*
* Return:
* * %0 on success
* * %negative on failure (see llapi_layout_sanity_perror)
*/
int llapi_layout_v2_sanity(struct llapi_layout *layout,
bool incomplete, bool flr, char *fsname)
{
struct llapi_layout_sanity_args args = { 0 };
struct llapi_layout_comp *curr;
int rc = 0;
if (!layout)
return 0;
curr = layout->llot_cur_comp;
if (!curr)
return 0;
/* Set up args */
args.lsa_rc = 0;
args.lsa_flr = flr;
args.lsa_incomplete = incomplete;
args.fsname = fsname;
args.lsa_mirror_count = 0; /* tracks actual mirror count for each cb */
args.lsa_in_parity = false;
args.lsa_last_parity_end = 0;
/* When we modify an existing layout, this tells us if it's FLR */
if (curr->llc_mirror_id > 0)
args.lsa_flr = true;
errno = 0;
rc = llapi_layout_comp_iterate(layout,
llapi_layout_sanity_cb,
&args);
if (errno == ENOENT)
errno = 0;
if (rc != LLAPI_LAYOUT_ITER_CONT)
rc = args.lsa_rc;
/* Verify mirror count is valid and matches component structure */
if (rc == 0 && layout->llot_is_composite) {
if (!llapi_layout_mirror_count_is_valid(args.lsa_mirror_count))
rc = LSE_MIRROR_COUNT_INVALID;
else if (args.lsa_mirror_count != layout->llot_mirror_count)
rc = LSE_MIRROR_COUNT_MISMATCH;
}
layout->llot_cur_comp = curr;
return rc;
}
int llapi_layout_dom_size(struct llapi_layout *layout, uint64_t *size)
{
uint64_t pattern, start;
int rc;
if (!layout || !llapi_layout_is_composite(layout)) {
*size = 0;
return 0;
}
rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
if (rc)
return -errno;
rc = llapi_layout_pattern_get(layout, &pattern);
if (rc)
return -errno;
if ((pattern & LLAPI_LAYOUT_INVALID) ||
!(pattern & (LOV_PATTERN_MDT | LLAPI_LAYOUT_MDT))) {
*size = 0;
return 0;
}
rc = llapi_layout_comp_extent_get(layout, &start, size);
if (rc)
return -errno;
if (start)
return -ERANGE;
return 0;
}
int lov_comp_md_size(struct lov_comp_md_v1 *lcm)
{
if (lcm->lcm_magic == LOV_MAGIC_V1 || lcm->lcm_magic == LOV_MAGIC_V3) {
struct lov_user_md *lum = (void *)lcm;
return lov_user_md_size(lum->lmm_stripe_count, lum->lmm_magic);
}
if (lcm->lcm_magic == LOV_MAGIC_FOREIGN) {
struct lov_foreign_md *lfm = (void *)lcm;
return lfm->lfm_length;
}
if (lcm->lcm_magic != LOV_MAGIC_COMP_V1)
return -EOPNOTSUPP;
return lcm->lcm_size;
}
int llapi_get_lum_file_fd(int dir_fd, const char *fname, __u64 *valid,
lstatx_t *statx, struct lov_user_md *lum,
size_t lumsize)
{
struct lov_user_mds_data *lmd;
char buf[65536 + offsetof(typeof(*lmd), lmd_lmm)];
int parent_fd = -1;
int rc;
if (lum && lumsize < sizeof(*lum))
return -EINVAL;
/* If a file name is provided, it is relative to the parent directory */
if (fname) {
parent_fd = dir_fd;
dir_fd = -1;
}
lmd = (struct lov_user_mds_data *)buf;
rc = get_lmd_info_fd(fname, parent_fd, dir_fd, buf, sizeof(buf),
GET_LMD_INFO);
if (rc)
return rc;
if (valid)
*valid = lmd->lmd_flags;
if (statx)
memcpy(statx, &lmd->lmd_stx, sizeof(*statx));
if (lum) {
if (lmd->lmd_lmmsize > lumsize)
return -EOVERFLOW;
memcpy(lum, &lmd->lmd_lmm, lmd->lmd_lmmsize);
}
return 0;
}
int llapi_get_lum_dir_fd(int dir_fd, __u64 *valid, lstatx_t *statx,
struct lov_user_md *lum, size_t lumsize)
{
return llapi_get_lum_file_fd(dir_fd, NULL, valid, statx, lum, lumsize);
}
int llapi_get_lum_file(const char *path, __u64 *valid, lstatx_t *statx,
struct lov_user_md *lum, size_t lumsize)
{
char parent[PATH_MAX];
const char *fname;
const char *tmp;
int offset;
int dir_fd;
int rc;
tmp = strrchr(path, '/');
if (!tmp) {
strncpy(parent, ".", sizeof(parent) - 1);
offset = -1;
} else {
strncpy(parent, path, tmp - path);
offset = tmp - path - 1;
parent[tmp - path] = 0;
}
fname = path;
if (offset >= 0)
fname += offset + 2;
dir_fd = open(parent, O_RDONLY);
if (dir_fd < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
return rc;
}
rc = llapi_get_lum_file_fd(dir_fd, fname, valid, statx, lum, lumsize);
close(dir_fd);
return rc;
}
int llapi_get_lum_dir(const char *path, __u64 *valid, lstatx_t *statx,
struct lov_user_md *lum, size_t lumsize)
{
int dir_fd;
int rc;
dir_fd = open(path, O_RDONLY);
if (dir_fd < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
return rc;
}
rc = llapi_get_lum_dir_fd(dir_fd, valid, statx, lum, lumsize);
close(dir_fd);
return rc;
}