int llog_read_header(const struct lu_env *env, struct llog_handle *handle, const struct obd_uuid *uuid) { struct llog_operations *lop; int rc; ENTRY; rc = llog_handle2ops(handle, &lop); if (rc) RETURN(rc); if (lop->lop_read_header == NULL) RETURN(-EOPNOTSUPP); rc = lop->lop_read_header(env, handle); if (rc == LLOG_EEMPTY) { struct llog_log_hdr *llh = handle->lgh_hdr; /* lrh_len should be initialized in llog_init_handle */ handle->lgh_last_idx = 0; /* header is record with index 0 */ handle->lgh_write_offset = 0; llh->llh_count = 1; /* for the header record */ llh->llh_hdr.lrh_type = LLOG_HDR_MAGIC; LASSERT(handle->lgh_ctxt->loc_chunk_size >= LLOG_MIN_CHUNK_SIZE); llh->llh_hdr.lrh_len = handle->lgh_ctxt->loc_chunk_size; llh->llh_hdr.lrh_index = 0; llh->llh_timestamp = cfs_time_current_sec(); if (uuid) memcpy(&llh->llh_tgtuuid, uuid, sizeof(llh->llh_tgtuuid)); llh->llh_bitmap_offset = offsetof(typeof(*llh), llh_bitmap); /* Since update llog header might also call this function, * let's reset the bitmap to 0 here */ memset(LLOG_HDR_BITMAP(llh), 0, llh->llh_hdr.lrh_len - llh->llh_bitmap_offset - sizeof(llh->llh_tail)); ext2_set_bit(0, LLOG_HDR_BITMAP(llh)); LLOG_HDR_TAIL(llh)->lrt_len = llh->llh_hdr.lrh_len; LLOG_HDR_TAIL(llh)->lrt_index = llh->llh_hdr.lrh_index; rc = 0; } RETURN(rc); }
int llog_reverse_process(const struct lu_env *env, struct llog_handle *loghandle, llog_cb_t cb, void *data, void *catdata) { struct llog_log_hdr *llh = loghandle->lgh_hdr; struct llog_process_cat_data *cd = catdata; void *buf; int rc = 0, first_index = 1, index, idx; __u32 chunk_size = llh->llh_hdr.lrh_len; ENTRY; OBD_ALLOC_LARGE(buf, chunk_size); if (buf == NULL) RETURN(-ENOMEM); if (cd != NULL) first_index = cd->lpcd_first_idx + 1; if (cd != NULL && cd->lpcd_last_idx) index = cd->lpcd_last_idx; else index = LLOG_HDR_BITMAP_SIZE(llh) - 1; while (rc == 0) { struct llog_rec_hdr *rec; struct llog_rec_tail *tail; /* skip records not set in bitmap */ while (index >= first_index && !ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) --index; LASSERT(index >= first_index - 1); if (index == first_index - 1) break; /* get the buf with our target record; avoid old garbage */ memset(buf, 0, chunk_size); rc = llog_prev_block(env, loghandle, index, buf, chunk_size); if (rc) GOTO(out, rc); rec = buf; idx = rec->lrh_index; CDEBUG(D_RPCTRACE, "index %u : idx %u\n", index, idx); while (idx < index) { rec = (void *)rec + rec->lrh_len; if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); idx ++; } LASSERT(idx == index); tail = (void *)rec + rec->lrh_len - sizeof(*tail); /* process records in buffer, starting where we found one */ while ((void *)tail > buf) { if (tail->lrt_index == 0) GOTO(out, rc = 0); /* no more records */ /* if set, process the callback on this record */ if (ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) { rec = (void *)tail - tail->lrt_len + sizeof(*tail); rc = cb(env, loghandle, rec, data); if (rc == LLOG_PROC_BREAK) { GOTO(out, rc); } else if (rc == LLOG_DEL_RECORD) { rc = llog_cancel_rec(env, loghandle, tail->lrt_index); } if (rc) GOTO(out, rc); } /* previous record, still in buffer? */ --index; if (index < first_index) GOTO(out, rc = 0); tail = (void *)tail - tail->lrt_len; } } out: if (buf != NULL) OBD_FREE_LARGE(buf, chunk_size); RETURN(rc); }
static int llog_process_thread(void *arg) { struct llog_process_info *lpi = arg; struct llog_handle *loghandle = lpi->lpi_loghandle; struct llog_log_hdr *llh = loghandle->lgh_hdr; struct llog_process_cat_data *cd = lpi->lpi_catdata; char *buf; size_t chunk_size; __u64 cur_offset, tmp_offset; int rc = 0, index = 1, last_index; int saved_index = 0; int last_called_index = 0; ENTRY; if (llh == NULL) RETURN(-EINVAL); cur_offset = chunk_size = llh->llh_hdr.lrh_len; /* expect chunk_size to be power of two */ LASSERT(is_power_of_2(chunk_size)); OBD_ALLOC_LARGE(buf, chunk_size); if (buf == NULL) { lpi->lpi_rc = -ENOMEM; RETURN(0); } if (cd != NULL) { last_called_index = cd->lpcd_first_idx; index = cd->lpcd_first_idx + 1; } if (cd != NULL && cd->lpcd_last_idx) last_index = cd->lpcd_last_idx; else last_index = LLOG_HDR_BITMAP_SIZE(llh) - 1; while (rc == 0) { struct llog_rec_hdr *rec; off_t chunk_offset; unsigned int buf_offset = 0; bool partial_chunk; /* skip records not set in bitmap */ while (index <= last_index && !ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) ++index; /* There are no indices prior the last_index */ if (index > last_index) break; CDEBUG(D_OTHER, "index: %d last_index %d\n", index, last_index); repeat: /* get the buf with our target record; avoid old garbage */ memset(buf, 0, chunk_size); rc = llog_next_block(lpi->lpi_env, loghandle, &saved_index, index, &cur_offset, buf, chunk_size); if (rc != 0) GOTO(out, rc); /* NB: after llog_next_block() call the cur_offset is the * offset of the next block after read one. * The absolute offset of the current chunk is calculated * from cur_offset value and stored in chunk_offset variable. */ tmp_offset = cur_offset; if (do_div(tmp_offset, chunk_size) != 0) { partial_chunk = true; chunk_offset = cur_offset & ~(chunk_size - 1); } else { partial_chunk = false; chunk_offset = cur_offset - chunk_size; } /* NB: when rec->lrh_len is accessed it is already swabbed * since it is used at the "end" of the loop and the rec * swabbing is done at the beginning of the loop. */ for (rec = (struct llog_rec_hdr *)(buf + buf_offset); (char *)rec < buf + chunk_size; rec = llog_rec_hdr_next(rec)) { CDEBUG(D_OTHER, "processing rec 0x%p type %#x\n", rec, rec->lrh_type); if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); CDEBUG(D_OTHER, "after swabbing, type=%#x idx=%d\n", rec->lrh_type, rec->lrh_index); /* for partial chunk the end of it is zeroed, check * for index 0 to distinguish it. */ if (partial_chunk && rec->lrh_index == 0) { /* concurrent llog_add() might add new records * while llog_processing, check this is not * the case and re-read the current chunk * otherwise. */ if (index > loghandle->lgh_last_idx) GOTO(out, rc = 0); CDEBUG(D_OTHER, "Re-read last llog buffer for " "new records, index %u, last %u\n", index, loghandle->lgh_last_idx); /* save offset inside buffer for the re-read */ buf_offset = (char *)rec - (char *)buf; cur_offset = chunk_offset; goto repeat; } if (rec->lrh_len == 0 || rec->lrh_len > chunk_size) { CWARN("invalid length %d in llog record for " "index %d/%d\n", rec->lrh_len, rec->lrh_index, index); GOTO(out, rc = -EINVAL); } if (rec->lrh_index < index) { CDEBUG(D_OTHER, "skipping lrh_index %d\n", rec->lrh_index); continue; } if (rec->lrh_index != index) { CERROR("%s: Invalid record: index %u but " "expected %u\n", loghandle->lgh_ctxt->loc_obd->obd_name, rec->lrh_index, index); GOTO(out, rc = -ERANGE); } CDEBUG(D_OTHER, "lrh_index: %d lrh_len: %d (%d remains)\n", rec->lrh_index, rec->lrh_len, (int)(buf + chunk_size - (char *)rec)); loghandle->lgh_cur_idx = rec->lrh_index; loghandle->lgh_cur_offset = (char *)rec - (char *)buf + chunk_offset; /* if set, process the callback on this record */ if (ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) { rc = lpi->lpi_cb(lpi->lpi_env, loghandle, rec, lpi->lpi_cbdata); last_called_index = index; if (rc == LLOG_PROC_BREAK) { GOTO(out, rc); } else if (rc == LLOG_DEL_RECORD) { rc = llog_cancel_rec(lpi->lpi_env, loghandle, rec->lrh_index); } if (rc) GOTO(out, rc); } /* exit if the last index is reached */ if (index >= last_index) GOTO(out, rc = 0); ++index; } } out: if (cd != NULL) cd->lpcd_last_idx = last_called_index; if (unlikely(rc == -EIO && loghandle->lgh_obj != NULL)) { if (dt_object_remote(loghandle->lgh_obj)) { /* If it is remote object, then -EIO might means * disconnection or eviction, let's return -EAGAIN, * so for update recovery log processing, it will * retry until the umount or abort recovery, see * lod_sub_recovery_thread() */ CERROR("%s retry remote llog process\n", loghandle->lgh_ctxt->loc_obd->obd_name); rc = -EAGAIN; } else { /* something bad happened to the processing of a local * llog file, probably I/O error or the log got * corrupted to be able to finally release the log we * discard any remaining bits in the header */ CERROR("Local llog found corrupted\n"); while (index <= last_index) { if (ext2_test_bit(index, LLOG_HDR_BITMAP(llh)) != 0) llog_cancel_rec(lpi->lpi_env, loghandle, index); index++; } rc = 0; } } OBD_FREE_LARGE(buf, chunk_size); lpi->lpi_rc = rc; return 0; }
/* returns negative on error; 0 if success; 1 if success & log destroyed */ int llog_cancel_rec(const struct lu_env *env, struct llog_handle *loghandle, int index) { struct llog_thread_info *lgi = llog_info(env); struct dt_device *dt; struct llog_log_hdr *llh = loghandle->lgh_hdr; struct thandle *th; int rc; int rc1; bool subtract_count = false; ENTRY; CDEBUG(D_RPCTRACE, "Canceling %d in log "DOSTID"\n", index, POSTID(&loghandle->lgh_id.lgl_oi)); if (index == 0) { CERROR("Can't cancel index 0 which is header\n"); RETURN(-EINVAL); } LASSERT(loghandle != NULL); LASSERT(loghandle->lgh_ctxt != NULL); LASSERT(loghandle->lgh_obj != NULL); dt = lu2dt_dev(loghandle->lgh_obj->do_lu.lo_dev); th = dt_trans_create(env, dt); if (IS_ERR(th)) RETURN(PTR_ERR(th)); rc = llog_declare_write_rec(env, loghandle, &llh->llh_hdr, index, th); if (rc < 0) GOTO(out_trans, rc); if ((llh->llh_flags & LLOG_F_ZAP_WHEN_EMPTY)) rc = llog_declare_destroy(env, loghandle, th); th->th_wait_submit = 1; rc = dt_trans_start_local(env, dt, th); if (rc < 0) GOTO(out_trans, rc); down_write(&loghandle->lgh_lock); /* clear bitmap */ mutex_lock(&loghandle->lgh_hdr_mutex); if (!ext2_clear_bit(index, LLOG_HDR_BITMAP(llh))) { CDEBUG(D_RPCTRACE, "Catalog index %u already clear?\n", index); GOTO(out_unlock, rc); } loghandle->lgh_hdr->llh_count--; subtract_count = true; /* Pass this index to llog_osd_write_rec(), which will use the index * to only update the necesary bitmap. */ lgi->lgi_cookie.lgc_index = index; /* update header */ rc = llog_write_rec(env, loghandle, &llh->llh_hdr, &lgi->lgi_cookie, LLOG_HEADER_IDX, th); if (rc != 0) GOTO(out_unlock, rc); if ((llh->llh_flags & LLOG_F_ZAP_WHEN_EMPTY) && (llh->llh_count == 1) && ((loghandle->lgh_last_idx == LLOG_HDR_BITMAP_SIZE(llh) - 1) || (loghandle->u.phd.phd_cat_handle != NULL && loghandle->u.phd.phd_cat_handle->u.chd.chd_current_log != loghandle))) { /* never try to destroy it again */ llh->llh_flags &= ~LLOG_F_ZAP_WHEN_EMPTY; rc = llog_trans_destroy(env, loghandle, th); if (rc < 0) { /* Sigh, can not destroy the final plain llog, but * the bitmap has been clearly, so the record can not * be accessed anymore, let's return 0 for now, and * the orphan will be handled by LFSCK. */ CERROR("%s: can't destroy empty llog #"DOSTID "#%08x: rc = %d\n", loghandle->lgh_ctxt->loc_obd->obd_name, POSTID(&loghandle->lgh_id.lgl_oi), loghandle->lgh_id.lgl_ogen, rc); GOTO(out_unlock, rc); } rc = LLOG_DEL_PLAIN; } out_unlock: mutex_unlock(&loghandle->lgh_hdr_mutex); up_write(&loghandle->lgh_lock); out_trans: rc1 = dt_trans_stop(env, dt, th); if (rc == 0) rc = rc1; if (rc < 0 && subtract_count) { mutex_lock(&loghandle->lgh_hdr_mutex); loghandle->lgh_hdr->llh_count++; ext2_set_bit(index, LLOG_HDR_BITMAP(llh)); mutex_unlock(&loghandle->lgh_hdr_mutex); } RETURN(rc); }
static int llog_process_thread(void *arg) { struct llog_process_info *lpi = arg; struct llog_handle *loghandle = lpi->lpi_loghandle; struct llog_log_hdr *llh = loghandle->lgh_hdr; struct llog_process_cat_data *cd = lpi->lpi_catdata; char *buf; u64 cur_offset, tmp_offset; int chunk_size; int rc = 0, index = 1, last_index; int saved_index = 0; int last_called_index = 0; if (!llh) return -EINVAL; cur_offset = llh->llh_hdr.lrh_len; chunk_size = llh->llh_hdr.lrh_len; /* expect chunk_size to be power of two */ LASSERT(is_power_of_2(chunk_size)); buf = libcfs_kvzalloc(chunk_size, GFP_NOFS); if (!buf) { lpi->lpi_rc = -ENOMEM; return 0; } if (cd) { last_called_index = cd->lpcd_first_idx; index = cd->lpcd_first_idx + 1; } if (cd && cd->lpcd_last_idx) last_index = cd->lpcd_last_idx; else last_index = LLOG_HDR_BITMAP_SIZE(llh) - 1; while (rc == 0) { unsigned int buf_offset = 0; struct llog_rec_hdr *rec; bool partial_chunk; off_t chunk_offset; /* skip records not set in bitmap */ while (index <= last_index && !ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) ++index; if (index > last_index) break; CDEBUG(D_OTHER, "index: %d last_index %d\n", index, last_index); repeat: /* get the buf with our target record; avoid old garbage */ memset(buf, 0, chunk_size); rc = llog_next_block(lpi->lpi_env, loghandle, &saved_index, index, &cur_offset, buf, chunk_size); if (rc) goto out; /* * NB: after llog_next_block() call the cur_offset is the * offset of the next block after read one. * The absolute offset of the current chunk is calculated * from cur_offset value and stored in chunk_offset variable. */ tmp_offset = cur_offset; if (do_div(tmp_offset, chunk_size)) { partial_chunk = true; chunk_offset = cur_offset & ~(chunk_size - 1); } else { partial_chunk = false; chunk_offset = cur_offset - chunk_size; } /* NB: when rec->lrh_len is accessed it is already swabbed * since it is used at the "end" of the loop and the rec * swabbing is done at the beginning of the loop. */ for (rec = (struct llog_rec_hdr *)(buf + buf_offset); (char *)rec < buf + chunk_size; rec = llog_rec_hdr_next(rec)) { CDEBUG(D_OTHER, "processing rec 0x%p type %#x\n", rec, rec->lrh_type); if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); CDEBUG(D_OTHER, "after swabbing, type=%#x idx=%d\n", rec->lrh_type, rec->lrh_index); /* * for partial chunk the end of it is zeroed, check * for index 0 to distinguish it. */ if (partial_chunk && !rec->lrh_index) { /* concurrent llog_add() might add new records * while llog_processing, check this is not * the case and re-read the current chunk * otherwise. */ if (index > loghandle->lgh_last_idx) { rc = 0; goto out; } CDEBUG(D_OTHER, "Re-read last llog buffer for new records, index %u, last %u\n", index, loghandle->lgh_last_idx); /* save offset inside buffer for the re-read */ buf_offset = (char *)rec - (char *)buf; cur_offset = chunk_offset; goto repeat; } if (!rec->lrh_len || rec->lrh_len > chunk_size) { CWARN("invalid length %d in llog record for index %d/%d\n", rec->lrh_len, rec->lrh_index, index); rc = -EINVAL; goto out; } if (rec->lrh_index < index) { CDEBUG(D_OTHER, "skipping lrh_index %d\n", rec->lrh_index); continue; } if (rec->lrh_index != index) { CERROR("%s: Invalid record: index %u but expected %u\n", loghandle->lgh_ctxt->loc_obd->obd_name, rec->lrh_index, index); rc = -ERANGE; goto out; } CDEBUG(D_OTHER, "lrh_index: %d lrh_len: %d (%d remains)\n", rec->lrh_index, rec->lrh_len, (int)(buf + chunk_size - (char *)rec)); loghandle->lgh_cur_idx = rec->lrh_index; loghandle->lgh_cur_offset = (char *)rec - (char *)buf + chunk_offset; /* if set, process the callback on this record */ if (ext2_test_bit(index, LLOG_HDR_BITMAP(llh))) { rc = lpi->lpi_cb(lpi->lpi_env, loghandle, rec, lpi->lpi_cbdata); last_called_index = index; if (rc) goto out; } /* exit if the last index is reached */ if (index >= last_index) { rc = 0; goto out; } index++; } } out: if (cd) cd->lpcd_last_idx = last_called_index; kfree(buf); lpi->lpi_rc = rc; return 0; }