static int __blk_label_update()

in label.c [1057:1289]


static int __blk_label_update(struct nd_region *nd_region,
		struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk,
		int num_labels)
{
	int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO;
	struct nd_interleave_set *nd_set = nd_region->nd_set;
	struct nd_namespace_common *ndns = &nsblk->common;
	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
	struct nd_namespace_label *nd_label;
	struct nd_label_ent *label_ent, *e;
	struct nd_namespace_index *nsindex;
	unsigned long *free, *victim_map = NULL;
	struct resource *res, **old_res_list;
	struct nd_label_id label_id;
	int min_dpa_idx = 0;
	LIST_HEAD(list);
	u32 nslot, slot;

	if (!preamble_next(ndd, &nsindex, &free, &nslot))
		return -ENXIO;

	old_res_list = nsblk->res;
	nfree = nd_label_nfree(ndd);
	old_num_resources = nsblk->num_resources;
	nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL);

	/*
	 * We need to loop over the old resources a few times, which seems a
	 * bit inefficient, but we need to know that we have the label
	 * space before we start mutating the tracking structures.
	 * Otherwise the recovery method of last resort for userspace is
	 * disable and re-enable the parent region.
	 */
	alloc = 0;
	for_each_dpa_resource(ndd, res) {
		if (strcmp(res->name, label_id.id) != 0)
			continue;
		if (!is_old_resource(res, old_res_list, old_num_resources))
			alloc++;
	}

	victims = 0;
	if (old_num_resources) {
		/* convert old local-label-map to dimm-slot victim-map */
		victim_map = bitmap_zalloc(nslot, GFP_KERNEL);
		if (!victim_map)
			return -ENOMEM;

		/* mark unused labels for garbage collection */
		for_each_clear_bit_le(slot, free, nslot) {
			nd_label = to_label(ndd, slot);
			if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
				continue;
			res = to_resource(ndd, nd_label);
			if (res && is_old_resource(res, old_res_list,
						old_num_resources))
				continue;
			slot = to_slot(ndd, nd_label);
			set_bit(slot, victim_map);
			victims++;
		}
	}

	/* don't allow updates that consume the last label */
	if (nfree - alloc < 0 || nfree - alloc + victims < 1) {
		dev_info(&nsblk->common.dev, "insufficient label space\n");
		bitmap_free(victim_map);
		return -ENOSPC;
	}
	/* from here on we need to abort on error */


	/* assign all resources to the namespace before writing the labels */
	nsblk->res = NULL;
	nsblk->num_resources = 0;
	for_each_dpa_resource(ndd, res) {
		if (strcmp(res->name, label_id.id) != 0)
			continue;
		if (!nsblk_add_resource(nd_region, ndd, nsblk, res->start)) {
			rc = -ENOMEM;
			goto abort;
		}
	}

	/* release slots associated with any invalidated UUIDs */
	mutex_lock(&nd_mapping->lock);
	list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list)
		if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)) {
			reap_victim(nd_mapping, label_ent);
			list_move(&label_ent->list, &list);
		}
	mutex_unlock(&nd_mapping->lock);

	/*
	 * Find the resource associated with the first label in the set
	 * per the v1.2 namespace specification.
	 */
	for (i = 0; i < nsblk->num_resources; i++) {
		struct resource *min = nsblk->res[min_dpa_idx];

		res = nsblk->res[i];
		if (res->start < min->start)
			min_dpa_idx = i;
	}

	for (i = 0; i < nsblk->num_resources; i++) {
		size_t offset;

		res = nsblk->res[i];
		if (is_old_resource(res, old_res_list, old_num_resources))
			continue; /* carry-over */
		slot = nd_label_alloc_slot(ndd);
		if (slot == UINT_MAX) {
			rc = -ENXIO;
			goto abort;
		}
		dev_dbg(ndd->dev, "allocated: %d\n", slot);

		nd_label = to_label(ndd, slot);
		memset(nd_label, 0, sizeof_namespace_label(ndd));
		nsl_set_uuid(ndd, nd_label, nsblk->uuid);
		nsl_set_name(ndd, nd_label, nsblk->alt_name);
		nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL);

		nsl_set_blk_nlabel(ndd, nd_label, nsblk->num_resources,
				   i == min_dpa_idx);
		nsl_set_blk_position(ndd, nd_label, i == min_dpa_idx);
		nsl_set_blk_isetcookie(ndd, nd_label, nd_set->cookie2);

		nsl_set_dpa(ndd, nd_label, res->start);
		nsl_set_rawsize(ndd, nd_label, resource_size(res));
		nsl_set_lbasize(ndd, nd_label, nsblk->lbasize);
		nsl_set_slot(ndd, nd_label, slot);
		nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid);
		nsl_set_claim_class(ndd, nd_label, ndns->claim_class);
		nsl_calculate_checksum(ndd, nd_label);

		/* update label */
		offset = nd_label_offset(ndd, nd_label);
		rc = nvdimm_set_config_data(ndd, offset, nd_label,
				sizeof_namespace_label(ndd));
		if (rc < 0)
			goto abort;
	}

	/* free up now unused slots in the new index */
	for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) {
		dev_dbg(ndd->dev, "free: %d\n", slot);
		nd_label_free_slot(ndd, slot);
	}

	/* update index */
	rc = nd_label_write_index(ndd, ndd->ns_next,
			nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
	if (rc)
		goto abort;

	/*
	 * Now that the on-dimm labels are up to date, fix up the tracking
	 * entries in nd_mapping->labels
	 */
	nlabel = 0;
	mutex_lock(&nd_mapping->lock);
	list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
		nd_label = label_ent->label;
		if (!nd_label)
			continue;
		nlabel++;
		if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
			continue;
		nlabel--;
		list_move(&label_ent->list, &list);
		label_ent->label = NULL;
	}
	list_splice_tail_init(&list, &nd_mapping->labels);
	mutex_unlock(&nd_mapping->lock);

	if (nlabel + nsblk->num_resources > num_labels) {
		/*
		 * Bug, we can't end up with more resources than
		 * available labels
		 */
		WARN_ON_ONCE(1);
		rc = -ENXIO;
		goto out;
	}

	mutex_lock(&nd_mapping->lock);
	label_ent = list_first_entry_or_null(&nd_mapping->labels,
			typeof(*label_ent), list);
	if (!label_ent) {
		WARN_ON(1);
		mutex_unlock(&nd_mapping->lock);
		rc = -ENXIO;
		goto out;
	}
	for_each_clear_bit_le(slot, free, nslot) {
		nd_label = to_label(ndd, slot);
		if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
			continue;
		res = to_resource(ndd, nd_label);
		res->flags &= ~DPA_RESOURCE_ADJUSTED;
		dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot);
		list_for_each_entry_from(label_ent, &nd_mapping->labels, list) {
			if (label_ent->label)
				continue;
			label_ent->label = nd_label;
			nd_label = NULL;
			break;
		}
		if (nd_label)
			dev_WARN(&nsblk->common.dev,
					"failed to track label slot%d\n", slot);
	}
	mutex_unlock(&nd_mapping->lock);

 out:
	kfree(old_res_list);
	bitmap_free(victim_map);
	return rc;

 abort:
	/*
	 * 1/ repair the allocated label bitmap in the index
	 * 2/ restore the resource list
	 */
	nd_label_copy(ndd, nsindex, to_current_namespace_index(ndd));
	kfree(nsblk->res);
	nsblk->res = old_res_list;
	nsblk->num_resources = old_num_resources;
	old_res_list = NULL;
	goto out;
}