Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/client/dfs/dfs_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,9 @@ file_stat_by_oid(dfs_t *dfs, daos_obj_id_t head_oid, daos_obj_id_t tail_oid, boo
int
file_truncate_zero(dfs_obj_t *obj, daos_handle_t th);
int
dfs_default_file_pl(dfs_t *dfs, daos_size_t chunk_size, daos_oclass_id_t *head_cid,
daos_oclass_id_t *tail_cid, daos_size_t *split_off, bool *has_tail);
int
get_num_entries(daos_handle_t oh, daos_handle_t th, uint32_t *nr, bool check_empty);
int
update_stbuf_times(struct dfs_entry entry, daos_epoch_t max_epoch, struct stat *stbuf,
Expand Down
31 changes: 31 additions & 0 deletions src/client/dfs/mnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,8 @@ dfs_query(dfs_t *dfs, dfs_attr_t *attr)
return EINVAL;

memcpy(attr, &dfs->attr, sizeof(dfs_attr_t));
/* Progressive layout is only reported for the default file class; clear until confirmed. */
attr->da_file_pl_nr = 0;

if (!dfs->attr.da_dir_oclass_id) {
rc = daos_obj_get_oclass(dfs->coh, DAOS_OT_MULTI_HASHED, 0, 0,
Expand All @@ -985,13 +987,42 @@ dfs_query(dfs_t *dfs, dfs_attr_t *attr)
}
}

/*
* A non-zero da_file_oclass_id means the container has an explicit default file class, so
* the default layout is NOT progressive. Only when it is unset (0) do we resolve the
* default byte-array class and the progressive-layout head/tail segment(s) it would
* produce: da_file_oclass_id becomes the compact head class and da_file_pl_segs[] the wider
* tail segment(s); da_file_pl_nr stays 0 when PL does not apply.
*/
if (!dfs->attr.da_file_oclass_id) {
daos_oclass_id_t head_cid = OC_UNKNOWN;
daos_oclass_id_t tail_cid = OC_UNKNOWN;
daos_size_t split_off = 0;
daos_size_t chunk_size;
bool has_tail = false;

rc = daos_obj_get_oclass(dfs->coh, DAOS_OT_ARRAY_BYTE, 0, 0,
&attr->da_file_oclass_id);
if (rc) {
D_ERROR("daos_obj_get_oclass() failed " DF_RC "\n", DP_RC(rc));
return daos_der2errno(rc);
}

chunk_size =
dfs->attr.da_chunk_size ? dfs->attr.da_chunk_size : DFS_DEFAULT_CHUNK_SIZE;
rc = dfs_default_file_pl(dfs, chunk_size, &head_cid, &tail_cid, &split_off,
&has_tail);
if (rc) {
D_ERROR("failed to resolve default file layout: %d\n", rc);
return rc;
}
if (has_tail) {
attr->da_file_oclass_id = head_cid;
attr->da_file_pl_nr = 1;
attr->da_file_pl_segs[0].pls_oclass_id = tail_cid;
attr->da_file_pl_segs[0].pls_split_off = split_off;
attr->da_file_pl_segs[0].pls_oid = DAOS_OBJ_NIL;
}
}

return 0;
Expand Down
88 changes: 67 additions & 21 deletions src/client/dfs/obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ file_split_off(uint32_t target_nr, uint64_t total_scm, uint64_t total_nvme,

/* Resolve file head/tail oclasses, applying PL only when no explicit file class was selected. */
static int
file_oclasses(dfs_t *dfs, dfs_obj_t *parent, daos_oclass_id_t cid, daos_size_t chunk_size,
daos_oclass_id_t *head_cid, daos_oclass_id_t *tail_cid, daos_size_t *split_off,
bool *has_tail)
file_oclasses(dfs_t *dfs, daos_oclass_id_t parent_oclass, daos_oclass_id_t cid,
daos_size_t chunk_size, daos_oclass_id_t *head_cid, daos_oclass_id_t *tail_cid,
daos_size_t *split_off, bool *has_tail)
{
struct daos_oclass_attr *tail_attr;
daos_size_t local_split_off = 0;
Expand All @@ -168,10 +168,10 @@ file_oclasses(dfs_t *dfs, dfs_obj_t *parent, daos_oclass_id_t cid, daos_size_t c
if (cid == 0) {
/* Respect explicit file-class choices before considering progressive layout
* defaults. */
if (parent->d.oclass == 0)
if (parent_oclass == 0)
cid = dfs->attr.da_file_oclass_id;
else
cid = parent->d.oclass;
cid = parent_oclass;
}

*head_cid = cid;
Expand Down Expand Up @@ -236,6 +236,22 @@ file_oclasses(dfs_t *dfs, dfs_obj_t *parent, daos_oclass_id_t cid, daos_size_t c
return 0;
}

/*
* Report the head/tail object classes and split offset a default-class regular file would receive
* under progressive layout in this container (no explicit or parent class). Used by dfs_query() to
* expose the effective default layout. On return \a has_tail is false (and \a tail_cid/\a split_off
* are 0) when progressive layout does not apply to default files.
*/
int
dfs_default_file_pl(dfs_t *dfs, daos_size_t chunk_size, daos_oclass_id_t *head_cid,
daos_oclass_id_t *tail_cid, daos_size_t *split_off, bool *has_tail)
{
if (dfs == NULL)
return EINVAL;

return file_oclasses(dfs, 0, 0, chunk_size, head_cid, tail_cid, split_off, has_tail);
}

static int
check_access(uid_t c_uid, gid_t c_gid, uid_t uid, gid_t gid, mode_t mode, int mask)
{
Expand Down Expand Up @@ -293,14 +309,20 @@ dfs_obj_get_info(dfs_t *dfs, dfs_obj_t *obj, dfs_obj_info_t *info)
return EINVAL;

info->doi_oid = obj->oid;
info->doi_tail_oid = DAOS_OBJ_NIL;
info->doi_split_off = 0;
info->doi_pl_nr = 0;

switch (obj->mode & S_IFMT) {
case S_IFDIR: {
/** the oclass of the directory object itself */
info->doi_oclass_id = daos_obj_id2class(obj->oid);

if (obj->d.chunk_size)
info->doi_chunk_size = obj->d.chunk_size;
else if (dfs->attr.da_chunk_size)
info->doi_chunk_size = dfs->attr.da_chunk_size;
else
info->doi_chunk_size = DFS_DEFAULT_CHUNK_SIZE;

/** what is the default oclass files and dirs will be created with in this dir */
if (obj->d.oclass) {
/** if parent oclass is EC, dir would be chosen as container default */
Expand Down Expand Up @@ -330,23 +352,45 @@ dfs_obj_get_info(dfs_t *dfs, dfs_obj_t *obj, dfs_obj_info_t *info)
return daos_der2errno(rc);
}

if (dfs->attr.da_file_oclass_id)
if (dfs->attr.da_file_oclass_id) {
info->doi_file_oclass_id = dfs->attr.da_file_oclass_id;
else
} else {
daos_oclass_id_t pl_head = 0;
daos_oclass_id_t pl_tail = 0;
daos_size_t pl_split = 0;
bool pl_has_tail = false;

/** container uses the DAOS-default file class */
rc = daos_obj_get_oclass(dfs->coh, DAOS_OT_ARRAY_BYTE, 0, 0,
&info->doi_file_oclass_id);
if (rc) {
D_ERROR("daos_obj_get_oclass() failed " DF_RC "\n", DP_RC(rc));
return daos_der2errno(rc);
if (rc) {
D_ERROR("daos_obj_get_oclass() failed " DF_RC "\n",
DP_RC(rc));
return daos_der2errno(rc);
}

/*
* Files created here use the default class, so they may be
* progressive: report the head/tail classes and split offset a file
* created in this directory would receive. doi_file_oclass_id
* becomes the compact head class and doi_pl_segs[] the wider tail
* segment(s); left as no PL when the pool is too small or the
* layout predates PL.
*/
rc = file_oclasses(dfs, 0, 0, info->doi_chunk_size, &pl_head,
&pl_tail, &pl_split, &pl_has_tail);
if (rc)
return rc;
if (pl_has_tail) {
info->doi_file_oclass_id = pl_head;
info->doi_pl_nr = 1;
info->doi_pl_segs[0].pls_oclass_id = pl_tail;
info->doi_pl_segs[0].pls_split_off = pl_split;
info->doi_pl_segs[0].pls_oid = DAOS_OBJ_NIL;
}
}
}

if (obj->d.chunk_size)
info->doi_chunk_size = obj->d.chunk_size;
else if (dfs->attr.da_chunk_size)
info->doi_chunk_size = dfs->attr.da_chunk_size;
else
info->doi_chunk_size = DFS_DEFAULT_CHUNK_SIZE;
break;
}
case S_IFREG: {
Expand All @@ -358,8 +402,10 @@ dfs_obj_get_info(dfs_t *dfs, dfs_obj_t *obj, dfs_obj_info_t *info)

info->doi_oclass_id = daos_obj_id2class(obj->oid);
if (obj->f.has_tail) {
info->doi_tail_oid = obj->f.tail_oid;
info->doi_split_off = obj->f.split_off;
info->doi_pl_nr = 1;
info->doi_pl_segs[0].pls_oclass_id = daos_obj_id2class(obj->f.tail_oid);
info->doi_pl_segs[0].pls_split_off = obj->f.split_off;
info->doi_pl_segs[0].pls_oid = obj->f.tail_oid;
}
break;
}
Expand Down Expand Up @@ -403,7 +449,7 @@ open_file(dfs_t *dfs, dfs_obj_t *parent, int flags, daos_oclass_id_t cid, daos_s
chunk_size = parent->d.chunk_size;
}

rc = file_oclasses(dfs, parent, cid, chunk_size, &cid, &tail_cid,
rc = file_oclasses(dfs, parent->d.oclass, cid, chunk_size, &cid, &tail_cid,
&file->f.split_off, &file->f.has_tail);
if (rc != 0)
return rc;
Expand Down
143 changes: 111 additions & 32 deletions src/control/cmd/daos/filesystem.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// (C) Copyright 2021-2024 Intel Corporation.
// (C) Copyright 2026 Hewlett Packard Enterprise Development LP
// (C) Copyright 2025 Google LLC
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
Expand Down Expand Up @@ -286,23 +287,63 @@ func (cmd *fsGetAttrCmd) Execute(_ []string) error {
var fileoclassName [16]C.char
var oid C.daos_obj_id_t = attrs.doi_oid
var oidStr string = fmt.Sprintf("%d.%d", oid.hi, oid.lo)
if C.mode_is_dir(cmode) {
isDir := bool(C.mode_is_dir(cmode))

// Collect the progressive-layout tail segment(s) beyond the head (empty when not PL). For a
// file each carries its own tail OID; for a directory template the segment OIDs are nil.
type plTail struct {
oidStr string
oclass string
splitOff uint64
}
var tails []plTail
for i := 0; i < int(attrs.doi_pl_nr); i++ {
seg := attrs.doi_pl_segs[i]
var segClass [16]C.char
C.daos_oclass_id2name(seg.pls_oclass_id, &segClass[0])
t := plTail{
oclass: C.GoString(&segClass[0]),
splitOff: uint64(seg.pls_split_off),
}
if seg.pls_oid.hi != 0 || seg.pls_oid.lo != 0 {
t.oidStr = fmt.Sprintf("%d.%d", seg.pls_oid.hi, seg.pls_oid.lo)
}
tails = append(tails, t)
}
isPL := len(tails) > 0

if isDir {
C.daos_oclass_id2name(attrs.doi_dir_oclass_id, &diroclassName[0])
C.daos_oclass_id2name(attrs.doi_file_oclass_id, &fileoclassName[0])
}

if cmd.JSONOutputEnabled() {
if C.mode_is_dir(cmode) {
if isDir {
type jsonTail struct {
ObjClass string `json:"oclass"`
SplitOff uint64 `json:"split_off"`
}
var jsonTails []jsonTail
for _, t := range tails {
jsonTails = append(jsonTails, jsonTail{ObjClass: t.oclass, SplitOff: t.splitOff})
}
dirAttr := struct {
DirObjClass string `json:"dir_oclass"`
FileObjClass string `json:"file_oclass"`
FilePLTails []jsonTail `json:"file_pl_tails,omitempty"`
ChunkSize uint64 `json:"chunk_size"`
}{
FileObjClass: C.GoString(&diroclassName[0]),
DirObjClass: C.GoString(&fileoclassName[0]),
FilePLTails: jsonTails,
ChunkSize: uint64(attrs.doi_chunk_size),
}
jsonAttrs := struct {
ObjAttr struct {
OID string `json:"oid"`
ObjClass string `json:"oclass"`
} `json:"object"`
DirAttr struct {
DirObjClass string `json:"dir_oclass"`
FileObjClass string `json:"file_oclass"`
ChunkSize uint64 `json:"chunk_size"`
} `json:"directory"`
DirAttr interface{} `json:"directory"`
}{
ObjAttr: struct {
OID string `json:"oid"`
Expand All @@ -311,38 +352,76 @@ func (cmd *fsGetAttrCmd) Execute(_ []string) error {
OID: oidStr,
ObjClass: C.GoString(&oclassName[0]),
},
DirAttr: struct {
DirObjClass string `json:"dir_oclass"`
FileObjClass string `json:"file_oclass"`
ChunkSize uint64 `json:"chunk_size"`
}{
FileObjClass: C.GoString(&diroclassName[0]),
DirObjClass: C.GoString(&fileoclassName[0]),
ChunkSize: uint64(attrs.doi_chunk_size),
},
}
return cmd.OutputJSON(jsonAttrs, nil)
} else {
jsonAttrs := &struct {
OID string `json:"oid"`
ObjClass string `json:"oclass"`
ChunkSize uint64 `json:"chunk_size"`
}{
OID: oidStr,
ObjClass: C.GoString(&oclassName[0]),
ChunkSize: uint64(attrs.doi_chunk_size),
DirAttr: dirAttr,
}
return cmd.OutputJSON(jsonAttrs, nil)
}
type jsonTail struct {
OID string `json:"oid,omitempty"`
ObjClass string `json:"oclass"`
SplitOff uint64 `json:"split_off"`
}
var jsonTails []jsonTail
for _, t := range tails {
jsonTails = append(jsonTails, jsonTail{OID: t.oidStr, ObjClass: t.oclass, SplitOff: t.splitOff})
}
jsonAttrs := &struct {
OID string `json:"oid"`
ObjClass string `json:"oclass"`
ChunkSize uint64 `json:"chunk_size"`
Tails []jsonTail `json:"tails,omitempty"`
}{
OID: oidStr,
ObjClass: C.GoString(&oclassName[0]),
ChunkSize: uint64(attrs.doi_chunk_size),
Tails: jsonTails,
}
return cmd.OutputJSON(jsonAttrs, nil)
}

cmd.Infof("OID = %s", oidStr)
cmd.Infof("Object Class = %s", C.GoString(&oclassName[0]))
if C.mode_is_dir(cmode) {
if isDir {
cmd.Infof("OID = %s", oidStr)
cmd.Infof("Object Class = %s", C.GoString(&oclassName[0]))
cmd.Infof("Directory Creation Object Class = %s", C.GoString(&diroclassName[0]))
cmd.Infof("File Creation Object Class = %s", C.GoString(&fileoclassName[0]))
cmd.Infof("File Creation Chunk Size = %d", attrs.doi_chunk_size)
if isPL {
cmd.Infof("File Creation Head Object Class = %s", C.GoString(&fileoclassName[0]))
for i, t := range tails {
if len(tails) > 1 {
cmd.Infof("File Creation Tail %d Object Class = %s", i+1, t.oclass)
cmd.Infof("File Creation Split Offset %d = %d", i+1, t.splitOff)
} else {
cmd.Infof("File Creation Tail Object Class = %s", t.oclass)
cmd.Infof("File Creation Split Offset = %d", t.splitOff)
}
}
cmd.Infof("File Creation Chunk Size = %d", attrs.doi_chunk_size)
} else {
cmd.Infof("File Creation Object Class = %s", C.GoString(&fileoclassName[0]))
cmd.Infof("File Creation Chunk Size = %d", attrs.doi_chunk_size)
}
} else if isPL {
cmd.Infof("Head OID = %s", oidStr)
cmd.Infof("Head Object Class = %s", C.GoString(&oclassName[0]))
for i, t := range tails {
if len(tails) > 1 {
cmd.Infof("Tail %d OID = %s", i+1, t.oidStr)
cmd.Infof("Tail %d Object Class = %s", i+1, t.oclass)
} else {
cmd.Infof("Tail OID = %s", t.oidStr)
cmd.Infof("Tail Object Class = %s", t.oclass)
}
}
cmd.Infof("Object Chunk Size = %d", attrs.doi_chunk_size)
for i, t := range tails {
if len(tails) > 1 {
cmd.Infof("Split Offset %d = %d", i+1, t.splitOff)
} else {
cmd.Infof("Split Offset = %d", t.splitOff)
}
}
} else {
cmd.Infof("OID = %s", oidStr)
cmd.Infof("Object Class = %s", C.GoString(&oclassName[0]))
cmd.Infof("Object Chunk Size = %d", attrs.doi_chunk_size)
}
return nil
Expand Down
Loading
Loading