Blame view

drivers/dax/pmem.c 4.09 KB
ab68f2622   Dan Williams   /dev/dax, pmem: d...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * Copyright(c) 2016 Intel Corporation. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of version 2 of the GNU General Public License as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   */
  #include <linux/percpu-refcount.h>
  #include <linux/memremap.h>
  #include <linux/module.h>
  #include <linux/pfn_t.h>
  #include "../nvdimm/pfn.h"
  #include "../nvdimm/nd.h"
7b6be8444   Dan Williams   dax: refactor dax...
19
  #include "device-dax.h"
ab68f2622   Dan Williams   /dev/dax, pmem: d...
20
21
22
23
24
25
  
  struct dax_pmem {
  	struct device *dev;
  	struct percpu_ref ref;
  	struct completion cmp;
  };
ccdb07f62   Dan Williams   dax: cleanup need...
26
  static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
ab68f2622   Dan Williams   /dev/dax, pmem: d...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  {
  	return container_of(ref, struct dax_pmem, ref);
  }
  
  static void dax_pmem_percpu_release(struct percpu_ref *ref)
  {
  	struct dax_pmem *dax_pmem = to_dax_pmem(ref);
  
  	dev_dbg(dax_pmem->dev, "%s
  ", __func__);
  	complete(&dax_pmem->cmp);
  }
  
  static void dax_pmem_percpu_exit(void *data)
  {
  	struct percpu_ref *ref = data;
  	struct dax_pmem *dax_pmem = to_dax_pmem(ref);
  
  	dev_dbg(dax_pmem->dev, "%s
  ", __func__);
713897038   Dan Williams   mm, zone_device: ...
47
  	wait_for_completion(&dax_pmem->cmp);
ab68f2622   Dan Williams   /dev/dax, pmem: d...
48
  	percpu_ref_exit(ref);
ab68f2622   Dan Williams   /dev/dax, pmem: d...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  }
  
  static void dax_pmem_percpu_kill(void *data)
  {
  	struct percpu_ref *ref = data;
  	struct dax_pmem *dax_pmem = to_dax_pmem(ref);
  
  	dev_dbg(dax_pmem->dev, "%s
  ", __func__);
  	percpu_ref_kill(ref);
  }
  
  static int dax_pmem_probe(struct device *dev)
  {
ab68f2622   Dan Williams   /dev/dax, pmem: d...
63
64
  	void *addr;
  	struct resource res;
bbb3be170   Dan Williams   device-dax: fix s...
65
  	int rc, id, region_id;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
66
  	struct nd_pfn_sb *pfn_sb;
5f0694b30   Dan Williams   device-dax: renam...
67
  	struct dev_dax *dev_dax;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
68
  	struct dax_pmem *dax_pmem;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
69
70
71
72
73
74
75
76
77
78
79
80
81
  	struct nd_namespace_io *nsio;
  	struct dax_region *dax_region;
  	struct nd_namespace_common *ndns;
  	struct nd_dax *nd_dax = to_nd_dax(dev);
  	struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
  	struct vmem_altmap __altmap, *altmap = NULL;
  
  	ndns = nvdimm_namespace_common_probe(dev);
  	if (IS_ERR(ndns))
  		return PTR_ERR(ndns);
  	nsio = to_nd_namespace_io(&ndns->dev);
  
  	/* parse the 'pfn' info block via ->rw_bytes */
6a84fb4b4   Dan Williams   device-dax: check...
82
83
84
  	rc = devm_nsio_enable(dev, nsio);
  	if (rc)
  		return rc;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
85
86
87
88
89
90
91
92
  	altmap = nvdimm_setup_pfn(nd_pfn, &res, &__altmap);
  	if (IS_ERR(altmap))
  		return PTR_ERR(altmap);
  	devm_nsio_disable(dev, nsio);
  
  	pfn_sb = nd_pfn->pfn_sb;
  
  	if (!devm_request_mem_region(dev, nsio->res.start,
450c6633e   Dan Williams   libnvdimm: use co...
93
94
  				resource_size(&nsio->res),
  				dev_name(&ndns->dev))) {
ab68f2622   Dan Williams   /dev/dax, pmem: d...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  		dev_warn(dev, "could not reserve region %pR
  ", &nsio->res);
  		return -EBUSY;
  	}
  
  	dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL);
  	if (!dax_pmem)
  		return -ENOMEM;
  
  	dax_pmem->dev = dev;
  	init_completion(&dax_pmem->cmp);
  	rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0,
  			GFP_KERNEL);
  	if (rc)
  		return rc;
d1c8e0c52   Sajjan, Vikas C   dax: use devm_add...
110
111
112
  	rc = devm_add_action_or_reset(dev, dax_pmem_percpu_exit,
  							&dax_pmem->ref);
  	if (rc)
ab68f2622   Dan Williams   /dev/dax, pmem: d...
113
  		return rc;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
114
115
116
117
  
  	addr = devm_memremap_pages(dev, &res, &dax_pmem->ref, altmap);
  	if (IS_ERR(addr))
  		return PTR_ERR(addr);
d1c8e0c52   Sajjan, Vikas C   dax: use devm_add...
118
119
120
  	rc = devm_add_action_or_reset(dev, dax_pmem_percpu_kill,
  							&dax_pmem->ref);
  	if (rc)
ab68f2622   Dan Williams   /dev/dax, pmem: d...
121
  		return rc;
ab68f2622   Dan Williams   /dev/dax, pmem: d...
122

d0e584556   Dan Williams   dax: fix device-d...
123
124
  	/* adjust the dax_region resource to the start of data */
  	res.start += le64_to_cpu(pfn_sb->dataoff);
bbb3be170   Dan Williams   device-dax: fix s...
125
126
127
128
129
  	rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", &region_id, &id);
  	if (rc != 2)
  		return -EINVAL;
  
  	dax_region = alloc_dax_region(dev, region_id, &res,
ab68f2622   Dan Williams   /dev/dax, pmem: d...
130
131
132
133
134
  			le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
  	if (!dax_region)
  		return -ENOMEM;
  
  	/* TODO: support for subdividing a dax region... */
bbb3be170   Dan Williams   device-dax: fix s...
135
  	dev_dax = devm_create_dev_dax(dax_region, id, &res, 1);
ab68f2622   Dan Williams   /dev/dax, pmem: d...
136

5f0694b30   Dan Williams   device-dax: renam...
137
  	/* child dev_dax instances now own the lifetime of the dax_region */
ab68f2622   Dan Williams   /dev/dax, pmem: d...
138
  	dax_region_put(dax_region);
5f0694b30   Dan Williams   device-dax: renam...
139
  	return PTR_ERR_OR_ZERO(dev_dax);
ab68f2622   Dan Williams   /dev/dax, pmem: d...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  }
  
  static struct nd_device_driver dax_pmem_driver = {
  	.probe = dax_pmem_probe,
  	.drv = {
  		.name = "dax_pmem",
  	},
  	.type = ND_DRIVER_DAX_PMEM,
  };
  
  static int __init dax_pmem_init(void)
  {
  	return nd_driver_register(&dax_pmem_driver);
  }
  module_init(dax_pmem_init);
  
  static void __exit dax_pmem_exit(void)
  {
  	driver_unregister(&dax_pmem_driver.drv);
  }
  module_exit(dax_pmem_exit);
  
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Intel Corporation");
  MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);