drivers/dax/fsdev.c

Source file repositories/reference/linux-study-clean/drivers/dax/fsdev.c

File Facts

System
Linux kernel
Corpus path
drivers/dax/fsdev.c
Extension
.c
Size
9434 bytes
Lines
350
Domain
Driver Families
Bucket
drivers/dax
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern 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

static const struct file_operations fsdev_fops = {
	.llseek = noop_llseek,
	.owner = THIS_MODULE,
	.open = fsdev_open,
	.release = fsdev_release,
};

static int fsdev_dax_probe(struct dev_dax *dev_dax)
{
	struct dax_device *dax_dev = dev_dax->dax_dev;
	struct device *dev = &dev_dax->dev;
	struct dev_pagemap *pgmap;
	struct inode *inode;
	u64 data_offset = 0;
	struct cdev *cdev;
	void *addr;
	int rc, i;

	if (static_dev_dax(dev_dax)) {
		if (dev_dax->nr_range > 1) {
			dev_warn(dev, "static pgmap / multi-range device conflict\n");
			return -EINVAL;
		}

		pgmap = dev_dax->pgmap;
	} else {
		size_t pgmap_size;

		if (dev_dax->pgmap) {
			dev_warn(dev, "dynamic-dax with pre-populated page map\n");
			return -EINVAL;
		}

		pgmap_size = struct_size(pgmap, ranges, dev_dax->nr_range - 1);
		pgmap = devm_kzalloc(dev, pgmap_size, GFP_KERNEL);
		if (!pgmap)
			return -ENOMEM;

		pgmap->nr_range = dev_dax->nr_range;
		dev_dax->pgmap = pgmap;

		for (i = 0; i < dev_dax->nr_range; i++) {
			struct range *range = &dev_dax->ranges[i].range;

			pgmap->ranges[i] = *range;
		}
	}

	for (i = 0; i < dev_dax->nr_range; i++) {
		struct range *range = &dev_dax->ranges[i].range;

		if (!devm_request_mem_region(dev, range->start,
					range_len(range), dev_name(dev))) {
			dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve range\n",
				 i, range->start, range->end);
			return -EBUSY;
		}
	}

	/* Cache size now; it cannot change while driver is bound */
	dev_dax->cached_size = 0;
	for (i = 0; i < dev_dax->nr_range; i++)
		dev_dax->cached_size += range_len(&dev_dax->ranges[i].range);

	/*
	 * Use MEMORY_DEVICE_FS_DAX without setting vmemmap_shift, leaving
	 * folios at order-0. Unlike device.c (MEMORY_DEVICE_GENERIC), this
	 * lets fs-dax dynamically build compound folios as needed, similar
	 * to pmem behavior.
	 */
	pgmap->type = MEMORY_DEVICE_FS_DAX;
	pgmap->ops = &fsdev_pagemap_ops;
	pgmap->owner = dev_dax;

	addr = devm_memremap_pages(dev, pgmap);
	if (IS_ERR(addr))
		return PTR_ERR(addr);

	/*
	 * Clear any stale compound folio state left over from a previous
	 * driver (e.g., device_dax with vmemmap_shift). Also register this
	 * as a devm action so folio state is cleared on unbind, ensuring
	 * clean pages for subsequent drivers (e.g., kmem for system-ram).
	 */
	fsdev_clear_folio_state(dev_dax);
	rc = devm_add_action_or_reset(dev, fsdev_clear_folio_state_action,
				      dev_dax);
	if (rc)
		return rc;

Annotation

Implementation Notes