Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cxl/region: Add interleave geometry attributes

Add ABI to allow the number of devices that comprise a region to be
set as well as the interleave granularity for the region.

Signed-off-by: Ben Widawsky <bwidawsk@kernel.org>
[djbw: reword changelog]
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/20220624041950.559155-11-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Ben Widawsky and committed by
Dan Williams
80d10a6c dd5ba0eb

+188
+21
Documentation/ABI/testing/sysfs-bus-cxl
··· 303 303 (RW) Write a unique identifier for the region. This field must 304 304 be set for persistent regions and it must not conflict with the 305 305 UUID of another region. 306 + 307 + 308 + What: /sys/bus/cxl/devices/regionZ/interleave_granularity 309 + Date: May, 2022 310 + KernelVersion: v5.20 311 + Contact: linux-cxl@vger.kernel.org 312 + Description: 313 + (RW) Set the number of consecutive bytes each device in the 314 + interleave set will claim. The possible interleave granularity 315 + values are determined by the CXL spec and the participating 316 + devices. 317 + 318 + 319 + What: /sys/bus/cxl/devices/regionZ/interleave_ways 320 + Date: May, 2022 321 + KernelVersion: v5.20 322 + Contact: linux-cxl@vger.kernel.org 323 + Description: 324 + (RW) Configures the number of devices participating in the 325 + region is set by writing this value. Each device will provide 326 + 1/interleave_ways of storage for the region.
+134
drivers/cxl/core/region.c
··· 7 7 #include <linux/slab.h> 8 8 #include <linux/uuid.h> 9 9 #include <linux/idr.h> 10 + #include <cxlmem.h> 10 11 #include <cxl.h> 11 12 #include "core.h" 12 13 ··· 22 21 * 23 22 * Region configuration has ordering constraints. UUID may be set at any time 24 23 * but is only visible for persistent regions. 24 + * 1. Interleave granularity 25 + * 2. Interleave size 25 26 */ 26 27 27 28 /* ··· 125 122 return a->mode; 126 123 } 127 124 125 + static ssize_t interleave_ways_show(struct device *dev, 126 + struct device_attribute *attr, char *buf) 127 + { 128 + struct cxl_region *cxlr = to_cxl_region(dev); 129 + struct cxl_region_params *p = &cxlr->params; 130 + ssize_t rc; 131 + 132 + rc = down_read_interruptible(&cxl_region_rwsem); 133 + if (rc) 134 + return rc; 135 + rc = sysfs_emit(buf, "%d\n", p->interleave_ways); 136 + up_read(&cxl_region_rwsem); 137 + 138 + return rc; 139 + } 140 + 141 + static ssize_t interleave_ways_store(struct device *dev, 142 + struct device_attribute *attr, 143 + const char *buf, size_t len) 144 + { 145 + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); 146 + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 147 + struct cxl_region *cxlr = to_cxl_region(dev); 148 + struct cxl_region_params *p = &cxlr->params; 149 + int rc, val; 150 + u8 iw; 151 + 152 + rc = kstrtoint(buf, 0, &val); 153 + if (rc) 154 + return rc; 155 + 156 + rc = ways_to_cxl(val, &iw); 157 + if (rc) 158 + return rc; 159 + 160 + /* 161 + * Even for x3, x9, and x12 interleaves the region interleave must be a 162 + * power of 2 multiple of the host bridge interleave. 163 + */ 164 + if (!is_power_of_2(val / cxld->interleave_ways) || 165 + (val % cxld->interleave_ways)) { 166 + dev_dbg(&cxlr->dev, "invalid interleave: %d\n", val); 167 + return -EINVAL; 168 + } 169 + 170 + rc = down_write_killable(&cxl_region_rwsem); 171 + if (rc) 172 + return rc; 173 + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { 174 + rc = -EBUSY; 175 + goto out; 176 + } 177 + 178 + p->interleave_ways = val; 179 + out: 180 + up_write(&cxl_region_rwsem); 181 + if (rc) 182 + return rc; 183 + return len; 184 + } 185 + static DEVICE_ATTR_RW(interleave_ways); 186 + 187 + static ssize_t interleave_granularity_show(struct device *dev, 188 + struct device_attribute *attr, 189 + char *buf) 190 + { 191 + struct cxl_region *cxlr = to_cxl_region(dev); 192 + struct cxl_region_params *p = &cxlr->params; 193 + ssize_t rc; 194 + 195 + rc = down_read_interruptible(&cxl_region_rwsem); 196 + if (rc) 197 + return rc; 198 + rc = sysfs_emit(buf, "%d\n", p->interleave_granularity); 199 + up_read(&cxl_region_rwsem); 200 + 201 + return rc; 202 + } 203 + 204 + static ssize_t interleave_granularity_store(struct device *dev, 205 + struct device_attribute *attr, 206 + const char *buf, size_t len) 207 + { 208 + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); 209 + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 210 + struct cxl_region *cxlr = to_cxl_region(dev); 211 + struct cxl_region_params *p = &cxlr->params; 212 + int rc, val; 213 + u16 ig; 214 + 215 + rc = kstrtoint(buf, 0, &val); 216 + if (rc) 217 + return rc; 218 + 219 + rc = granularity_to_cxl(val, &ig); 220 + if (rc) 221 + return rc; 222 + 223 + /* 224 + * Disallow region granularity less than root granularity to 225 + * simplify the implementation. Otherwise, region's with a 226 + * granularity less than the root interleave result in needing 227 + * multiple endpoints to support a single slot in the 228 + * interleave. 229 + */ 230 + if (val < cxld->interleave_granularity) 231 + return -EINVAL; 232 + 233 + rc = down_write_killable(&cxl_region_rwsem); 234 + if (rc) 235 + return rc; 236 + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { 237 + rc = -EBUSY; 238 + goto out; 239 + } 240 + 241 + p->interleave_granularity = val; 242 + out: 243 + up_write(&cxl_region_rwsem); 244 + if (rc) 245 + return rc; 246 + return len; 247 + } 248 + static DEVICE_ATTR_RW(interleave_granularity); 249 + 128 250 static struct attribute *cxl_region_attrs[] = { 129 251 &dev_attr_uuid.attr, 252 + &dev_attr_interleave_ways.attr, 253 + &dev_attr_interleave_granularity.attr, 130 254 NULL, 131 255 }; 132 256 ··· 346 216 enum cxl_decoder_type type) 347 217 { 348 218 struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent); 219 + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 220 + struct cxl_region_params *p; 349 221 struct cxl_region *cxlr; 350 222 struct device *dev; 351 223 int rc; ··· 355 223 cxlr = cxl_region_alloc(cxlrd, id); 356 224 if (IS_ERR(cxlr)) 357 225 return cxlr; 226 + p = &cxlr->params; 358 227 cxlr->mode = mode; 359 228 cxlr->type = type; 229 + p->interleave_granularity = cxld->interleave_granularity; 360 230 361 231 dev = &cxlr->dev; 362 232 rc = dev_set_name(dev, "region%d", id);
+33
drivers/cxl/cxl.h
··· 7 7 #include <linux/libnvdimm.h> 8 8 #include <linux/bitfield.h> 9 9 #include <linux/bitops.h> 10 + #include <linux/log2.h> 10 11 #include <linux/io.h> 11 12 12 13 /** ··· 90 89 return -EINVAL; 91 90 } 92 91 92 + return 0; 93 + } 94 + 95 + static inline int granularity_to_cxl(int g, u16 *ig) 96 + { 97 + if (g > SZ_16K || g < 256 || !is_power_of_2(g)) 98 + return -EINVAL; 99 + *ig = ilog2(g) - 8; 100 + return 0; 101 + } 102 + 103 + static inline int ways_to_cxl(int ways, u8 *iw) 104 + { 105 + if (ways > 16) 106 + return -EINVAL; 107 + if (is_power_of_2(ways)) { 108 + *iw = ilog2(ways); 109 + return 0; 110 + } 111 + if (ways % 3) 112 + return -EINVAL; 113 + ways /= 3; 114 + if (!is_power_of_2(ways)) 115 + return -EINVAL; 116 + *iw = ilog2(ways) + 8; 93 117 return 0; 94 118 } 95 119 ··· 324 298 /* 325 299 * enum cxl_config_state - State machine for region configuration 326 300 * @CXL_CONFIG_IDLE: Any sysfs attribute can be written freely 301 + * @CXL_CONFIG_INTERLEAVE_ACTIVE: region size has been set, no more 302 + * changes to interleave_ways or interleave_granularity 327 303 * @CXL_CONFIG_ACTIVE: All targets have been added the region is now 328 304 * active 329 305 */ 330 306 enum cxl_config_state { 331 307 CXL_CONFIG_IDLE, 308 + CXL_CONFIG_INTERLEAVE_ACTIVE, 332 309 CXL_CONFIG_ACTIVE, 333 310 }; 334 311 ··· 339 310 * struct cxl_region_params - region settings 340 311 * @state: allow the driver to lockdown further parameter changes 341 312 * @uuid: unique id for persistent regions 313 + * @interleave_ways: number of endpoints in the region 314 + * @interleave_granularity: capacity each endpoint contributes to a stripe 342 315 * 343 316 * State transitions are protected by the cxl_region_rwsem 344 317 */ 345 318 struct cxl_region_params { 346 319 enum cxl_config_state state; 347 320 uuid_t uuid; 321 + int interleave_ways; 322 + int interleave_granularity; 348 323 }; 349 324 350 325 /**