ia64/linux-2.6.18-xen.hg

annotate drivers/mtd/rfd_ftl.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 * rfd_ftl.c -- resident flash disk (flash translation layer)
ian@0 3 *
ian@0 4 * Copyright (C) 2005 Sean Young <sean@mess.org>
ian@0 5 *
ian@0 6 * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $
ian@0 7 *
ian@0 8 * This type of flash translation layer (FTL) is used by the Embedded BIOS
ian@0 9 * by General Software. It is known as the Resident Flash Disk (RFD), see:
ian@0 10 *
ian@0 11 * http://www.gensw.com/pages/prod/bios/rfd.htm
ian@0 12 *
ian@0 13 * based on ftl.c
ian@0 14 */
ian@0 15
ian@0 16 #include <linux/hdreg.h>
ian@0 17 #include <linux/init.h>
ian@0 18 #include <linux/mtd/blktrans.h>
ian@0 19 #include <linux/mtd/mtd.h>
ian@0 20 #include <linux/vmalloc.h>
ian@0 21 #include <linux/slab.h>
ian@0 22 #include <linux/jiffies.h>
ian@0 23
ian@0 24 #include <asm/types.h>
ian@0 25
ian@0 26 #define const_cpu_to_le16 __constant_cpu_to_le16
ian@0 27
ian@0 28 static int block_size = 0;
ian@0 29 module_param(block_size, int, 0);
ian@0 30 MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
ian@0 31
ian@0 32 #define PREFIX "rfd_ftl: "
ian@0 33
ian@0 34 /* This major has been assigned by device@lanana.org */
ian@0 35 #ifndef RFD_FTL_MAJOR
ian@0 36 #define RFD_FTL_MAJOR 256
ian@0 37 #endif
ian@0 38
ian@0 39 /* Maximum number of partitions in an FTL region */
ian@0 40 #define PART_BITS 4
ian@0 41
ian@0 42 /* An erase unit should start with this value */
ian@0 43 #define RFD_MAGIC 0x9193
ian@0 44
ian@0 45 /* the second value is 0xffff or 0xffc8; function unknown */
ian@0 46
ian@0 47 /* the third value is always 0xffff, ignored */
ian@0 48
ian@0 49 /* next is an array of mapping for each corresponding sector */
ian@0 50 #define HEADER_MAP_OFFSET 3
ian@0 51 #define SECTOR_DELETED 0x0000
ian@0 52 #define SECTOR_ZERO 0xfffe
ian@0 53 #define SECTOR_FREE 0xffff
ian@0 54
ian@0 55 #define SECTOR_SIZE 512
ian@0 56
ian@0 57 #define SECTORS_PER_TRACK 63
ian@0 58
ian@0 59 struct block {
ian@0 60 enum {
ian@0 61 BLOCK_OK,
ian@0 62 BLOCK_ERASING,
ian@0 63 BLOCK_ERASED,
ian@0 64 BLOCK_UNUSED,
ian@0 65 BLOCK_FAILED
ian@0 66 } state;
ian@0 67 int free_sectors;
ian@0 68 int used_sectors;
ian@0 69 int erases;
ian@0 70 u_long offset;
ian@0 71 };
ian@0 72
ian@0 73 struct partition {
ian@0 74 struct mtd_blktrans_dev mbd;
ian@0 75
ian@0 76 u_int block_size; /* size of erase unit */
ian@0 77 u_int total_blocks; /* number of erase units */
ian@0 78 u_int header_sectors_per_block; /* header sectors in erase unit */
ian@0 79 u_int data_sectors_per_block; /* data sectors in erase unit */
ian@0 80 u_int sector_count; /* sectors in translated disk */
ian@0 81 u_int header_size; /* bytes in header sector */
ian@0 82 int reserved_block; /* block next up for reclaim */
ian@0 83 int current_block; /* block to write to */
ian@0 84 u16 *header_cache; /* cached header */
ian@0 85
ian@0 86 int is_reclaiming;
ian@0 87 int cylinders;
ian@0 88 int errors;
ian@0 89 u_long *sector_map;
ian@0 90 struct block *blocks;
ian@0 91 };
ian@0 92
ian@0 93 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
ian@0 94
ian@0 95 static int build_block_map(struct partition *part, int block_no)
ian@0 96 {
ian@0 97 struct block *block = &part->blocks[block_no];
ian@0 98 int i;
ian@0 99
ian@0 100 block->offset = part->block_size * block_no;
ian@0 101
ian@0 102 if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
ian@0 103 block->state = BLOCK_UNUSED;
ian@0 104 return -ENOENT;
ian@0 105 }
ian@0 106
ian@0 107 block->state = BLOCK_OK;
ian@0 108
ian@0 109 for (i=0; i<part->data_sectors_per_block; i++) {
ian@0 110 u16 entry;
ian@0 111
ian@0 112 entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
ian@0 113
ian@0 114 if (entry == SECTOR_DELETED)
ian@0 115 continue;
ian@0 116
ian@0 117 if (entry == SECTOR_FREE) {
ian@0 118 block->free_sectors++;
ian@0 119 continue;
ian@0 120 }
ian@0 121
ian@0 122 if (entry == SECTOR_ZERO)
ian@0 123 entry = 0;
ian@0 124
ian@0 125 if (entry >= part->sector_count) {
ian@0 126 printk(KERN_WARNING PREFIX
ian@0 127 "'%s': unit #%d: entry %d corrupt, "
ian@0 128 "sector %d out of range\n",
ian@0 129 part->mbd.mtd->name, block_no, i, entry);
ian@0 130 continue;
ian@0 131 }
ian@0 132
ian@0 133 if (part->sector_map[entry] != -1) {
ian@0 134 printk(KERN_WARNING PREFIX
ian@0 135 "'%s': more than one entry for sector %d\n",
ian@0 136 part->mbd.mtd->name, entry);
ian@0 137 part->errors = 1;
ian@0 138 continue;
ian@0 139 }
ian@0 140
ian@0 141 part->sector_map[entry] = block->offset +
ian@0 142 (i + part->header_sectors_per_block) * SECTOR_SIZE;
ian@0 143
ian@0 144 block->used_sectors++;
ian@0 145 }
ian@0 146
ian@0 147 if (block->free_sectors == part->data_sectors_per_block)
ian@0 148 part->reserved_block = block_no;
ian@0 149
ian@0 150 return 0;
ian@0 151 }
ian@0 152
ian@0 153 static int scan_header(struct partition *part)
ian@0 154 {
ian@0 155 int sectors_per_block;
ian@0 156 int i, rc = -ENOMEM;
ian@0 157 int blocks_found;
ian@0 158 size_t retlen;
ian@0 159
ian@0 160 sectors_per_block = part->block_size / SECTOR_SIZE;
ian@0 161 part->total_blocks = part->mbd.mtd->size / part->block_size;
ian@0 162
ian@0 163 if (part->total_blocks < 2)
ian@0 164 return -ENOENT;
ian@0 165
ian@0 166 /* each erase block has three bytes header, followed by the map */
ian@0 167 part->header_sectors_per_block =
ian@0 168 ((HEADER_MAP_OFFSET + sectors_per_block) *
ian@0 169 sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
ian@0 170
ian@0 171 part->data_sectors_per_block = sectors_per_block -
ian@0 172 part->header_sectors_per_block;
ian@0 173
ian@0 174 part->header_size = (HEADER_MAP_OFFSET +
ian@0 175 part->data_sectors_per_block) * sizeof(u16);
ian@0 176
ian@0 177 part->cylinders = (part->data_sectors_per_block *
ian@0 178 (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
ian@0 179
ian@0 180 part->sector_count = part->cylinders * SECTORS_PER_TRACK;
ian@0 181
ian@0 182 part->current_block = -1;
ian@0 183 part->reserved_block = -1;
ian@0 184 part->is_reclaiming = 0;
ian@0 185
ian@0 186 part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
ian@0 187 if (!part->header_cache)
ian@0 188 goto err;
ian@0 189
ian@0 190 part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
ian@0 191 GFP_KERNEL);
ian@0 192 if (!part->blocks)
ian@0 193 goto err;
ian@0 194
ian@0 195 part->sector_map = vmalloc(part->sector_count * sizeof(u_long));
ian@0 196 if (!part->sector_map) {
ian@0 197 printk(KERN_ERR PREFIX "'%s': unable to allocate memory for "
ian@0 198 "sector map", part->mbd.mtd->name);
ian@0 199 goto err;
ian@0 200 }
ian@0 201
ian@0 202 for (i=0; i<part->sector_count; i++)
ian@0 203 part->sector_map[i] = -1;
ian@0 204
ian@0 205 for (i=0, blocks_found=0; i<part->total_blocks; i++) {
ian@0 206 rc = part->mbd.mtd->read(part->mbd.mtd,
ian@0 207 i * part->block_size, part->header_size,
ian@0 208 &retlen, (u_char*)part->header_cache);
ian@0 209
ian@0 210 if (!rc && retlen != part->header_size)
ian@0 211 rc = -EIO;
ian@0 212
ian@0 213 if (rc)
ian@0 214 goto err;
ian@0 215
ian@0 216 if (!build_block_map(part, i))
ian@0 217 blocks_found++;
ian@0 218 }
ian@0 219
ian@0 220 if (blocks_found == 0) {
ian@0 221 printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
ian@0 222 part->mbd.mtd->name);
ian@0 223 rc = -ENOENT;
ian@0 224 goto err;
ian@0 225 }
ian@0 226
ian@0 227 if (part->reserved_block == -1) {
ian@0 228 printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
ian@0 229 part->mbd.mtd->name);
ian@0 230
ian@0 231 part->errors = 1;
ian@0 232 }
ian@0 233
ian@0 234 return 0;
ian@0 235
ian@0 236 err:
ian@0 237 vfree(part->sector_map);
ian@0 238 kfree(part->header_cache);
ian@0 239 kfree(part->blocks);
ian@0 240
ian@0 241 return rc;
ian@0 242 }
ian@0 243
ian@0 244 static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
ian@0 245 {
ian@0 246 struct partition *part = (struct partition*)dev;
ian@0 247 u_long addr;
ian@0 248 size_t retlen;
ian@0 249 int rc;
ian@0 250
ian@0 251 if (sector >= part->sector_count)
ian@0 252 return -EIO;
ian@0 253
ian@0 254 addr = part->sector_map[sector];
ian@0 255 if (addr != -1) {
ian@0 256 rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE,
ian@0 257 &retlen, (u_char*)buf);
ian@0 258 if (!rc && retlen != SECTOR_SIZE)
ian@0 259 rc = -EIO;
ian@0 260
ian@0 261 if (rc) {
ian@0 262 printk(KERN_WARNING PREFIX "error reading '%s' at "
ian@0 263 "0x%lx\n", part->mbd.mtd->name, addr);
ian@0 264 return rc;
ian@0 265 }
ian@0 266 } else
ian@0 267 memset(buf, 0, SECTOR_SIZE);
ian@0 268
ian@0 269 return 0;
ian@0 270 }
ian@0 271
ian@0 272 static void erase_callback(struct erase_info *erase)
ian@0 273 {
ian@0 274 struct partition *part;
ian@0 275 u16 magic;
ian@0 276 int i, rc;
ian@0 277 size_t retlen;
ian@0 278
ian@0 279 part = (struct partition*)erase->priv;
ian@0 280
ian@0 281 i = erase->addr / part->block_size;
ian@0 282 if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) {
ian@0 283 printk(KERN_ERR PREFIX "erase callback for unknown offset %x "
ian@0 284 "on '%s'\n", erase->addr, part->mbd.mtd->name);
ian@0 285 return;
ian@0 286 }
ian@0 287
ian@0 288 if (erase->state != MTD_ERASE_DONE) {
ian@0 289 printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', "
ian@0 290 "state %d\n", erase->addr,
ian@0 291 part->mbd.mtd->name, erase->state);
ian@0 292
ian@0 293 part->blocks[i].state = BLOCK_FAILED;
ian@0 294 part->blocks[i].free_sectors = 0;
ian@0 295 part->blocks[i].used_sectors = 0;
ian@0 296
ian@0 297 kfree(erase);
ian@0 298
ian@0 299 return;
ian@0 300 }
ian@0 301
ian@0 302 magic = const_cpu_to_le16(RFD_MAGIC);
ian@0 303
ian@0 304 part->blocks[i].state = BLOCK_ERASED;
ian@0 305 part->blocks[i].free_sectors = part->data_sectors_per_block;
ian@0 306 part->blocks[i].used_sectors = 0;
ian@0 307 part->blocks[i].erases++;
ian@0 308
ian@0 309 rc = part->mbd.mtd->write(part->mbd.mtd,
ian@0 310 part->blocks[i].offset, sizeof(magic), &retlen,
ian@0 311 (u_char*)&magic);
ian@0 312
ian@0 313 if (!rc && retlen != sizeof(magic))
ian@0 314 rc = -EIO;
ian@0 315
ian@0 316 if (rc) {
ian@0 317 printk(KERN_ERR PREFIX "'%s': unable to write RFD "
ian@0 318 "header at 0x%lx\n",
ian@0 319 part->mbd.mtd->name,
ian@0 320 part->blocks[i].offset);
ian@0 321 part->blocks[i].state = BLOCK_FAILED;
ian@0 322 }
ian@0 323 else
ian@0 324 part->blocks[i].state = BLOCK_OK;
ian@0 325
ian@0 326 kfree(erase);
ian@0 327 }
ian@0 328
ian@0 329 static int erase_block(struct partition *part, int block)
ian@0 330 {
ian@0 331 struct erase_info *erase;
ian@0 332 int rc = -ENOMEM;
ian@0 333
ian@0 334 erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
ian@0 335 if (!erase)
ian@0 336 goto err;
ian@0 337
ian@0 338 erase->mtd = part->mbd.mtd;
ian@0 339 erase->callback = erase_callback;
ian@0 340 erase->addr = part->blocks[block].offset;
ian@0 341 erase->len = part->block_size;
ian@0 342 erase->priv = (u_long)part;
ian@0 343
ian@0 344 part->blocks[block].state = BLOCK_ERASING;
ian@0 345 part->blocks[block].free_sectors = 0;
ian@0 346
ian@0 347 rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
ian@0 348
ian@0 349 if (rc) {
ian@0 350 printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' "
ian@0 351 "failed\n", erase->addr, erase->len,
ian@0 352 part->mbd.mtd->name);
ian@0 353 kfree(erase);
ian@0 354 }
ian@0 355
ian@0 356 err:
ian@0 357 return rc;
ian@0 358 }
ian@0 359
ian@0 360 static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
ian@0 361 {
ian@0 362 void *sector_data;
ian@0 363 u16 *map;
ian@0 364 size_t retlen;
ian@0 365 int i, rc = -ENOMEM;
ian@0 366
ian@0 367 part->is_reclaiming = 1;
ian@0 368
ian@0 369 sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
ian@0 370 if (!sector_data)
ian@0 371 goto err3;
ian@0 372
ian@0 373 map = kmalloc(part->header_size, GFP_KERNEL);
ian@0 374 if (!map)
ian@0 375 goto err2;
ian@0 376
ian@0 377 rc = part->mbd.mtd->read(part->mbd.mtd,
ian@0 378 part->blocks[block_no].offset, part->header_size,
ian@0 379 &retlen, (u_char*)map);
ian@0 380
ian@0 381 if (!rc && retlen != part->header_size)
ian@0 382 rc = -EIO;
ian@0 383
ian@0 384 if (rc) {
ian@0 385 printk(KERN_ERR PREFIX "error reading '%s' at "
ian@0 386 "0x%lx\n", part->mbd.mtd->name,
ian@0 387 part->blocks[block_no].offset);
ian@0 388
ian@0 389 goto err;
ian@0 390 }
ian@0 391
ian@0 392 for (i=0; i<part->data_sectors_per_block; i++) {
ian@0 393 u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
ian@0 394 u_long addr;
ian@0 395
ian@0 396
ian@0 397 if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
ian@0 398 continue;
ian@0 399
ian@0 400 if (entry == SECTOR_ZERO)
ian@0 401 entry = 0;
ian@0 402
ian@0 403 /* already warned about and ignored in build_block_map() */
ian@0 404 if (entry >= part->sector_count)
ian@0 405 continue;
ian@0 406
ian@0 407 addr = part->blocks[block_no].offset +
ian@0 408 (i + part->header_sectors_per_block) * SECTOR_SIZE;
ian@0 409
ian@0 410 if (*old_sector == addr) {
ian@0 411 *old_sector = -1;
ian@0 412 if (!part->blocks[block_no].used_sectors--) {
ian@0 413 rc = erase_block(part, block_no);
ian@0 414 break;
ian@0 415 }
ian@0 416 continue;
ian@0 417 }
ian@0 418 rc = part->mbd.mtd->read(part->mbd.mtd, addr,
ian@0 419 SECTOR_SIZE, &retlen, sector_data);
ian@0 420
ian@0 421 if (!rc && retlen != SECTOR_SIZE)
ian@0 422 rc = -EIO;
ian@0 423
ian@0 424 if (rc) {
ian@0 425 printk(KERN_ERR PREFIX "'%s': Unable to "
ian@0 426 "read sector for relocation\n",
ian@0 427 part->mbd.mtd->name);
ian@0 428
ian@0 429 goto err;
ian@0 430 }
ian@0 431
ian@0 432 rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
ian@0 433 entry, sector_data);
ian@0 434
ian@0 435 if (rc)
ian@0 436 goto err;
ian@0 437 }
ian@0 438
ian@0 439 err:
ian@0 440 kfree(map);
ian@0 441 err2:
ian@0 442 kfree(sector_data);
ian@0 443 err3:
ian@0 444 part->is_reclaiming = 0;
ian@0 445
ian@0 446 return rc;
ian@0 447 }
ian@0 448
ian@0 449 static int reclaim_block(struct partition *part, u_long *old_sector)
ian@0 450 {
ian@0 451 int block, best_block, score, old_sector_block;
ian@0 452 int rc;
ian@0 453
ian@0 454 /* we have a race if sync doesn't exist */
ian@0 455 if (part->mbd.mtd->sync)
ian@0 456 part->mbd.mtd->sync(part->mbd.mtd);
ian@0 457
ian@0 458 score = 0x7fffffff; /* MAX_INT */
ian@0 459 best_block = -1;
ian@0 460 if (*old_sector != -1)
ian@0 461 old_sector_block = *old_sector / part->block_size;
ian@0 462 else
ian@0 463 old_sector_block = -1;
ian@0 464
ian@0 465 for (block=0; block<part->total_blocks; block++) {
ian@0 466 int this_score;
ian@0 467
ian@0 468 if (block == part->reserved_block)
ian@0 469 continue;
ian@0 470
ian@0 471 /*
ian@0 472 * Postpone reclaiming if there is a free sector as
ian@0 473 * more removed sectors is more efficient (have to move
ian@0 474 * less).
ian@0 475 */
ian@0 476 if (part->blocks[block].free_sectors)
ian@0 477 return 0;
ian@0 478
ian@0 479 this_score = part->blocks[block].used_sectors;
ian@0 480
ian@0 481 if (block == old_sector_block)
ian@0 482 this_score--;
ian@0 483 else {
ian@0 484 /* no point in moving a full block */
ian@0 485 if (part->blocks[block].used_sectors ==
ian@0 486 part->data_sectors_per_block)
ian@0 487 continue;
ian@0 488 }
ian@0 489
ian@0 490 this_score += part->blocks[block].erases;
ian@0 491
ian@0 492 if (this_score < score) {
ian@0 493 best_block = block;
ian@0 494 score = this_score;
ian@0 495 }
ian@0 496 }
ian@0 497
ian@0 498 if (best_block == -1)
ian@0 499 return -ENOSPC;
ian@0 500
ian@0 501 part->current_block = -1;
ian@0 502 part->reserved_block = best_block;
ian@0 503
ian@0 504 pr_debug("reclaim_block: reclaiming block #%d with %d used "
ian@0 505 "%d free sectors\n", best_block,
ian@0 506 part->blocks[best_block].used_sectors,
ian@0 507 part->blocks[best_block].free_sectors);
ian@0 508
ian@0 509 if (part->blocks[best_block].used_sectors)
ian@0 510 rc = move_block_contents(part, best_block, old_sector);
ian@0 511 else
ian@0 512 rc = erase_block(part, best_block);
ian@0 513
ian@0 514 return rc;
ian@0 515 }
ian@0 516
ian@0 517 /*
ian@0 518 * IMPROVE: It would be best to choose the block with the most deleted sectors,
ian@0 519 * because if we fill that one up first it'll have the most chance of having
ian@0 520 * the least live sectors at reclaim.
ian@0 521 */
ian@0 522 static int find_free_block(struct partition *part)
ian@0 523 {
ian@0 524 int block, stop;
ian@0 525
ian@0 526 block = part->current_block == -1 ?
ian@0 527 jiffies % part->total_blocks : part->current_block;
ian@0 528 stop = block;
ian@0 529
ian@0 530 do {
ian@0 531 if (part->blocks[block].free_sectors &&
ian@0 532 block != part->reserved_block)
ian@0 533 return block;
ian@0 534
ian@0 535 if (part->blocks[block].state == BLOCK_UNUSED)
ian@0 536 erase_block(part, block);
ian@0 537
ian@0 538 if (++block >= part->total_blocks)
ian@0 539 block = 0;
ian@0 540
ian@0 541 } while (block != stop);
ian@0 542
ian@0 543 return -1;
ian@0 544 }
ian@0 545
ian@0 546 static int find_writable_block(struct partition *part, u_long *old_sector)
ian@0 547 {
ian@0 548 int rc, block;
ian@0 549 size_t retlen;
ian@0 550
ian@0 551 block = find_free_block(part);
ian@0 552
ian@0 553 if (block == -1) {
ian@0 554 if (!part->is_reclaiming) {
ian@0 555 rc = reclaim_block(part, old_sector);
ian@0 556 if (rc)
ian@0 557 goto err;
ian@0 558
ian@0 559 block = find_free_block(part);
ian@0 560 }
ian@0 561
ian@0 562 if (block == -1) {
ian@0 563 rc = -ENOSPC;
ian@0 564 goto err;
ian@0 565 }
ian@0 566 }
ian@0 567
ian@0 568 rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset,
ian@0 569 part->header_size, &retlen, (u_char*)part->header_cache);
ian@0 570
ian@0 571 if (!rc && retlen != part->header_size)
ian@0 572 rc = -EIO;
ian@0 573
ian@0 574 if (rc) {
ian@0 575 printk(KERN_ERR PREFIX "'%s': unable to read header at "
ian@0 576 "0x%lx\n", part->mbd.mtd->name,
ian@0 577 part->blocks[block].offset);
ian@0 578 goto err;
ian@0 579 }
ian@0 580
ian@0 581 part->current_block = block;
ian@0 582
ian@0 583 err:
ian@0 584 return rc;
ian@0 585 }
ian@0 586
ian@0 587 static int mark_sector_deleted(struct partition *part, u_long old_addr)
ian@0 588 {
ian@0 589 int block, offset, rc;
ian@0 590 u_long addr;
ian@0 591 size_t retlen;
ian@0 592 u16 del = const_cpu_to_le16(SECTOR_DELETED);
ian@0 593
ian@0 594 block = old_addr / part->block_size;
ian@0 595 offset = (old_addr % part->block_size) / SECTOR_SIZE -
ian@0 596 part->header_sectors_per_block;
ian@0 597
ian@0 598 addr = part->blocks[block].offset +
ian@0 599 (HEADER_MAP_OFFSET + offset) * sizeof(u16);
ian@0 600 rc = part->mbd.mtd->write(part->mbd.mtd, addr,
ian@0 601 sizeof(del), &retlen, (u_char*)&del);
ian@0 602
ian@0 603 if (!rc && retlen != sizeof(del))
ian@0 604 rc = -EIO;
ian@0 605
ian@0 606 if (rc) {
ian@0 607 printk(KERN_ERR PREFIX "error writing '%s' at "
ian@0 608 "0x%lx\n", part->mbd.mtd->name, addr);
ian@0 609 if (rc)
ian@0 610 goto err;
ian@0 611 }
ian@0 612 if (block == part->current_block)
ian@0 613 part->header_cache[offset + HEADER_MAP_OFFSET] = del;
ian@0 614
ian@0 615 part->blocks[block].used_sectors--;
ian@0 616
ian@0 617 if (!part->blocks[block].used_sectors &&
ian@0 618 !part->blocks[block].free_sectors)
ian@0 619 rc = erase_block(part, block);
ian@0 620
ian@0 621 err:
ian@0 622 return rc;
ian@0 623 }
ian@0 624
ian@0 625 static int find_free_sector(const struct partition *part, const struct block *block)
ian@0 626 {
ian@0 627 int i, stop;
ian@0 628
ian@0 629 i = stop = part->data_sectors_per_block - block->free_sectors;
ian@0 630
ian@0 631 do {
ian@0 632 if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
ian@0 633 == SECTOR_FREE)
ian@0 634 return i;
ian@0 635
ian@0 636 if (++i == part->data_sectors_per_block)
ian@0 637 i = 0;
ian@0 638 }
ian@0 639 while(i != stop);
ian@0 640
ian@0 641 return -1;
ian@0 642 }
ian@0 643
ian@0 644 static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
ian@0 645 {
ian@0 646 struct partition *part = (struct partition*)dev;
ian@0 647 struct block *block;
ian@0 648 u_long addr;
ian@0 649 int i;
ian@0 650 int rc;
ian@0 651 size_t retlen;
ian@0 652 u16 entry;
ian@0 653
ian@0 654 if (part->current_block == -1 ||
ian@0 655 !part->blocks[part->current_block].free_sectors) {
ian@0 656
ian@0 657 rc = find_writable_block(part, old_addr);
ian@0 658 if (rc)
ian@0 659 goto err;
ian@0 660 }
ian@0 661
ian@0 662 block = &part->blocks[part->current_block];
ian@0 663
ian@0 664 i = find_free_sector(part, block);
ian@0 665
ian@0 666 if (i < 0) {
ian@0 667 rc = -ENOSPC;
ian@0 668 goto err;
ian@0 669 }
ian@0 670
ian@0 671 addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
ian@0 672 block->offset;
ian@0 673 rc = part->mbd.mtd->write(part->mbd.mtd,
ian@0 674 addr, SECTOR_SIZE, &retlen, (u_char*)buf);
ian@0 675
ian@0 676 if (!rc && retlen != SECTOR_SIZE)
ian@0 677 rc = -EIO;
ian@0 678
ian@0 679 if (rc) {
ian@0 680 printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
ian@0 681 part->mbd.mtd->name, addr);
ian@0 682 if (rc)
ian@0 683 goto err;
ian@0 684 }
ian@0 685
ian@0 686 part->sector_map[sector] = addr;
ian@0 687
ian@0 688 entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
ian@0 689
ian@0 690 part->header_cache[i + HEADER_MAP_OFFSET] = entry;
ian@0 691
ian@0 692 addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
ian@0 693 rc = part->mbd.mtd->write(part->mbd.mtd, addr,
ian@0 694 sizeof(entry), &retlen, (u_char*)&entry);
ian@0 695
ian@0 696 if (!rc && retlen != sizeof(entry))
ian@0 697 rc = -EIO;
ian@0 698
ian@0 699 if (rc) {
ian@0 700 printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
ian@0 701 part->mbd.mtd->name, addr);
ian@0 702 if (rc)
ian@0 703 goto err;
ian@0 704 }
ian@0 705 block->used_sectors++;
ian@0 706 block->free_sectors--;
ian@0 707
ian@0 708 err:
ian@0 709 return rc;
ian@0 710 }
ian@0 711
ian@0 712 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
ian@0 713 {
ian@0 714 struct partition *part = (struct partition*)dev;
ian@0 715 u_long old_addr;
ian@0 716 int i;
ian@0 717 int rc = 0;
ian@0 718
ian@0 719 pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
ian@0 720
ian@0 721 if (part->reserved_block == -1) {
ian@0 722 rc = -EACCES;
ian@0 723 goto err;
ian@0 724 }
ian@0 725
ian@0 726 if (sector >= part->sector_count) {
ian@0 727 rc = -EIO;
ian@0 728 goto err;
ian@0 729 }
ian@0 730
ian@0 731 old_addr = part->sector_map[sector];
ian@0 732
ian@0 733 for (i=0; i<SECTOR_SIZE; i++) {
ian@0 734 if (!buf[i])
ian@0 735 continue;
ian@0 736
ian@0 737 rc = do_writesect(dev, sector, buf, &old_addr);
ian@0 738 if (rc)
ian@0 739 goto err;
ian@0 740 break;
ian@0 741 }
ian@0 742
ian@0 743 if (i == SECTOR_SIZE)
ian@0 744 part->sector_map[sector] = -1;
ian@0 745
ian@0 746 if (old_addr != -1)
ian@0 747 rc = mark_sector_deleted(part, old_addr);
ian@0 748
ian@0 749 err:
ian@0 750 return rc;
ian@0 751 }
ian@0 752
ian@0 753 static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
ian@0 754 {
ian@0 755 struct partition *part = (struct partition*)dev;
ian@0 756
ian@0 757 geo->heads = 1;
ian@0 758 geo->sectors = SECTORS_PER_TRACK;
ian@0 759 geo->cylinders = part->cylinders;
ian@0 760
ian@0 761 return 0;
ian@0 762 }
ian@0 763
ian@0 764 static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
ian@0 765 {
ian@0 766 struct partition *part;
ian@0 767
ian@0 768 if (mtd->type != MTD_NORFLASH)
ian@0 769 return;
ian@0 770
ian@0 771 part = kcalloc(1, sizeof(struct partition), GFP_KERNEL);
ian@0 772 if (!part)
ian@0 773 return;
ian@0 774
ian@0 775 part->mbd.mtd = mtd;
ian@0 776
ian@0 777 if (block_size)
ian@0 778 part->block_size = block_size;
ian@0 779 else {
ian@0 780 if (!mtd->erasesize) {
ian@0 781 printk(KERN_WARNING PREFIX "please provide block_size");
ian@0 782 return;
ian@0 783 }
ian@0 784 else
ian@0 785 part->block_size = mtd->erasesize;
ian@0 786 }
ian@0 787
ian@0 788 if (scan_header(part) == 0) {
ian@0 789 part->mbd.size = part->sector_count;
ian@0 790 part->mbd.blksize = SECTOR_SIZE;
ian@0 791 part->mbd.tr = tr;
ian@0 792 part->mbd.devnum = -1;
ian@0 793 if (!(mtd->flags & MTD_WRITEABLE))
ian@0 794 part->mbd.readonly = 1;
ian@0 795 else if (part->errors) {
ian@0 796 printk(KERN_WARNING PREFIX "'%s': errors found, "
ian@0 797 "setting read-only\n", mtd->name);
ian@0 798 part->mbd.readonly = 1;
ian@0 799 }
ian@0 800
ian@0 801 printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
ian@0 802 mtd->name, mtd->type, mtd->flags);
ian@0 803
ian@0 804 if (!add_mtd_blktrans_dev((void*)part))
ian@0 805 return;
ian@0 806 }
ian@0 807
ian@0 808 kfree(part);
ian@0 809 }
ian@0 810
ian@0 811 static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
ian@0 812 {
ian@0 813 struct partition *part = (struct partition*)dev;
ian@0 814 int i;
ian@0 815
ian@0 816 for (i=0; i<part->total_blocks; i++) {
ian@0 817 pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
ian@0 818 part->mbd.mtd->name, i, part->blocks[i].erases);
ian@0 819 }
ian@0 820
ian@0 821 del_mtd_blktrans_dev(dev);
ian@0 822 vfree(part->sector_map);
ian@0 823 kfree(part->header_cache);
ian@0 824 kfree(part->blocks);
ian@0 825 kfree(part);
ian@0 826 }
ian@0 827
ian@0 828 struct mtd_blktrans_ops rfd_ftl_tr = {
ian@0 829 .name = "rfd",
ian@0 830 .major = RFD_FTL_MAJOR,
ian@0 831 .part_bits = PART_BITS,
ian@0 832 .readsect = rfd_ftl_readsect,
ian@0 833 .writesect = rfd_ftl_writesect,
ian@0 834 .getgeo = rfd_ftl_getgeo,
ian@0 835 .add_mtd = rfd_ftl_add_mtd,
ian@0 836 .remove_dev = rfd_ftl_remove_dev,
ian@0 837 .owner = THIS_MODULE,
ian@0 838 };
ian@0 839
ian@0 840 static int __init init_rfd_ftl(void)
ian@0 841 {
ian@0 842 return register_mtd_blktrans(&rfd_ftl_tr);
ian@0 843 }
ian@0 844
ian@0 845 static void __exit cleanup_rfd_ftl(void)
ian@0 846 {
ian@0 847 deregister_mtd_blktrans(&rfd_ftl_tr);
ian@0 848 }
ian@0 849
ian@0 850 module_init(init_rfd_ftl);
ian@0 851 module_exit(cleanup_rfd_ftl);
ian@0 852
ian@0 853 MODULE_LICENSE("GPL");
ian@0 854 MODULE_AUTHOR("Sean Young <sean@mess.org>");
ian@0 855 MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
ian@0 856 "used by General Software's Embedded BIOS");
ian@0 857