1/* 2 * Xpram.c -- the S/390 expanded memory RAM-disk 3 * 4 * significant parts of this code are based on 5 * the sbull device driver presented in 6 * A. Rubini: Linux Device Drivers 7 * 8 * Author of XPRAM specific coding: Reinhard Buendgen 9 * buendgen@de.ibm.com 10 * Rewrite for 2.5: Martin Schwidefsky <schwidefsky@de.ibm.com> 11 * 12 * External interfaces: 13 * Interfaces to linux kernel 14 * xpram_setup: read kernel parameters 15 * Device specific file operations 16 * xpram_iotcl 17 * xpram_open 18 * 19 * "ad-hoc" partitioning: 20 * the expanded memory can be partitioned among several devices 21 * (with different minors). The partitioning set up can be 22 * set by kernel or module parameters (int devs & int sizes[]) 23 * 24 * Potential future improvements: 25 * generic hard disk support to replace ad-hoc partitioning 26 */ 27 28#include <linux/module.h> 29#include <linux/moduleparam.h> 30#include <linux/ctype.h> /* isdigit, isxdigit */ 31#include <linux/errno.h> 32#include <linux/init.h> 33#include <linux/slab.h> 34#include <linux/blkdev.h> 35#include <linux/blkpg.h> 36#include <linux/hdreg.h> /* HDIO_GETGEO */ 37#include <linux/sysdev.h> 38#include <linux/bio.h> 39#include <asm/uaccess.h> 40 41#define XPRAM_NAME "xpram" 42#define XPRAM_DEVS 1 /* one partition */ 43#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */ 44 45#define PRINT_DEBUG(x...) printk(KERN_DEBUG XPRAM_NAME " debug:" x) 46#define PRINT_INFO(x...) printk(KERN_INFO XPRAM_NAME " info:" x) 47#define PRINT_WARN(x...) printk(KERN_WARNING XPRAM_NAME " warning:" x) 48#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x) 49 50 51typedef struct { 52 unsigned int size; /* size of xpram segment in pages */ 53 unsigned int offset; /* start page of xpram segment */ 54} xpram_device_t; 55 56static xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; 57static unsigned int xpram_sizes[XPRAM_MAX_DEVS]; 58static struct gendisk *xpram_disks[XPRAM_MAX_DEVS]; 59static unsigned int xpram_pages; 60static int xpram_devs; 61 62/* 63 * Parameter parsing functions. 64 */ 65static int __initdata devs = XPRAM_DEVS; 66static char __initdata *sizes[XPRAM_MAX_DEVS]; 67 68module_param(devs, int, 0); 69module_param_array(sizes, charp, NULL, 0); 70 71MODULE_PARM_DESC(devs, "number of devices (\"partitions\"), " \ 72 "the default is " __MODULE_STRING(XPRAM_DEVS) "\n"); 73MODULE_PARM_DESC(sizes, "list of device (partition) sizes " \ 74 "the defaults are 0s \n" \ 75 "All devices with size 0 equally partition the " 76 "remaining space on the expanded strorage not " 77 "claimed by explicit sizes\n"); 78MODULE_LICENSE("GPL"); 79 80/* 81 * Copy expanded memory page (4kB) into main memory 82 * Arguments 83 * page_addr: address of target page 84 * xpage_index: index of expandeded memory page 85 * Return value 86 * 0: if operation succeeds 87 * -EIO: if pgin failed 88 * -ENXIO: if xpram has vanished 89 */ 90static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index) 91{ 92 int cc = 2; /* return unused cc 2 if pgin traps */ 93 94 asm volatile( 95 " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */ 96 "0: ipm %0\n" 97 " srl %0,28\n" 98 "1:\n" 99 EX_TABLE(0b,1b) 100 : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); 101 if (cc == 3) 102 return -ENXIO; 103 if (cc == 2) { 104 PRINT_ERR("expanded storage lost!\n"); 105 return -ENXIO; 106 } 107 if (cc == 1) { 108 PRINT_ERR("page in failed for page index %u.\n", 109 xpage_index); 110 return -EIO; 111 } 112 return 0; 113} 114 115/* 116 * Copy a 4kB page of main memory to an expanded memory page 117 * Arguments 118 * page_addr: address of source page 119 * xpage_index: index of expandeded memory page 120 * Return value 121 * 0: if operation succeeds 122 * -EIO: if pgout failed 123 * -ENXIO: if xpram has vanished 124 */ 125static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) 126{ 127 int cc = 2; /* return unused cc 2 if pgin traps */ 128 129 asm volatile( 130 " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */ 131 "0: ipm %0\n" 132 " srl %0,28\n" 133 "1:\n" 134 EX_TABLE(0b,1b) 135 : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); 136 if (cc == 3) 137 return -ENXIO; 138 if (cc == 2) { 139 PRINT_ERR("expanded storage lost!\n"); 140 return -ENXIO; 141 } 142 if (cc == 1) { 143 PRINT_ERR("page out failed for page index %u.\n", 144 xpage_index); 145 return -EIO; 146 } 147 return 0; 148} 149 150/* 151 * Check if xpram is available. 152 */ 153static int __init xpram_present(void) 154{ 155 unsigned long mem_page; 156 int rc; 157 158 mem_page = (unsigned long) __get_free_page(GFP_KERNEL); 159 if (!mem_page) 160 return -ENOMEM; 161 rc = xpram_page_in(mem_page, 0); 162 free_page(mem_page); 163 return rc ? -ENXIO : 0; 164} 165 166/* 167 * Return index of the last available xpram page. 168 */ 169static unsigned long __init xpram_highest_page_index(void) 170{ 171 unsigned int page_index, add_bit; 172 unsigned long mem_page; 173 174 mem_page = (unsigned long) __get_free_page(GFP_KERNEL); 175 if (!mem_page) 176 return 0; 177 178 page_index = 0; 179 add_bit = 1ULL << (sizeof(unsigned int)*8 - 1); 180 while (add_bit > 0) { 181 if (xpram_page_in(mem_page, page_index | add_bit) == 0) 182 page_index |= add_bit; 183 add_bit >>= 1; 184 } 185 186 free_page (mem_page); 187 188 return page_index; 189} 190 191/* 192 * Block device make request function. 193 */ 194static int xpram_make_request(request_queue_t *q, struct bio *bio) 195{ 196 xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data; 197 struct bio_vec *bvec; 198 unsigned int index; 199 unsigned long page_addr; 200 unsigned long bytes; 201 int i; 202 203 if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0) 204 /* Request is not page-aligned. */ 205 goto fail; 206 if ((bio->bi_size >> 12) > xdev->size) 207 /* Request size is no page-aligned. */ 208 goto fail; 209 if ((bio->bi_sector >> 3) > 0xffffffffU - xdev->offset) 210 goto fail; 211 index = (bio->bi_sector >> 3) + xdev->offset; 212 bio_for_each_segment(bvec, bio, i) { 213 page_addr = (unsigned long) 214 kmap(bvec->bv_page) + bvec->bv_offset; 215 bytes = bvec->bv_len; 216 if ((page_addr & 4095) != 0 || (bytes & 4095) != 0) 217 /* More paranoia. */ 218 goto fail; 219 while (bytes > 0) { 220 if (bio_data_dir(bio) == READ) { 221 if (xpram_page_in(page_addr, index) != 0) 222 goto fail; 223 } else { 224 if (xpram_page_out(page_addr, index) != 0) 225 goto fail; 226 } 227 page_addr += 4096; 228 bytes -= 4096; 229 index++; 230 } 231 } 232 set_bit(BIO_UPTODATE, &bio->bi_flags); 233 bytes = bio->bi_size; 234 bio->bi_size = 0; 235 bio->bi_end_io(bio, bytes, 0); 236 return 0; 237fail: 238 bio_io_error(bio, bio->bi_size); 239 return 0; 240} 241 242static int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo) 243{ 244 unsigned long size; 245 246 /* 247 * get geometry: we have to fake one... trim the size to a 248 * multiple of 64 (32k): tell we have 16 sectors, 4 heads, 249 * whatever cylinders. Tell also that data starts at sector. 4. 250 */ 251 size = (xpram_pages * 8) & ~0x3f; 252 geo->cylinders = size >> 6; 253 geo->heads = 4; 254 geo->sectors = 16; 255 geo->start = 4; 256 return 0; 257} 258 259static struct block_device_operations xpram_devops = 260{ 261 .owner = THIS_MODULE, 262 .getgeo = xpram_getgeo, 263}; 264 265/* 266 * Setup xpram_sizes array. 267 */ 268static int __init xpram_setup_sizes(unsigned long pages) 269{ 270 unsigned long mem_needed; 271 unsigned long mem_auto; 272 unsigned long long size; 273 int mem_auto_no; 274 int i; 275 276 /* Check number of devices. */ 277 if (devs <= 0 || devs > XPRAM_MAX_DEVS) { 278 PRINT_ERR("invalid number %d of devices\n",devs); 279 return -EINVAL; 280 } 281 xpram_devs = devs; 282 283 /* 284 * Copy sizes array to xpram_sizes and align partition 285 * sizes to page boundary. 286 */ 287 mem_needed = 0; 288 mem_auto_no = 0; 289 for (i = 0; i < xpram_devs; i++) { 290 if (sizes[i]) { 291 size = simple_strtoull(sizes[i], &sizes[i], 0); 292 switch (sizes[i][0]) { 293 case 'g': 294 case 'G': 295 size <<= 20; 296 break; 297 case 'm': 298 case 'M': 299 size <<= 10; 300 } 301 xpram_sizes[i] = (size + 3) & -4UL; 302 } 303 if (xpram_sizes[i]) 304 mem_needed += xpram_sizes[i]; 305 else 306 mem_auto_no++; 307 } 308 309 PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs); 310 for (i = 0; i < xpram_devs; i++) { 311 if (xpram_sizes[i]) 312 PRINT_INFO(" size of partition %d: %u kB\n", 313 i, xpram_sizes[i]); 314 else 315 PRINT_INFO(" size of partition %d to be set " 316 "automatically\n",i); 317 } 318 PRINT_DEBUG(" memory needed (for sized partitions): %lu kB\n", 319 mem_needed); 320 PRINT_DEBUG(" partitions to be sized automatically: %d\n", 321 mem_auto_no); 322 323 if (mem_needed > pages * 4) { 324 PRINT_ERR("Not enough expanded memory available\n"); 325 return -EINVAL; 326 } 327 328 /* 329 * partitioning: 330 * xpram_sizes[i] != 0; partition i has size xpram_sizes[i] kB 331 * else: ; all partitions with zero xpram_sizes[i] 332 * partition equally the remaining space 333 */ 334 if (mem_auto_no) { 335 mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4; 336 PRINT_INFO(" automatically determined " 337 "partition size: %lu kB\n", mem_auto); 338 for (i = 0; i < xpram_devs; i++) 339 if (xpram_sizes[i] == 0) 340 xpram_sizes[i] = mem_auto; 341 } 342 return 0; 343} 344 345static struct request_queue *xpram_queue; 346 347static int __init xpram_setup_blkdev(void) 348{ 349 unsigned long offset; 350 int i, rc = -ENOMEM; 351 352 for (i = 0; i < xpram_devs; i++) { 353 struct gendisk *disk = alloc_disk(1); 354 if (!disk) 355 goto out; 356 xpram_disks[i] = disk; 357 } 358 359 /* 360 * Register xpram major. 361 */ 362 rc = register_blkdev(XPRAM_MAJOR, XPRAM_NAME); 363 if (rc < 0) 364 goto out; 365 366 /* 367 * Assign the other needed values: make request function, sizes and 368 * hardsect size. All the minor devices feature the same value. 369 */ 370 xpram_queue = blk_alloc_queue(GFP_KERNEL); 371 if (!xpram_queue) { 372 rc = -ENOMEM; 373 goto out_unreg; 374 } 375 blk_queue_make_request(xpram_queue, xpram_make_request); 376 blk_queue_hardsect_size(xpram_queue, 4096); 377 378 /* 379 * Setup device structures. 380 */ 381 offset = 0; 382 for (i = 0; i < xpram_devs; i++) { 383 struct gendisk *disk = xpram_disks[i]; 384 385 xpram_devices[i].size = xpram_sizes[i] / 4; 386 xpram_devices[i].offset = offset; 387 offset += xpram_devices[i].size; 388 disk->major = XPRAM_MAJOR; 389 disk->first_minor = i; 390 disk->fops = &xpram_devops; 391 disk->private_data = &xpram_devices[i]; 392 disk->queue = xpram_queue; 393 sprintf(disk->disk_name, "slram%d", i); 394 set_capacity(disk, xpram_sizes[i] << 1); 395 add_disk(disk); 396 } 397 398 return 0; 399out_unreg: 400 unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); 401out: 402 while (i--) 403 put_disk(xpram_disks[i]); 404 return rc; 405} 406 407/* 408 * Finally, the init/exit functions. 409 */ 410static void __exit xpram_exit(void) 411{ 412 int i; 413 for (i = 0; i < xpram_devs; i++) { 414 del_gendisk(xpram_disks[i]); 415 put_disk(xpram_disks[i]); 416 } 417 unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); 418 blk_cleanup_queue(xpram_queue); 419} 420 421static int __init xpram_init(void) 422{ 423 int rc; 424 425 /* Find out size of expanded memory. */ 426 if (xpram_present() != 0) { 427 PRINT_WARN("No expanded memory available\n"); 428 return -ENODEV; 429 } 430 xpram_pages = xpram_highest_page_index() + 1; 431 PRINT_INFO(" %u pages expanded memory found (%lu KB).\n", 432 xpram_pages, (unsigned long) xpram_pages*4); 433 rc = xpram_setup_sizes(xpram_pages); 434 if (rc) 435 return rc; 436 return xpram_setup_blkdev(); 437} 438 439module_init(xpram_init); 440module_exit(xpram_exit); 441