示例#1
0
/**
 * 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);
}
示例#2
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;
	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;
}
示例#3
0
文件: llog.c 项目: Announcement/linux
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;
}