Commit 91990be9 authored by Kai Germaschewski's avatar Kai Germaschewski

Merge tp1.ruhr-uni-bochum.de:/home/kai/src/kernel/v2.5/linux-2.5

into tp1.ruhr-uni-bochum.de:/home/kai/src/kernel/v2.5/linux-2.5.make
parents abcdaf4b 61c4b8fb
Ext3 Filesystem
===============
ext3 was originally released in September 1999. Written by Stephen Tweedie
for 2.2 branch, and ported to 2.4 kernels by Peter Braam, Andreas Dilger,
Andrew Morton, Alexander Viro, Ted Ts'o and Stephen Tweedie.
ext3 is ext2 filesystem enhanced with journalling capabilities.
Options
=======
When mounting an ext3 filesystem, the following option are accepted:
(*) == default
jounal=update Update the ext3 file system's journal to the
current format.
journal=inum When a journal already exists, this option is
ignored. Otherwise, it specifies the number of
the inode which will represent the ext3 file
system's journal file.
bsddf (*) Make 'df' act like BSD.
minixdf Make 'df' act like Minix.
check=none Don't do extra checking of bitmaps on mount.
nocheck
debug Extra debugging information is sent to syslog.
noload Don't load the journal on mounting.
errors=remount-ro(*) Remount the filesystem read-only on an error.
errors=continue Keep going on a filesystem error.
errors=panic Panic and halt the machine if an error occurs.
grpid Give objects the same group ID as their creator.
bsdgroups
nogrpid (*) New objects have the group ID of their creator.
sysvgroups
resgid=n The group ID which may use the reserved blocks.
resuid=n The user ID which may use the reserved blocks.
sb=n Use alternate superblock at this location.
data=journal All data are commited into the journal prior
to being written into the main file system.
data=ordered (*) All data are forced directly out to the main file
system prior to its metadata being commited to
the journal.
data=writeback Data ordering is not preserved, data may be
written into the main file system after its
metadata has been committed to the journal.
quota Quota options are currently silently ignored.
noquota (see fs/ext3/super.c, line 594)
grpquota
usrquota
Specification
=============
ext3 shares all disk implementation with ext2 filesystem, and add
transactions capabilities to ext2. Journaling is done by the
Journaling block device layer.
Journaling Block Device layer
-----------------------------
The Journaling Block Device layer (JBD) isn't ext3 specific. It was
design to add journaling capabilities on a block device. The ext3
filesystem code will inform the JBD of modifications it is performing
(Call a transaction). the journal support the transactions start and
stop, and in case of crash, the journal can replayed the transactions
to put the partition on a consistant state fastly.
handles represent a single atomic update to a filesystem. JBD can
handle external journal on a block device.
Data Mode
---------
There's 3 different data modes:
* writeback mode
In data=writeback mode, ext3 does not journal data at all. This mode
provides a similar level of journaling as XFS, JFS, and ReiserFS in its
default mode - metadata journaling. A crash+recovery can cause
incorrect data to appear in files which were written shortly before the
crash. This mode will typically provide the best ext3 performance.
* ordered mode
In data=ordered mode, ext3 only officially journals metadata, but it
logically groups metadata and data blocks into a single unit called a
transaction. When it's time to write the new metadata out to disk, the
associated data blocks are written first. In general, this mode
perform slightly slower than writeback but significantly faster than
journal mode.
* journal mode
data=journal mode provides full data and metadata journaling. All new
data is written to the journal first, and then to its final location.
In the event of a crash, the journal can be replayed, bringing both
data and metadata into a consistent state. This mode is the slowest
except when data needs to be read from and written to disk at the same
time where it outperform all others mode.
Compatibility
-------------
Ext2 partitions can be easily convert to ext3, with `tune2fs -j <dev>`.
Ext3 is fully compatible with Ext2. Ext3 partitions can easily be
mounted as Ext2.
External Tools
==============
see manual pages to know more.
tune2fs: create a ext3 journal on a ext2 partition with the -j flags
mke2fs: create a ext3 partition with the -j flags
debugfs: ext2 and ext3 file system debugger
References
==========
kernel source: file:/usr/src/linux/fs/ext3
file:/usr/src/linux/fs/jbd
programs: http://e2fsprogs.sourceforge.net
useful link:
http://www.zip.com.au/~akpm/linux/ext3/ext3-usage.html
http://www-106.ibm.com/developerworks/linux/library/l-fs7/
http://www-106.ibm.com/developerworks/linux/library/l-fs8/
......@@ -1433,6 +1433,12 @@ M: Kai.Makisara@metla.fi
L: linux-scsi@vger.kernel.org
S: Maintained
SCx200 CPU SUPPORT
P: Christer Weinigel
M: christer@weinigel.se
W: http://www.weinigel.se
S: Supported
SGI VISUAL WORKSTATION 320 AND 540
P: Bent Hagemark
M: bh@sgi.com
......
......@@ -1058,3 +1058,12 @@ CONFIG_SOFTWARE_SUSPEND
absence of features.
For more information take a look at Documentation/swsusp.txt.
CONFIG_SCx200
This provides basic support for the National Semiconductor SCx200
processor. Right now this is just a driver for the GPIO pins.
If you don't know what to do here, say N.
This support is also available as a module. If compiled as a
module, it will be called scx200.o.
......@@ -293,6 +293,8 @@ else
fi
fi
tristate 'NatSemi SCx200 support' CONFIG_SCx200
source drivers/pci/Config.in
bool 'EISA support' CONFIG_EISA
......
......@@ -29,4 +29,7 @@ obj-$(CONFIG_X86_NUMAQ) += numaq.o
EXTRA_AFLAGS := -traditional
export-objs += scx200.o
obj-$(CONFIG_SCx200) += scx200.o
include $(TOPDIR)/Rules.make
/* linux/arch/i386/kernel/scx200.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 support. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/scx200.h>
#include <linux/scx200.h>
#define NAME "scx200"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Driver");
MODULE_LICENSE("GPL");
unsigned scx200_gpio_base = 0;
long scx200_gpio_shadow[2];
spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED;
u32 scx200_gpio_configure(int index, u32 mask, u32 bits)
{
u32 config, new_config;
unsigned long flags;
spin_lock_irqsave(&scx200_gpio_config_lock, flags);
outl(index, scx200_gpio_base + 0x20);
config = inl(scx200_gpio_base + 0x24);
new_config = (config & mask) | bits;
outl(new_config, scx200_gpio_base + 0x24);
spin_unlock_irqrestore(&scx200_gpio_config_lock, flags);
return config;
}
void scx200_gpio_dump(unsigned index)
{
u32 config = scx200_gpio_configure(index, ~0, 0);
printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config);
if (config & 1)
printk(" OE"); /* output enabled */
else
printk(" TS"); /* tristate */
if (config & 2)
printk(" PP"); /* push pull */
else
printk(" OD"); /* open drain */
if (config & 4)
printk(" PUE"); /* pull up enabled */
else
printk(" PUD"); /* pull up disabled */
if (config & 8)
printk(" LOCKED"); /* locked */
if (config & 16)
printk(" LEVEL"); /* level input */
else
printk(" EDGE"); /* edge input */
if (config & 32)
printk(" HI"); /* trigger on rising edge */
else
printk(" LO"); /* trigger on falling edge */
if (config & 64)
printk(" DEBOUNCE"); /* debounce */
printk("\n");
}
int __init scx200_init(void)
{
struct pci_dev *bridge;
int bank;
unsigned base;
printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n");
if ((bridge = pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
base = pci_resource_start(bridge, 0);
printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
return -EBUSY;
}
scx200_gpio_base = base;
/* read the current values driven on the GPIO signals */
for (bank = 0; bank < 2; ++bank)
scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
return 0;
}
void __exit scx200_cleanup(void)
{
release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
}
module_init(scx200_init);
module_exit(scx200_cleanup);
EXPORT_SYMBOL(scx200_gpio_base);
EXPORT_SYMBOL(scx200_gpio_shadow);
EXPORT_SYMBOL(scx200_gpio_lock);
EXPORT_SYMBOL(scx200_gpio_configure);
EXPORT_SYMBOL(scx200_gpio_dump);
/*
Local variables:
compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules"
c-basic-offset: 8
End:
*/
......@@ -44,24 +44,22 @@ find_key_inode(int key)
static struct page *
alloc_hugetlb_page(void)
{
struct list_head *curr, *head;
int i;
struct page *page;
spin_lock(&htlbpage_lock);
head = &htlbpage_freelist;
curr = head->next;
if (curr == head) {
if (list_empty(&htlbpage_freelist)) {
spin_unlock(&htlbpage_lock);
return NULL;
}
page = list_entry(curr, struct page, list);
list_del(curr);
page = list_entry(htlbpage_freelist.next, struct page, list);
list_del(&page->list);
htlbpagemem--;
spin_unlock(&htlbpage_lock);
set_page_count(page, 1);
memset(page_address(page), 0, HPAGE_SIZE);
for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i)
clear_highpage(&page[i]);
return page;
}
......
......@@ -1876,7 +1876,7 @@ void generic_make_request(struct bio *bio)
*/
int submit_bio(int rw, struct bio *bio)
{
int count = bio_sectors(bio);
int count = bio_sectors(bio) >> 1;
BUG_ON(!bio->bi_end_io);
BIO_BUG_ON(!bio->bi_size);
......
......@@ -818,6 +818,12 @@ CONFIG_MIXCOMWD
module, say M here and read <file:Documentation/modules.txt>. Most
people will say N.
CONFIG_SCx200_WDT
Enable the built-in watchdog timer support on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_watchdog.o.
CONFIG_MACHZ_WDT
If you are using a ZF Micro MachZ processor, say Y here, otherwise
N. This is the driver for the watchdog timer builtin on that
......@@ -1021,3 +1027,9 @@ CONFIG_RAW_DRIVER
Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
See the raw(8) manpage for more details.
CONFIG_SCx200_GPIO
Give userspace access to the GPIO pins on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_gpio.o.
......@@ -131,6 +131,7 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT
tristate ' Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO
tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD
tristate ' NatSemi SCx200 Watchdog' CONFIG_SCx200_WDT
tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
tristate ' W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT
tristate ' ZF MachZ Watchdog' CONFIG_MACHZ_WDT
......@@ -193,6 +194,8 @@ if [ "$CONFIG_X86" = "y" ]; then
tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE
fi
dep_tristate 'NatSemi SCx200 GPIO Support' CONFIG_SCx200_GPIO $CONFIG_SCx200
tristate ' RAW driver (/dev/raw/rawN)' CONFIG_RAW_DRIVER
endmenu
......@@ -75,6 +75,7 @@ obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_DZ) += dz.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
# Only one watchdog can succeed. We probe the hardware watchdog
# drivers first, then the softdog driver. This means if your hardware
......@@ -86,6 +87,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_IB700_WDT) += ib700wdt.o
obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_WDTPCI) += wdt_pci.o
......
/* linux/drivers/char/scx200_gpio.c
National Semiconductor SCx200 GPIO driver. Allows a user space
process to play with the GPIO pins.
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define NAME "scx200_gpio"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver");
MODULE_LICENSE("GPL");
static int major = 0; /* default to dynamic major */
MODULE_PARM(major, "i");
MODULE_PARM_DESC(major, "Major device number");
static ssize_t scx200_gpio_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
unsigned m = minor(file->f_dentry->d_inode->i_rdev);
size_t i;
if (ppos != &file->f_pos)
return -ESPIPE;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data+i))
return -EFAULT;
switch (c)
{
case '0':
scx200_gpio_set(m, 0);
break;
case '1':
scx200_gpio_set(m, 1);
break;
case 'O':
printk(KERN_INFO NAME ": GPIO%d output enabled\n", m);
scx200_gpio_configure(m, ~1, 1);
break;
case 'o':
printk(KERN_INFO NAME ": GPIO%d output disabled\n", m);
scx200_gpio_configure(m, ~1, 0);
break;
case 'T':
printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m);
scx200_gpio_configure(m, ~2, 2);
break;
case 't':
printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m);
scx200_gpio_configure(m, ~2, 0);
break;
case 'P':
printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m);
scx200_gpio_configure(m, ~4, 4);
break;
case 'p':
printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m);
scx200_gpio_configure(m, ~4, 0);
break;
}
}
return len;
}
static ssize_t scx200_gpio_read(struct file *file, char *buf,
size_t len, loff_t *ppos)
{
unsigned m = minor(file->f_dentry->d_inode->i_rdev);
int value;
if (ppos != &file->f_pos)
return -ESPIPE;
value = scx200_gpio_get(m);
if (put_user(value ? '1' : '0', buf))
return -EFAULT;
return 1;
}
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = minor(inode->i_rdev);
if (m > 63)
return -EINVAL;
return 0;
}
static int scx200_gpio_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations scx200_gpio_fops = {
.owner = THIS_MODULE,
.write = scx200_gpio_write,
.read = scx200_gpio_read,
.open = scx200_gpio_open,
.release = scx200_gpio_release,
};
static int __init scx200_gpio_init(void)
{
int r;
printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n");
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
r = register_chrdev(major, NAME, &scx200_gpio_fops);
if (r < 0) {
printk(KERN_ERR NAME ": unable to register character device\n");
return r;
}
if (!major) {
major = r;
printk(KERN_DEBUG NAME ": got dynamic major %d\n", major);
}
return 0;
}
static void __exit scx200_gpio_cleanup(void)
{
unregister_chrdev(major, NAME);
}
module_init(scx200_gpio_init);
module_exit(scx200_gpio_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
c-basic-offset: 8
End:
*/
/* linux/drivers/char/scx200_wdt.c
National Semiconductor SCx200 Watchdog support
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
Som code taken from:
National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
(c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The author(s) of this software shall not be held liable for damages
of any nature resulting due to the use of this software. This
software is provided AS-IS with no warranties. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/scx200.h>
#define NAME "scx200_wdt"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
MODULE_LICENSE("GPL");
#ifndef CONFIG_WATCHDOG_NOWAYOUT
#define CONFIG_WATCHDOG_NOWAYOUT 0
#endif
static int margin = 60; /* in seconds */
MODULE_PARM(margin, "i");
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
static int nowayout = CONFIG_WATCHDOG_NOWAYOUT;
MODULE_PARM(nowayout, "i");
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
static u16 wdto_restart;
static struct semaphore open_semaphore;
static unsigned expect_close;
/* Bits of the WDCNFG register */
#define W_ENABLE 0x00fa /* Enable watchdog */
#define W_DISABLE 0x0000 /* Disable watchdog */
/* The scaling factor for the timer, this depends on the value of W_ENABLE */
#define W_SCALE (32768/1024)
static void scx200_wdt_ping(void)
{
outw(wdto_restart, SCx200_CB_BASE + SCx200_WDT_WDTO);
}
static void scx200_wdt_update_margin(void)
{
printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
wdto_restart = margin * W_SCALE;
}
static void scx200_wdt_enable(void)
{
printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
wdto_restart);
outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
outw(W_ENABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
scx200_wdt_ping();
}
static void scx200_wdt_disable(void)
{
printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
outw(W_DISABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
}
static int scx200_wdt_open(struct inode *inode, struct file *file)
{
/* only allow one at a time */
if (down_trylock(&open_semaphore))
return -EBUSY;
scx200_wdt_enable();
expect_close = 0;
return 0;
}
static int scx200_wdt_release(struct inode *inode, struct file *file)
{
if (!expect_close) {
printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n");
} else if (!nowayout) {
scx200_wdt_disable();
}
up(&open_semaphore);
return 0;
}
static int scx200_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
if (code == SYS_HALT || code == SYS_POWER_OFF)
if (!nowayout)
scx200_wdt_disable();
return NOTIFY_DONE;
}
static struct notifier_block scx200_wdt_notifier =
{
notifier_call: scx200_wdt_notify_sys
};
static ssize_t scx200_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
if (ppos != &file->f_pos)
return -ESPIPE;
/* check for a magic close character */
if (len)
{
size_t i;
scx200_wdt_ping();
expect_close = 0;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data+i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
return len;
}
return 0;
}
static int scx200_wdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
.identity = "NatSemi SCx200 Watchdog",
.firmware_version = 1,
.options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
};
int new_margin;
switch (cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
if(copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)))
return -EFAULT;
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
if (put_user(0, (int *)arg))
return -EFAULT;
return 0;
case WDIOC_KEEPALIVE:
scx200_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, (int *)arg))
return -EFAULT;
if (new_margin < 1)
return -EINVAL;
margin = new_margin;
scx200_wdt_update_margin();
scx200_wdt_ping();
case WDIOC_GETTIMEOUT:
if (put_user(margin, (int *)arg))
return -EFAULT;
return 0;
}
}
static struct file_operations scx200_wdt_fops = {
.owner = THIS_MODULE,
.write = scx200_wdt_write,
.ioctl = scx200_wdt_ioctl,
.open = scx200_wdt_open,
.release = scx200_wdt_release,
};
static struct miscdevice scx200_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = NAME,
.fops = &scx200_wdt_fops,
};
static int __init scx200_wdt_init(void)
{
int r;
printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
/* First check that this really is a NatSemi SCx200 CPU */
if ((pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
/* More sanity checks, verify that the configuration block is there */
if (!scx200_cb_probe(SCx200_CB_BASE)) {
printk(KERN_WARNING NAME ": no configuration block found\n");
return -ENODEV;
}
if (!request_region(SCx200_CB_BASE + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE,
"NatSemi SCx200 Watchdog")) {
printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
return -EBUSY;
}
scx200_wdt_update_margin();
scx200_wdt_disable();
sema_init(&open_semaphore, 1);
r = misc_register(&scx200_wdt_miscdev);
if (r)
return r;
r = register_reboot_notifier(&scx200_wdt_notifier);
if (r) {
printk(KERN_ERR NAME ": unable to register reboot notifier");
misc_deregister(&scx200_wdt_miscdev);
return r;
}
return 0;
}
static void __exit scx200_wdt_cleanup(void)
{
unregister_reboot_notifier(&scx200_wdt_notifier);
misc_deregister(&scx200_wdt_miscdev);
release_region(SCx200_CB_BASE + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE);
}
module_init(scx200_wdt_init);
module_exit(scx200_wdt_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
c-basic-offset: 8
End:
*/
......@@ -91,6 +91,28 @@ CONFIG_I2C_CHARDEV
<file:Documentation/modules.txt>.
The module will be called i2c-dev.o.
CONFIG_SCx200_I2C
Enable the use of two GPIO pins of a SCx200 processor as an I2C bus.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_i2c.o.
CONFIG_SCx200_I2C_SCL
Enter the GPIO pin number used for the SCL signal. This value can
also be specified with a module parameter.
CONFIG_SCx200_I2C_SDA
Enter the GPIO pin number used for the SSA signal. This value can
also be specified with a module parameter.
CONFIG_SCx200_ACB
Enable the use of the ACCESS.bus controllers of a SCx200 processor.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_acb.o.
CONFIG_I2C_PROC
This provides support for i2c device entries in the /proc filesystem.
The entries will be found in /proc/sys/dev/sensors.
......
......@@ -13,6 +13,12 @@ if [ "$CONFIG_I2C" != "n" ]; then
dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT $CONFIG_PARPORT
dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT
dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT
dep_tristate ' NatSemi SCx200 I2C using GPIO pins' CONFIG_SCx200_I2C $CONFIG_SCx200 $CONFIG_I2C_ALGOBIT
if [ "$CONFIG_SCx200_I2C" != "n" ]; then
int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12
int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13
fi
dep_tristate ' NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C
fi
dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C
......
......@@ -15,6 +15,8 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o
obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_I2C_PROC) += i2c-proc.o
# This is needed for automatic patch generation: sensors code starts here
......
/* linux/drivers/i2c/scx200_acb.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 ACCESS.bus support
Based on i2c-keywest.c which is:
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <linux/scx200.h>
#define NAME "scx200_acb"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
MODULE_LICENSE("GPL");
#define MAX_DEVICES 4
static int base[MAX_DEVICES] = { 0x840 };
MODULE_PARM(base, "1-4i");
MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers");
#define DEBUG 0
#if DEBUG
#define DBG(x...) printk(KERN_DEBUG NAME ": " x)
#else
#define DBG(x...)
#endif
/* The hardware supports interrupt driven mode too, but I haven't
implemented that. */
#define POLLED_MODE 1
#define POLL_TIMEOUT (HZ)
enum scx200_acb_state {
state_idle,
state_address,
state_command,
state_repeat_start,
state_quick,
state_read,
state_write,
};
static const char *scx200_acb_state_name[] = {
"idle",
"address",
"command",
"repeat_start",
"quick",
"read",
"write",
};
/* Physical interface */
struct scx200_acb_iface
{
struct scx200_acb_iface *next;
struct i2c_adapter adapter;
unsigned base;
struct semaphore sem;
/* State machine data */
enum scx200_acb_state state;
int result;
u8 address_byte;
u8 command;
u8 *ptr;
char needs_reset;
unsigned len;
};
/* Register Definitions */
#define ACBSDA (iface->base + 0)
#define ACBST (iface->base + 1)
#define ACBST_SDAST 0x40 /* SDA Status */
#define ACBST_BER 0x20
#define ACBST_NEGACK 0x10 /* Negative Acknowledge */
#define ACBST_STASTR 0x08 /* Stall After Start */
#define ACBST_MASTER 0x02
#define ACBCST (iface->base + 2)
#define ACBCST_BB 0x02
#define ACBCTL1 (iface->base + 3)
#define ACBCTL1_STASTRE 0x80
#define ACBCTL1_NMINTE 0x40
#define ACBCTL1_ACK 0x10
#define ACBCTL1_STOP 0x02
#define ACBCTL1_START 0x01
#define ACBADDR (iface->base + 4)
#define ACBCTL2 (iface->base + 5)
#define ACBCTL2_ENABLE 0x01
/************************************************************************/
static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
{
const char *errmsg;
DBG("state %s, status = 0x%02x\n",
scx200_acb_state_name[iface->state], status);
if (status & ACBST_BER) {
errmsg = "bus error";
goto error;
}
if (!(status & ACBST_MASTER)) {
errmsg = "not master";
goto error;
}
if (status & ACBST_NEGACK)
goto negack;
switch (iface->state) {
case state_idle:
printk(KERN_WARNING NAME ": %s, interrupt in idle state\n",
iface->adapter.name);
break;
case state_address:
/* Do a pointer write first */
outb(iface->address_byte & ~1, ACBSDA);
iface->state = state_command;
break;
case state_command:
outb(iface->command, ACBSDA);
if (iface->address_byte & 1)
iface->state = state_repeat_start;
else
iface->state = state_write;
break;
case state_repeat_start:
outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
/* fallthrough */
case state_quick:
if (iface->address_byte & 1) {
if (iface->len == 1)
outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
else
outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
outb(iface->address_byte, ACBSDA);
iface->state = state_read;
} else {
outb(iface->address_byte, ACBSDA);
iface->state = state_write;
}
break;
case state_read:
/* Set ACK if receiving the last byte */
if (iface->len == 1)
outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
else
outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
*iface->ptr++ = inb(ACBSDA);
--iface->len;
if (iface->len == 0) {
iface->result = 0;
iface->state = state_idle;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
}
break;
case state_write:
if (iface->len == 0) {
iface->result = 0;
iface->state = state_idle;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
break;
}
outb(*iface->ptr++, ACBSDA);
--iface->len;
break;
}
return;
negack:
DBG("negative acknowledge in state %s\n",
scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -ENXIO;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
outb(ACBST_STASTR | ACBST_NEGACK, ACBST);
return;
error:
printk(KERN_ERR NAME ": %s, %s in state %s\n", iface->adapter.name,
errmsg, scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -EIO;
iface->needs_reset = 1;
}
static void scx200_acb_timeout(struct scx200_acb_iface *iface)
{
printk(KERN_ERR NAME ": %s, timeout in state %s\n",
iface->adapter.name, scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -EIO;
iface->needs_reset = 1;
}
#ifdef POLLED_MODE
static void scx200_acb_poll(struct scx200_acb_iface *iface)
{
u8 status = 0;
unsigned long timeout;
timeout = jiffies + POLL_TIMEOUT;
while (time_before(jiffies, timeout)) {
status = inb(ACBST);
if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) {
scx200_acb_machine(iface, status);
return;
}
schedule_timeout(HZ/100+1);
}
scx200_acb_timeout(iface);
}
#endif /* POLLED_MODE */
static void scx200_acb_reset(struct scx200_acb_iface *iface)
{
/* Disable the ACCESS.bus device and Configure the SCL
frequency: 16 clock cycles */
outb(0x70, ACBCTL2);
/* Polling mode */
outb(0, ACBCTL1);
/* Disable slave address */
outb(0, ACBADDR);
/* Enable the ACCESS.bus device */
outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
/* Free STALL after START */
outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1);
/* Send a STOP */
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
/* Clear BER, NEGACK and STASTR bits */
outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST);
/* Clear BB bit */
outb(inb(ACBCST) | ACBCST_BB, ACBCST);
}
static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
u16 address, unsigned short flags,
char rw, u8 command, int size,
union i2c_smbus_data *data)
{
struct scx200_acb_iface *iface = adapter->data;
int len;
u8 *buffer;
u16 cur_word;
int rc;
switch (size) {
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
break;
case I2C_SMBUS_BYTE:
if (rw == I2C_SMBUS_READ) {
len = 1;
buffer = &data->byte;
} else {
len = 1;
buffer = &command;
}
break;
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
break;
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
break;
default:
return -EINVAL;
}
DBG("size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n",
size, address, command, len, rw == I2C_SMBUS_READ);
if (!len && rw == I2C_SMBUS_READ) {
printk(KERN_WARNING NAME ": %s, zero length read\n",
adapter->name);
return -EINVAL;
}
if (len && !buffer) {
printk(KERN_WARNING NAME ": %s, nonzero length but no buffer\n", adapter->name);
return -EFAULT;
}
down(&iface->sem);
iface->address_byte = address<<1;
if (rw == I2C_SMBUS_READ)
iface->address_byte |= 1;
iface->command = command;
iface->ptr = buffer;
iface->len = len;
iface->result = -EINVAL;
iface->needs_reset = 0;
outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)
iface->state = state_quick;
else
iface->state = state_address;
#ifdef POLLED_MODE
while (iface->state != state_idle)
scx200_acb_poll(iface);
#else /* POLLED_MODE */
#error Interrupt driven mode not implemented
#endif /* POLLED_MODE */
if (iface->needs_reset)
scx200_acb_reset(iface);
rc = iface->result;
up(&iface->sem);
if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ)
data->word = le16_to_cpu(cur_word);
#if DEBUG
printk(KERN_DEBUG NAME ": transfer done, result: %d", rc);
if (buffer) {
int i;
printk(" data:");
for (i = 0; i < len; ++i)
printk(" %02x", buffer[i]);
}
printk("\n");
#endif
return rc;
}
static u32 scx200_acb_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static int scx200_acb_reg(struct i2c_client *client)
{
return 0;
}
static int scx200_acb_unreg(struct i2c_client *client)
{
return 0;
}
static void scx200_acb_inc_use(struct i2c_adapter *adapter)
{
MOD_INC_USE_COUNT;
}
static void scx200_acb_dec_use(struct i2c_adapter *adapter)
{
MOD_DEC_USE_COUNT;
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm scx200_acb_algorithm = {
name: "NatSemi SCx200 ACCESS.bus",
id: I2C_ALGO_SMBUS,
smbus_xfer: scx200_acb_smbus_xfer,
functionality: scx200_acb_func,
};
struct scx200_acb_iface *scx200_acb_list;
int scx200_acb_probe(struct scx200_acb_iface *iface)
{
u8 val;
/* Disable the ACCESS.bus device and Configure the SCL
frequency: 16 clock cycles */
outb(0x70, ACBCTL2);
if (inb(ACBCTL2) != 0x70) {
DBG("ACBCTL2 readback failed\n");
return -ENXIO;
}
outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
val = inb(ACBCTL1);
if (val) {
DBG("disabled, but ACBCTL1=0x%02x\n", val);
return -ENXIO;
}
outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
val = inb(ACBCTL1);
if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) {
DBG("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", val);
return -ENXIO;
}
return 0;
}
static int __init scx200_acb_create(int base, int index)
{
struct scx200_acb_iface *iface;
struct i2c_adapter *adapter;
int rc = 0;
char description[64];
iface = kmalloc(sizeof(*iface), GFP_KERNEL);
if (!iface) {
printk(KERN_ERR NAME ": can't allocate memory\n");
rc = -ENOMEM;
goto errout;
}
memset(iface, 0, sizeof(*iface));
adapter = &iface->adapter;
adapter->data = iface;
sprintf(adapter->name, "SCx200 ACB%d", index);
adapter->id = I2C_ALGO_SMBUS;
adapter->algo = &scx200_acb_algorithm;
adapter->inc_use = scx200_acb_inc_use;
adapter->dec_use = scx200_acb_dec_use;
adapter->client_register = scx200_acb_reg;
adapter->client_unregister = scx200_acb_unreg;
init_MUTEX(&iface->sem);
sprintf(description, "NatSemi SCx200 ACCESS.bus [%s]", adapter->name);
if (request_region(base, 8, description) == 0) {
printk(KERN_ERR NAME ": %s, can't allocate io 0x%x-0x%x\n",
adapter->name, base, base + 8-1);
rc = -EBUSY;
goto errout;
}
iface->base = base;
rc = scx200_acb_probe(iface);
if (rc) {
printk(KERN_WARNING NAME ": %s, probe failed\n", adapter->name);
goto errout;
}
scx200_acb_reset(iface);
if (i2c_add_adapter(adapter) < 0) {
printk(KERN_ERR NAME ": %s, failed to register\n", adapter->name);
rc = -ENODEV;
goto errout;
}
lock_kernel();
iface->next = scx200_acb_list;
scx200_acb_list = iface;
unlock_kernel();
return 0;
errout:
if (iface) {
if (iface->base)
release_region(iface->base, 8);
kfree(iface);
}
return rc;
}
static int __init scx200_acb_init(void)
{
int i;
int rc;
printk(KERN_DEBUG NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
/* Verify that this really is a SCx200 processor */
if (pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL) == NULL)
return -ENODEV;
rc = -ENXIO;
for (i = 0; i < MAX_DEVICES; ++i) {
if (base[i] > 0)
rc = scx200_acb_create(base[i], i);
}
if (scx200_acb_list)
return 0;
return rc;
}
static void __exit scx200_acb_cleanup(void)
{
struct scx200_acb_iface *iface;
lock_kernel();
while ((iface = scx200_acb_list) != NULL) {
scx200_acb_list = iface->next;
unlock_kernel();
i2c_del_adapter(&iface->adapter);
release_region(iface->base, 8);
kfree(iface);
lock_kernel();
}
unlock_kernel();
}
module_init(scx200_acb_init);
module_exit(scx200_acb_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
c-basic-offset: 8
End:
*/
/* linux/drivers/i2c/scx200_i2c.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 I2C bus on GPIO pins
Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define NAME "scx200_i2c"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver");
MODULE_LICENSE("GPL");
MODULE_PARM(scl, "i");
MODULE_PARM_DESC(scl, "GPIO line for SCL");
MODULE_PARM(sda, "i");
MODULE_PARM_DESC(sda, "GPIO line for SDA");
static int scl = CONFIG_SCx200_I2C_SCL;
static int sda = CONFIG_SCx200_I2C_SDA;
static void scx200_i2c_setscl(void *data, int state)
{
scx200_gpio_set(scl, state);
}
static void scx200_i2c_setsda(void *data, int state)
{
scx200_gpio_set(sda, state);
}
static int scx200_i2c_getscl(void *data)
{
return scx200_gpio_get(scl);
}
static int scx200_i2c_getsda(void *data)
{
return scx200_gpio_get(sda);
}
static int scx200_i2c_reg(struct i2c_client *client)
{
return 0;
}
static int scx200_i2c_unreg(struct i2c_client *client)
{
return 0;
}
static void scx200_i2c_inc_use(struct i2c_adapter *adap)
{
MOD_INC_USE_COUNT;
}
static void scx200_i2c_dec_use(struct i2c_adapter *adap)
{
MOD_DEC_USE_COUNT;
}
/* ------------------------------------------------------------------------
* Encapsulate the above functions in the correct operations structure.
* This is only done when more than one hardware adapter is supported.
*/
static struct i2c_algo_bit_data scx200_i2c_data = {
NULL,
scx200_i2c_setsda,
scx200_i2c_setscl,
scx200_i2c_getsda,
scx200_i2c_getscl,
10, 10, 100, /* waits, timeout */
};
static struct i2c_adapter scx200_i2c_ops = {
.name = "NatSemi SCx200 I2C",
.id = I2C_HW_B_VELLE,
.algo_data = &scx200_i2c_data,
.inc_use = scx200_i2c_inc_use,
.dec_use = scx200_i2c_dec_use,
.client_register = scx200_i2c_reg,
.client_unregister = scx200_i2c_unreg,
};
int scx200_i2c_init(void)
{
printk(KERN_DEBUG NAME ": NatSemi SCx200 I2C Driver\n");
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
printk(KERN_DEBUG NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n",
scl, sda);
if (scl == -1 || sda == -1 || scl == sda) {
printk(KERN_ERR NAME ": scl and sda must be specified\n");
return -EINVAL;
}
/* Configure GPIOs as open collector outputs */
scx200_gpio_configure(scl, ~2, 5);
scx200_gpio_configure(sda, ~2, 5);
if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) {
printk(KERN_ERR NAME ": adapter %s registration failed\n",
scx200_i2c_ops.name);
return -ENODEV;
}
return 0;
}
void scx200_i2c_cleanup(void)
{
i2c_bit_del_bus(&scx200_i2c_ops);
}
module_init(scx200_i2c_init);
module_exit(scx200_i2c_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
c-basic-offset: 8
End:
*/
......@@ -127,6 +127,14 @@ CONFIG_MTD_MIXMEM
you probably want to enable this mapping driver. More info is at
<http://www.itc.hu/>.
CONFIG_MTD_SCx200_DOCFLASH
Enable support for a flash chip mapped using the DOCCS signal on a
National Semiconductor SCx200 processor.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_docflash.o.
CONFIG_MTD_OCTAGON
This provides a 'mapping' driver which supports the way in which
the flash chips are connected in the Octagon-5066 Single Board
......
......@@ -26,6 +26,7 @@ if [ "$CONFIG_X86" = "y" ]; then
dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDEC
fi
......
......@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_VMAX) += vmax301.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_OCELOT) += ocelot.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
......
/* linux/drivers/mtd/maps/scx200_docflash.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 flash mapped with DOCCS
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/pci.h>
#include <linux/scx200.h>
#define NAME "scx200_docflash"
MODULE_AUTHOR("Christer Weinigel <wingel@hack.org>");
MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver");
MODULE_LICENSE("GPL");
/* Set this to one if you want to partition the flash */
#define PARTITION 1
MODULE_PARM(probe, "i");
MODULE_PARM_DESC(probe, "Probe for a BIOS mapping");
MODULE_PARM(size, "i");
MODULE_PARM_DESC(size, "Size of the flash mapping");
MODULE_PARM(width, "i");
MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)");
MODULE_PARM(flashtype, "s");
MODULE_PARM_DESC(flashtype, "Type of MTD probe to do");
static int probe = 0; /* Don't autoprobe */
static unsigned size = 0x1000000; /* 16 MB the whole ISA address space */
static unsigned width = 8; /* Default to 8 bits wide */
static char *flashtype = "cfi_probe";
static struct resource docmem = {
.flags = IORESOURCE_MEM,
.name = "NatSemi SCx200 DOCCS Flash",
};
static struct mtd_info *mymtd;
#if PARTITION
static struct mtd_partition partition_info[] = {
{
.name = "DOCCS Boot kernel",
.offset = 0,
.size = 0xc0000
},
{
.name = "DOCCS Low BIOS",
.offset = 0xc0000,
.size = 0x40000
},
{
.name = "DOCCS File system",
.offset = 0x100000,
.size = ~0 /* calculate from flash size */
},
{
.name = "DOCCS High BIOS",
.offset = ~0, /* calculate from flash size */
.size = 0x80000
},
};
#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
#endif
static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
{
return __raw_readb(map->map_priv_1 + ofs);
}
static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
{
return __raw_readw(map->map_priv_1 + ofs);
}
static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy_fromio(to, map->map_priv_1 + from, len);
}
static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
{
__raw_writeb(d, map->map_priv_1 + adr);
mb();
}
static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
{
__raw_writew(d, map->map_priv_1 + adr);
mb();
}
static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(map->map_priv_1 + to, from, len);
}
static struct map_info scx200_docflash_map = {
.name = "NatSemi SCx200 DOCCS Flash",
.read8 = scx200_docflash_read8,
.read16 = scx200_docflash_read16,
.copy_from = scx200_docflash_copy_from,
.write8 = scx200_docflash_write8,
.write16 = scx200_docflash_write16,
.copy_to = scx200_docflash_copy_to
};
int __init init_scx200_docflash(void)
{
unsigned u;
unsigned base;
unsigned ctrl;
unsigned pmr;
struct pci_dev *bridge;
printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n");
if ((bridge = pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
if (!scx200_cb_probe(SCx200_CB_BASE)) {
printk(KERN_WARNING NAME ": no configuration block found\n");
return -ENODEV;
}
if (probe) {
/* Try to use the present flash mapping if any */
pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base);
pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl);
pmr = inl(SCx200_CB_BASE + SCx200_PMR);
if (base == 0
|| (ctrl & 0x07000000) != 0x07000000
|| (ctrl & 0x0007ffff) == 0)
return -ENODEV;
size = ((ctrl&0x1fff)<<13) + (1<<13);
for (u = size; u > 1; u >>= 1)
;
if (u != 1)
return -ENODEV;
if (pmr & (1<<6))
width = 16;
else
width = 8;
docmem.start = base;
docmem.end = base + size;
if (request_resource(&iomem_resource, &docmem)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
} else {
for (u = size; u > 1; u >>= 1)
;
if (u != 1) {
printk(KERN_ERR NAME ": invalid size for flash mapping\n");
return -EINVAL;
}
if (width != 8 && width != 16) {
printk(KERN_ERR NAME ": invalid bus width for flash mapping\n");
return -EINVAL;
}
if (allocate_resource(&iomem_resource, &docmem,
size,
0xc0000000, 0xffffffff,
size, NULL, NULL)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
ctrl = 0x07000000 | ((size-1) >> 13);
printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl);
pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start);
pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl);
pmr = inl(SCx200_CB_BASE + SCx200_PMR);
if (width == 8) {
pmr &= ~(1<<6);
} else {
pmr |= (1<<6);
}
outl(pmr, SCx200_CB_BASE + SCx200_PMR);
}
printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n",
docmem.start, docmem.end, width);
scx200_docflash_map.size = size;
if (width == 8)
scx200_docflash_map.buswidth = 1;
else
scx200_docflash_map.buswidth = 2;
scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
if (!scx200_docflash_map.map_priv_1) {
printk(KERN_ERR NAME ": failed to ioremap the flash\n");
release_resource(&docmem);
return -EIO;
}
mymtd = do_map_probe(flashtype, &scx200_docflash_map);
if (!mymtd) {
printk(KERN_ERR NAME ": unable to detect flash\n");
iounmap((void *)scx200_docflash_map.map_priv_1);
release_resource(&docmem);
return -ENXIO;
}
if (size < mymtd->size)
printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
mymtd->module = THIS_MODULE;
#if PARTITION
partition_info[3].offset = mymtd->size-partition_info[3].size;
partition_info[2].size = partition_info[3].offset-partition_info[2].offset;
add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
#else
add_mtd_device(mymtd);
#endif
return 0;
}
static void __exit cleanup_scx200_docflash(void)
{
if (mymtd) {
#if PARTITION
del_mtd_partitions(mymtd);
#else
del_mtd_device(mymtd);
#endif
map_destroy(mymtd);
}
if (scx200_docflash_map.map_priv_1) {
iounmap((void *)scx200_docflash_map.map_priv_1);
release_resource(&docmem);
}
}
module_init(init_scx200_docflash);
module_exit(cleanup_scx200_docflash);
/*
Local variables:
compile-command: "make -k -C ../../.. SUBDIRS=drivers/mtd/maps modules"
c-basic-offset: 8
End:
*/
......@@ -3832,14 +3832,14 @@ static int st_attach(Scsi_Device * SDp)
tpnt->driverfs_dev_r[mode].parent = &SDp->sdev_driverfs_dev;
tpnt->driverfs_dev_r[mode].bus = &scsi_driverfs_bus_type;
tpnt->driverfs_dev_r[mode].driver_data =
(void *)(long)__mkdev(MAJOR_NR, i + (mode << 5));
(void *)(long)__mkdev(MAJOR_NR, dev_num + (mode << 5));
device_register(&tpnt->driverfs_dev_r[mode]);
device_create_file(&tpnt->driverfs_dev_r[mode],
&dev_attr_type);
device_create_file(&tpnt->driverfs_dev_r[mode], &dev_attr_kdev);
tpnt->de_r[mode] =
devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
MAJOR_NR, i + (mode << 5),
MAJOR_NR, dev_num + (mode << 5),
S_IFCHR | S_IRUGO | S_IWUGO,
&st_fops, NULL);
/* No-rewind entry */
......@@ -3851,7 +3851,7 @@ static int st_attach(Scsi_Device * SDp)
tpnt->driverfs_dev_n[mode].parent= &SDp->sdev_driverfs_dev;
tpnt->driverfs_dev_n[mode].bus = &scsi_driverfs_bus_type;
tpnt->driverfs_dev_n[mode].driver_data =
(void *)(long)__mkdev(MAJOR_NR, i + (mode << 5) + 128);
(void *)(long)__mkdev(MAJOR_NR, dev_num + (mode << 5) + 128);
device_register(&tpnt->driverfs_dev_n[mode]);
device_create_file(&tpnt->driverfs_dev_n[mode],
&dev_attr_type);
......@@ -3859,7 +3859,7 @@ static int st_attach(Scsi_Device * SDp)
&dev_attr_kdev);
tpnt->de_n[mode] =
devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
MAJOR_NR, i + (mode << 5) + 128,
MAJOR_NR, dev_num + (mode << 5) + 128,
S_IFCHR | S_IRUGO | S_IWUGO,
&st_fops, NULL);
}
......
......@@ -735,80 +735,22 @@ int sync_mapping_buffers(struct address_space *mapping)
}
EXPORT_SYMBOL(sync_mapping_buffers);
/**
* write_mapping_buffers - Start writeout of a mapping's "associated" buffers.
* @mapping - the mapping which wants those buffers written.
*
* Starts I/O against dirty buffers which are on @mapping->private_list.
* Those buffers must be backed by @mapping->assoc_mapping.
*
* The private_list buffers generally contain filesystem indirect blocks.
* The idea is that the filesystem can start I/O against the indirects at
* the same time as running generic_writepages(), so the indirect's
* I/O will be merged with the data.
*
* We sneakliy write the buffers in probable tail-to-head order. This is
* because generic_writepages() writes in probable head-to-tail
* order. If the file is so huge that the data or the indirects overflow
* the request queue we will at least get some merging this way.
*
* Any clean+unlocked buffers are de-listed. clean/locked buffers must be
* left on the list for an fsync() to wait on.
*
* Couldn't think of a smart way of avoiding livelock, so chose the dumb
* way instead.
*
* FIXME: duplicates fsync_inode_buffers() functionality a bit.
/*
* Called when we've recently written block `bblock', and it is known that
* `bblock' was for a buffer_boundary() buffer. This means that the block at
* `bblock + 1' is probably a dirty indirect block. Hunt it down and, if it's
* dirty, schedule it for IO. So that indirects merge nicely with their data.
*/
int write_mapping_buffers(struct address_space *mapping)
void write_boundary_block(struct block_device *bdev,
sector_t bblock, unsigned blocksize)
{
spinlock_t *lock;
struct address_space *buffer_mapping;
unsigned nr_to_write; /* livelock avoidance */
struct list_head *lh;
int ret = 0;
if (list_empty(&mapping->private_list))
goto out;
buffer_mapping = mapping->assoc_mapping;
lock = &buffer_mapping->private_lock;
spin_lock(lock);
nr_to_write = 0;
lh = mapping->private_list.next;
while (lh != &mapping->private_list) {
lh = lh->next;
nr_to_write++;
}
nr_to_write *= 2; /* Allow for some late additions */
while (nr_to_write-- && !list_empty(&mapping->private_list)) {
struct buffer_head *bh;
bh = BH_ENTRY(mapping->private_list.prev);
list_del_init(&bh->b_assoc_buffers);
if (!buffer_dirty(bh) && !buffer_locked(bh))
continue;
/* Stick it on the far end of the list. Order is preserved. */
list_add(&bh->b_assoc_buffers, &mapping->private_list);
if (test_set_buffer_locked(bh))
continue;
get_bh(bh);
spin_unlock(lock);
if (test_clear_buffer_dirty(bh)) {
bh->b_end_io = end_buffer_io_sync;
submit_bh(WRITE, bh);
} else {
unlock_buffer(bh);
struct buffer_head *bh = __find_get_block(bdev, bblock + 1, blocksize);
if (bh) {
if (buffer_dirty(bh))
ll_rw_block(WRITE, 1, &bh);
put_bh(bh);
}
spin_lock(lock);
}
spin_unlock(lock);
out:
return ret;
}
EXPORT_SYMBOL(write_mapping_buffers);
void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
{
......@@ -2487,77 +2429,32 @@ int submit_bh(int rw, struct buffer_head * bh)
* All of the buffers must be for the same device, and must also be a
* multiple of the current approved size for the device.
*/
void ll_rw_block(int rw, int nr, struct buffer_head * bhs[])
void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
{
unsigned int major;
int correct_size;
int i;
if (!nr)
return;
major = major(to_kdev_t(bhs[0]->b_bdev->bd_dev));
/* Determine correct block size for this device. */
correct_size = bdev_hardsect_size(bhs[0]->b_bdev);
/* Verify requested block sizes. */
for (i = 0; i < nr; i++) {
struct buffer_head *bh = bhs[i];
if (bh->b_size & (correct_size - 1)) {
printk(KERN_NOTICE "ll_rw_block: device %s: "
"only %d-char blocks implemented (%u)\n",
bdevname(bhs[0]->b_bdev),
correct_size, bh->b_size);
goto sorry;
}
}
if ((rw & WRITE) && bdev_read_only(bhs[0]->b_bdev)) {
printk(KERN_NOTICE "Can't write to read-only device %s\n",
bdevname(bhs[0]->b_bdev));
goto sorry;
}
for (i = 0; i < nr; i++) {
struct buffer_head *bh = bhs[i];
/* Only one thread can actually submit the I/O. */
if (test_set_buffer_locked(bh))
continue;
/* We have the buffer lock */
atomic_inc(&bh->b_count);
get_bh(bh);
bh->b_end_io = end_buffer_io_sync;
switch(rw) {
case WRITE:
if (!test_clear_buffer_dirty(bh))
/* Hmmph! Nothing to write */
goto end_io;
break;
case READA:
case READ:
if (buffer_uptodate(bh))
/* Hmmph! Already have it */
goto end_io;
break;
default:
BUG();
end_io:
bh->b_end_io(bh, buffer_uptodate(bh));
if (rw == WRITE) {
if (test_clear_buffer_dirty(bh)) {
submit_bh(WRITE, bh);
continue;
}
submit_bh(rw, bh);
} else {
if (!buffer_uptodate(bh)) {
submit_bh(READ, bh);
continue;
}
}
unlock_buffer(bh);
put_bh(bh);
}
return;
sorry:
/* Make sure we don't get infinite dirty retries.. */
for (i = 0; i < nr; i++)
clear_buffer_dirty(bhs[i]);
}
/*
......
......@@ -49,6 +49,7 @@ struct dio {
/* Page fetching state */
int curr_page; /* changes */
int total_pages; /* doesn't change */
int pages_left; /* approximate total IO pages */
unsigned long curr_user_address;/* changes */
/* Page queue */
......@@ -396,7 +397,8 @@ static int dio_new_bio(struct dio *dio)
if (ret)
goto out;
sector = dio->next_block_in_bio << (dio->blkbits - 9);
nr_pages = min(dio->total_pages, BIO_MAX_PAGES);
nr_pages = min(dio->pages_left, bio_get_nr_vecs(dio->map_bh.b_bdev));
BUG_ON(nr_pages <= 0);
ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages);
dio->boundary = 0;
out:
......@@ -423,6 +425,7 @@ dio_bio_add_page(struct dio *dio, struct page *page,
}
}
page_cache_release(page);
dio->pages_left--;
out:
return ret;
}
......@@ -566,6 +569,10 @@ direct_io_worker(int rw, struct inode *inode, const struct iovec *iov,
spin_lock_init(&dio.bio_list_lock);
dio.bio_list = NULL;
dio.waiter = NULL;
dio.pages_left = 0;
for (seg = 0; seg < nr_segs; seg++)
dio.pages_left += (iov[seg].iov_len / PAGE_SIZE) + 2;
for (seg = 0; seg < nr_segs; seg++) {
user_addr = (unsigned long)iov[seg].iov_base;
......@@ -620,13 +627,11 @@ generic_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
int seg;
size_t size;
unsigned long addr;
struct address_space *mapping = inode->i_mapping;
unsigned blocksize_mask = (1 << inode->i_blkbits) - 1;
ssize_t retval = -EINVAL;
if (offset & blocksize_mask) {
if (offset & blocksize_mask)
goto out;
}
/* Check the memory alignment. Blocks cannot straddle pages */
for (seg = 0; seg < nr_segs; seg++) {
......@@ -636,14 +641,6 @@ generic_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
goto out;
}
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = direct_io_worker(rw, inode, iov, offset, nr_segs, get_blocks);
out:
return retval;
......@@ -656,8 +653,17 @@ generic_file_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
struct address_space *mapping = inode->i_mapping;
ssize_t retval;
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = mapping->a_ops->direct_IO(rw, inode, iov, offset, nr_segs);
if (inode->i_mapping->nrpages)
invalidate_inode_pages2(inode->i_mapping);
out:
return retval;
}
......@@ -629,14 +629,7 @@ ext2_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
static int
ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
int ret;
int err;
ret = write_mapping_buffers(mapping);
err = mpage_writepages(mapping, wbc, ext2_get_block);
if (!ret)
ret = err;
return ret;
return mpage_writepages(mapping, wbc, ext2_get_block);
}
struct address_space_operations ext2_aops = {
......
......@@ -1477,14 +1477,7 @@ struct address_space_operations ext3_aops = {
static int
ext3_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
int ret;
int err;
ret = write_mapping_buffers(mapping);
err = mpage_writepages(mapping, wbc, ext3_get_block);
if (!ret)
ret = err;
return ret;
return mpage_writepages(mapping, wbc, ext3_get_block);
}
#endif
......
......@@ -228,8 +228,6 @@ static int parse_options(char *options, int *debug,
save = 0;
savep = NULL;
while ((this_char = strsep(&options,",")) != NULL) {
if (!*this_char)
continue;
if ((value = strchr(this_char,'=')) != NULL) {
save = *value;
savep = value;
......@@ -351,7 +349,7 @@ static int parse_options(char *options, int *debug,
strncpy(cvf_options,value,100);
}
if (this_char != options) *(this_char-1) = ',';
if (options) *(options-1) = ',';
if (value) *savep = save;
if (ret == 0)
break;
......
......@@ -24,8 +24,8 @@ static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
goto fail1;
}
hpfs_brelse4(&qbh);
if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4;
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
......@@ -48,11 +48,11 @@ static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
{
if (start + len < start || start < 0x12 ||
start + len > s->s_hpfs_fs_size) {
start + len > hpfs_sb(s)->sb_fs_size) {
hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
return 1;
}
if (s->s_hpfs_chk>=2) {
if (hpfs_sb(s)->sb_chk>=2) {
int i;
for (i = 0; i < len; i++)
if (chk_if_allocated(s, start + i, msg)) return 1;
......@@ -127,7 +127,7 @@ static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigne
}
rt:
if (ret) {
if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
ret = 0;
goto b;
......@@ -155,14 +155,15 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa
secno sec;
unsigned i;
unsigned n_bmps;
int b = s->s_hpfs_c_bitmap;
struct hpfs_sb_info *sbi = hpfs_sb(s);
int b = sbi->sb_c_bitmap;
int f_p = 0;
if (forward < 0) {
forward = -forward;
f_p = 1;
}
if (lock) hpfs_lock_creation(s);
if (near && near < s->s_hpfs_fs_size)
if (near && near < sbi->sb_fs_size)
if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
if (b != -1) {
if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
......@@ -171,25 +172,25 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa
}
if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
}
n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14;
for (i = 0; i < n_bmps / 2; i++) {
if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) {
s->s_hpfs_c_bitmap = n_bmps/2+i;
sbi->sb_c_bitmap = n_bmps/2+i;
goto ret;
}
if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) {
s->s_hpfs_c_bitmap = n_bmps/2-i-1;
sbi->sb_c_bitmap = n_bmps/2-i-1;
goto ret;
}
}
if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) {
s->s_hpfs_c_bitmap = n_bmps-1;
sbi->sb_c_bitmap = n_bmps-1;
goto ret;
}
if (!f_p) {
for (i = 0; i < n_bmps; i++)
if ((sec = alloc_in_bmp(s, i << 14, n, 0))) {
s->s_hpfs_c_bitmap = 0x10000000 + i;
sbi->sb_c_bitmap = 0x10000000 + i;
goto ret;
}
}
......@@ -212,17 +213,18 @@ static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
{
unsigned nr = near;
secno sec;
if (nr < s->s_hpfs_dirband_start)
nr = s->s_hpfs_dirband_start;
if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
nr -= s->s_hpfs_dirband_start;
struct hpfs_sb_info *sbi = hpfs_sb(s);
if (nr < sbi->sb_dirband_start)
nr = sbi->sb_dirband_start;
if (nr >= sbi->sb_dirband_start + sbi->sb_dirband_size)
nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4;
nr -= sbi->sb_dirband_start;
nr >>= 2;
if (lock) hpfs_lock_creation(s);
sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
if (lock) hpfs_unlock_creation(s);
if (!sec) return 0;
return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
}
/* Alloc sector if it's free */
......@@ -303,8 +305,8 @@ void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
int hpfs_check_free_dnodes(struct super_block *s, int n)
{
int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
int b = s->s_hpfs_c_bitmap & 0x0fffffff;
int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14;
int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff;
int i, j;
unsigned *bmp;
struct quad_buffer_head qbh;
......@@ -320,7 +322,7 @@ int hpfs_check_free_dnodes(struct super_block *s, int n)
}
hpfs_brelse4(&qbh);
i = 0;
if (s->s_hpfs_c_bitmap != -1 ) {
if (hpfs_sb(s)->sb_c_bitmap != -1 ) {
bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
goto chk_bmp;
}
......@@ -349,17 +351,17 @@ int hpfs_check_free_dnodes(struct super_block *s, int n)
void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
{
if (s->s_hpfs_chk) if (dno & 3) {
if (hpfs_sb(s)->sb_chk) if (dno & 3) {
hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
return;
}
if (dno < s->s_hpfs_dirband_start ||
dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
if (dno < hpfs_sb(s)->sb_dirband_start ||
dno >= hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
hpfs_free_sectors(s, dno, 4);
} else {
struct quad_buffer_head qbh;
unsigned *bmp;
unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4;
lock_super(s);
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
unlock_super(s);
......@@ -377,7 +379,7 @@ struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
int lock)
{
struct dnode *d;
if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) {
if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) {
if (!(*dno = alloc_in_dirband(s, near, lock)))
if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
} else {
......
......@@ -20,7 +20,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
int i;
int c1, c2 = 0;
go_down:
if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
if (btree->internal) {
for (i = 0; i < btree->n_used_nodes; i++)
if (btree->u.internal[i].file_secno > sec) {
......@@ -38,7 +38,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
if (btree->u.external[i].file_secno <= sec &&
btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
brelse(bh);
return -1;
}
......@@ -88,7 +88,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
btree->u.internal[n].file_secno = -1;
mark_buffer_dirty(bh);
brelse(bh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
btree = &anode->btree;
......@@ -164,7 +164,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
c2 = 0;
while (up != -1) {
struct anode *new_anode;
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
if (up != node || !fnod) {
if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
......@@ -283,7 +283,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
while (btree1->internal) {
ano = btree1->u.internal[pos].down;
if (level) brelse(bh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
return;
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
......@@ -296,7 +296,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
go_up:
if (!level) return;
brelse(bh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
hpfs_free_sectors(s, ano, 1);
oano = ano;
......@@ -343,7 +343,7 @@ int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
return -1;
} else sec = a + (pos >> 9);
if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
return -1;
l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
......@@ -366,7 +366,7 @@ int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
return -1;
} else sec = a + (pos >> 9);
if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
return -1;
l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
......@@ -440,7 +440,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
}
node = btree->u.internal[i].down;
brelse(bh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
return;
if (!(anode = hpfs_map_anode(s, node, &bh))) return;
......
......@@ -15,7 +15,7 @@ void hpfs_lock_creation(struct super_block *s)
#ifdef DEBUG_LOCKS
printk("lock creation\n");
#endif
down(&s->u.hpfs_sb.hpfs_creation_de);
down(&hpfs_sb(s)->hpfs_creation_de);
}
void hpfs_unlock_creation(struct super_block *s)
......@@ -23,7 +23,7 @@ void hpfs_unlock_creation(struct super_block *s)
#ifdef DEBUG_LOCKS
printk("unlock creation\n");
#endif
up(&s->u.hpfs_sb.hpfs_creation_de);
up(&hpfs_sb(s)->hpfs_creation_de);
}
void hpfs_lock_iget(struct super_block *s, int mode)
......@@ -31,8 +31,8 @@ void hpfs_lock_iget(struct super_block *s, int mode)
#ifdef DEBUG_LOCKS
printk("lock iget\n");
#endif
while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
s->s_hpfs_rd_inode = mode;
while (hpfs_sb(s)->sb_rd_inode) sleep_on(&hpfs_sb(s)->sb_iget_q);
hpfs_sb(s)->sb_rd_inode = mode;
}
void hpfs_unlock_iget(struct super_block *s)
......@@ -40,8 +40,8 @@ void hpfs_unlock_iget(struct super_block *s)
#ifdef DEBUG_LOCKS
printk("unlock iget\n");
#endif
s->s_hpfs_rd_inode = 0;
wake_up(&s->s_hpfs_iget_q);
hpfs_sb(s)->sb_rd_inode = 0;
wake_up(&hpfs_sb(s)->sb_iget_q);
}
void hpfs_lock_inode(struct inode *i)
......
......@@ -28,7 +28,7 @@ int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
hash = init_name_hash();
for (i = 0; i < l; i++)
hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
hash = partial_name_hash(hpfs_upcase(hpfs_sb(dentry->d_sb)->sb_cp_table,qstr->name[i]), hash);
qstr->hash = end_name_hash(hash);
return 0;
......
......@@ -65,7 +65,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
int c1, c2 = 0;
int ret = 0;
if (inode->i_sb->s_hpfs_chk) {
if (hpfs_sb(inode->i_sb)->sb_chk) {
if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) {
ret = -EFSERROR;
goto out;
......@@ -75,7 +75,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out;
}
}
if (inode->i_sb->s_hpfs_chk >= 2) {
if (hpfs_sb(inode->i_sb)->sb_chk >= 2) {
struct buffer_head *bh;
struct fnode *fno;
int e = 0;
......@@ -97,7 +97,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out;
}
}
lc = inode->i_sb->s_hpfs_lowercase;
lc = hpfs_sb(inode->i_sb)->sb_lowercase;
if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */
filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */
goto out;
......@@ -114,7 +114,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
/* This won't work when cycle is longer than number of dirents
accepted by filldir, but what can I do?
maybe killall -9 ls helps */
if (inode->i_sb->s_hpfs_chk)
if (hpfs_sb(inode->i_sb)->sb_chk)
if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
hpfs_unlock_inode(inode);
ret = -EFSERROR;
......@@ -160,7 +160,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out;
}
if (de->first || de->last) {
if (inode->i_sb->s_hpfs_chk) {
if (hpfs_sb(inode->i_sb)->sb_chk) {
if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
}
......@@ -241,7 +241,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
* Go find or make an inode.
*/
hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas) ? 1 : 2);
if (!(result = iget(dir->i_sb, ino))) {
hpfs_unlock_iget(dir->i_sb);
hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
......
......@@ -134,7 +134,7 @@ static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno
hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
return;
}
if (s->s_hpfs_chk) {
if (hpfs_sb(s)->sb_chk) {
if (de->down) {
hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
d->self, de_down_pointer(de));
......@@ -253,7 +253,7 @@ int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, uns
return 1;
}
go_up_a:
if (i->i_sb->s_hpfs_chk)
if (hpfs_sb(i->i_sb)->sb_chk)
if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
hpfs_brelse4(&qbh);
if (nd) kfree(nd);
......@@ -379,7 +379,7 @@ int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
int c1, c2 = 0;
dno = hpfs_inode->i_dno;
down:
if (i->i_sb->s_hpfs_chk)
if (hpfs_sb(i->i_sb)->sb_chk)
if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
de_end = dnode_end_de(d);
......@@ -427,11 +427,11 @@ static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
int c1, c2 = 0;
dno = from;
while (1) {
if (i->i_sb->s_hpfs_chk)
if (hpfs_sb(i->i_sb)->sb_chk)
if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
return 0;
if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
if (i->i_sb->s_hpfs_chk) {
if (hpfs_sb(i->i_sb)->sb_chk) {
if (dnode->up != chk_up) {
hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
dno, chk_up, dnode->up);
......@@ -519,7 +519,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
up = dnode->up;
de = dnode_first_de(dnode);
down = de->down ? de_down_pointer(de) : 0;
if (i->i_sb->s_hpfs_chk) if (root && !down) {
if (hpfs_sb(i->i_sb)->sb_chk) if (root && !down) {
hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
goto end;
}
......@@ -532,7 +532,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
struct buffer_head *bh;
struct dnode *d1;
struct quad_buffer_head qbh1;
if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
if (hpfs_sb(i->i_sb)->sb_chk) if (up != i->i_ino) {
hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
return;
}
......@@ -628,14 +628,14 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
dlp = del->down ? de_down_pointer(del) : 0;
if (!dlp && down) {
if (d1->first_free > 2044) {
if (i->i_sb->s_hpfs_chk >= 2) {
if (hpfs_sb(i->i_sb)->sb_chk >= 2) {
printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
printk("HPFS: warning: terminating balancing operation\n");
}
hpfs_brelse4(&qbh1);
goto endm;
}
if (i->i_sb->s_hpfs_chk >= 2) {
if (hpfs_sb(i->i_sb)->sb_chk >= 2) {
printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
printk("HPFS: warning: goin'on\n");
}
......@@ -738,12 +738,12 @@ void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
int d1, d2 = 0;
go_down:
if (n_dnodes) (*n_dnodes)++;
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
ptr = 0;
go_up:
if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
if (hpfs_sb(s)->sb_chk) if (odno && odno != -1 && dnode->up != odno)
hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
de = dnode_first_de(dnode);
if (ptr) while(1) {
......@@ -774,7 +774,7 @@ void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
return;
}
hpfs_brelse4(&qbh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
odno = -1;
goto go_up;
......@@ -811,11 +811,11 @@ dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno)
int c1, c2 = 0;
again:
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
return d;
if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (up && ((struct dnode *)qbh.data)->up != up)
hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
if (!de->down) {
......@@ -901,7 +901,7 @@ struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name,
if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
again:
if (inode->i_sb->s_hpfs_chk)
if (hpfs_sb(inode->i_sb)->sb_chk)
if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
......@@ -1046,7 +1046,7 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
if (c < 0 && de->down) {
dno = de_down_pointer(de);
hpfs_brelse4(qbh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
kfree(name2);
return NULL;
......@@ -1065,7 +1065,7 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
downd = dno;
dno = d->up;
hpfs_brelse4(qbh);
if (s->s_hpfs_chk)
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
kfree(name2);
return NULL;
......
......@@ -13,6 +13,7 @@
#include <linux/fs.h>
#include <linux/hpfs_fs.h>
#include <linux/hpfs_fs_i.h>
#include <linux/hpfs_fs_sb.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
......@@ -57,22 +58,6 @@
typedef void nonconst; /* What this is for ? */
/*
* local time (HPFS) to GMT (Unix)
*/
extern inline time_t local_to_gmt(struct super_block *s, time_t t)
{
extern struct timezone sys_tz;
return t + sys_tz.tz_minuteswest * 60 + s->s_hpfs_timeshift;
}
extern inline time_t gmt_to_local(struct super_block *s, time_t t)
{
extern struct timezone sys_tz;
return t - sys_tz.tz_minuteswest * 60 - s->s_hpfs_timeshift;
}
/*
* conv= options
*/
......@@ -309,6 +294,11 @@ static inline struct hpfs_inode_info *hpfs_i(struct inode *inode)
return list_entry(inode, struct hpfs_inode_info, vfs_inode);
}
static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb)
{
return sb->u.generic_sbp;
}
/* super.c */
void hpfs_error(struct super_block *, char *, ...);
......@@ -319,3 +309,20 @@ unsigned hpfs_count_one_bitmap(struct super_block *, secno);
int hpfs_statfs(struct super_block *, struct statfs *);
extern struct address_space_operations hpfs_aops;
/*
* local time (HPFS) to GMT (Unix)
*/
extern inline time_t local_to_gmt(struct super_block *s, time_t t)
{
extern struct timezone sys_tz;
return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift;
}
extern inline time_t gmt_to_local(struct super_block *s, time_t t)
{
extern struct timezone sys_tz;
return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift;
}
......@@ -66,10 +66,10 @@ void hpfs_read_inode(struct inode *i)
unsigned char *ea;
int ea_size;
i->i_uid = sb->s_hpfs_uid;
i->i_gid = sb->s_hpfs_gid;
i->i_mode = sb->s_hpfs_mode;
hpfs_inode->i_conv = sb->s_hpfs_conv;
i->i_uid = hpfs_sb(sb)->sb_uid;
i->i_gid = hpfs_sb(sb)->sb_gid;
i->i_mode = hpfs_sb(sb)->sb_mode;
hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv;
i->i_blksize = 512;
i->i_size = -1;
i->i_blocks = -1;
......@@ -93,9 +93,9 @@ void hpfs_read_inode(struct inode *i)
i->i_mtime = 0;
i->i_ctime = 0;
if (!i->i_sb->s_hpfs_rd_inode)
hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
if (i->i_sb->s_hpfs_rd_inode == 2) {
if (!hpfs_sb(i->i_sb)->sb_rd_inode)
hpfs_error(i->i_sb, "read_inode: sb_rd_inode == 0");
if (hpfs_sb(i->i_sb)->sb_rd_inode == 2) {
i->i_mode |= S_IFREG;
i->i_mode &= ~0111;
i->i_op = &hpfs_file_iops;
......@@ -112,7 +112,7 @@ void hpfs_read_inode(struct inode *i)
make_bad_inode(i);
return;
}
if (i->i_sb->s_hpfs_eas) {
if (hpfs_sb(i->i_sb)->sb_eas) {
if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
if (ea_size == 2) {
i->i_uid = ea[0] + (ea[1] << 8);
......@@ -140,7 +140,7 @@ void hpfs_read_inode(struct inode *i)
}
if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
int rdev = 0;
umode_t mode = sb->s_hpfs_mode;
umode_t mode = hpfs_sb(sb)->sb_mode;
if (ea_size == 2) {
mode = ea[0] + (ea[1] << 8);
hpfs_inode->i_ea_mode = 1;
......@@ -171,7 +171,7 @@ void hpfs_read_inode(struct inode *i)
i->i_fop = &hpfs_dir_ops;
hpfs_inode->i_parent_dir = fnode->up;
hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
if (sb->s_hpfs_chk >= 2) {
if (hpfs_sb(sb)->sb_chk >= 2) {
struct buffer_head *bh0;
if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
}
......@@ -201,24 +201,24 @@ void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
/* Some unknown structures like ACL may be in fnode,
we'd better not overwrite them */
hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
} else if (i->i_sb->s_hpfs_eas >= 2) {
} else if (hpfs_sb(i->i_sb)->sb_eas >= 2) {
unsigned char ea[4];
if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) {
if ((i->i_uid != hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) {
ea[0] = i->i_uid & 0xff;
ea[1] = i->i_uid >> 8;
hpfs_set_ea(i, fnode, "UID", ea, 2);
hpfs_inode->i_ea_uid = 1;
}
if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) {
if ((i->i_gid != hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) {
ea[0] = i->i_gid & 0xff;
ea[1] = i->i_gid >> 8;
hpfs_set_ea(i, fnode, "GID", ea, 2);
hpfs_inode->i_ea_gid = 1;
}
if (!S_ISLNK(i->i_mode))
if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
| (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
&& i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
&& i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
| (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
ea[0] = i->i_mode & 0xff;
ea[1] = i->i_mode >> 8;
......@@ -241,7 +241,7 @@ void hpfs_write_inode(struct inode *i)
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct inode *parent;
if (!i->i_nlink) return;
if (i->i_ino == i->i_sb->s_hpfs_root) return;
if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n");
kfree(hpfs_inode->i_rddir_off);
......@@ -264,9 +264,9 @@ void hpfs_write_inode_nolock(struct inode *i)
struct fnode *fnode;
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
if (i->i_ino == i->i_sb->s_hpfs_root) return;
if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
if (i->i_ino != i->i_sb->s_hpfs_root) {
if (i->i_ino != hpfs_sb(i->i_sb)->sb_root) {
if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
brelse(bh);
return;
......@@ -309,7 +309,7 @@ int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
int error=0;
lock_kernel();
if ( ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) ||
(inode->i_sb->s_hpfs_root == inode->i_ino) ) {
(hpfs_sb(inode->i_sb)->sb_root == inode->i_ino) ) {
error = -EINVAL;
} else if ((error = inode_change_ok(inode, attr))) {
} else if ((error = inode_setattr(inode, attr))) {
......
......@@ -11,19 +11,19 @@
unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
{
return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0);
}
unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
struct quad_buffer_head *qbh, char *id)
{
secno sec;
if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) {
hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
return NULL;
}
sec = s->s_hpfs_bmp_dir[bmp_block];
if (!sec || sec > s->s_hpfs_fs_size-4) {
sec = hpfs_sb(s)->sb_bmp_dir[bmp_block];
if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) {
hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
return NULL;
}
......@@ -93,7 +93,7 @@ char *hpfs_load_code_page(struct super_block *s, secno cps)
secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
{
struct buffer_head *bh;
int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21;
int i;
secno *b;
if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
......@@ -119,11 +119,11 @@ secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
{
struct fnode *fnode;
if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
return NULL;
}
if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
if (s->s_hpfs_chk) {
if (hpfs_sb(s)->sb_chk) {
struct extended_attribute *ea;
struct extended_attribute *ea_end;
if (fnode->magic != FNODE_MAGIC) {
......@@ -168,9 +168,9 @@ struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_hea
struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
{
struct anode *anode;
if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
if (s->s_hpfs_chk) {
if (hpfs_sb(s)->sb_chk) {
if (anode->magic != ANODE_MAGIC || anode->self != ano) {
hpfs_error(s, "bad magic on anode %08x", ano);
goto bail;
......@@ -200,7 +200,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
struct quad_buffer_head *qbh)
{
struct dnode *dnode;
if (s->s_hpfs_chk) {
if (hpfs_sb(s)->sb_chk) {
if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
if (secno & 3) {
hpfs_error(s, "dnode %08x not byte-aligned", secno);
......@@ -208,7 +208,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
}
}
if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
if (s->s_hpfs_chk) {
if (hpfs_sb(s)->sb_chk) {
unsigned p, pp = 0;
unsigned char *d = (char *)dnode;
int b = 0;
......@@ -234,7 +234,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
if (hpfs_sb(s)->sb_chk >= 2) b |= 1 << de->down;
if (de->down) if (de_down_pointer(de) < 0x10) {
hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
......
......@@ -89,7 +89,7 @@ char *hpfs_translate_name(struct super_block *s, unsigned char *from,
{
char *to;
int i;
if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
printk("HPFS: Long name flag mismatch - name ");
for (i=0; i<len; i++) printk("%c", from[i]);
printk(" misidentified as %s.\n", lng ? "short" : "long");
......@@ -100,7 +100,7 @@ char *hpfs_translate_name(struct super_block *s, unsigned char *from,
printk("HPFS: can't allocate memory for name conversion buffer\n");
return from;
}
for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
for (i = 0; i < len; i++) to[i] = locase(hpfs_sb(s)->sb_cp_table,from[i]);
return to;
}
......@@ -111,8 +111,8 @@ int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
unsigned i;
if (last) return -1;
for (i = 0; i < l; i++) {
unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
unsigned char c1 = upcase(hpfs_sb(s)->sb_cp_table,n1[i]);
unsigned char c2 = upcase(hpfs_sb(s)->sb_cp_table,n2[i]);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
......
......@@ -187,7 +187,7 @@ int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
struct inode *result = NULL;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM;
lock_kernel();
if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
memset(&dee, 0, sizeof dee);
......@@ -255,7 +255,7 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
lock_kernel();
if (dir->i_sb->s_hpfs_eas < 2) {
if (hpfs_sb(dir->i_sb)->sb_eas < 2) {
unlock_kernel();
return -EPERM;
}
......@@ -559,7 +559,7 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
mark_buffer_dirty(bh);
brelse(bh);
}
hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv;
hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
hpfs_decide_conv(i, (char *)new_name, new_len);
end1:
hpfs_unlock_3inodes(old_dir, new_dir, i);
......
......@@ -16,7 +16,7 @@
static void mark_dirty(struct super_block *s)
{
if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) {
struct buffer_head *bh;
struct hpfs_spare_block *sb;
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
......@@ -37,8 +37,8 @@ static void unmark_dirty(struct super_block *s)
struct hpfs_spare_block *sb;
if (s->s_flags & MS_RDONLY) return;
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
mark_buffer_dirty(bh);
brelse(bh);
}
......@@ -60,12 +60,12 @@ void hpfs_error(struct super_block *s, char *m,...)
printk("HPFS: filesystem error: ");
if (buf) printk("%s", buf);
else printk("%s\n",m);
if (!s->s_hpfs_was_error) {
if (s->s_hpfs_err == 2) {
if (!hpfs_sb(s)->sb_was_error) {
if (hpfs_sb(s)->sb_err == 2) {
printk("; crashing the system because you wanted it\n");
mark_dirty(s);
panic("HPFS panic");
} else if (s->s_hpfs_err == 1) {
} else if (hpfs_sb(s)->sb_err == 1) {
if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
else {
printk("; remounting read-only\n");
......@@ -76,7 +76,7 @@ void hpfs_error(struct super_block *s, char *m,...)
else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
} else printk("\n");
if (buf) kfree(buf);
s->s_hpfs_was_error = 1;
hpfs_sb(s)->sb_was_error = 1;
}
/*
......@@ -101,9 +101,12 @@ int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
void hpfs_put_super(struct super_block *s)
{
if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
struct hpfs_sb_info *sbi = hpfs_sb(s);
if (sbi->sb_cp_table) kfree(sbi->sb_cp_table);
if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir);
unmark_dirty(s);
s->u.generic_sbp = NULL;
kfree(sbi);
}
unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
......@@ -125,28 +128,29 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
static unsigned count_bitmaps(struct super_block *s)
{
unsigned n, count, n_bands;
n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
count = 0;
for (n = 0; n < n_bands; n++)
count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]);
return count;
}
int hpfs_statfs(struct super_block *s, struct statfs *buf)
{
struct hpfs_sb_info *sbi = hpfs_sb(s);
lock_kernel();
/*if (s->s_hpfs_n_free == -1) {*/
s->s_hpfs_n_free = count_bitmaps(s);
s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
/*if (sbi->sb_n_free == -1) {*/
sbi->sb_n_free = count_bitmaps(s);
sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap);
/*}*/
buf->f_type = s->s_magic;
buf->f_bsize = 512;
buf->f_blocks = s->s_hpfs_fs_size;
buf->f_bfree = s->s_hpfs_n_free;
buf->f_bavail = s->s_hpfs_n_free;
buf->f_files = s->s_hpfs_dirband_size / 4;
buf->f_ffree = s->s_hpfs_n_free_dnodes;
buf->f_blocks = sbi->sb_fs_size;
buf->f_bfree = sbi->sb_n_free;
buf->f_bavail = sbi->sb_n_free;
buf->f_files = sbi->sb_dirband_size / 4;
buf->f_ffree = sbi->sb_n_free_dnodes;
buf->f_namelen = 254;
unlock_kernel();
......@@ -377,14 +381,15 @@ int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
umode_t umask;
int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
int o;
struct hpfs_sb_info *sbi = hpfs_sb(s);
*flags |= MS_NOATIME;
uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
umask = 0777 & ~s->s_hpfs_mode;
lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
uid = sbi->sb_uid; gid = sbi->sb_gid;
umask = 0777 & ~sbi->sb_mode;
lowercase = sbi->sb_lowercase; conv = sbi->sb_conv;
eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
&eas, &chk, &errs, &chkdsk, &timeshift))) {
......@@ -395,18 +400,18 @@ int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
hpfs_help();
return 1;
}
if (timeshift != s->s_hpfs_timeshift) {
if (timeshift != sbi->sb_timeshift) {
printk("HPFS: timeshift can't be changed using remount.\n");
return 1;
}
unmark_dirty(s);
s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
s->s_hpfs_mode = 0777 & ~umask;
s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
sbi->sb_uid = uid; sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
sbi->sb_lowercase = lowercase; sbi->sb_conv = conv;
sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
if (!(*flags & MS_RDONLY)) mark_dirty(s);
......@@ -419,6 +424,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
struct hpfs_boot_block *bootblock;
struct hpfs_super_block *superblock;
struct hpfs_spare_block *spareblock;
struct hpfs_sb_info *sbi;
uid_t uid;
gid_t gid;
......@@ -431,12 +437,18 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
int o;
s->s_hpfs_bmp_dir = NULL;
s->s_hpfs_cp_table = NULL;
sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
s->u.generic_sbp = sbi;
memset(sbi, 0, sizeof(*sbi));
sbi->sb_bmp_dir = NULL;
sbi->sb_cp_table = NULL;
s->s_hpfs_rd_inode = 0;
init_MUTEX(&s->u.hpfs_sb.hpfs_creation_de);
init_waitqueue_head(&s->s_hpfs_iget_q);
sbi->sb_rd_inode = 0;
init_MUTEX(&sbi->hpfs_creation_de);
init_waitqueue_head(&sbi->sb_iget_q);
uid = current->uid;
gid = current->gid;
......@@ -459,9 +471,9 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
goto bail0;
}
/*s->s_hpfs_mounting = 1;*/
/*sbi->sb_mounting = 1;*/
sb_set_blocksize(s, 512);
s->s_hpfs_fs_size = -1;
sbi->sb_fs_size = -1;
if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
......@@ -489,30 +501,30 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
s->s_magic = HPFS_SUPER_MAGIC;
s->s_op = &hpfs_sops;
s->s_hpfs_root = superblock->root;
s->s_hpfs_fs_size = superblock->n_sectors;
s->s_hpfs_bitmaps = superblock->bitmaps;
s->s_hpfs_dirband_start = superblock->dir_band_start;
s->s_hpfs_dirband_size = superblock->n_dir_band;
s->s_hpfs_dmap = superblock->dir_band_bitmap;
s->s_hpfs_uid = uid;
s->s_hpfs_gid = gid;
s->s_hpfs_mode = 0777 & ~umask;
s->s_hpfs_n_free = -1;
s->s_hpfs_n_free_dnodes = -1;
s->s_hpfs_lowercase = lowercase;
s->s_hpfs_conv = conv;
s->s_hpfs_eas = eas;
s->s_hpfs_chk = chk;
s->s_hpfs_chkdsk = chkdsk;
s->s_hpfs_err = errs;
s->s_hpfs_timeshift = timeshift;
s->s_hpfs_was_error = 0;
s->s_hpfs_cp_table = NULL;
s->s_hpfs_c_bitmap = -1;
sbi->sb_root = superblock->root;
sbi->sb_fs_size = superblock->n_sectors;
sbi->sb_bitmaps = superblock->bitmaps;
sbi->sb_dirband_start = superblock->dir_band_start;
sbi->sb_dirband_size = superblock->n_dir_band;
sbi->sb_dmap = superblock->dir_band_bitmap;
sbi->sb_uid = uid;
sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
sbi->sb_n_free = -1;
sbi->sb_n_free_dnodes = -1;
sbi->sb_lowercase = lowercase;
sbi->sb_conv = conv;
sbi->sb_eas = eas;
sbi->sb_chk = chk;
sbi->sb_chkdsk = chkdsk;
sbi->sb_err = errs;
sbi->sb_timeshift = timeshift;
sbi->sb_was_error = 0;
sbi->sb_cp_table = NULL;
sbi->sb_c_bitmap = -1;
/* Load bitmap directory */
if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
goto bail4;
/* Check for general fs errors*/
......@@ -557,20 +569,20 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
goto bail4;
}
a = s->s_hpfs_dirband_size;
s->s_hpfs_dirband_size = 0;
a = sbi->sb_dirband_size;
sbi->sb_dirband_size = 0;
if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
mark_dirty(s);
goto bail4;
}
s->s_hpfs_dirband_size = a;
sbi->sb_dirband_size = a;
} else printk("HPFS: You really don't want any checks? You are crazy...\n");
/* Load code page table */
if (spareblock->n_code_pages)
if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
printk("HPFS: Warning: code page support is disabled\n");
brelse(bh2);
......@@ -578,7 +590,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
brelse(bh0);
hpfs_lock_iget(s, 1);
s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
s->s_root = d_alloc_root(iget(s, sbi->sb_root));
hpfs_unlock_iget(s);
if (!s->s_root || !s->s_root->d_inode) {
printk("HPFS: iget failed. Why???\n");
......@@ -590,7 +602,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
* find the root directory's . pointer & finish filling in the inode
*/
root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
root_dno = hpfs_fnode_dno(s, sbi->sb_root);
if (root_dno)
de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh);
if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
......@@ -612,8 +624,10 @@ bail3: brelse(bh1);
bail2: brelse(bh0);
bail1:
bail0:
if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir);
if (sbi->sb_cp_table) kfree(sbi->sb_cp_table);
s->u.generic_sbp = NULL;
kfree(sbi);
return -EINVAL;
}
......
......@@ -331,6 +331,8 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
unsigned first_unmapped = blocks_per_page;
struct block_device *bdev = NULL;
int boundary = 0;
sector_t boundary_block = 0;
struct block_device *boundary_bdev = NULL;
if (page_has_buffers(page)) {
struct buffer_head *head = page_buffers(page);
......@@ -363,6 +365,10 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
}
blocks[page_block++] = bh->b_blocknr;
boundary = buffer_boundary(bh);
if (boundary) {
boundary_block = bh->b_blocknr;
boundary_bdev = bh->b_bdev;
}
bdev = bh->b_bdev;
} while ((bh = bh->b_this_page) != head);
......@@ -393,6 +399,10 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
if (buffer_new(&map_bh))
unmap_underlying_metadata(map_bh.b_bdev,
map_bh.b_blocknr);
if (buffer_boundary(&map_bh)) {
boundary_block = map_bh.b_blocknr;
boundary_bdev = map_bh.b_bdev;
}
if (page_block) {
if (map_bh.b_blocknr != blocks[page_block-1] + 1)
goto confused;
......@@ -430,10 +440,8 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
alloc_new:
if (bio == NULL) {
const unsigned __nr_pages = 64; /* FIXME */
bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
__nr_pages, GFP_NOFS|__GFP_HIGH);
bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH);
if (bio == NULL)
goto confused;
}
......@@ -466,10 +474,15 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
BUG_ON(PageWriteback(page));
SetPageWriteback(page);
unlock_page(page);
if (boundary || (first_unmapped != blocks_per_page))
if (boundary || (first_unmapped != blocks_per_page)) {
bio = mpage_bio_submit(WRITE, bio);
else
if (boundary_block) {
write_boundary_block(boundary_bdev,
boundary_block, 1 << blkbits);
}
} else {
*last_block_in_bio = blocks[blocks_per_page - 1];
}
goto out;
confused:
......
......@@ -117,8 +117,6 @@ static int parse_options(char *options, struct fat_mount_options *opts)
savep = NULL;
ret = 1;
while ((this_char = strsep(&options,",")) != NULL) {
if (!*this_char)
continue;
if ((value = strchr(this_char,'=')) != NULL) {
save = *value;
savep = value;
......@@ -154,8 +152,8 @@ static int parse_options(char *options, struct fat_mount_options *opts)
else
ret = 0;
}
if (this_char != options)
*(this_char-1) = ',';
if (options)
*(options-1) = ',';
if (value) {
*savep = save;
}
......
......@@ -140,7 +140,6 @@ void end_buffer_io_sync(struct buffer_head *bh, int uptodate);
void buffer_insert_list(spinlock_t *lock,
struct buffer_head *, struct list_head *);
void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode);
int write_mapping_buffers(struct address_space *mapping);
int inode_has_buffers(struct inode *);
void invalidate_inode_buffers(struct inode *);
int fsync_buffers_list(spinlock_t *lock, struct list_head *);
......@@ -168,6 +167,9 @@ void free_buffer_head(struct buffer_head * bh);
void FASTCALL(unlock_buffer(struct buffer_head *bh));
void ll_rw_block(int, int, struct buffer_head * bh[]);
int submit_bh(int, struct buffer_head *);
void write_boundary_block(struct block_device *bdev,
sector_t bblock, unsigned blocksize);
extern int buffer_heads_over_limit;
/*
......
......@@ -17,6 +17,7 @@
#define _LINUX_EXT3_FS_H
#include <linux/types.h>
#include <linux/ext3_fs_sb.h>
/*
* The second extended filesystem constants/structures
......
......@@ -627,9 +627,6 @@ extern int send_sigurg(struct fown_struct *fown);
#define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
#define MNT_DETACH 0x00000002 /* Just detach from the tree */
#include <linux/ext3_fs_sb.h>
#include <linux/hpfs_fs_sb.h>
extern struct list_head super_blocks;
extern spinlock_t sb_lock;
......@@ -670,8 +667,6 @@ struct super_block {
char s_id[32]; /* Informational name */
union {
struct ext3_sb_info ext3_sb;
struct hpfs_sb_info hpfs_sb;
void *generic_sbp;
} u;
/*
......
......@@ -36,31 +36,4 @@ struct hpfs_sb_info {
int sb_timeshift;
};
#define s_hpfs_root u.hpfs_sb.sb_root
#define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
#define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
#define s_hpfs_dirband_start u.hpfs_sb.sb_dirband_start
#define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
#define s_hpfs_dmap u.hpfs_sb.sb_dmap
#define s_hpfs_uid u.hpfs_sb.sb_uid
#define s_hpfs_gid u.hpfs_sb.sb_gid
#define s_hpfs_mode u.hpfs_sb.sb_mode
#define s_hpfs_n_free u.hpfs_sb.sb_n_free
#define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
#define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
#define s_hpfs_conv u.hpfs_sb.sb_conv
#define s_hpfs_eas u.hpfs_sb.sb_eas
#define s_hpfs_err u.hpfs_sb.sb_err
#define s_hpfs_chk u.hpfs_sb.sb_chk
#define s_hpfs_was_error u.hpfs_sb.sb_was_error
#define s_hpfs_chkdsk u.hpfs_sb.sb_chkdsk
/*#define s_hpfs_rd_fnode u.hpfs_sb.sb_rd_fnode*/
#define s_hpfs_rd_inode u.hpfs_sb.sb_rd_inode
#define s_hpfs_cp_table u.hpfs_sb.sb_cp_table
#define s_hpfs_bmp_dir u.hpfs_sb.sb_bmp_dir
#define s_hpfs_c_bitmap u.hpfs_sb.sb_c_bitmap
#define s_hpfs_iget_q u.hpfs_sb.sb_iget_q
/*#define s_hpfs_mounting u.hpfs_sb.sb_mounting*/
#define s_hpfs_timeshift u.hpfs_sb.sb_timeshift
#endif
......@@ -86,13 +86,12 @@ static inline void __list_del(struct list_head * prev, struct list_head * next)
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is in an undefined state.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = (void *) 0;
entry->prev = (void *) 0;
}
/**
......
......@@ -176,7 +176,7 @@ struct page {
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */
......@@ -289,38 +289,34 @@ static inline void set_page_zone(struct page *page, unsigned long zone_num)
page->flags |= zone_num << ZONE_SHIFT;
}
/*
* In order to avoid #ifdefs within C code itself, we define
* set_page_address to a noop for non-highmem machines, where
* the field isn't useful.
* The same is true for page_address() in arch-dependent code.
*/
#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
#define lowmem_page_address(page) \
__va( ( ((page) - page_zone(page)->zone_mem_map) \
+ page_zone(page)->zone_start_pfn) << PAGE_SHIFT)
#if defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL)
#define HASHED_PAGE_VIRTUAL
#endif
#if defined(WANT_PAGE_VIRTUAL)
#define page_address(page) ((page)->virtual)
#define set_page_address(page, address) \
do { \
(page)->virtual = (address); \
} while(0)
#define page_address_init() do { } while(0)
#endif
#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
#define set_page_address(page, address) do { } while(0)
#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
/*
* Permanent address of a page. Obviously must never be
* called on a highmem page.
*/
#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
#define page_address(page) ((page)->virtual)
#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
#define page_address(page) \
__va( ( ((page) - page_zone(page)->zone_mem_map) \
+ page_zone(page)->zone_start_pfn) << PAGE_SHIFT)
#if defined(HASHED_PAGE_VIRTUAL)
void *page_address(struct page *page);
void set_page_address(struct page *page, void *virtual);
void page_address_init(void);
#endif
#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)
#define set_page_address(page, address) do { } while(0)
#define page_address_init() do { } while(0)
#endif
/*
* Return true if this page is mapped into pagetables. Subtle: test pte.direct
......
......@@ -62,7 +62,6 @@ struct zone {
spinlock_t lock;
unsigned long free_pages;
unsigned long pages_min, pages_low, pages_high;
int need_balance;
ZONE_PADDING(_pad1_)
......@@ -120,7 +119,8 @@ struct zone {
* rarely used fields:
*/
char *name;
unsigned long size;
unsigned long spanned_pages; /* total size, including holes */
unsigned long present_pages; /* amount of memory (excluding holes) */
} ____cacheline_maxaligned_in_smp;
#define ZONE_DMA 0
......
......@@ -288,6 +288,12 @@
#define PCI_DEVICE_ID_NS_87560_USB 0x0012
#define PCI_DEVICE_ID_NS_83815 0x0020
#define PCI_DEVICE_ID_NS_83820 0x0022
#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500
#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501
#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502
#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503
#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504
#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505
#define PCI_DEVICE_ID_NS_87410 0xd001
#define PCI_VENDOR_ID_TSENG 0x100c
......
/* linux/include/linux/scx200.h
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
Defines for the National Semiconductor SCx200 Processors
*/
/* Interesting stuff for the National Semiconductor SCx200 CPU */
/* F0 PCI Header/Bridge Configuration Registers */
#define SCx200_DOCCS_BASE 0x78 /* DOCCS Base Address Register */
#define SCx200_DOCCS_CTRL 0x7c /* DOCCS Control Register */
/* GPIO Register Block */
#define SCx200_GPIO_SIZE 0x2c /* Size of GPIO register block */
/* General Configuration Block */
#define SCx200_CB_BASE 0x9000 /* Base fixed at 0x9000 according to errata */
/* Watchdog Timer */
#define SCx200_WDT_OFFSET 0x00 /* offset within configuration block */
#define SCx200_WDT_SIZE 0x05 /* size */
#define SCx200_WDT_WDTO 0x00 /* Time-Out Register */
#define SCx200_WDT_WDCNFG 0x02 /* Configuration Register */
#define SCx200_WDT_WDSTS 0x04 /* Status Register */
#define SCx200_WDT_WDSTS_WDOVF (1<<0) /* Overflow bit */
/* High Resolution Timer */
#define SCx200_TIMER_OFFSET 0x08
#define SCx200_TIMER_SIZE 0x05
/* Clock Generators */
#define SCx200_CLOCKGEN_OFFSET 0x10
#define SCx200_CLOCKGEN_SIZE 0x10
/* Pin Multiplexing and Miscellaneous Configuration Registers */
#define SCx200_MISC_OFFSET 0x30
#define SCx200_MISC_SIZE 0x10
#define SCx200_PMR 0x30 /* Pin Multiplexing Register */
#define SCx200_MCR 0x34 /* Miscellaneous Configuration Register */
#define SCx200_INTSEL 0x38 /* Interrupt Selection Register */
#define SCx200_IID 0x3c /* IA On a Chip Identification Number Reg */
#define SCx200_REV 0x3d /* Revision Register */
#define SCx200_CBA 0x3e /* Configuration Base Address Register */
/* Verify that the configuration block really is there */
#define scx200_cb_probe(base) (inw((base) + SCx200_CBA) == (base))
/*
Local variables:
compile-command: "make -C ../.. bzImage modules"
c-basic-offset: 8
End:
*/
#include <linux/spinlock.h>
u32 scx200_gpio_configure(int index, u32 set, u32 clear);
void scx200_gpio_dump(unsigned index);
extern unsigned scx200_gpio_base;
extern spinlock_t scx200_gpio_lock;
extern long scx200_gpio_shadow[2];
#define scx200_gpio_present() (scx200_gpio_base!=0)
/* Definitions to make sure I do the same thing in all functions */
#define __SCx200_GPIO_BANK unsigned bank = index>>5
#define __SCx200_GPIO_IOADDR unsigned short ioaddr = scx200_gpio_base+0x10*bank
#define __SCx200_GPIO_SHADOW long *shadow = scx200_gpio_shadow+bank
#define __SCx200_GPIO_INDEX index &= 31
#define __SCx200_GPIO_OUT __asm__ __volatile__("outsl":"=mS" (shadow):"d" (ioaddr), "0" (shadow))
/* returns the value of the GPIO pin */
static inline int scx200_gpio_get(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR + 0x04;
__SCx200_GPIO_INDEX;
return (inl(ioaddr) & (1<<index)) ? 1 : 0;
}
/* return the value driven on the GPIO signal (the value that will be
driven if the GPIO is configured as an output, it might not be the
state of the GPIO right now if the GPIO is configured as an input) */
static inline int scx200_gpio_current(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_INDEX;
return (scx200_gpio_shadow[bank] & (1<<index)) ? 1 : 0;
}
/* drive the GPIO signal high */
static inline void scx200_gpio_set_high(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
set_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* drive the GPIO signal low */
static inline void scx200_gpio_set_low(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
clear_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* drive the GPIO signal to state */
static inline void scx200_gpio_set(int index, int state) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
if (state)
set_bit(index, shadow);
else
clear_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* toggle the GPIO signal */
static inline void scx200_gpio_change(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
change_bit(index, shadow);
__SCx200_GPIO_OUT;
}
#undef __SCx200_GPIO_BANK
#undef __SCx200_GPIO_IOADDR
#undef __SCx200_GPIO_SHADOW
#undef __SCx200_GPIO_INDEX
#undef __SCx200_GPIO_OUT
/*
Local variables:
compile-command: "make -C ../.. bzImage modules"
c-basic-offset: 8
End:
*/
......@@ -433,6 +433,7 @@ asmlinkage void __init start_kernel(void)
initrd_start = 0;
}
#endif
page_address_init();
mem_init();
kmem_cache_sizes_init();
pidhash_init();
......
......@@ -132,6 +132,9 @@ EXPORT_SYMBOL(highmem_start_page);
EXPORT_SYMBOL(kmap_prot);
EXPORT_SYMBOL(kmap_pte);
#endif
#ifdef HASHED_PAGE_VIRTUAL
EXPORT_SYMBOL(page_address);
#endif
EXPORT_SYMBOL(get_user_pages);
/* filesystem internal functions */
......
......@@ -22,6 +22,7 @@
#include <linux/mempool.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/hash.h>
#include <asm/pgalloc.h>
static mempool_t *page_pool, *isa_page_pool;
......@@ -88,7 +89,7 @@ static void flush_all_zero_pkmaps(void)
page = pte_page(pkmap_page_table[i]);
pte_clear(&pkmap_page_table[i]);
page->virtual = NULL;
set_page_address(page, NULL);
}
flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
}
......@@ -126,8 +127,8 @@ static inline unsigned long map_new_virtual(struct page *page)
spin_lock(&kmap_lock);
/* Somebody else might have mapped it while we slept */
if (page->virtual)
return (unsigned long) page->virtual;
if (page_address(page))
return (unsigned long)page_address(page);
/* Re-start */
goto start;
......@@ -137,7 +138,7 @@ static inline unsigned long map_new_virtual(struct page *page)
set_pte(&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
pkmap_count[last_pkmap_nr] = 1;
page->virtual = (void *) vaddr;
set_page_address(page, (void *)vaddr);
return vaddr;
}
......@@ -153,7 +154,7 @@ void *kmap_high(struct page *page)
* We cannot call this from interrupts, as it may block
*/
spin_lock(&kmap_lock);
vaddr = (unsigned long) page->virtual;
vaddr = (unsigned long)page_address(page);
if (!vaddr)
vaddr = map_new_virtual(page);
pkmap_count[PKMAP_NR(vaddr)]++;
......@@ -170,7 +171,7 @@ void kunmap_high(struct page *page)
int need_wakeup;
spin_lock(&kmap_lock);
vaddr = (unsigned long) page->virtual;
vaddr = (unsigned long)page_address(page);
if (!vaddr)
BUG();
nr = PKMAP_NR(vaddr);
......@@ -467,7 +468,7 @@ void blk_queue_bounce(request_queue_t *q, struct bio **bio_orig)
*bio_orig = bio;
}
#if CONFIG_DEBUG_HIGHMEM
#if defined(CONFIG_DEBUG_HIGHMEM) && defined(CONFIG_HIGHMEM)
void check_highmem_ptes(void)
{
int idx, type;
......@@ -484,3 +485,121 @@ void check_highmem_ptes(void)
}
#endif
#if defined(HASHED_PAGE_VIRTUAL)
#define PA_HASH_ORDER 7
/*
* Describes one page->virtual association
*/
struct page_address_map {
struct page *page;
void *virtual;
struct list_head list;
};
/*
* page_address_map freelist, allocated from page_address_maps.
*/
static struct list_head page_address_pool; /* freelist */
static spinlock_t pool_lock; /* protects page_address_pool */
/*
* Hash table bucket
*/
static struct page_address_slot {
struct list_head lh; /* List of page_address_maps */
spinlock_t lock; /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
static struct page_address_slot *page_slot(struct page *page)
{
return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
}
void *page_address(struct page *page)
{
unsigned long flags;
void *ret;
struct page_address_slot *pas;
if (!PageHighMem(page))
return lowmem_page_address(page);
pas = page_slot(page);
ret = NULL;
spin_lock_irqsave(&pas->lock, flags);
if (!list_empty(&pas->lh)) {
struct page_address_map *pam;
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) {
ret = pam->virtual;
goto done;
}
}
}
done:
spin_unlock_irqrestore(&pas->lock, flags);
return ret;
}
void set_page_address(struct page *page, void *virtual)
{
unsigned long flags;
struct page_address_slot *pas;
struct page_address_map *pam;
BUG_ON(!PageHighMem(page));
pas = page_slot(page);
if (virtual) { /* Add */
BUG_ON(list_empty(&page_address_pool));
spin_lock_irqsave(&pool_lock, flags);
pam = list_entry(page_address_pool.next,
struct page_address_map, list);
list_del(&pam->list);
spin_unlock_irqrestore(&pool_lock, flags);
pam->page = page;
pam->virtual = virtual;
spin_lock_irqsave(&pas->lock, flags);
list_add_tail(&pam->list, &pas->lh);
spin_unlock_irqrestore(&pas->lock, flags);
} else { /* Remove */
spin_lock_irqsave(&pas->lock, flags);
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) {
list_del(&pam->list);
spin_unlock_irqrestore(&pas->lock, flags);
spin_lock_irqsave(&pool_lock, flags);
list_add_tail(&pam->list, &page_address_pool);
spin_unlock_irqrestore(&pool_lock, flags);
goto done;
}
}
spin_unlock_irqrestore(&pas->lock, flags);
}
done:
return;
}
static struct page_address_map page_address_maps[LAST_PKMAP];
void __init page_address_init(void)
{
int i;
INIT_LIST_HEAD(&page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
list_add(&page_address_maps[i].list, &page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
INIT_LIST_HEAD(&page_address_htable[i].lh);
spin_lock_init(&page_address_htable[i].lock);
}
spin_lock_init(&pool_lock);
}
#endif /* defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL) */
......@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mempool.h>
#include <linux/blkdev.h>
#include <linux/writeback.h>
static void add_element(mempool_t *pool, void *element)
......@@ -184,8 +185,7 @@ void * mempool_alloc(mempool_t *pool, int gfp_mask)
{
void *element;
unsigned long flags;
int curr_nr;
DECLARE_WAITQUEUE(wait, current);
DEFINE_WAIT(wait);
int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
int pf_flags = current->flags;
......@@ -226,18 +226,10 @@ void * mempool_alloc(mempool_t *pool, int gfp_mask)
blk_run_queues();
add_wait_queue_exclusive(&pool->wait, &wait);
set_task_state(current, TASK_UNINTERRUPTIBLE);
spin_lock_irqsave(&pool->lock, flags);
curr_nr = pool->curr_nr;
spin_unlock_irqrestore(&pool->lock, flags);
if (!curr_nr)
schedule();
current->state = TASK_RUNNING;
remove_wait_queue(&pool->wait, &wait);
prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
if (!pool->curr_nr)
io_schedule();
finish_wait(&pool->wait, &wait);
goto repeat_alloc;
}
......
......@@ -41,10 +41,8 @@
*/
static long ratelimit_pages = 32;
/*
* The total number of pages in the machine.
*/
static long total_pages;
static long total_pages; /* The total number of pages in the machine. */
static int dirty_exceeded; /* Dirty mem may be over limit */
/*
* When balance_dirty_pages decides that the caller needs to perform some
......@@ -60,16 +58,12 @@ static inline long sync_writeback_pages(void)
/* The following parameters are exported via /proc/sys/vm */
/*
* Dirty memory thresholds, in percentages
*/
/*
* Start background writeback (via pdflush) at this level
* Start background writeback (via pdflush) at this percentage
*/
int dirty_background_ratio = 10;
/*
* The generator of dirty data starts async writeback at this level
* The generator of dirty data starts async writeback at this percentage
*/
int dirty_async_ratio = 40;
......@@ -80,7 +74,7 @@ int dirty_async_ratio = 40;
int dirty_writeback_centisecs = 5 * 100;
/*
* The longest amount of time for which data is allowed to remain dirty
* The longest number of centiseconds for which data is allowed to remain dirty
*/
int dirty_expire_centisecs = 30 * 100;
......@@ -90,22 +84,17 @@ int dirty_expire_centisecs = 30 * 100;
static void background_writeout(unsigned long _min_pages);
/*
* balance_dirty_pages() must be called by processes which are
* generating dirty data. It looks at the number of dirty pages
* in the machine and either:
*
* - Starts background writeback or
* - Causes the caller to perform async writeback or
* - Causes the caller to perform synchronous writeback, then
* tells a pdflush thread to perform more writeback or
* - Does nothing at all.
*
* balance_dirty_pages() can sleep.
* balance_dirty_pages() must be called by processes which are generating dirty
* data. It looks at the number of dirty pages in the machine and will force
* the caller to perform writeback if the system is over `async_thresh'.
* If we're over `background_thresh' then pdflush is woken to perform some
* writeout.
*/
void balance_dirty_pages(struct address_space *mapping)
{
struct page_state ps;
long background_thresh, async_thresh;
long background_thresh;
long async_thresh;
unsigned long dirty_and_writeback;
struct backing_dev_info *bdi;
......@@ -123,9 +112,13 @@ void balance_dirty_pages(struct address_space *mapping)
.older_than_this = NULL,
.nr_to_write = sync_writeback_pages(),
};
if (!dirty_exceeded)
dirty_exceeded = 1;
writeback_inodes(&wbc);
get_page_state(&ps);
} else {
if (dirty_exceeded)
dirty_exceeded = 0;
}
if (!writeback_in_progress(bdi) && ps.nr_dirty > background_thresh)
......@@ -141,17 +134,25 @@ EXPORT_SYMBOL_GPL(balance_dirty_pages);
* which was newly dirtied. The function will periodically check the system's
* dirty state and will initiate writeback if needed.
*
* balance_dirty_pages_ratelimited() may sleep.
* On really big machines, get_page_state is expensive, so try to avoid calling
* it too often (ratelimiting). But once we're over the dirty memory limit we
* decrease the ratelimiting by a lot, to prevent individual processes from
* overshooting the limit by (ratelimit_pages) each.
*/
void balance_dirty_pages_ratelimited(struct address_space *mapping)
{
static struct rate_limit_struct {
int count;
} ____cacheline_aligned ratelimits[NR_CPUS];
} ____cacheline_aligned_in_smp ratelimits[NR_CPUS];
int cpu;
long ratelimit;
ratelimit = ratelimit_pages;
if (dirty_exceeded)
ratelimit = 8;
cpu = get_cpu();
if (ratelimits[cpu].count++ >= ratelimit_pages) {
if (ratelimits[cpu].count++ >= ratelimit) {
ratelimits[cpu].count = 0;
put_cpu();
balance_dirty_pages(mapping);
......
......@@ -48,7 +48,7 @@ static int zone_balance_max[MAX_NR_ZONES] __initdata = { 255 , 255, 255, };
*/
static inline int bad_range(struct zone *zone, struct page *page)
{
if (page_to_pfn(page) >= zone->zone_start_pfn + zone->size)
if (page_to_pfn(page) >= zone->zone_start_pfn + zone->spanned_pages)
return 1;
if (page_to_pfn(page) < zone->zone_start_pfn)
return 1;
......@@ -346,8 +346,6 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order,
}
}
classzone->need_balance = 1;
mb();
/* we're somewhat low on memory, failed to find what we needed */
for (i = 0; zones[i] != NULL; i++) {
struct zone *z = zones[i];
......@@ -509,7 +507,7 @@ static unsigned int nr_free_zone_pages(int offset)
struct zone *zone;
for (zone = *zonep++; zone; zone = *zonep++) {
unsigned long size = zone->size;
unsigned long size = zone->present_pages;
unsigned long high = zone->pages_high;
if (size > high)
sum += size - high;
......@@ -681,7 +679,7 @@ void show_free_areas(void)
struct zone *zone = &pgdat->node_zones[type];
unsigned long nr, flags, order, total = 0;
if (!zone->size)
if (!zone->present_pages)
continue;
spin_lock_irqsave(&zone->lock, flags);
......@@ -710,7 +708,7 @@ static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zoneli
BUG();
case ZONE_HIGHMEM:
zone = pgdat->node_zones + ZONE_HIGHMEM;
if (zone->size) {
if (zone->present_pages) {
#ifndef CONFIG_HIGHMEM
BUG();
#endif
......@@ -718,11 +716,11 @@ static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zoneli
}
case ZONE_NORMAL:
zone = pgdat->node_zones + ZONE_NORMAL;
if (zone->size)
if (zone->present_pages)
zonelist->zones[j++] = zone;
case ZONE_DMA:
zone = pgdat->node_zones + ZONE_DMA;
if (zone->size)
if (zone->present_pages)
zonelist->zones[j++] = zone;
}
......@@ -866,13 +864,13 @@ void __init free_area_init_core(pg_data_t *pgdat,
realsize -= zholes_size[j];
printk(" %s zone: %lu pages\n", zone_names[j], realsize);
zone->size = size;
zone->spanned_pages = size;
zone->present_pages = realsize;
zone->name = zone_names[j];
spin_lock_init(&zone->lock);
spin_lock_init(&zone->lru_lock);
zone->zone_pgdat = pgdat;
zone->free_pages = 0;
zone->need_balance = 0;
INIT_LIST_HEAD(&zone->active_list);
INIT_LIST_HEAD(&zone->inactive_list);
atomic_set(&zone->refill_counter, 0);
......@@ -923,12 +921,15 @@ void __init free_area_init_core(pg_data_t *pgdat,
set_page_count(page, 0);
SetPageReserved(page);
INIT_LIST_HEAD(&page->list);
#ifdef WANT_PAGE_VIRTUAL
if (j != ZONE_HIGHMEM)
/*
* The shift left won't overflow because the
* ZONE_NORMAL is below 4G.
*/
set_page_address(page, __va(zone_start_pfn << PAGE_SHIFT));
set_page_address(page,
__va(zone_start_pfn << PAGE_SHIFT));
#endif
zone_start_pfn++;
}
......@@ -1034,7 +1035,7 @@ static int frag_show(struct seq_file *m, void *arg)
int order;
for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
if (!zone->size)
if (!zone->present_pages)
continue;
spin_lock_irqsave(&zone->lock, flags);
......
......@@ -35,9 +35,18 @@ static inline void truncate_partial_page(struct page *page, unsigned partial)
* If truncate cannot remove the fs-private metadata from the page, the page
* becomes anonymous. It will be left on the LRU and may even be mapped into
* user pagetables if we're racing with filemap_nopage().
*
* We need to bale out if page->mapping is no longer equal to the original
* mapping. This happens a) when the VM reclaimed the page while we waited on
* its lock, b) when a concurrent invalidate_inode_pages got there first and
* c) when tmpfs swizzles a page between a tmpfs inode and swapper_space.
*/
static void truncate_complete_page(struct page *page)
static void
truncate_complete_page(struct address_space *mapping, struct page *page)
{
if (page->mapping != mapping)
return;
if (PagePrivate(page))
do_invalidatepage(page, 0);
......@@ -61,6 +70,9 @@ static void truncate_complete_page(struct page *page)
* The first pass will remove most pages, so the search cost of the second pass
* is low.
*
* When looking at page->index outside the page lock we need to be careful to
* copy it into a local to avoid races (it could change at any time).
*
* Called under (and serialised by) inode->i_sem.
*/
void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
......@@ -76,15 +88,18 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
pgoff_t page_index = page->index;
next = page->index + 1;
if (page_index > next)
next = page_index;
next++;
if (TestSetPageLocked(page))
continue;
if (PageWriteback(page)) {
unlock_page(page);
continue;
}
truncate_complete_page(page);
truncate_complete_page(mapping, page);
unlock_page(page);
}
pagevec_release(&pvec);
......@@ -114,8 +129,10 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
lock_page(page);
wait_on_page_writeback(page);
next = page->index + 1;
truncate_complete_page(page);
if (page->index > next)
next = page->index;
next++;
truncate_complete_page(mapping, page);
unlock_page(page);
}
pagevec_release(&pvec);
......@@ -150,14 +167,16 @@ void invalidate_inode_pages(struct address_space *mapping)
next++;
continue;
}
next = page->index + 1;
if (page->index > next)
next = page->index;
next++;
if (PageDirty(page) || PageWriteback(page))
goto unlock;
if (PagePrivate(page) && !try_to_release_page(page, 0))
goto unlock;
if (page_mapped(page))
goto unlock;
truncate_complete_page(page);
truncate_complete_page(mapping, page);
unlock:
unlock_page(page);
}
......@@ -183,18 +202,18 @@ void invalidate_inode_pages2(struct address_space *mapping)
int i;
pagevec_init(&pvec);
while (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
lock_page(page);
if (page->mapping) { /* truncate race? */
if (page->mapping == mapping) { /* truncate race? */
wait_on_page_writeback(page);
next = page->index + 1;
if (page_mapped(page))
clear_page_dirty(page);
else
truncate_complete_page(page);
truncate_complete_page(mapping, page);
}
unlock_page(page);
}
......
......@@ -101,15 +101,19 @@ static inline int is_page_cache_freeable(struct page *page)
return page_count(page) - !!PagePrivate(page) == 2;
}
/*
* shrink_list returns the number of reclaimed pages
*/
static /* inline */ int
shrink_list(struct list_head *page_list, int nr_pages,
unsigned int gfp_mask, int *max_scan, int *nr_mapped)
shrink_list(struct list_head *page_list, unsigned int gfp_mask,
int *max_scan, int *nr_mapped)
{
struct address_space *mapping;
LIST_HEAD(ret_pages);
struct pagevec freed_pvec;
const int nr_pages_in = nr_pages;
int pgactivate = 0;
int ret = 0;
pagevec_init(&freed_pvec);
while (!list_empty(page_list)) {
......@@ -295,7 +299,7 @@ shrink_list(struct list_head *page_list, int nr_pages,
__put_page(page); /* The pagecache ref */
free_it:
unlock_page(page);
nr_pages--;
ret++;
if (!pagevec_add(&freed_pvec, page))
__pagevec_release_nonlru(&freed_pvec);
continue;
......@@ -312,11 +316,11 @@ shrink_list(struct list_head *page_list, int nr_pages,
list_splice(&ret_pages, page_list);
if (pagevec_count(&freed_pvec))
__pagevec_release_nonlru(&freed_pvec);
mod_page_state(pgsteal, nr_pages_in - nr_pages);
mod_page_state(pgsteal, ret);
if (current->flags & PF_KSWAPD)
mod_page_state(kswapd_steal, nr_pages_in - nr_pages);
mod_page_state(kswapd_steal, ret);
mod_page_state(pgactivate, pgactivate);
return nr_pages;
return ret;
}
/*
......@@ -325,18 +329,19 @@ shrink_list(struct list_head *page_list, int nr_pages,
* not freed will be added back to the LRU.
*
* shrink_cache() is passed the number of pages to try to free, and returns
* the number which are yet-to-free.
* the number of pages which were reclaimed.
*
* For pagecache intensive workloads, the first loop here is the hottest spot
* in the kernel (apart from the copy_*_user functions).
*/
static /* inline */ int
shrink_cache(int nr_pages, struct zone *zone,
shrink_cache(const int nr_pages, struct zone *zone,
unsigned int gfp_mask, int max_scan, int *nr_mapped)
{
LIST_HEAD(page_list);
struct pagevec pvec;
int nr_to_process;
int ret = 0;
/*
* Try to ensure that we free `nr_pages' pages in one pass of the loop.
......@@ -349,10 +354,11 @@ shrink_cache(int nr_pages, struct zone *zone,
lru_add_drain();
spin_lock_irq(&zone->lru_lock);
while (max_scan > 0 && nr_pages > 0) {
while (max_scan > 0 && ret < nr_pages) {
struct page *page;
int nr_taken = 0;
int nr_scan = 0;
int nr_freed;
while (nr_scan++ < nr_to_process &&
!list_empty(&zone->inactive_list)) {
......@@ -383,10 +389,10 @@ shrink_cache(int nr_pages, struct zone *zone,
max_scan -= nr_scan;
mod_page_state(pgscan, nr_scan);
nr_pages = shrink_list(&page_list, nr_pages,
gfp_mask, &max_scan, nr_mapped);
if (nr_pages <= 0 && list_empty(&page_list))
nr_freed = shrink_list(&page_list, gfp_mask,
&max_scan, nr_mapped);
ret += nr_freed;
if (nr_freed <= 0 && list_empty(&page_list))
goto done;
spin_lock_irq(&zone->lru_lock);
......@@ -412,7 +418,7 @@ shrink_cache(int nr_pages, struct zone *zone,
spin_unlock_irq(&zone->lru_lock);
done:
pagevec_release(&pvec);
return nr_pages;
return ret;
}
/*
......@@ -533,9 +539,14 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in)
mod_page_state(pgdeactivate, pgdeactivate);
}
/*
* Try to reclaim `nr_pages' from this zone. Returns the number of reclaimed
* pages. This is a basic per-zone page freer. Used by both kswapd and
* direct reclaim.
*/
static /* inline */ int
shrink_zone(struct zone *zone, int max_scan,
unsigned int gfp_mask, int nr_pages, int *nr_mapped)
shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask,
const int nr_pages, int *nr_mapped)
{
unsigned long ratio;
......@@ -556,36 +567,60 @@ shrink_zone(struct zone *zone, int max_scan,
atomic_sub(SWAP_CLUSTER_MAX, &zone->refill_counter);
refill_inactive_zone(zone, SWAP_CLUSTER_MAX);
}
nr_pages = shrink_cache(nr_pages, zone, gfp_mask,
max_scan, nr_mapped);
return nr_pages;
return shrink_cache(nr_pages, zone, gfp_mask, max_scan, nr_mapped);
}
/*
* FIXME: don't do this for ZONE_HIGHMEM
*/
/*
* Here we assume it costs one seek to replace a lru page and that it also
* takes a seek to recreate a cache object. With this in mind we age equal
* percentages of the lru and ageable caches. This should balance the seeks
* generated by these structures.
*
* NOTE: for now I do this for all zones. If we find this is too aggressive
* on large boxes we may want to exclude ZONE_HIGHMEM.
*
* If we're encountering mapped pages on the LRU then increase the pressure on
* slab to avoid swapping.
*/
static void shrink_slab(int total_scanned, int gfp_mask)
{
int shrink_ratio;
int pages = nr_used_zone_pages();
shrink_ratio = (pages / (total_scanned + 1)) + 1;
shrink_dcache_memory(shrink_ratio, gfp_mask);
shrink_icache_memory(shrink_ratio, gfp_mask);
shrink_dqcache_memory(shrink_ratio, gfp_mask);
}
/*
* This is the direct reclaim path, for page-allocating processes. We only
* try to reclaim pages from zones which will satisfy the caller's allocation
* request.
*/
static int
shrink_caches(struct zone *classzone, int priority,
int *total_scanned, int gfp_mask, int nr_pages)
shrink_caches(struct zone *classzone, int priority, int *total_scanned,
int gfp_mask, const int nr_pages, int order)
{
struct zone *first_classzone;
struct zone *zone;
int ratio;
int nr_mapped = 0;
int pages = nr_used_zone_pages();
int ret = 0;
first_classzone = classzone->zone_pgdat->node_zones;
for (zone = classzone; zone >= first_classzone; zone--) {
int max_scan;
int to_reclaim;
int unreclaimed;
to_reclaim = zone->pages_high - zone->free_pages;
if (to_reclaim < 0)
if (order == 0 && to_reclaim < 0)
continue; /* zone has enough memory */
if (to_reclaim > SWAP_CLUSTER_MAX)
to_reclaim = SWAP_CLUSTER_MAX;
if (to_reclaim < nr_pages)
to_reclaim = nr_pages;
to_reclaim = min(to_reclaim, SWAP_CLUSTER_MAX);
to_reclaim = max(to_reclaim, nr_pages);
/*
* If we cannot reclaim `nr_pages' pages by scanning twice
......@@ -594,33 +629,18 @@ shrink_caches(struct zone *classzone, int priority,
max_scan = zone->nr_inactive >> priority;
if (max_scan < to_reclaim * 2)
max_scan = to_reclaim * 2;
unreclaimed = shrink_zone(zone, max_scan,
gfp_mask, to_reclaim, &nr_mapped);
nr_pages -= to_reclaim - unreclaimed;
ret += shrink_zone(zone, max_scan, gfp_mask,
to_reclaim, &nr_mapped);
*total_scanned += max_scan;
*total_scanned += nr_mapped;
if (ret >= nr_pages)
break;
}
/*
* Here we assume it costs one seek to replace a lru page and that
* it also takes a seek to recreate a cache object. With this in
* mind we age equal percentages of the lru and ageable caches.
* This should balance the seeks generated by these structures.
*
* NOTE: for now I do this for all zones. If we find this is too
* aggressive on large boxes we may want to exclude ZONE_HIGHMEM
*
* If we're encountering mapped pages on the LRU then increase the
* pressure on slab to avoid swapping.
*/
ratio = (pages / (*total_scanned + nr_mapped + 1)) + 1;
shrink_dcache_memory(ratio, gfp_mask);
shrink_icache_memory(ratio, gfp_mask);
shrink_dqcache_memory(ratio, gfp_mask);
return nr_pages;
return ret;
}
/*
* This is the main entry point to page reclaim.
* This is the main entry point to direct page reclaim.
*
* If a full scan of the inactive list fails to free enough memory then we
* are "out of memory" and something needs to be killed.
......@@ -640,17 +660,19 @@ int
try_to_free_pages(struct zone *classzone,
unsigned int gfp_mask, unsigned int order)
{
int priority = DEF_PRIORITY;
int nr_pages = SWAP_CLUSTER_MAX;
int priority;
const int nr_pages = SWAP_CLUSTER_MAX;
int nr_reclaimed = 0;
inc_page_state(pageoutrun);
for (priority = DEF_PRIORITY; priority; priority--) {
int total_scanned = 0;
nr_pages = shrink_caches(classzone, priority, &total_scanned,
gfp_mask, nr_pages);
if (nr_pages <= 0)
nr_reclaimed += shrink_caches(classzone, priority,
&total_scanned, gfp_mask,
nr_pages, order);
if (nr_reclaimed >= nr_pages)
return 1;
if (total_scanned == 0)
return 1; /* All zones had enough free memory */
......@@ -665,62 +687,46 @@ try_to_free_pages(struct zone *classzone,
/* Take a nap, wait for some writeback to complete */
blk_congestion_wait(WRITE, HZ/4);
shrink_slab(total_scanned, gfp_mask);
}
if (gfp_mask & __GFP_FS)
out_of_memory();
return 0;
}
static int check_classzone_need_balance(struct zone *classzone)
/*
* kswapd will work across all this node's zones until they are all at
* pages_high.
*/
static void kswapd_balance_pgdat(pg_data_t *pgdat)
{
struct zone *first_classzone;
int priority = DEF_PRIORITY;
int i;
first_classzone = classzone->zone_pgdat->node_zones;
while (classzone >= first_classzone) {
if (classzone->free_pages > classzone->pages_high)
return 0;
classzone--;
}
return 1;
}
for (priority = DEF_PRIORITY; priority; priority--) {
int success = 1;
static int kswapd_balance_pgdat(pg_data_t * pgdat)
{
int need_more_balance = 0, i;
struct zone *zone;
for (i = 0; i < pgdat->nr_zones; i++) {
struct zone *zone = pgdat->node_zones + i;
int nr_mapped = 0;
int max_scan;
int to_reclaim;
for (i = pgdat->nr_zones-1; i >= 0; i--) {
zone = pgdat->node_zones + i;
cond_resched();
if (!zone->need_balance)
continue;
if (!try_to_free_pages(zone, GFP_KSWAPD, 0)) {
zone->need_balance = 0;
__set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
to_reclaim = zone->pages_high - zone->free_pages;
if (to_reclaim <= 0)
continue;
success = 0;
max_scan = zone->nr_inactive >> priority;
if (max_scan < to_reclaim * 2)
max_scan = to_reclaim * 2;
shrink_zone(zone, max_scan, GFP_KSWAPD,
to_reclaim, &nr_mapped);
shrink_slab(max_scan + nr_mapped, GFP_KSWAPD);
}
if (check_classzone_need_balance(zone))
need_more_balance = 1;
else
zone->need_balance = 0;
}
return need_more_balance;
}
static int kswapd_can_sleep_pgdat(pg_data_t * pgdat)
{
struct zone *zone;
int i;
for (i = pgdat->nr_zones-1; i >= 0; i--) {
zone = pgdat->node_zones + i;
if (zone->need_balance)
return 0;
if (success)
break; /* All zones are at pages_high */
blk_congestion_wait(WRITE, HZ/4);
}
return 1;
}
/*
......@@ -740,7 +746,7 @@ int kswapd(void *p)
{
pg_data_t *pgdat = (pg_data_t*)p;
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
DEFINE_WAIT(wait);
daemonize();
set_cpus_allowed(tsk, __node_to_cpu_mask(pgdat->node_id));
......@@ -761,27 +767,12 @@ int kswapd(void *p)
*/
tsk->flags |= PF_MEMALLOC|PF_KSWAPD;
/*
* Kswapd main loop.
*/
for (;;) {
for ( ; ; ) {
if (current->flags & PF_FREEZE)
refrigerator(PF_IOTHREAD);
__set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pgdat->kswapd_wait, &wait);
mb();
if (kswapd_can_sleep_pgdat(pgdat))
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&pgdat->kswapd_wait, &wait);
/*
* If we actually get into a low-memory situation,
* the processes needing more memory will wake us
* up on a more timely basis.
*/
finish_wait(&pgdat->kswapd_wait, &wait);
kswapd_balance_pgdat(pgdat);
blk_run_queues();
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment