/** * Implementation of dt_object_operations::do_create * * It adds an OUT_CREATE sub-request into the OUT RPC that will be flushed * when the transaction stop, and sets necessary flags for created object. * * \param[in] env execution environment * \param[in] dt object to be created * \param[in] attr attribute of the created object * \param[in] hint creation hint * \param[in] dof creation format information * \param[in] th the transaction handle * * \retval 0 if packing creation succeeds. * \retval negative errno if packing creation fails. */ int osp_md_object_create(const struct lu_env *env, struct dt_object *dt, struct lu_attr *attr, struct dt_allocation_hint *hint, struct dt_object_format *dof, struct thandle *th) { struct osp_update_request *update; struct osp_object *obj = dt2osp_obj(dt); int rc; update = thandle_to_osp_update_request(th); LASSERT(update != NULL); LASSERT(attr->la_valid & LA_TYPE); rc = osp_update_rpc_pack(env, create, update, OUT_CREATE, lu_object_fid(&dt->do_lu), attr, hint, dof); if (rc != 0) GOTO(out, rc); rc = osp_insert_update_callback(env, update, dt2osp_obj(dt), NULL, osp_object_create_interpreter); if (rc < 0) GOTO(out, rc); dt->do_lu.lo_header->loh_attr |= LOHA_EXISTS | (attr->la_mode & S_IFMT); dt2osp_obj(dt)->opo_non_exist = 0; LASSERT(obj->opo_ooa != NULL); obj->opo_ooa->ooa_attr = *attr; out: return rc; }
/** * Implement OSP layer dt_object_operations::do_destroy() interface. * * Pack the destroy update into the RPC buffer, which will be sent * to the remote MDT during transaction stop. * * It also marks the object as non-cached. * * \param[in] env pointer to the thread context * \param[in] dt pointer to the OSP layer dt_object to be destroyed * \param[in] th pointer to the transaction handler * * \retval 0 for success * \retval negative error number on failure */ int osp_md_object_destroy(const struct lu_env *env, struct dt_object *dt, struct thandle *th) { struct osp_object *o = dt2osp_obj(dt); struct osp_device *osp = lu2osp_dev(dt->do_lu.lo_dev); struct osp_update_request *update; int rc = 0; ENTRY; o->opo_non_exist = 1; LASSERT(osp->opd_connect_mdt); update = thandle_to_osp_update_request(th); LASSERT(update != NULL); rc = osp_update_rpc_pack(env, object_destroy, update, OUT_DESTROY, lu_object_fid(&dt->do_lu)); if (rc != 0) RETURN(rc); set_bit(LU_OBJECT_HEARD_BANSHEE, &dt->do_lu.lo_header->loh_flags); rc = osp_insert_update_callback(env, update, dt2osp_obj(dt), NULL, NULL); RETURN(rc); }
/** * Implementation of dt_object_operations::do_write_locked * * Test if the object is locked in write mode. * * \param[in] env execution environment * \param[in] dt object to be tested */ static int osp_md_object_write_locked(const struct lu_env *env, struct dt_object *dt) { struct osp_object *obj = dt2osp_obj(dt); return obj->opo_owner == env; }
/** * Implementation of dt_object_operations::do_read_unlock * * Unlock the read lock of remote object. * * \param[in] env execution environment * \param[in] dt object to be unlocked */ static void osp_md_object_read_unlock(const struct lu_env *env, struct dt_object *dt) { struct osp_object *obj = dt2osp_obj(dt); up_read(&obj->opo_sem); }
/** * Implementation of dt_body_operations::dbo_write * * Pack the write object update into the RPC buffer, which will be sent * to the remote MDT during transaction stop. * * \param[in] env execution environment * \param[in] dt object to be written * \param[in] buf buffer to write which includes an embedded size field * \param[in] pos offet in the object to start writing at * \param[in] th transaction handle * \param[in] ignore_quota quota enforcement for this write * * \retval the buffer size in bytes if packing succeeds. * \retval negative errno if packing fails. */ static ssize_t osp_md_write(const struct lu_env *env, struct dt_object *dt, const struct lu_buf *buf, loff_t *pos, struct thandle *th, int ignore_quota) { struct osp_object *obj = dt2osp_obj(dt); struct osp_update_request *update; struct osp_thandle *oth = thandle_to_osp_thandle(th); ssize_t rc; ENTRY; update = thandle_to_osp_update_request(th); LASSERT(update != NULL); CDEBUG(D_INFO, "write "DFID" offset = "LPU64" length = %zu\n", PFID(lu_object_fid(&dt->do_lu)), *pos, buf->lb_len); rc = osp_update_rpc_pack(env, write, update, OUT_WRITE, lu_object_fid(&dt->do_lu), buf, *pos); if (rc < 0) RETURN(rc); rc = osp_check_and_set_rpc_version(oth, obj); if (rc < 0) RETURN(rc); /* XXX: how about the write error happened later? */ *pos += buf->lb_len; if (obj->opo_ooa != NULL && obj->opo_ooa->ooa_attr.la_valid & LA_SIZE && obj->opo_ooa->ooa_attr.la_size < *pos) obj->opo_ooa->ooa_attr.la_size = *pos; RETURN(buf->lb_len); }
static int osp_attr_set(const struct lu_env *env, struct dt_object *dt, const struct lu_attr *attr, struct thandle *th, struct lustre_capa *capa) { struct osp_object *o = dt2osp_obj(dt); int rc = 0; ENTRY; /* we're interested in uid/gid changes only */ if (!(attr->la_valid & (LA_UID | LA_GID))) RETURN(0); /* new object, the very first ->attr_set() * initializing attributes needs no logging * all subsequent one are subject to the * logging and synchronization with OST */ if (o->opo_new) { o->opo_new = 0; RETURN(0); } /* * once transaction is committed put proper command on * the queue going to our OST */ rc = osp_sync_add(env, o, MDS_SETATTR64_REC, th, attr); /* XXX: send new uid/gid to OST ASAP? */ RETURN(rc); }
/** * Implementation of dt_object_operations::do_write_unlock * * Unlock the write lock of remote object. * * \param[in] env execution environment * \param[in] dt object to be unlocked */ static void osp_md_object_write_unlock(const struct lu_env *env, struct dt_object *dt) { struct osp_object *obj = dt2osp_obj(dt); LASSERT(obj->opo_owner == env); obj->opo_owner = NULL; up_write(&obj->opo_sem); }
static int osp_declare_attr_set(const struct lu_env *env, struct dt_object *dt, const struct lu_attr *attr, struct thandle *th) { struct osp_device *d = lu2osp_dev(dt->do_lu.lo_dev); struct osp_object *o = dt2osp_obj(dt); int rc = 0; ENTRY; /* * Usually we don't allow server stack to manipulate size * but there is a special case when striping is created * late, after stripless file got truncated to non-zero. * * In this case we do the following: * * 1) grab id in declare - this can lead to leaked OST objects * but we don't currently have proper mechanism and the only * options we have are to do truncate RPC holding transaction * open (very bad) or to grab id in declare at cost of leaked * OST object in same very rare unfortunate case (just bad) * notice 1.6-2.0 do assignment outside of running transaction * all the time, meaning many more chances for leaked objects. * * 2) send synchronous truncate RPC with just assigned id */ /* there are few places in MDD code still passing NULL * XXX: to be fixed soon */ if (attr == NULL) RETURN(0); if (attr->la_valid & LA_SIZE && attr->la_size > 0 && fid_is_zero(lu_object_fid(&o->opo_obj.do_lu))) { LASSERT(!dt_object_exists(dt)); osp_object_assign_fid(env, d, o); rc = osp_object_truncate(env, dt, attr->la_size); if (rc) RETURN(rc); } if (o->opo_new) { /* no need in logging for new objects being created */ RETURN(0); } if (!(attr->la_valid & (LA_UID | LA_GID))) RETURN(0); /* * track all UID/GID changes via llog */ rc = osp_sync_declare_add(env, o, MDS_SETATTR64_REC, th); RETURN(rc); }
/** * Implementation of dt_object_operations::do_write_lock * * Lock the remote object in write mode. * * \param[in] env execution environment * \param[in] dt object to be locked * \param[in] role lock role from MDD layer, see mdd_object_role(). */ static void osp_md_object_write_lock(const struct lu_env *env, struct dt_object *dt, unsigned role) { struct osp_object *obj = dt2osp_obj(dt); down_write_nested(&obj->opo_sem, role); LASSERT(obj->opo_owner == NULL); obj->opo_owner = env; }
static int osp_declare_object_destroy(const struct lu_env *env, struct dt_object *dt, struct thandle *th) { struct osp_object *o = dt2osp_obj(dt); int rc = 0; ENTRY; /* * track objects to be destroyed via llog */ rc = osp_sync_declare_add(env, o, MDS_UNLINK64_REC, th); RETURN(rc); }
/** * Implementation of dt_object_operations::do_declare_create * * Create the osp_update_request to track the update for this OSP * in the transaction. * * \param[in] env execution environment * \param[in] dt remote object to be created * \param[in] attr attribute of the created object * \param[in] hint creation hint * \param[in] dof creation format information * \param[in] th the transaction handle * * \retval 0 if preparation succeeds. * \retval negative errno if preparation fails. */ int osp_md_declare_object_create(const struct lu_env *env, struct dt_object *dt, struct lu_attr *attr, struct dt_allocation_hint *hint, struct dt_object_format *dof, struct thandle *th) { struct osp_object *obj = dt2osp_obj(dt); int rc; if (obj->opo_ooa == NULL) { rc = osp_oac_init(obj); if (rc != 0) return rc; } return osp_trans_update_request_create(th); }
static int osp_object_destroy(const struct lu_env *env, struct dt_object *dt, struct thandle *th) { struct osp_object *o = dt2osp_obj(dt); int rc = 0; ENTRY; /* * once transaction is committed put proper command on * the queue going to our OST */ rc = osp_sync_add(env, o, MDS_UNLINK64_REC, th, NULL); /* not needed in cache any more */ set_bit(LU_OBJECT_HEARD_BANSHEE, &dt->do_lu.lo_header->loh_flags); RETURN(rc); }
/** * Implementation of dt_body_operations::dbo_declare_write * * Create the osp_update_request to track the update for this OSP * in the transaction. * * \param[in] env execution environment * \param[in] dt object to be written * \param[in] buf buffer to write which includes an embedded size field * \param[in] pos offet in the object to start writing at * \param[in] th transaction handle * * \retval 0 if preparation succeeds. * \retval negative errno if preparation fails. */ static ssize_t osp_md_declare_write(const struct lu_env *env, struct dt_object *dt, const struct lu_buf *buf, loff_t pos, struct thandle *th) { struct osp_device *osp = dt2osp_dev(th->th_dev); int rc; rc = osp_trans_update_request_create(th); if (rc != 0) return rc; if (osp->opd_update == NULL) return 0; if (dt2osp_obj(dt)->opo_stale) return -ESTALE; return 0; }
static int osp_object_create(const struct lu_env *env, struct dt_object *dt, struct lu_attr *attr, struct dt_allocation_hint *hint, struct dt_object_format *dof, struct thandle *th) { struct osp_thread_info *osi = osp_env_info(env); struct osp_device *d = lu2osp_dev(dt->do_lu.lo_dev); struct osp_object *o = dt2osp_obj(dt); int rc = 0; struct lu_fid *fid = &osi->osi_fid; ENTRY; if (o->opo_reserved) { /* regular case, fid is assigned holding trunsaction open */ osp_object_assign_fid(env, d, o); } memcpy(fid, lu_object_fid(&dt->do_lu), sizeof(*fid)); LASSERTF(fid_is_sane(fid), "fid for osp_obj %p is insane"DFID"!\n", osp_obj, PFID(fid)); if (!o->opo_reserved) { /* special case, id was assigned outside of transaction * see comments in osp_declare_attr_set */ spin_lock(&d->opd_pre_lock); osp_update_last_fid(d, fid); spin_unlock(&d->opd_pre_lock); } CDEBUG(D_INODE, "fid for osp_obj %p is "DFID"!\n", osp_obj, PFID(fid)); /* If the precreate ends, it means it will be ready to rollover to * the new sequence soon, all the creation should be synchronized, * otherwise during replay, the replay fid will be inconsistent with * last_used/create fid */ if (osp_precreate_end_seq(env, d) && osp_is_fid_client(d)) th->th_sync = 1; /* * it's OK if the import is inactive by this moment - id was created * by OST earlier, we just need to maintain it consistently on the disk * once import is reconnected, OSP will claim this and other objects * used and OST either keep them, if they exist or recreate */ /* we might have lost precreated objects */ if (unlikely(d->opd_gap_count) > 0) { spin_lock(&d->opd_pre_lock); if (d->opd_gap_count > 0) { int count = d->opd_gap_count; ostid_set_id(&osi->osi_oi, fid_oid(&d->opd_gap_start_fid)); d->opd_gap_count = 0; spin_unlock(&d->opd_pre_lock); CDEBUG(D_HA, "Writting gap "DFID"+%d in llog\n", PFID(&d->opd_gap_start_fid), count); /* real gap handling is disabled intil ORI-692 will be * fixed, now we only report gaps */ } else { spin_unlock(&d->opd_pre_lock); } } /* new object, the very first ->attr_set() * initializing attributes needs no logging */ o->opo_new = 1; /* Only need update last_used oid file, seq file will only be update * during seq rollover */ osp_objid_buf_prep(&osi->osi_lb, &osi->osi_off, &d->opd_last_used_fid.f_oid, d->opd_index); rc = dt_record_write(env, d->opd_last_used_oid_file, &osi->osi_lb, &osi->osi_off, th); CDEBUG(D_HA, "%s: Wrote last used FID: "DFID", index %d: %d\n", d->opd_obd->obd_name, PFID(fid), d->opd_index, rc); RETURN(rc); }
static int osp_declare_object_create(const struct lu_env *env, struct dt_object *dt, struct lu_attr *attr, struct dt_allocation_hint *hint, struct dt_object_format *dof, struct thandle *th) { struct osp_thread_info *osi = osp_env_info(env); struct osp_device *d = lu2osp_dev(dt->do_lu.lo_dev); struct osp_object *o = dt2osp_obj(dt); const struct lu_fid *fid; int rc = 0; ENTRY; /* should happen to non-0 OSP only so that at least one object * has been already declared in the scenario and LOD should * cleanup that */ if (OBD_FAIL_CHECK(OBD_FAIL_MDS_OSC_CREATE_FAIL) && d->opd_index == 1) RETURN(-ENOSPC); LASSERT(d->opd_last_used_oid_file); fid = lu_object_fid(&dt->do_lu); /* * There can be gaps in precreated ids and record to unlink llog * XXX: we do not handle gaps yet, implemented before solution * was found to be racy, so we disabled that. there is no * point in making useless but expensive llog declaration. */ /* rc = osp_sync_declare_add(env, o, MDS_UNLINK64_REC, th); */ if (unlikely(!fid_is_zero(fid))) { /* replay case: caller knows fid */ osi->osi_off = sizeof(osi->osi_id) * d->opd_index; rc = dt_declare_record_write(env, d->opd_last_used_oid_file, sizeof(osi->osi_id), osi->osi_off, th); RETURN(rc); } /* * in declaration we need to reserve object so that we don't block * awaiting precreation RPC to complete */ rc = osp_precreate_reserve(env, d); /* * we also need to declare update to local "last used id" file for * recovery if object isn't used for a reason, we need to release * reservation, this can be made in osd_object_release() */ if (rc == 0) { /* mark id is reserved: in create we don't want to talk * to OST */ LASSERT(o->opo_reserved == 0); o->opo_reserved = 1; /* common for all OSPs file hystorically */ osi->osi_off = sizeof(osi->osi_id) * d->opd_index; rc = dt_declare_record_write(env, d->opd_last_used_oid_file, sizeof(osi->osi_id), osi->osi_off, th); } else { /* not needed in the cache anymore */ set_bit(LU_OBJECT_HEARD_BANSHEE, &dt->do_lu.lo_header->loh_flags); } RETURN(rc); }