Whamcloud - gitweb
- OST compatibility stuff
authoralex <alex>
Thu, 14 May 2009 06:27:58 +0000 (06:27 +0000)
committeralex <alex>
Thu, 14 May 2009 06:27:58 +0000 (06:27 +0000)
lustre/osd/Makefile.in
lustre/osd/osd_compat.c [new file with mode: 0644]
lustre/osd/osd_handler.c
lustre/osd/osd_io.c

index 0f6dc11..6769f8a 100644 (file)
@@ -1,5 +1,5 @@
 MODULES := osd
-osd-objs := osd_handler.o osd_oi.o osd_igif.o osd_lproc.o osd_io.o
+osd-objs := osd_handler.o osd_oi.o osd_igif.o osd_lproc.o osd_io.o osd_compat.o
 
 EXTRA_PRE_CFLAGS := -I@LINUX@/fs -I@LDISKFS_DIR@ -I@LDISKFS_DIR@/ldiskfs
 
diff --git a/lustre/osd/osd_compat.c b/lustre/osd/osd_compat.c
new file mode 100644 (file)
index 0000000..819bcba
--- /dev/null
@@ -0,0 +1,693 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2008 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/osd/osd_compat.c
+ *
+ * on-disk compatibility stuff for OST
+ *
+ */
+
+#include <linux/module.h>
+
+/* LUSTRE_VERSION_CODE */
+#include <lustre_ver.h>
+/* prerequisite for linux/xattr.h */
+#include <linux/types.h>
+/* prerequisite for linux/xattr.h */
+#include <linux/fs.h>
+/*
+ * XXX temporary stuff: direct access to ldiskfs/jdb. Interface between osd
+ * and file system is not yet specified.
+ */
+/* handle_t, journal_start(), journal_stop() */
+#include <linux/jbd.h>
+/* LDISKFS_SB() */
+#include <linux/ldiskfs_fs.h>
+#include <linux/ldiskfs_jbd.h>
+
+/*
+ * struct OBD_{ALLOC,FREE}*()
+ * OBD_FAIL_CHECK
+ */
+#include <obd_support.h>
+/* struct ptlrpc_thread */
+#include <lustre_net.h>
+
+/* fid_is_local() */
+#include <lustre_fid.h>
+#include <linux/lustre_iam.h>
+
+#include "osd_internal.h"
+#include <lvfs.h>
+
+struct osd_compat_objid_group {
+        struct semaphore       dir_init_sem; /* protects on-fly initialization */
+        struct osd_inode_id    last_id;      /* file storing last created objid */
+        struct dentry         *groot;        /* O/<group> */
+        struct dentry        **dirs;         /* O/<group>/d0-dXX */
+};
+
+#define MAX_OBJID_GROUP         1024
+
+struct osd_compat_objid {
+        int                             subdir_count;
+        struct dentry                  *root;
+        struct osd_inode_id             last_rcvd_id;
+        struct osd_inode_id             last_group_id;
+        struct osd_compat_objid_group   groups[MAX_OBJID_GROUP];
+};
+
+
+
+static struct super_block *osd_sb(const struct osd_device *dev)
+{
+        return dev->od_mnt->mnt_sb;
+}
+
+static void osd_push_ctxt(const struct osd_device *dev,
+                          struct lvfs_run_ctxt *newctxt,
+                          struct lvfs_run_ctxt *save)
+{
+        OBD_SET_CTXT_MAGIC(newctxt);
+        newctxt->pwdmnt = dev->od_mnt;
+        newctxt->pwd = dev->od_mnt->mnt_root;
+        newctxt->fs = get_ds();
+
+        push_ctxt(save, newctxt, NULL);
+}
+
+/*
+ * directory structure on legacy OST:
+ *
+ * O/<group>/d0-31/<objid>
+ * O/<group>/LAST_ID
+ * last_rcvd
+ * LAST_GROUP
+ * CONFIGS
+ *
+ */
+
+int osd_ost_init(struct osd_device *dev)
+{
+        struct lvfs_run_ctxt  new;
+        struct lvfs_run_ctxt  save;
+        struct dentry        *rootd = osd_sb(dev)->s_root;
+        struct dentry        *d;
+        int                   rc = 0;
+        int                   i; 
+
+        OBD_ALLOC_PTR(dev->od_ost_map);
+        if (dev->od_ost_map == NULL)
+                RETURN(-ENOMEM);
+
+        /* XXX: to be initialized from last_rcvd */
+        dev->od_ost_map->subdir_count = 32;
+        for (i = 0; i < MAX_OBJID_GROUP; i++)
+                sema_init(&dev->od_ost_map->groups[i].dir_init_sem, 1);
+
+        LASSERT(dev->od_fsops);
+
+        osd_push_ctxt(dev, &new, &save);
+       
+        d = simple_mkdir(rootd, dev->od_mnt, "O", 0755, 1);
+        if (IS_ERR(d))
+                GOTO(cleanup, rc = PTR_ERR(d));
+
+        dev->od_ost_map->root = d;
+
+cleanup:
+        pop_ctxt(&save, &new, NULL);
+
+
+        RETURN(rc);
+}
+
+int osd_compat_init(struct osd_device *dev)
+{
+        int rc;
+        ENTRY;
+
+        /* prepare structures for OST */
+        rc = osd_ost_init(dev);
+
+        /* prepare structures for MDS */
+
+        RETURN(rc);
+}
+
+void osd_compat_fini(const struct osd_device *dev)
+{
+        struct osd_compat_objid_group *grp;
+        struct osd_compat_objid       *map = dev->od_ost_map;
+        int                            i, j;
+        ENTRY;
+
+        map = dev->od_ost_map;
+        if (map == NULL)
+                return;
+
+        for (i = 0; i < MAX_OBJID_GROUP; i++) {
+                grp = map->groups + i;
+                if (grp == NULL)
+                        continue;
+
+                if (grp->groot)
+                        dput(grp->groot);
+
+                if (grp->dirs) {
+                        for (j = 0; j < map->subdir_count; j++) {
+                                if (grp->dirs[j])
+                                        dput(grp->dirs[j]);
+                        }
+                        OBD_FREE(grp->dirs,
+                                 sizeof(struct dentry *) * map->subdir_count);
+                }
+        }
+        if (map->root)
+                dput(map->root);
+        OBD_FREE_PTR(dev->od_ost_map);
+
+        EXIT;
+}
+
+int osd_compat_del_entry(struct osd_thread_info *info,
+                         struct osd_device *osd,
+                         struct dentry *dir, char *name,
+                         struct thandle *th)
+{
+       struct ldiskfs_dir_entry_2 *de;
+        struct buffer_head         *bh;
+        struct osd_thandle         *oh;
+        struct dentry              *child;
+        int                         rc;
+        ENTRY;
+
+        oh = container_of(th, struct osd_thandle, ot_super);
+        LASSERT(oh->ot_handle != NULL);
+        LASSERT(oh->ot_handle->h_transaction != NULL);
+
+
+        child = &info->oti_child_dentry;
+        child->d_name.hash = 0;
+        child->d_name.name = name;
+        child->d_name.len = strlen(name);
+        child->d_parent = dir;
+        child->d_inode = NULL;
+
+        LOCK_INODE_MUTEX(dir->d_inode);
+       rc = -ENOENT;
+       bh = ldiskfs_find_entry(child, &de);
+       if (bh) {
+               rc = ldiskfs_delete_entry(oh->ot_handle, dir->d_inode, de, bh);
+               brelse(bh);
+        }
+        UNLOCK_INODE_MUTEX(dir->d_inode);
+
+        RETURN(rc);
+}
+
+int osd_compat_add_entry(struct osd_thread_info *info,
+                         struct osd_device *osd,
+                         struct dentry *dir, char *name,
+                         const struct osd_inode_id *id,
+                         struct thandle *th)
+{
+        struct osd_thandle *oh;
+        struct dentry *child;
+        struct inode *inode;
+        int rc;
+        ENTRY;
+
+        oh = container_of(th, struct osd_thandle, ot_super);
+        LASSERT(oh->ot_handle != NULL);
+        LASSERT(oh->ot_handle->h_transaction != NULL);
+
+        inode = &info->oti_inode;
+        inode->i_sb = osd_sb(osd);
+        inode->i_ino = id->oii_ino;
+        inode->i_generation = id->oii_gen;
+
+        child = &info->oti_child_dentry;
+        child->d_name.hash = 0;
+        child->d_name.name = name;
+        child->d_name.len = strlen(name);
+        child->d_parent = dir;
+        child->d_inode = inode;
+
+        LOCK_INODE_MUTEX(dir->d_inode);
+        rc = ldiskfs_add_entry(oh->ot_handle, child, inode);
+        UNLOCK_INODE_MUTEX(dir->d_inode);
+
+        RETURN(rc);
+}
+
+static inline obd_id lu_idif_id(const struct lu_fid *fid)
+{
+        return ((fid->f_seq & 0xffff) << 32) | fid->f_oid;
+}
+
+static inline obd_gr lu_idif_gr(const struct lu_fid * fid)
+{
+        return fid->f_ver;
+}
+
+/* external locking is required */
+int __osd_compat_load_group(struct osd_device *osd, int group)
+{
+        struct osd_compat_objid *map;
+        struct dentry           *o;
+        int                      rc = 0;
+        char                     name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        if (map->groups[group].groot != NULL)
+                RETURN(0);
+
+        sprintf(name, "%u", group);
+        o = ll_lookup_one_len(name, map->root, strlen(name));
+        if (IS_ERR(o))
+                rc = PTR_ERR(o);
+        else if (o->d_inode != NULL)
+                map->groups[group].groot = o;
+        else {
+                rc = -ENOENT;
+                dput(o);
+        }
+
+        if (map->groups[group].dirs == NULL)
+                OBD_ALLOC(map->groups[group].dirs,
+                          sizeof(o) * map->subdir_count);
+        LASSERT(map->groups[group].dirs);
+
+        RETURN(rc);
+}
+
+int osd_compat_load_group(struct osd_device *osd, int group)
+{
+        struct osd_compat_objid *map;
+        int                      rc = 0;
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        if (map->groups[group].groot != NULL)
+                RETURN(0);
+
+        down(&map->groups[group].dir_init_sem);
+        rc = __osd_compat_load_group(osd, group);
+        up(&map->groups[group].dir_init_sem);
+
+        RETURN(rc);
+}
+
+int osd_compat_load_or_make_group(struct osd_device *osd, int group)
+{
+        struct osd_compat_objid *map;
+        struct dentry           *o;
+        int                      rc = 0;
+        char                     name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        if (map->groups[group].groot != NULL)
+                RETURN(0);
+
+        down(&map->groups[group].dir_init_sem);
+
+        rc = __osd_compat_load_group(osd, group);
+
+        if (rc == -ENOENT) {
+                sprintf(name, "%d", group);
+                o = simple_mkdir(map->root, osd->od_mnt, name, 0700, 1);
+
+                if (IS_ERR(o)) {
+                        rc = PTR_ERR(o);
+
+                } else if (o->d_inode) {
+                        map->groups[group].groot = o;
+                        rc = 0;
+                } else {
+                        LBUG();
+                }
+        }
+
+        up(&map->groups[group].dir_init_sem);
+
+        RETURN(rc);
+}
+
+/* external locking is required */
+int __osd_compat_load_dir(struct osd_device *osd, int group, int dirn)
+{
+        struct osd_compat_objid_group *grp;
+        struct osd_compat_objid       *map;
+        struct dentry                 *o;
+        int                            rc = 0;
+        char                           name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        grp = map->groups + group;
+        LASSERT(grp);
+        LASSERT(grp->groot);
+
+        if (grp->dirs[dirn] != NULL)
+                RETURN(0);
+
+        sprintf(name, "d%u", dirn);
+        o = ll_lookup_one_len(name, grp->groot, strlen(name));
+        if (IS_ERR(o))
+                rc = PTR_ERR(o);
+        else if (o->d_inode != NULL)
+                grp->dirs[dirn] = o;
+        else {
+                rc = -ENOENT;
+                dput(o);
+        }
+
+        RETURN(rc);
+}
+
+int osd_compat_load_dir(struct osd_device *osd, int group, int dirn)
+{
+        struct osd_compat_objid_group *grp;
+        struct osd_compat_objid       *map;
+        int                            rc = 0;
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        grp = map->groups + group;
+        LASSERT(grp);
+        LASSERT(grp->groot);
+
+        if (grp->dirs[dirn] != NULL)
+                RETURN(0);
+
+        down(&grp->dir_init_sem);
+        rc = __osd_compat_load_dir(osd, group, dirn);
+        up(&grp->dir_init_sem);
+
+        RETURN(rc);
+}
+
+int osd_compat_load_or_make_dir(struct osd_device *osd, int group, int dirn)
+{
+        struct osd_compat_objid_group *grp;
+        struct osd_compat_objid       *map;
+        struct dentry                 *o;
+        int                            rc = 0;
+        char                           name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        grp = map->groups + group;
+        LASSERT(grp);
+        LASSERT(grp->groot);
+
+        if (grp->dirs[dirn] != NULL)
+                RETURN(0);
+
+        down(&grp->dir_init_sem);
+
+        rc = __osd_compat_load_dir(osd, group, dirn);
+
+        if (rc == -ENOENT) {
+                sprintf(name, "d%u", dirn);
+                o = simple_mkdir(grp->groot, osd->od_mnt, name, 0700, 1);
+
+                if (IS_ERR(o)) {
+                        rc = PTR_ERR(o);
+                } else if (o->d_inode) {
+                        grp->dirs[dirn] = o;
+                        rc = 0;
+                } else {
+                        LBUG();
+                }
+        }
+
+        up(&grp->dir_init_sem);
+
+        RETURN(rc);
+}
+
+int osd_compat_objid_lookup(struct osd_thread_info *info, struct osd_device *dev,
+                            const struct lu_fid *fid, struct osd_inode_id *id)
+{
+        struct osd_compat_objid *map;
+        struct dentry           *d;
+        struct dentry           *o;
+        __u64                    group;
+        __u64                    objid;
+        int                      rc = 0;
+        int                      dirn;
+        char                     name[32];
+        ENTRY;
+
+        /* on the very first lookup we find and open directories */
+
+        map = dev->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+        LASSERT(map->subdir_count);
+
+        group = lu_idif_gr(fid);
+        objid = lu_idif_id(fid);
+        LASSERT(group < MAX_OBJID_GROUP);
+
+        rc = osd_compat_load_group(dev, group);
+        if (rc)
+                RETURN(rc);
+
+        dirn = objid & (map->subdir_count - 1);
+        rc = osd_compat_load_dir(dev, group, dirn);
+        if (rc)
+                RETURN(rc);
+
+        d = map->groups[group].dirs[dirn];
+        LASSERT(d);
+
+        sprintf(name, "%Lu", objid);
+        o = ll_lookup_one_len(name, d, strlen(name));
+
+        if (IS_ERR(o))
+                GOTO(cleanup, rc = PTR_ERR(o));
+        if (o->d_inode) {
+                id->oii_ino = o->d_inode->i_ino;
+                id->oii_gen = o->d_inode->i_generation;
+        } else {
+                rc = -ENOENT;
+        }
+        dput(o);
+
+cleanup:
+        RETURN(rc);
+}
+
+int osd_compat_objid_insert(struct osd_thread_info *info,
+                            struct osd_device *osd,
+                            const struct lu_fid *fid,
+                            const struct osd_inode_id *id,
+                            struct thandle *th)
+{
+        struct osd_compat_objid        *map;
+        struct dentry                  *d;
+        obd_id                          objid;
+        obd_gr                          group ;
+        int                             dirn, rc = 0;
+        char                            name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+        LASSERT(map->subdir_count);
+
+        /* map fid to group:objid */
+        group = lu_idif_gr(fid);
+        objid = lu_idif_id(fid);
+
+        rc = osd_compat_load_or_make_group(osd, group);
+        if (rc)
+                GOTO(cleanup, rc);
+        
+        dirn = objid & (map->subdir_count - 1);
+        rc = osd_compat_load_or_make_dir(osd, group, dirn);
+        if (rc)
+                GOTO(cleanup, rc);
+
+        d = map->groups[group].dirs[dirn];
+        LASSERT(d);
+
+        sprintf(name, "%Lu", objid);
+        rc = osd_compat_add_entry(info, osd, d, name, id, th);
+
+cleanup:
+        RETURN(rc);
+}
+
+int osd_compat_objid_delete(struct osd_thread_info *info, struct osd_device *osd,
+                            const struct lu_fid *fid, struct thandle *th)
+{
+        struct osd_compat_objid        *map;
+        struct dentry                  *d;
+        obd_id                          objid;
+        obd_gr                          group ;
+        int                             dirn, rc = 0;
+        char                            name[32];
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+        LASSERT(map->subdir_count);
+
+        /* map fid to group:objid */
+        group = lu_idif_gr(fid);
+        objid = lu_idif_id(fid);
+
+        rc = osd_compat_load_group(osd, group);
+        if (rc)
+                GOTO(cleanup, rc);
+        
+        dirn = objid & (map->subdir_count - 1);
+        rc = osd_compat_load_dir(osd, group, dirn);
+        if (rc)
+                GOTO(cleanup, rc);
+
+        d = map->groups[group].dirs[dirn];
+        LASSERT(d);
+
+        sprintf(name, "%Lu", objid);
+        rc = osd_compat_del_entry(info, osd, d, name, th);
+
+cleanup:
+        RETURN(rc);
+}
+
+
+int osd_compat_spec_insert(struct osd_thread_info *info, struct osd_device *osd,
+                           const struct lu_fid *fid, const struct osd_inode_id *id,
+                           struct thandle *th)
+{
+        struct osd_compat_objid *map;
+        struct dentry           *root;
+        int                      rc = 0;
+        int                      group;
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        root = osd_sb(osd)->s_root;
+
+        if (fid_oid(fid) == OFD_LAST_RECV_OID) {
+                rc = osd_compat_add_entry(info, osd, root, "last_rcvd", id, th);
+
+        } else if (fid_oid(fid) >= OFD_GROUP0_LAST_ID &&
+                        fid_oid(fid) < OFD_GROUP4K_LAST_ID) {
+                /* on creation of LAST_ID we create O/<group> hierarchy */
+                group = fid_oid(fid) - OFD_GROUP0_LAST_ID;
+                rc = osd_compat_load_or_make_group(osd, group);
+                if (rc)
+                        GOTO(cleanup, rc);
+                rc = osd_compat_add_entry(info, osd, map->groups[group].groot,
+                                          "LAST_ID", id, th);
+
+        } else if (fid_oid(fid) == OFD_LAST_GROUP) {
+                /* on creation of LAST_GROUP we create legacy OST hierarchy */
+                rc = osd_compat_add_entry(info, osd, root, "LAST_GROUP", id, th);
+        } else {
+                /* unknown special file: probably OI can handle this */
+        }
+
+cleanup:
+        RETURN(rc);
+}
+
+#if 0
+int osd_compat_spec_lookup(struct osd_thread_info *info, struct osd_device *osd,
+                           const struct lu_fid *fid, struct osd_inode_id *id)
+{
+        struct osd_compat_objid       *map;
+        int                            rc = 0;
+        int                            group;
+        ENTRY;
+
+        map = osd->od_ost_map;
+        LASSERT(map);
+        LASSERT(map->root);
+
+        CERROR("spec lookup for %lu\n", (unsigned long) fid_oid(fid));
+        if (fid_oid(fid) == OFD_LAST_RECV_OID) {
+                /* last_rcvd */
+                *id = map->last_rcvd_id;
+
+        } else if (fid_oid(fid) >= OFD_GROUP0_LAST_ID &&
+                        fid_oid(fid) >= OFD_GROUP4K_LAST_ID) {
+                /* LAST_ID: up on first access directory structure is created */
+                CERROR("access to group %d\n", group);
+                group = fid_oid(fid) - OFD_GROUP0_LAST_ID;
+                rc = osd_compat_objid_init_group(dev, group);
+                if (rc == 0)
+                        *id = map->groups[group].grp->last_id;
+
+        } else if (fid_oid(fid) == OFD_LAST_GROUP) {
+                *id = map->last_group_id;
+        } else {
+                /* unknown special file: probably OI can handle this */
+                rc = -ERESTART;
+
+        }
+        RETURN(rc);
+}
+#endif
+
index ef9499c..5850dea 100644 (file)
@@ -686,9 +686,14 @@ static void osd_trans_commit_cb(struct journal_callback *jcb, int error)
 static struct thandle *osd_trans_create(const struct lu_env *env,
                                         struct dt_device *d)
 {
-        struct osd_thandle *oh;
-        struct thandle     *th;
+        struct osd_thread_info *oti = osd_oti_get(env);
+        struct filter_iobuf    *iobuf = &oti->oti_iobuf;
+        struct osd_thandle     *oh;
+        struct thandle         *th;
         ENTRY;
+       
+        /* on pending IO in this thread should left from prev. request */
+        LASSERT(atomic_read(&iobuf->dr_numreqs) == 0);
 
         th = ERR_PTR(-ENOMEM);
         OBD_ALLOC_GFP(oh, sizeof *oh, CFS_ALLOC_IO);
@@ -826,7 +831,9 @@ static int osd_trans_stop(const struct lu_env *env, struct thandle *th)
 
         /* as we want IO to journal and data IO be concurrent, we don't block
          * awaiting data IO completion in osd_do_bio(), instead we wait here
-         * once transaction is submitted to the journal.
+         * once transaction is submitted to the journal. all reqular requests
+         * don't do direct IO (except read/write), thus this wait_even becomes
+         * no-op for them.
          *
          * IMPORTANT: we have to wait till any IO submited by the thread is
          * completed otherwise iobuf may be corrupted by different request
index 1752e54..1525865 100644 (file)
@@ -332,7 +332,7 @@ static int osd_do_bio(struct inode *inode, struct filter_iobuf *iobuf, int rw)
          * completion here. instead we proceed with transaction commit in
          * parallel and wait for IO completion once transaction is stopped
          * see osd_trans_stop() for more details -bzzz */
-        if (rw == OBD_BRW_WRITE)
+        if (rw != OBD_BRW_WRITE)
                 wait_event(iobuf->dr_wait, atomic_read(&iobuf->dr_numreqs) == 0);
 
         if (rc == 0)