mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
libxl: memory size in kb requires 64 bit variable
libxl_set_memory_target() and several other interface functions of libxl use a 32 bit sized parameter for a memory size value in kBytes. This limits the maximum size to be passed in such a parameter depending on signedness of the parameter to 2TB or 4TB. Correct this by using 64 bit types. Signed-off-by: Juergen Gross <jgross@suse.com> Reviewed-by: Dario Faggioli <dario.faggioli@citrix.com> Acked-by: Wei Liu <wei.liu2@citrix.com>
This commit is contained in:
1 parent
f3356e1d4d
commit
57f8b13c72
6 files changed
+126
-51
No files matched your search
+86
-36
@@ -3993,11 +3993,11 @@ out:
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t max_memkb)
|
||||
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
|
||||
{
|
||||
GC_INIT(ctx);
|
||||
char *mem, *endptr;
|
||||
uint32_t memorykb;
|
||||
uint64_t memorykb;
|
||||
char *dompath = libxl__xs_get_dompath(gc, domid);
|
||||
int rc = 1;
|
||||
libxl__domain_userdata_lock *lock = NULL;
|
||||
@@ -4015,7 +4015,7 @@ int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t max_memkb)
|
||||
LOGE(ERROR, "cannot get memory info from %s/memory/target", dompath);
|
||||
goto out;
|
||||
}
|
||||
memorykb = strtoul(mem, &endptr, 10);
|
||||
memorykb = strtoull(mem, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid memory %s from %s/memory/target\n", mem, dompath);
|
||||
goto out;
|
||||
@@ -4028,7 +4028,8 @@ int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t max_memkb)
|
||||
}
|
||||
rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + LIBXL_MAXMEM_CONSTANT);
|
||||
if (rc != 0) {
|
||||
LOGE(ERROR, "xc_domain_setmaxmem domid=%d memkb=%d failed ""rc=%d\n",
|
||||
LOGE(ERROR,
|
||||
"xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
|
||||
domid, max_memkb + LIBXL_MAXMEM_CONSTANT, rc);
|
||||
goto out;
|
||||
}
|
||||
@@ -4041,8 +4042,8 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint32_t *target_memkb,
|
||||
uint32_t *max_memkb)
|
||||
static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
|
||||
uint64_t *max_memkb)
|
||||
{
|
||||
int rc;
|
||||
libxl_dominfo info;
|
||||
@@ -4066,7 +4067,7 @@ retry_transaction:
|
||||
}
|
||||
|
||||
if (target) {
|
||||
*target_memkb = strtoul(target, &endptr, 10);
|
||||
*target_memkb = strtoull(target, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid memory target %s from %s\n", target,
|
||||
target_path);
|
||||
@@ -4076,7 +4077,7 @@ retry_transaction:
|
||||
}
|
||||
|
||||
if (staticmax) {
|
||||
*max_memkb = strtoul(staticmax, &endptr, 10);
|
||||
*max_memkb = strtoull(staticmax, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid memory static-max %s from %s\n", staticmax,
|
||||
max_path);
|
||||
@@ -4096,14 +4097,12 @@ retry_transaction:
|
||||
goto out;
|
||||
|
||||
if (target == NULL) {
|
||||
libxl__xs_printf(gc, t, target_path, "%"PRIu32,
|
||||
(uint32_t) info.current_memkb);
|
||||
*target_memkb = (uint32_t) info.current_memkb;
|
||||
libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
|
||||
*target_memkb = info.current_memkb;
|
||||
}
|
||||
if (staticmax == NULL) {
|
||||
libxl__xs_printf(gc, t, max_path, "%"PRIu32,
|
||||
(uint32_t) info.max_memkb);
|
||||
*max_memkb = (uint32_t) info.max_memkb;
|
||||
libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
|
||||
*max_memkb = info.max_memkb;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
@@ -4121,14 +4120,14 @@ out:
|
||||
}
|
||||
|
||||
int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
|
||||
int32_t target_memkb, int relative, int enforce)
|
||||
int64_t target_memkb, int relative, int enforce)
|
||||
{
|
||||
GC_INIT(ctx);
|
||||
int rc, r, lrc, abort_transaction = 0;
|
||||
uint64_t memorykb;
|
||||
uint32_t videoram = 0;
|
||||
uint32_t current_target_memkb = 0, new_target_memkb = 0;
|
||||
uint32_t current_max_memkb = 0;
|
||||
uint64_t videoram = 0;
|
||||
uint64_t current_target_memkb = 0, new_target_memkb = 0;
|
||||
uint64_t current_max_memkb = 0;
|
||||
char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
|
||||
char *dompath = libxl__xs_get_dompath(gc, domid);
|
||||
xc_domaininfo_t info;
|
||||
@@ -4165,7 +4164,7 @@ retry_transaction:
|
||||
rc = ERROR_FAIL;
|
||||
goto out;
|
||||
} else {
|
||||
current_target_memkb = strtoul(target, &endptr, 10);
|
||||
current_target_memkb = strtoull(target, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid memory target %s from %s/memory/target\n",
|
||||
target, dompath);
|
||||
@@ -4182,7 +4181,7 @@ retry_transaction:
|
||||
rc = ERROR_FAIL;
|
||||
goto out;
|
||||
}
|
||||
memorykb = strtoul(memmax, &endptr, 10);
|
||||
memorykb = strtoull(memmax, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid max memory %s from %s/memory/static-max\n",
|
||||
memmax, dompath);
|
||||
@@ -4212,7 +4211,8 @@ retry_transaction:
|
||||
}
|
||||
|
||||
if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
|
||||
LOG(ERROR, "new target %d for dom0 is below the minimum threshold",
|
||||
LOG(ERROR,
|
||||
"new target %"PRIu64" for dom0 is below the minimum threshold",
|
||||
new_target_memkb);
|
||||
abort_transaction = 1;
|
||||
rc = ERROR_INVAL;
|
||||
@@ -4239,7 +4239,7 @@ retry_transaction:
|
||||
(new_target_memkb + LIBXL_MAXMEM_CONSTANT) / 4, NULL, NULL, NULL);
|
||||
if (r != 0) {
|
||||
LOGE(ERROR,
|
||||
"xc_domain_set_pod_target domid=%d, memkb=%d ""failed rc=%d\n",
|
||||
"xc_domain_set_pod_target domid=%d, memkb=%"PRIu64" failed rc=%d\n",
|
||||
domid,
|
||||
new_target_memkb / 4,
|
||||
r);
|
||||
@@ -4249,13 +4249,13 @@ retry_transaction:
|
||||
}
|
||||
|
||||
libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
|
||||
"%"PRIu32, new_target_memkb);
|
||||
"%"PRIu64, new_target_memkb);
|
||||
|
||||
libxl_dominfo_init(&ptr);
|
||||
xcinfo2xlinfo(ctx, &info, &ptr);
|
||||
uuid = libxl__uuid2string(gc, ptr.uuid);
|
||||
libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
|
||||
"%"PRIu32, new_target_memkb / 1024);
|
||||
"%"PRIu64, new_target_memkb / 1024);
|
||||
libxl_dominfo_dispose(&ptr);
|
||||
|
||||
rc = 0;
|
||||
@@ -4274,13 +4274,13 @@ out_no_transaction:
|
||||
|
||||
/* out_target_memkb and out_max_memkb can be NULL */
|
||||
static int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
|
||||
uint32_t *out_target_memkb,
|
||||
uint32_t *out_max_memkb)
|
||||
uint64_t *out_target_memkb,
|
||||
uint64_t *out_max_memkb)
|
||||
{
|
||||
int rc;
|
||||
char *target = NULL, *static_max = NULL, *endptr = NULL;
|
||||
char *dompath = libxl__xs_get_dompath(gc, domid);
|
||||
uint32_t target_memkb, max_memkb;
|
||||
uint64_t target_memkb, max_memkb;
|
||||
|
||||
target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
|
||||
dompath));
|
||||
@@ -4303,13 +4303,13 @@ static int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
|
||||
dompath);
|
||||
goto out;
|
||||
} else {
|
||||
target_memkb = strtoul(target, &endptr, 10);
|
||||
target_memkb = strtoull(target, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR, "invalid memory target %s from %s/memory/target\n",
|
||||
target, dompath);
|
||||
goto out;
|
||||
}
|
||||
max_memkb = strtoul(static_max, &endptr, 10);
|
||||
max_memkb = strtoull(static_max, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOGE(ERROR,
|
||||
"invalid memory target %s from %s/memory/static-max\n",
|
||||
@@ -4332,8 +4332,28 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
|
||||
uint64_t val64, uint32_t *ptr32)
|
||||
{
|
||||
GC_INIT(ctx);
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
*ptr32 = val64;
|
||||
if (*ptr32 == val64)
|
||||
goto out;
|
||||
|
||||
LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
|
||||
rc = ERROR_FAIL;
|
||||
|
||||
out:
|
||||
GC_FREE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
|
||||
uint32_t *out_target)
|
||||
uint64_t *out_target)
|
||||
{
|
||||
GC_INIT(ctx);
|
||||
int rc;
|
||||
@@ -4344,9 +4364,19 @@ int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int libxl_get_memory_target_0x040700(
|
||||
libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
|
||||
{
|
||||
uint64_t my_out_target;
|
||||
int rc;
|
||||
|
||||
rc = libxl_get_memory_target(ctx, domid, &my_out_target);
|
||||
return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
|
||||
}
|
||||
|
||||
int libxl_domain_need_memory(libxl_ctx *ctx,
|
||||
const libxl_domain_build_info *b_info_in,
|
||||
uint32_t *need_memkb)
|
||||
uint64_t *need_memkb)
|
||||
{
|
||||
GC_INIT(ctx);
|
||||
libxl_domain_build_info b_info[1];
|
||||
@@ -4382,7 +4412,18 @@ out:
|
||||
|
||||
}
|
||||
|
||||
int libxl_get_free_memory(libxl_ctx *ctx, uint32_t *memkb)
|
||||
int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
|
||||
const libxl_domain_build_info *b_info_in,
|
||||
uint32_t *need_memkb)
|
||||
{
|
||||
uint64_t my_need_memkb;
|
||||
int rc;
|
||||
|
||||
rc = libxl_domain_need_memory(ctx, b_info_in, &my_need_memkb);
|
||||
return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
|
||||
}
|
||||
|
||||
int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
|
||||
{
|
||||
int rc = 0;
|
||||
libxl_physinfo info;
|
||||
@@ -4399,8 +4440,17 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint32_t
|
||||
memory_kb, int wait_secs)
|
||||
int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
|
||||
{
|
||||
uint64_t my_memkb;
|
||||
int rc;
|
||||
|
||||
rc = libxl_get_free_memory(ctx, &my_memkb);
|
||||
return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
|
||||
}
|
||||
|
||||
int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
|
||||
uint64_t memory_kb, int wait_secs)
|
||||
{
|
||||
int rc = 0;
|
||||
libxl_physinfo info;
|
||||
@@ -4427,7 +4477,7 @@ out:
|
||||
int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
|
||||
{
|
||||
int rc = 0;
|
||||
uint32_t target_memkb = 0;
|
||||
uint64_t target_memkb = 0;
|
||||
uint64_t current_memkb, prev_memkb;
|
||||
libxl_dominfo info;
|
||||
|
||||
@@ -6643,7 +6693,7 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
|
||||
* "target" and "static-max".
|
||||
*/
|
||||
{
|
||||
uint32_t target_memkb = 0, max_memkb = 0;
|
||||
uint64_t target_memkb = 0, max_memkb = 0;
|
||||
|
||||
/* "target" */
|
||||
rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
|
||||
|
||||
+32
-7
@@ -972,6 +972,17 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
|
||||
*/
|
||||
#define LIBXL_HAVE_BYTEARRAY_UUID 1
|
||||
|
||||
/*
|
||||
* LIBXL_HAVE_MEMKB_64BITS
|
||||
*
|
||||
* If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem()
|
||||
* and libxl_wait_for_free_memory() will take a 64 bit value for the memory
|
||||
* size parameter.
|
||||
* From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and
|
||||
* libxl_get_free_memory() return the memory size in a 64 bit value, too.
|
||||
*/
|
||||
#define LIBXL_HAVE_MEMKB_64BITS 1
|
||||
|
||||
typedef char **libxl_string_list;
|
||||
void libxl_string_list_dispose(libxl_string_list *sl);
|
||||
int libxl_string_list_length(const libxl_string_list *sl);
|
||||
@@ -1375,10 +1386,12 @@ int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
|
||||
const libxl_asyncop_how *ao_how)
|
||||
LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
|
||||
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t target_memkb);
|
||||
int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int32_t target_memkb, int relative, int enforce);
|
||||
int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint32_t *out_target);
|
||||
|
||||
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb);
|
||||
int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce);
|
||||
int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target);
|
||||
int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
|
||||
uint32_t *out_target)
|
||||
LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
|
||||
/*
|
||||
* WARNING
|
||||
@@ -1392,11 +1405,17 @@ int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint32_t *out_target
|
||||
/* how much free memory in the system a domain needs to be built */
|
||||
int libxl_domain_need_memory(libxl_ctx *ctx,
|
||||
const libxl_domain_build_info *b_info_in,
|
||||
uint32_t *need_memkb);
|
||||
uint64_t *need_memkb);
|
||||
int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
|
||||
const libxl_domain_build_info *b_info_in,
|
||||
uint32_t *need_memkb)
|
||||
LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
/* how much free memory is available in the system */
|
||||
int libxl_get_free_memory(libxl_ctx *ctx, uint32_t *memkb);
|
||||
int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
|
||||
int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
|
||||
LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
/* wait for a given amount of memory to be free in the system */
|
||||
int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint32_t memory_kb, int wait_secs);
|
||||
int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs);
|
||||
/*
|
||||
* Wait for the memory target of a domain to be reached. Does not
|
||||
* decrement wait_secs if the domain is making progress toward reaching
|
||||
@@ -1412,6 +1431,12 @@ int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint32_t memory_k
|
||||
*/
|
||||
int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs);
|
||||
|
||||
#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
|
||||
#define libxl_get_memory_target libxl_get_memory_target_0x040700
|
||||
#define libxl_domain_need_memory libxl_domain_need_memory_0x040700
|
||||
#define libxl_get_free_memory libxl_get_free_memory_0x040700
|
||||
#endif
|
||||
|
||||
int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass);
|
||||
int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type);
|
||||
/* libxl_primary_console_exec finds the domid and console number
|
||||
|
||||
@@ -123,7 +123,7 @@ static int numa_place_domain(libxl__gc *gc, uint32_t domid,
|
||||
libxl_bitmap cpupool_nodemap;
|
||||
libxl_cpupoolinfo cpupool_info;
|
||||
int i, cpupool, rc = 0;
|
||||
uint32_t memkb;
|
||||
uint64_t memkb;
|
||||
|
||||
libxl__numa_candidate_init(&candidate);
|
||||
libxl_bitmap_init(&cpupool_nodemap);
|
||||
@@ -178,7 +178,7 @@ static int numa_place_domain(libxl__gc *gc, uint32_t domid,
|
||||
}
|
||||
|
||||
LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and "
|
||||
"%"PRIu32" KB free selected", candidate.nr_nodes,
|
||||
"%"PRIu64" KB free selected", candidate.nr_nodes,
|
||||
candidate.nr_cpus, candidate.free_memkb / 1024);
|
||||
|
||||
out:
|
||||
|
||||
@@ -3937,7 +3937,7 @@ static inline void libxl__ctx_unlock(libxl_ctx *ctx) {
|
||||
typedef struct {
|
||||
int nr_cpus, nr_nodes;
|
||||
int nr_vcpus;
|
||||
uint32_t free_memkb;
|
||||
uint64_t free_memkb;
|
||||
libxl_bitmap nodemap;
|
||||
} libxl__numa_candidate;
|
||||
|
||||
@@ -3977,7 +3977,7 @@ typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1,
|
||||
* it.
|
||||
*/
|
||||
_hidden int libxl__get_numa_candidate(libxl__gc *gc,
|
||||
uint32_t min_free_memkb, int min_cpus,
|
||||
uint64_t min_free_memkb, int min_cpus,
|
||||
int min_nodes, int max_nodes,
|
||||
const libxl_bitmap *suitable_cpumap,
|
||||
libxl__numa_candidate_cmpf numa_cmpf,
|
||||
|
||||
@@ -285,7 +285,7 @@ static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus,
|
||||
* comparison function.
|
||||
*/
|
||||
int libxl__get_numa_candidate(libxl__gc *gc,
|
||||
uint32_t min_free_memkb, int min_cpus,
|
||||
uint64_t min_free_memkb, int min_cpus,
|
||||
int min_nodes, int max_nodes,
|
||||
const libxl_bitmap *suitable_cpumap,
|
||||
libxl__numa_candidate_cmpf numa_cmpf,
|
||||
@@ -436,7 +436,7 @@ int libxl__get_numa_candidate(libxl__gc *gc,
|
||||
for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes);
|
||||
comb_ok;
|
||||
comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) {
|
||||
uint32_t nodes_free_memkb;
|
||||
uint64_t nodes_free_memkb;
|
||||
int nodes_cpus;
|
||||
|
||||
/* Get the nodemap for the combination, only considering
|
||||
@@ -478,7 +478,7 @@ int libxl__get_numa_candidate(libxl__gc *gc,
|
||||
|
||||
LOG(DEBUG, "New best NUMA placement candidate found: "
|
||||
"nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, "
|
||||
"free_memkb=%"PRIu32"", new_cndt.nr_nodes,
|
||||
"free_memkb=%"PRIu64"", new_cndt.nr_nodes,
|
||||
new_cndt.nr_cpus, new_cndt.nr_vcpus,
|
||||
new_cndt.free_memkb / 1024);
|
||||
|
||||
|
||||
@@ -2691,7 +2691,7 @@ static int preserve_domain(uint32_t *r_domid, libxl_event *event,
|
||||
static bool freemem(uint32_t domid, libxl_domain_build_info *b_info)
|
||||
{
|
||||
int rc, retries = 3;
|
||||
uint32_t need_memkb, free_memkb;
|
||||
uint64_t need_memkb, free_memkb;
|
||||
|
||||
if (!autoballoon)
|
||||
return true;
|
||||
|
||||