ia64/linux-2.6.18-xen.hg

annotate drivers/mtd/mtdchar.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 * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $
ian@0 3 *
ian@0 4 * Character-device access to raw MTD devices.
ian@0 5 *
ian@0 6 */
ian@0 7
ian@0 8 #include <linux/device.h>
ian@0 9 #include <linux/fs.h>
ian@0 10 #include <linux/init.h>
ian@0 11 #include <linux/kernel.h>
ian@0 12 #include <linux/module.h>
ian@0 13 #include <linux/slab.h>
ian@0 14 #include <linux/sched.h>
ian@0 15
ian@0 16 #include <linux/mtd/mtd.h>
ian@0 17 #include <linux/mtd/compatmac.h>
ian@0 18
ian@0 19 #include <asm/uaccess.h>
ian@0 20
ian@0 21 static struct class *mtd_class;
ian@0 22
ian@0 23 static void mtd_notify_add(struct mtd_info* mtd)
ian@0 24 {
ian@0 25 if (!mtd)
ian@0 26 return;
ian@0 27
ian@0 28 class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
ian@0 29 NULL, "mtd%d", mtd->index);
ian@0 30
ian@0 31 class_device_create(mtd_class, NULL,
ian@0 32 MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
ian@0 33 NULL, "mtd%dro", mtd->index);
ian@0 34 }
ian@0 35
ian@0 36 static void mtd_notify_remove(struct mtd_info* mtd)
ian@0 37 {
ian@0 38 if (!mtd)
ian@0 39 return;
ian@0 40
ian@0 41 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
ian@0 42 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
ian@0 43 }
ian@0 44
ian@0 45 static struct mtd_notifier notifier = {
ian@0 46 .add = mtd_notify_add,
ian@0 47 .remove = mtd_notify_remove,
ian@0 48 };
ian@0 49
ian@0 50 /*
ian@0 51 * Data structure to hold the pointer to the mtd device as well
ian@0 52 * as mode information ofr various use cases.
ian@0 53 */
ian@0 54 struct mtd_file_info {
ian@0 55 struct mtd_info *mtd;
ian@0 56 enum mtd_file_modes mode;
ian@0 57 };
ian@0 58
ian@0 59 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
ian@0 60 {
ian@0 61 struct mtd_file_info *mfi = file->private_data;
ian@0 62 struct mtd_info *mtd = mfi->mtd;
ian@0 63
ian@0 64 switch (orig) {
ian@0 65 case SEEK_SET:
ian@0 66 break;
ian@0 67 case SEEK_CUR:
ian@0 68 offset += file->f_pos;
ian@0 69 break;
ian@0 70 case SEEK_END:
ian@0 71 offset += mtd->size;
ian@0 72 break;
ian@0 73 default:
ian@0 74 return -EINVAL;
ian@0 75 }
ian@0 76
ian@0 77 if (offset >= 0 && offset <= mtd->size)
ian@0 78 return file->f_pos = offset;
ian@0 79
ian@0 80 return -EINVAL;
ian@0 81 }
ian@0 82
ian@0 83
ian@0 84
ian@0 85 static int mtd_open(struct inode *inode, struct file *file)
ian@0 86 {
ian@0 87 int minor = iminor(inode);
ian@0 88 int devnum = minor >> 1;
ian@0 89 struct mtd_info *mtd;
ian@0 90 struct mtd_file_info *mfi;
ian@0 91
ian@0 92 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
ian@0 93
ian@0 94 if (devnum >= MAX_MTD_DEVICES)
ian@0 95 return -ENODEV;
ian@0 96
ian@0 97 /* You can't open the RO devices RW */
ian@0 98 if ((file->f_mode & 2) && (minor & 1))
ian@0 99 return -EACCES;
ian@0 100
ian@0 101 mtd = get_mtd_device(NULL, devnum);
ian@0 102
ian@0 103 if (!mtd)
ian@0 104 return -ENODEV;
ian@0 105
ian@0 106 if (MTD_ABSENT == mtd->type) {
ian@0 107 put_mtd_device(mtd);
ian@0 108 return -ENODEV;
ian@0 109 }
ian@0 110
ian@0 111 /* You can't open it RW if it's not a writeable device */
ian@0 112 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
ian@0 113 put_mtd_device(mtd);
ian@0 114 return -EACCES;
ian@0 115 }
ian@0 116
ian@0 117 mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
ian@0 118 if (!mfi) {
ian@0 119 put_mtd_device(mtd);
ian@0 120 return -ENOMEM;
ian@0 121 }
ian@0 122 mfi->mtd = mtd;
ian@0 123 file->private_data = mfi;
ian@0 124
ian@0 125 return 0;
ian@0 126 } /* mtd_open */
ian@0 127
ian@0 128 /*====================================================================*/
ian@0 129
ian@0 130 static int mtd_close(struct inode *inode, struct file *file)
ian@0 131 {
ian@0 132 struct mtd_file_info *mfi = file->private_data;
ian@0 133 struct mtd_info *mtd = mfi->mtd;
ian@0 134
ian@0 135 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
ian@0 136
ian@0 137 if (mtd->sync)
ian@0 138 mtd->sync(mtd);
ian@0 139
ian@0 140 put_mtd_device(mtd);
ian@0 141 file->private_data = NULL;
ian@0 142 kfree(mfi);
ian@0 143
ian@0 144 return 0;
ian@0 145 } /* mtd_close */
ian@0 146
ian@0 147 /* FIXME: This _really_ needs to die. In 2.5, we should lock the
ian@0 148 userspace buffer down and use it directly with readv/writev.
ian@0 149 */
ian@0 150 #define MAX_KMALLOC_SIZE 0x20000
ian@0 151
ian@0 152 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
ian@0 153 {
ian@0 154 struct mtd_file_info *mfi = file->private_data;
ian@0 155 struct mtd_info *mtd = mfi->mtd;
ian@0 156 size_t retlen=0;
ian@0 157 size_t total_retlen=0;
ian@0 158 int ret=0;
ian@0 159 int len;
ian@0 160 char *kbuf;
ian@0 161
ian@0 162 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
ian@0 163
ian@0 164 if (*ppos + count > mtd->size)
ian@0 165 count = mtd->size - *ppos;
ian@0 166
ian@0 167 if (!count)
ian@0 168 return 0;
ian@0 169
ian@0 170 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
ian@0 171 and pass them directly to the MTD functions */
ian@0 172
ian@0 173 if (count > MAX_KMALLOC_SIZE)
ian@0 174 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
ian@0 175 else
ian@0 176 kbuf=kmalloc(count, GFP_KERNEL);
ian@0 177
ian@0 178 if (!kbuf)
ian@0 179 return -ENOMEM;
ian@0 180
ian@0 181 while (count) {
ian@0 182
ian@0 183 if (count > MAX_KMALLOC_SIZE)
ian@0 184 len = MAX_KMALLOC_SIZE;
ian@0 185 else
ian@0 186 len = count;
ian@0 187
ian@0 188 switch (mfi->mode) {
ian@0 189 case MTD_MODE_OTP_FACTORY:
ian@0 190 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 191 break;
ian@0 192 case MTD_MODE_OTP_USER:
ian@0 193 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 194 break;
ian@0 195 case MTD_MODE_RAW:
ian@0 196 {
ian@0 197 struct mtd_oob_ops ops;
ian@0 198
ian@0 199 ops.mode = MTD_OOB_RAW;
ian@0 200 ops.datbuf = kbuf;
ian@0 201 ops.oobbuf = NULL;
ian@0 202 ops.len = len;
ian@0 203
ian@0 204 ret = mtd->read_oob(mtd, *ppos, &ops);
ian@0 205 retlen = ops.retlen;
ian@0 206 break;
ian@0 207 }
ian@0 208 default:
ian@0 209 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
ian@0 210 }
ian@0 211 /* Nand returns -EBADMSG on ecc errors, but it returns
ian@0 212 * the data. For our userspace tools it is important
ian@0 213 * to dump areas with ecc errors !
ian@0 214 * For kernel internal usage it also might return -EUCLEAN
ian@0 215 * to signal the caller that a bitflip has occured and has
ian@0 216 * been corrected by the ECC algorithm.
ian@0 217 * Userspace software which accesses NAND this way
ian@0 218 * must be aware of the fact that it deals with NAND
ian@0 219 */
ian@0 220 if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
ian@0 221 *ppos += retlen;
ian@0 222 if (copy_to_user(buf, kbuf, retlen)) {
ian@0 223 kfree(kbuf);
ian@0 224 return -EFAULT;
ian@0 225 }
ian@0 226 else
ian@0 227 total_retlen += retlen;
ian@0 228
ian@0 229 count -= retlen;
ian@0 230 buf += retlen;
ian@0 231 if (retlen == 0)
ian@0 232 count = 0;
ian@0 233 }
ian@0 234 else {
ian@0 235 kfree(kbuf);
ian@0 236 return ret;
ian@0 237 }
ian@0 238
ian@0 239 }
ian@0 240
ian@0 241 kfree(kbuf);
ian@0 242 return total_retlen;
ian@0 243 } /* mtd_read */
ian@0 244
ian@0 245 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
ian@0 246 {
ian@0 247 struct mtd_file_info *mfi = file->private_data;
ian@0 248 struct mtd_info *mtd = mfi->mtd;
ian@0 249 char *kbuf;
ian@0 250 size_t retlen;
ian@0 251 size_t total_retlen=0;
ian@0 252 int ret=0;
ian@0 253 int len;
ian@0 254
ian@0 255 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
ian@0 256
ian@0 257 if (*ppos == mtd->size)
ian@0 258 return -ENOSPC;
ian@0 259
ian@0 260 if (*ppos + count > mtd->size)
ian@0 261 count = mtd->size - *ppos;
ian@0 262
ian@0 263 if (!count)
ian@0 264 return 0;
ian@0 265
ian@0 266 if (count > MAX_KMALLOC_SIZE)
ian@0 267 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
ian@0 268 else
ian@0 269 kbuf=kmalloc(count, GFP_KERNEL);
ian@0 270
ian@0 271 if (!kbuf)
ian@0 272 return -ENOMEM;
ian@0 273
ian@0 274 while (count) {
ian@0 275
ian@0 276 if (count > MAX_KMALLOC_SIZE)
ian@0 277 len = MAX_KMALLOC_SIZE;
ian@0 278 else
ian@0 279 len = count;
ian@0 280
ian@0 281 if (copy_from_user(kbuf, buf, len)) {
ian@0 282 kfree(kbuf);
ian@0 283 return -EFAULT;
ian@0 284 }
ian@0 285
ian@0 286 switch (mfi->mode) {
ian@0 287 case MTD_MODE_OTP_FACTORY:
ian@0 288 ret = -EROFS;
ian@0 289 break;
ian@0 290 case MTD_MODE_OTP_USER:
ian@0 291 if (!mtd->write_user_prot_reg) {
ian@0 292 ret = -EOPNOTSUPP;
ian@0 293 break;
ian@0 294 }
ian@0 295 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 296 break;
ian@0 297
ian@0 298 case MTD_MODE_RAW:
ian@0 299 {
ian@0 300 struct mtd_oob_ops ops;
ian@0 301
ian@0 302 ops.mode = MTD_OOB_RAW;
ian@0 303 ops.datbuf = kbuf;
ian@0 304 ops.oobbuf = NULL;
ian@0 305 ops.len = len;
ian@0 306
ian@0 307 ret = mtd->write_oob(mtd, *ppos, &ops);
ian@0 308 retlen = ops.retlen;
ian@0 309 break;
ian@0 310 }
ian@0 311
ian@0 312 default:
ian@0 313 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
ian@0 314 }
ian@0 315 if (!ret) {
ian@0 316 *ppos += retlen;
ian@0 317 total_retlen += retlen;
ian@0 318 count -= retlen;
ian@0 319 buf += retlen;
ian@0 320 }
ian@0 321 else {
ian@0 322 kfree(kbuf);
ian@0 323 return ret;
ian@0 324 }
ian@0 325 }
ian@0 326
ian@0 327 kfree(kbuf);
ian@0 328 return total_retlen;
ian@0 329 } /* mtd_write */
ian@0 330
ian@0 331 /*======================================================================
ian@0 332
ian@0 333 IOCTL calls for getting device parameters.
ian@0 334
ian@0 335 ======================================================================*/
ian@0 336 static void mtdchar_erase_callback (struct erase_info *instr)
ian@0 337 {
ian@0 338 wake_up((wait_queue_head_t *)instr->priv);
ian@0 339 }
ian@0 340
ian@0 341 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
ian@0 342 static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
ian@0 343 {
ian@0 344 struct mtd_info *mtd = mfi->mtd;
ian@0 345 int ret = 0;
ian@0 346
ian@0 347 switch (mode) {
ian@0 348 case MTD_OTP_FACTORY:
ian@0 349 if (!mtd->read_fact_prot_reg)
ian@0 350 ret = -EOPNOTSUPP;
ian@0 351 else
ian@0 352 mfi->mode = MTD_MODE_OTP_FACTORY;
ian@0 353 break;
ian@0 354 case MTD_OTP_USER:
ian@0 355 if (!mtd->read_fact_prot_reg)
ian@0 356 ret = -EOPNOTSUPP;
ian@0 357 else
ian@0 358 mfi->mode = MTD_MODE_OTP_USER;
ian@0 359 break;
ian@0 360 default:
ian@0 361 ret = -EINVAL;
ian@0 362 case MTD_OTP_OFF:
ian@0 363 break;
ian@0 364 }
ian@0 365 return ret;
ian@0 366 }
ian@0 367 #else
ian@0 368 # define otp_select_filemode(f,m) -EOPNOTSUPP
ian@0 369 #endif
ian@0 370
ian@0 371 static int mtd_ioctl(struct inode *inode, struct file *file,
ian@0 372 u_int cmd, u_long arg)
ian@0 373 {
ian@0 374 struct mtd_file_info *mfi = file->private_data;
ian@0 375 struct mtd_info *mtd = mfi->mtd;
ian@0 376 void __user *argp = (void __user *)arg;
ian@0 377 int ret = 0;
ian@0 378 u_long size;
ian@0 379 struct mtd_info_user info;
ian@0 380
ian@0 381 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
ian@0 382
ian@0 383 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
ian@0 384 if (cmd & IOC_IN) {
ian@0 385 if (!access_ok(VERIFY_READ, argp, size))
ian@0 386 return -EFAULT;
ian@0 387 }
ian@0 388 if (cmd & IOC_OUT) {
ian@0 389 if (!access_ok(VERIFY_WRITE, argp, size))
ian@0 390 return -EFAULT;
ian@0 391 }
ian@0 392
ian@0 393 switch (cmd) {
ian@0 394 case MEMGETREGIONCOUNT:
ian@0 395 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
ian@0 396 return -EFAULT;
ian@0 397 break;
ian@0 398
ian@0 399 case MEMGETREGIONINFO:
ian@0 400 {
ian@0 401 struct region_info_user ur;
ian@0 402
ian@0 403 if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
ian@0 404 return -EFAULT;
ian@0 405
ian@0 406 if (ur.regionindex >= mtd->numeraseregions)
ian@0 407 return -EINVAL;
ian@0 408 if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
ian@0 409 sizeof(struct mtd_erase_region_info)))
ian@0 410 return -EFAULT;
ian@0 411 break;
ian@0 412 }
ian@0 413
ian@0 414 case MEMGETINFO:
ian@0 415 info.type = mtd->type;
ian@0 416 info.flags = mtd->flags;
ian@0 417 info.size = mtd->size;
ian@0 418 info.erasesize = mtd->erasesize;
ian@0 419 info.writesize = mtd->writesize;
ian@0 420 info.oobsize = mtd->oobsize;
ian@0 421 info.ecctype = mtd->ecctype;
ian@0 422 info.eccsize = mtd->eccsize;
ian@0 423 if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
ian@0 424 return -EFAULT;
ian@0 425 break;
ian@0 426
ian@0 427 case MEMERASE:
ian@0 428 {
ian@0 429 struct erase_info *erase;
ian@0 430
ian@0 431 if(!(file->f_mode & 2))
ian@0 432 return -EPERM;
ian@0 433
ian@0 434 erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
ian@0 435 if (!erase)
ian@0 436 ret = -ENOMEM;
ian@0 437 else {
ian@0 438 wait_queue_head_t waitq;
ian@0 439 DECLARE_WAITQUEUE(wait, current);
ian@0 440
ian@0 441 init_waitqueue_head(&waitq);
ian@0 442
ian@0 443 memset (erase,0,sizeof(struct erase_info));
ian@0 444 if (copy_from_user(&erase->addr, argp,
ian@0 445 sizeof(struct erase_info_user))) {
ian@0 446 kfree(erase);
ian@0 447 return -EFAULT;
ian@0 448 }
ian@0 449 erase->mtd = mtd;
ian@0 450 erase->callback = mtdchar_erase_callback;
ian@0 451 erase->priv = (unsigned long)&waitq;
ian@0 452
ian@0 453 /*
ian@0 454 FIXME: Allow INTERRUPTIBLE. Which means
ian@0 455 not having the wait_queue head on the stack.
ian@0 456
ian@0 457 If the wq_head is on the stack, and we
ian@0 458 leave because we got interrupted, then the
ian@0 459 wq_head is no longer there when the
ian@0 460 callback routine tries to wake us up.
ian@0 461 */
ian@0 462 ret = mtd->erase(mtd, erase);
ian@0 463 if (!ret) {
ian@0 464 set_current_state(TASK_UNINTERRUPTIBLE);
ian@0 465 add_wait_queue(&waitq, &wait);
ian@0 466 if (erase->state != MTD_ERASE_DONE &&
ian@0 467 erase->state != MTD_ERASE_FAILED)
ian@0 468 schedule();
ian@0 469 remove_wait_queue(&waitq, &wait);
ian@0 470 set_current_state(TASK_RUNNING);
ian@0 471
ian@0 472 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
ian@0 473 }
ian@0 474 kfree(erase);
ian@0 475 }
ian@0 476 break;
ian@0 477 }
ian@0 478
ian@0 479 case MEMWRITEOOB:
ian@0 480 {
ian@0 481 struct mtd_oob_buf buf;
ian@0 482 struct mtd_oob_ops ops;
ian@0 483
ian@0 484 if(!(file->f_mode & 2))
ian@0 485 return -EPERM;
ian@0 486
ian@0 487 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
ian@0 488 return -EFAULT;
ian@0 489
ian@0 490 if (buf.length > 4096)
ian@0 491 return -EINVAL;
ian@0 492
ian@0 493 if (!mtd->write_oob)
ian@0 494 ret = -EOPNOTSUPP;
ian@0 495 else
ian@0 496 ret = access_ok(VERIFY_READ, buf.ptr,
ian@0 497 buf.length) ? 0 : EFAULT;
ian@0 498
ian@0 499 if (ret)
ian@0 500 return ret;
ian@0 501
ian@0 502 ops.len = buf.length;
ian@0 503 ops.ooblen = buf.length;
ian@0 504 ops.ooboffs = buf.start & (mtd->oobsize - 1);
ian@0 505 ops.datbuf = NULL;
ian@0 506 ops.mode = MTD_OOB_PLACE;
ian@0 507
ian@0 508 if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
ian@0 509 return -EINVAL;
ian@0 510
ian@0 511 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
ian@0 512 if (!ops.oobbuf)
ian@0 513 return -ENOMEM;
ian@0 514
ian@0 515 if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
ian@0 516 kfree(ops.oobbuf);
ian@0 517 return -EFAULT;
ian@0 518 }
ian@0 519
ian@0 520 buf.start &= ~(mtd->oobsize - 1);
ian@0 521 ret = mtd->write_oob(mtd, buf.start, &ops);
ian@0 522
ian@0 523 if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
ian@0 524 sizeof(uint32_t)))
ian@0 525 ret = -EFAULT;
ian@0 526
ian@0 527 kfree(ops.oobbuf);
ian@0 528 break;
ian@0 529
ian@0 530 }
ian@0 531
ian@0 532 case MEMREADOOB:
ian@0 533 {
ian@0 534 struct mtd_oob_buf buf;
ian@0 535 struct mtd_oob_ops ops;
ian@0 536
ian@0 537 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
ian@0 538 return -EFAULT;
ian@0 539
ian@0 540 if (buf.length > 4096)
ian@0 541 return -EINVAL;
ian@0 542
ian@0 543 if (!mtd->read_oob)
ian@0 544 ret = -EOPNOTSUPP;
ian@0 545 else
ian@0 546 ret = access_ok(VERIFY_WRITE, buf.ptr,
ian@0 547 buf.length) ? 0 : -EFAULT;
ian@0 548 if (ret)
ian@0 549 return ret;
ian@0 550
ian@0 551 ops.len = buf.length;
ian@0 552 ops.ooblen = buf.length;
ian@0 553 ops.ooboffs = buf.start & (mtd->oobsize - 1);
ian@0 554 ops.datbuf = NULL;
ian@0 555 ops.mode = MTD_OOB_PLACE;
ian@0 556
ian@0 557 if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
ian@0 558 return -EINVAL;
ian@0 559
ian@0 560 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
ian@0 561 if (!ops.oobbuf)
ian@0 562 return -ENOMEM;
ian@0 563
ian@0 564 buf.start &= ~(mtd->oobsize - 1);
ian@0 565 ret = mtd->read_oob(mtd, buf.start, &ops);
ian@0 566
ian@0 567 if (put_user(ops.retlen, (uint32_t __user *)argp))
ian@0 568 ret = -EFAULT;
ian@0 569 else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
ian@0 570 ops.retlen))
ian@0 571 ret = -EFAULT;
ian@0 572
ian@0 573 kfree(ops.oobbuf);
ian@0 574 break;
ian@0 575 }
ian@0 576
ian@0 577 case MEMLOCK:
ian@0 578 {
ian@0 579 struct erase_info_user info;
ian@0 580
ian@0 581 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 582 return -EFAULT;
ian@0 583
ian@0 584 if (!mtd->lock)
ian@0 585 ret = -EOPNOTSUPP;
ian@0 586 else
ian@0 587 ret = mtd->lock(mtd, info.start, info.length);
ian@0 588 break;
ian@0 589 }
ian@0 590
ian@0 591 case MEMUNLOCK:
ian@0 592 {
ian@0 593 struct erase_info_user info;
ian@0 594
ian@0 595 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 596 return -EFAULT;
ian@0 597
ian@0 598 if (!mtd->unlock)
ian@0 599 ret = -EOPNOTSUPP;
ian@0 600 else
ian@0 601 ret = mtd->unlock(mtd, info.start, info.length);
ian@0 602 break;
ian@0 603 }
ian@0 604
ian@0 605 /* Legacy interface */
ian@0 606 case MEMGETOOBSEL:
ian@0 607 {
ian@0 608 struct nand_oobinfo oi;
ian@0 609
ian@0 610 if (!mtd->ecclayout)
ian@0 611 return -EOPNOTSUPP;
ian@0 612 if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
ian@0 613 return -EINVAL;
ian@0 614
ian@0 615 oi.useecc = MTD_NANDECC_AUTOPLACE;
ian@0 616 memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
ian@0 617 memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
ian@0 618 sizeof(oi.oobfree));
ian@0 619
ian@0 620 if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
ian@0 621 return -EFAULT;
ian@0 622 break;
ian@0 623 }
ian@0 624
ian@0 625 case MEMGETBADBLOCK:
ian@0 626 {
ian@0 627 loff_t offs;
ian@0 628
ian@0 629 if (copy_from_user(&offs, argp, sizeof(loff_t)))
ian@0 630 return -EFAULT;
ian@0 631 if (!mtd->block_isbad)
ian@0 632 ret = -EOPNOTSUPP;
ian@0 633 else
ian@0 634 return mtd->block_isbad(mtd, offs);
ian@0 635 break;
ian@0 636 }
ian@0 637
ian@0 638 case MEMSETBADBLOCK:
ian@0 639 {
ian@0 640 loff_t offs;
ian@0 641
ian@0 642 if (copy_from_user(&offs, argp, sizeof(loff_t)))
ian@0 643 return -EFAULT;
ian@0 644 if (!mtd->block_markbad)
ian@0 645 ret = -EOPNOTSUPP;
ian@0 646 else
ian@0 647 return mtd->block_markbad(mtd, offs);
ian@0 648 break;
ian@0 649 }
ian@0 650
ian@0 651 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
ian@0 652 case OTPSELECT:
ian@0 653 {
ian@0 654 int mode;
ian@0 655 if (copy_from_user(&mode, argp, sizeof(int)))
ian@0 656 return -EFAULT;
ian@0 657
ian@0 658 mfi->mode = MTD_MODE_NORMAL;
ian@0 659
ian@0 660 ret = otp_select_filemode(mfi, mode);
ian@0 661
ian@0 662 file->f_pos = 0;
ian@0 663 break;
ian@0 664 }
ian@0 665
ian@0 666 case OTPGETREGIONCOUNT:
ian@0 667 case OTPGETREGIONINFO:
ian@0 668 {
ian@0 669 struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
ian@0 670 if (!buf)
ian@0 671 return -ENOMEM;
ian@0 672 ret = -EOPNOTSUPP;
ian@0 673 switch (mfi->mode) {
ian@0 674 case MTD_MODE_OTP_FACTORY:
ian@0 675 if (mtd->get_fact_prot_info)
ian@0 676 ret = mtd->get_fact_prot_info(mtd, buf, 4096);
ian@0 677 break;
ian@0 678 case MTD_MODE_OTP_USER:
ian@0 679 if (mtd->get_user_prot_info)
ian@0 680 ret = mtd->get_user_prot_info(mtd, buf, 4096);
ian@0 681 break;
ian@0 682 default:
ian@0 683 break;
ian@0 684 }
ian@0 685 if (ret >= 0) {
ian@0 686 if (cmd == OTPGETREGIONCOUNT) {
ian@0 687 int nbr = ret / sizeof(struct otp_info);
ian@0 688 ret = copy_to_user(argp, &nbr, sizeof(int));
ian@0 689 } else
ian@0 690 ret = copy_to_user(argp, buf, ret);
ian@0 691 if (ret)
ian@0 692 ret = -EFAULT;
ian@0 693 }
ian@0 694 kfree(buf);
ian@0 695 break;
ian@0 696 }
ian@0 697
ian@0 698 case OTPLOCK:
ian@0 699 {
ian@0 700 struct otp_info info;
ian@0 701
ian@0 702 if (mfi->mode != MTD_MODE_OTP_USER)
ian@0 703 return -EINVAL;
ian@0 704 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 705 return -EFAULT;
ian@0 706 if (!mtd->lock_user_prot_reg)
ian@0 707 return -EOPNOTSUPP;
ian@0 708 ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
ian@0 709 break;
ian@0 710 }
ian@0 711 #endif
ian@0 712
ian@0 713 case ECCGETLAYOUT:
ian@0 714 {
ian@0 715 if (!mtd->ecclayout)
ian@0 716 return -EOPNOTSUPP;
ian@0 717
ian@0 718 if (copy_to_user(argp, &mtd->ecclayout,
ian@0 719 sizeof(struct nand_ecclayout)))
ian@0 720 return -EFAULT;
ian@0 721 break;
ian@0 722 }
ian@0 723
ian@0 724 case ECCGETSTATS:
ian@0 725 {
ian@0 726 if (copy_to_user(argp, &mtd->ecc_stats,
ian@0 727 sizeof(struct mtd_ecc_stats)))
ian@0 728 return -EFAULT;
ian@0 729 break;
ian@0 730 }
ian@0 731
ian@0 732 case MTDFILEMODE:
ian@0 733 {
ian@0 734 mfi->mode = 0;
ian@0 735
ian@0 736 switch(arg) {
ian@0 737 case MTD_MODE_OTP_FACTORY:
ian@0 738 case MTD_MODE_OTP_USER:
ian@0 739 ret = otp_select_filemode(mfi, arg);
ian@0 740 break;
ian@0 741
ian@0 742 case MTD_MODE_RAW:
ian@0 743 if (!mtd->read_oob || !mtd->write_oob)
ian@0 744 return -EOPNOTSUPP;
ian@0 745 mfi->mode = arg;
ian@0 746
ian@0 747 case MTD_MODE_NORMAL:
ian@0 748 break;
ian@0 749 default:
ian@0 750 ret = -EINVAL;
ian@0 751 }
ian@0 752 file->f_pos = 0;
ian@0 753 break;
ian@0 754 }
ian@0 755
ian@0 756 default:
ian@0 757 ret = -ENOTTY;
ian@0 758 }
ian@0 759
ian@0 760 return ret;
ian@0 761 } /* memory_ioctl */
ian@0 762
ian@0 763 static struct file_operations mtd_fops = {
ian@0 764 .owner = THIS_MODULE,
ian@0 765 .llseek = mtd_lseek,
ian@0 766 .read = mtd_read,
ian@0 767 .write = mtd_write,
ian@0 768 .ioctl = mtd_ioctl,
ian@0 769 .open = mtd_open,
ian@0 770 .release = mtd_close,
ian@0 771 };
ian@0 772
ian@0 773 static int __init init_mtdchar(void)
ian@0 774 {
ian@0 775 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
ian@0 776 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
ian@0 777 MTD_CHAR_MAJOR);
ian@0 778 return -EAGAIN;
ian@0 779 }
ian@0 780
ian@0 781 mtd_class = class_create(THIS_MODULE, "mtd");
ian@0 782
ian@0 783 if (IS_ERR(mtd_class)) {
ian@0 784 printk(KERN_ERR "Error creating mtd class.\n");
ian@0 785 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
ian@0 786 return PTR_ERR(mtd_class);
ian@0 787 }
ian@0 788
ian@0 789 register_mtd_user(&notifier);
ian@0 790 return 0;
ian@0 791 }
ian@0 792
ian@0 793 static void __exit cleanup_mtdchar(void)
ian@0 794 {
ian@0 795 unregister_mtd_user(&notifier);
ian@0 796 class_destroy(mtd_class);
ian@0 797 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
ian@0 798 }
ian@0 799
ian@0 800 module_init(init_mtdchar);
ian@0 801 module_exit(cleanup_mtdchar);
ian@0 802
ian@0 803
ian@0 804 MODULE_LICENSE("GPL");
ian@0 805 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
ian@0 806 MODULE_DESCRIPTION("Direct character-device access to MTD devices");