drivers/nvdimm/badrange.c

Source file repositories/reference/linux-study-clean/drivers/nvdimm/badrange.c

File Facts

System
Linux kernel
Corpus path
drivers/nvdimm/badrange.c
Extension
.c
Size
7871 bytes
Lines
285
Domain
Driver Families
Bucket
drivers/nvdimm
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (bre->start == addr) {
			/* If length has changed, update this list entry */
			if (bre->length != length)
				bre->length = length;
			kfree(bre_new);
			return 0;
		}

	/*
	 * If not a duplicate or a simple length update, add the entry as is,
	 * as any overlapping ranges will get resolved when the list is consumed
	 * and converted to badblocks
	 */
	if (!bre_new)
		return -ENOMEM;
	append_badrange_entry(badrange, bre_new, addr, length);

	return 0;
}

int badrange_add(struct badrange *badrange, u64 addr, u64 length)
{
	int rc;

	spin_lock(&badrange->lock);
	rc = add_badrange(badrange, addr, length);
	spin_unlock(&badrange->lock);

	return rc;
}
EXPORT_SYMBOL_GPL(badrange_add);

void badrange_forget(struct badrange *badrange, phys_addr_t start,
		unsigned int len)
{
	struct list_head *badrange_list = &badrange->list;
	u64 clr_end = start + len - 1;
	struct badrange_entry *bre, *next;

	spin_lock(&badrange->lock);

	/*
	 * [start, clr_end] is the badrange interval being cleared.
	 * [bre->start, bre_end] is the badrange_list entry we're comparing
	 * the above interval against. The badrange list entry may need
	 * to be modified (update either start or length), deleted, or
	 * split into two based on the overlap characteristics
	 */

	list_for_each_entry_safe(bre, next, badrange_list, list) {
		u64 bre_end = bre->start + bre->length - 1;

		/* Skip intervals with no intersection */
		if (bre_end < start)
			continue;
		if (bre->start >  clr_end)
			continue;
		/* Delete completely overlapped badrange entries */
		if ((bre->start >= start) && (bre_end <= clr_end)) {
			list_del(&bre->list);
			kfree(bre);
			continue;
		}
		/* Adjust start point of partially cleared entries */
		if ((start <= bre->start) && (clr_end > bre->start)) {
			bre->length -= clr_end - bre->start + 1;
			bre->start = clr_end + 1;
			continue;
		}
		/* Adjust bre->length for partial clearing at the tail end */
		if ((bre->start < start) && (bre_end <= clr_end)) {
			/* bre->start remains the same */
			bre->length = start - bre->start;
			continue;
		}
		/*
		 * If clearing in the middle of an entry, we split it into
		 * two by modifying the current entry to represent one half of
		 * the split, and adding a new entry for the second half.
		 */
		if ((bre->start < start) && (bre_end > clr_end)) {
			u64 new_start = clr_end + 1;
			u64 new_len = bre_end - new_start + 1;

			/* Add new entry covering the right half */
			alloc_and_append_badrange_entry(badrange, new_start,
					new_len, GFP_NOWAIT);
			/* Adjust this entry to cover the left half */
			bre->length = start - bre->start;
			continue;

Annotation

Implementation Notes