rc = llog_write_rec(env, cathandle, &rec->lid_hdr,
&loghandle->u.phd.phd_cookie, LLOG_NEXT_IDX, th);
if (rc < 0)
- GOTO(out, rc);
+ GOTO(out_destroy, rc);
CDEBUG(D_OTHER, "new plain log "DOSTID":%x for index %u of catalog"
DOSTID"\n", POSTID(&loghandle->lgh_id.lgl_oi),
dt_trans_stop(env, dt, handle);
RETURN(0);
+
+out_destroy:
+ /* to signal llog_cat_close() it shouldn't try to destroy the llog,
+ * we want to destroy it in this transaction, otherwise the object
+ * becomes an orphan */
+ loghandle->lgh_hdr->llh_flags &= ~LLOG_F_ZAP_WHEN_EMPTY;
+ /* this is to mimic full log, so another llog_cat_current_log()
+ * can skip it and ask for another onet */
+ loghandle->lgh_last_idx = LLOG_HDR_BITMAP_SIZE(llh) + 1;
+ llog_trans_destroy(env, loghandle, th);
+ RETURN(rc);
}
/* Open an existent log handle and add it to the open list.
struct llog_handle *loghandle = NULL;
ENTRY;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_MDS_LLOG_CREATE_FAILED2)) {
+ down_write_nested(&cathandle->lgh_lock, LLOGH_CAT);
+ GOTO(next, loghandle);
+ }
+
down_read_nested(&cathandle->lgh_lock, LLOGH_CAT);
loghandle = cathandle->u.chd.chd_current_log;
if (loghandle) {
down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
llh = loghandle->lgh_hdr;
- if (llh == NULL ||
- loghandle->lgh_last_idx < LLOG_HDR_BITMAP_SIZE(llh) - 1) {
+ if (llh == NULL || !llog_is_full(loghandle)) {
up_read(&cathandle->lgh_lock);
RETURN(loghandle);
} else {
down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
llh = loghandle->lgh_hdr;
LASSERT(llh);
- if (loghandle->lgh_last_idx < LLOG_HDR_BITMAP_SIZE(llh) - 1) {
+ if (!llog_is_full(loghandle)) {
up_write(&cathandle->lgh_lock);
RETURN(loghandle);
} else {
}
}
+next:
CDEBUG(D_INODE, "use next log\n");
loghandle = cathandle->u.chd.chd_next_log;
struct thandle *th)
{
struct llog_handle *loghandle;
- int rc;
- ENTRY;
+ int rc, retried = 0;
+ ENTRY;
LASSERT(rec->lrh_len <= cathandle->lgh_ctxt->loc_chunk_size);
+
+retry:
loghandle = llog_cat_current_log(cathandle, th);
LASSERT(!IS_ERR(loghandle));
rc = llog_cat_new_log(env, cathandle, loghandle, th);
if (rc < 0) {
up_write(&loghandle->lgh_lock);
+ /* nobody should be trying to use this llog */
+ down_write(&cathandle->lgh_lock);
+ if (cathandle->u.chd.chd_current_log == loghandle)
+ cathandle->u.chd.chd_current_log = NULL;
+ up_write(&cathandle->lgh_lock);
RETURN(rc);
}
}
/* now let's try to add the record */
rc = llog_write_rec(env, loghandle, rec, reccookie, LLOG_NEXT_IDX, th);
- if (rc < 0)
+ if (rc < 0) {
CDEBUG_LIMIT(rc == -ENOSPC ? D_HA : D_ERROR,
"llog_write_rec %d: lh=%p\n", rc, loghandle);
+ /* -ENOSPC is returned if no empty records left
+ * and when it's lack of space on the stogage.
+ * there is no point to try again if it's the second
+ * case. many callers (like llog test) expect ENOSPC,
+ * so we preserve this error code, but look for the
+ * actual cause here */
+ if (rc == -ENOSPC && llog_is_full(loghandle))
+ rc = -ENOBUFS;
+ }
up_write(&loghandle->lgh_lock);
- if (rc == -ENOSPC) {
- /* try to use next log */
- loghandle = llog_cat_current_log(cathandle, th);
- LASSERT(!IS_ERR(loghandle));
- /* new llog can be created concurrently */
- if (!llog_exist(loghandle)) {
- rc = llog_cat_new_log(env, cathandle, loghandle, th);
- if (rc < 0) {
- up_write(&loghandle->lgh_lock);
- RETURN(rc);
- }
- }
- /* now let's try to add the record */
- rc = llog_write_rec(env, loghandle, rec, reccookie,
- LLOG_NEXT_IDX, th);
- if (rc < 0)
- CERROR("llog_write_rec %d: lh=%p\n", rc, loghandle);
- up_write(&loghandle->lgh_lock);
+
+ if (rc == -ENOBUFS) {
+ if (retried++ == 0)
+ GOTO(retry, rc);
+ CERROR("%s: error on 2nd llog: rc = %d\n",
+ cathandle->lgh_ctxt->loc_obd->obd_name, rc);
}
RETURN(rc);
}
/**
+ * Implementation of the llog_operations::lop_exist
+ *
+ * This function checks that llog exists on storage.
+ *
+ * \param[in] handle llog handle of the current llog
+ *
+ * \retval true if llog object exists and is not just destroyed
+ * \retval false if llog doesn't exist or just destroyed
+ */
+static int llog_osd_exist(struct llog_handle *handle)
+{
+ LASSERT(handle->lgh_obj);
+ return dt_object_exists(handle->lgh_obj) &&
+ !lu_object_is_dying(handle->lgh_obj->do_lu.lo_header);
+}
+
+/**
* Write a padding record to the llog
*
* This function writes a padding record to the end of llog. That may
CDEBUG(D_OTHER, "new record %x to "DFID"\n",
rec->lrh_type, PFID(lu_object_fid(&o->do_lu)));
+ if (!llog_osd_exist(loghandle))
+ RETURN(-ENOENT);
+
/* record length should not bigger than */
if (reclen > loghandle->lgh_hdr->llh_hdr.lrh_len)
RETURN(-E2BIG);
* process them page-at-a-time if needed. If it will cross a chunk
* boundary, write in a fake (but referenced) entry to pad the chunk.
*/
+
+
+ /* simulate ENOSPC when new plain llog is being added to the
+ * catalog */
+ if (OBD_FAIL_CHECK(OBD_FAIL_MDS_LLOG_CREATE_FAILED2) &&
+ llh->llh_flags & LLOG_F_IS_CAT)
+ RETURN(-ENOSPC);
+
LASSERT(lgi->lgi_attr.la_valid & LA_SIZE);
lgi->lgi_off = lgi->lgi_attr.la_size;
left = chunk_size - (lgi->lgi_off & (chunk_size - 1));
}
/* if it's the last idx in log file, then return -ENOSPC
* or wrap around if a catalog */
- if ((loghandle->lgh_last_idx >= LLOG_HDR_BITMAP_SIZE(llh) - 1) ||
+ if (llog_is_full(loghandle) ||
unlikely(llh->llh_flags & LLOG_F_IS_CAT &&
OBD_FAIL_PRECHECK(OBD_FAIL_CAT_RECORDS) &&
loghandle->lgh_last_idx >= cfs_fail_val)) {
}
/**
- * Implementation of the llog_operations::lop_exist
- *
- * This function checks that llog exists on storage.
- *
- * \param[in] handle llog handle of the current llog
- *
- * \retval true if llog object exists and is not just destroyed
- * \retval false if llog doesn't exist or just destroyed
- */
-static int llog_osd_exist(struct llog_handle *handle)
-{
- LASSERT(handle->lgh_obj);
- return (dt_object_exists(handle->lgh_obj) &&
- !lu_object_is_dying(handle->lgh_obj->do_lu.lo_header));
-}
-
-/**
* Get dir for regular fid log object
*
* Get directory for regular fid log object, and these regular fid log
obj->oo_attr.la_gid, rc);
oid = obj->oo_db->db_object;
- if (obj->oo_destroy == OSD_DESTROY_SYNC) {
+ if (unlikely(obj->oo_destroy == OSD_DESTROY_NONE)) {
+ /* this may happen if the destroy wasn't declared
+ * e.g. when the object is created and then destroyed
+ * in the same transaction - we don't need additional
+ * space for destroy specifically */
+ LASSERT(obj->oo_attr.la_size <= osd_sync_destroy_max_size);
+ rc = -dmu_object_free(osd->od_os, oid, oh->ot_tx);
+ if (rc)
+ CERROR("%s: failed to free %s "LPU64": rc = %d\n",
+ osd->od_svname, buf, oid, rc);
+ } else if (obj->oo_destroy == OSD_DESTROY_SYNC) {
rc = -dmu_object_free(osd->od_os, oid, oh->ot_tx);
if (rc)
CERROR("%s: failed to free %s "LPU64": rc = %d\n",