/** * Remove optional fields that the client doesn't expect. * This is typically in order to ensure compatibility with older clients. * It is assumed that since we exclusively remove fields, the block will be * big enough to handle the remapped records. It is also assumed that records * of a block have the same format (i.e.: the same features enabled). * * \param[in,out] hdr Header of the block of records to remap. * \param[in,out] last_hdr Last header, don't read past this point. * \param[in] flags Flags describing the fields to keep. */ static void changelog_block_trim_ext(struct llog_rec_hdr *hdr, struct llog_rec_hdr *last_hdr, enum changelog_rec_flags flags) { if (hdr->lrh_type != CHANGELOG_REC) return; do { struct changelog_rec *rec = (struct changelog_rec *)(hdr + 1); changelog_remap_rec(rec, rec->cr_flags & flags); hdr = llog_rec_hdr_next(hdr); } while ((char *)hdr <= (char *)last_hdr); }
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; }
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; }