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