xref: /openbmc/linux/drivers/nvdimm/claim.c (revision a8fe58ce)
1 /*
2  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  */
13 #include <linux/device.h>
14 #include <linux/sizes.h>
15 #include "nd-core.h"
16 #include "pfn.h"
17 #include "btt.h"
18 #include "nd.h"
19 
20 void __nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns)
21 {
22 	struct nd_namespace_common *ndns = *_ndns;
23 
24 	dev_WARN_ONCE(dev, !mutex_is_locked(&ndns->dev.mutex)
25 			|| ndns->claim != dev,
26 			"%s: invalid claim\n", __func__);
27 	ndns->claim = NULL;
28 	*_ndns = NULL;
29 	put_device(&ndns->dev);
30 }
31 
32 void nd_detach_ndns(struct device *dev,
33 		struct nd_namespace_common **_ndns)
34 {
35 	struct nd_namespace_common *ndns = *_ndns;
36 
37 	if (!ndns)
38 		return;
39 	get_device(&ndns->dev);
40 	device_lock(&ndns->dev);
41 	__nd_detach_ndns(dev, _ndns);
42 	device_unlock(&ndns->dev);
43 	put_device(&ndns->dev);
44 }
45 
46 bool __nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
47 		struct nd_namespace_common **_ndns)
48 {
49 	if (attach->claim)
50 		return false;
51 	dev_WARN_ONCE(dev, !mutex_is_locked(&attach->dev.mutex)
52 			|| *_ndns,
53 			"%s: invalid claim\n", __func__);
54 	attach->claim = dev;
55 	*_ndns = attach;
56 	get_device(&attach->dev);
57 	return true;
58 }
59 
60 bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
61 		struct nd_namespace_common **_ndns)
62 {
63 	bool claimed;
64 
65 	device_lock(&attach->dev);
66 	claimed = __nd_attach_ndns(dev, attach, _ndns);
67 	device_unlock(&attach->dev);
68 	return claimed;
69 }
70 
71 static int namespace_match(struct device *dev, void *data)
72 {
73 	char *name = data;
74 
75 	return strcmp(name, dev_name(dev)) == 0;
76 }
77 
78 static bool is_idle(struct device *dev, struct nd_namespace_common *ndns)
79 {
80 	struct nd_region *nd_region = to_nd_region(dev->parent);
81 	struct device *seed = NULL;
82 
83 	if (is_nd_btt(dev))
84 		seed = nd_region->btt_seed;
85 	else if (is_nd_pfn(dev))
86 		seed = nd_region->pfn_seed;
87 
88 	if (seed == dev || ndns || dev->driver)
89 		return false;
90 	return true;
91 }
92 
93 static void nd_detach_and_reset(struct device *dev,
94 		struct nd_namespace_common **_ndns)
95 {
96 	/* detach the namespace and destroy / reset the device */
97 	nd_detach_ndns(dev, _ndns);
98 	if (is_idle(dev, *_ndns)) {
99 		nd_device_unregister(dev, ND_ASYNC);
100 	} else if (is_nd_btt(dev)) {
101 		struct nd_btt *nd_btt = to_nd_btt(dev);
102 
103 		nd_btt->lbasize = 0;
104 		kfree(nd_btt->uuid);
105 		nd_btt->uuid = NULL;
106 	} else if (is_nd_pfn(dev)) {
107 		struct nd_pfn *nd_pfn = to_nd_pfn(dev);
108 
109 		kfree(nd_pfn->uuid);
110 		nd_pfn->uuid = NULL;
111 		nd_pfn->mode = PFN_MODE_NONE;
112 	}
113 }
114 
115 ssize_t nd_namespace_store(struct device *dev,
116 		struct nd_namespace_common **_ndns, const char *buf,
117 		size_t len)
118 {
119 	struct nd_namespace_common *ndns;
120 	struct device *found;
121 	char *name;
122 
123 	if (dev->driver) {
124 		dev_dbg(dev, "%s: -EBUSY\n", __func__);
125 		return -EBUSY;
126 	}
127 
128 	name = kstrndup(buf, len, GFP_KERNEL);
129 	if (!name)
130 		return -ENOMEM;
131 	strim(name);
132 
133 	if (strncmp(name, "namespace", 9) == 0 || strcmp(name, "") == 0)
134 		/* pass */;
135 	else {
136 		len = -EINVAL;
137 		goto out;
138 	}
139 
140 	ndns = *_ndns;
141 	if (strcmp(name, "") == 0) {
142 		nd_detach_and_reset(dev, _ndns);
143 		goto out;
144 	} else if (ndns) {
145 		dev_dbg(dev, "namespace already set to: %s\n",
146 				dev_name(&ndns->dev));
147 		len = -EBUSY;
148 		goto out;
149 	}
150 
151 	found = device_find_child(dev->parent, name, namespace_match);
152 	if (!found) {
153 		dev_dbg(dev, "'%s' not found under %s\n", name,
154 				dev_name(dev->parent));
155 		len = -ENODEV;
156 		goto out;
157 	}
158 
159 	ndns = to_ndns(found);
160 	if (__nvdimm_namespace_capacity(ndns) < SZ_16M) {
161 		dev_dbg(dev, "%s too small to host\n", name);
162 		len = -ENXIO;
163 		goto out_attach;
164 	}
165 
166 	WARN_ON_ONCE(!is_nvdimm_bus_locked(dev));
167 	if (!nd_attach_ndns(dev, ndns, _ndns)) {
168 		dev_dbg(dev, "%s already claimed\n",
169 				dev_name(&ndns->dev));
170 		len = -EBUSY;
171 	}
172 
173  out_attach:
174 	put_device(&ndns->dev); /* from device_find_child */
175  out:
176 	kfree(name);
177 	return len;
178 }
179 
180 /*
181  * nd_sb_checksum: compute checksum for a generic info block
182  *
183  * Returns a fletcher64 checksum of everything in the given info block
184  * except the last field (since that's where the checksum lives).
185  */
186 u64 nd_sb_checksum(struct nd_gen_sb *nd_gen_sb)
187 {
188 	u64 sum;
189 	__le64 sum_save;
190 
191 	BUILD_BUG_ON(sizeof(struct btt_sb) != SZ_4K);
192 	BUILD_BUG_ON(sizeof(struct nd_pfn_sb) != SZ_4K);
193 	BUILD_BUG_ON(sizeof(struct nd_gen_sb) != SZ_4K);
194 
195 	sum_save = nd_gen_sb->checksum;
196 	nd_gen_sb->checksum = 0;
197 	sum = nd_fletcher64(nd_gen_sb, sizeof(*nd_gen_sb), 1);
198 	nd_gen_sb->checksum = sum_save;
199 	return sum;
200 }
201 EXPORT_SYMBOL(nd_sb_checksum);
202