ia64/linux-2.6.18-xen.hg

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