From b67b4b117746aef686e527c3205792db0f2c9e16 Mon Sep 17 00:00:00 2001 From: Dominic Curran Date: Mon, 27 Oct 2008 22:30:53 -0400 Subject: Input: gpio-keys - add flag to allow auto repeat This patch adds a flag to gpio-key driver to turn on the input subsystems auto repeat feature if needed. Signed-off-by: Dominic Curran Signed-off-by: Dmitry Torokhov --- include/linux/gpio_keys.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ec6ecd74781d..1289fa7623ca 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -15,6 +15,7 @@ struct gpio_keys_button { struct gpio_keys_platform_data { struct gpio_keys_button *buttons; int nbuttons; + unsigned int rep:1; /* enable input subsystem auto repeat */ }; #endif -- cgit v1.2.3-71-gd317 From 3eb1aa43ef5cb871ba3fb2f08633675eca374d2e Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Wed, 19 Nov 2008 16:58:50 -0500 Subject: Input: add support for Wacom W8001 penabled serial touchscreen The Wacom W8001 sensor is a sensor device (uses electromagnetic resonance) and it is interfaced via its serial microcontroller to the host. Signed-off-by: Jaya Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 13 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wacom_w8001.c | 325 ++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 340 insertions(+) create mode 100644 drivers/input/touchscreen/wacom_w8001.c (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3d1ab8fa9acc..20eb52ed176d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -95,6 +95,19 @@ config TOUCHSCREEN_ELO To compile this driver as a module, choose M here: the module will be called elo. +config TOUCHSCREEN_WACOM_W8001 + tristate "Wacom W8001 penabled serial touchscreen" + select SERIO + help + Say Y here if you have an Wacom W8001 penabled serial touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wacom_w8001. + + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 15cf29079489..3dc84d3846c1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c new file mode 100644 index 000000000000..2f33a0167644 --- /dev/null +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -0,0 +1,325 @@ +/* + * Wacom W8001 penabled serial touchscreen driver + * + * Copyright (c) 2008 Jaya Kumar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * Layout based on Elo serial touchscreen driver by Vojtech Pavlik + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Wacom W8001 serial touchscreen driver" + +MODULE_AUTHOR("Jaya Kumar "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define W8001_MAX_LENGTH 11 +#define W8001_PACKET_LEN 11 +#define W8001_LEAD_MASK 0x80 +#define W8001_LEAD_BYTE 0x80 +#define W8001_TAB_MASK 0x40 +#define W8001_TAB_BYTE 0x40 + +#define W8001_QUERY_PACKET 0x20 + +struct w8001_coord { + u8 rdy; + u8 tsw; + u8 f1; + u8 f2; + u16 x; + u16 y; + u16 pen_pressure; + u8 tilt_x; + u8 tilt_y; +}; + +/* + * Per-touchscreen data. + */ + +struct w8001 { + struct input_dev *dev; + struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; + int id; + int idx; + unsigned char expected_packet; + unsigned char data[W8001_MAX_LENGTH]; + unsigned char response[W8001_PACKET_LEN]; + char phys[32]; +}; + +static int parse_data(u8 *data, struct w8001_coord *coord) +{ + coord->rdy = data[0] & 0x20; + coord->tsw = data[0] & 0x01; + coord->f1 = data[0] & 0x02; + coord->f2 = data[0] & 0x04; + + coord->x = (data[1] & 0x7F) << 9; + coord->x |= (data[2] & 0x7F) << 2; + coord->x |= (data[6] & 0x60) >> 5; + + coord->y = (data[3] & 0x7F) << 9; + coord->y |= (data[4] & 0x7F) << 2; + coord->y |= (data[6] & 0x18) >> 3; + + coord->pen_pressure = data[5] & 0x7F; + coord->pen_pressure |= (data[6] & 0x07) << 7 ; + + coord->tilt_x = data[7] & 0x7F; + coord->tilt_y = data[8] & 0x7F; + + return 0; +} + +static void w8001_process_data(struct w8001 *w8001, unsigned char data) +{ + struct input_dev *dev = w8001->dev; + u8 tmp; + struct w8001_coord coord; + + w8001->data[w8001->idx] = data; + switch (w8001->idx++) { + case 0: + if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) { + pr_debug("w8001: unsynchronized data: 0x%02x\n", data); + w8001->idx = 0; + } + break; + case 8: + tmp = w8001->data[0] & W8001_TAB_MASK; + if (unlikely(tmp == W8001_TAB_BYTE)) + break; + w8001->idx = 0; + memset(&coord, 0, sizeof(coord)); + parse_data(w8001->data, &coord); + input_report_abs(dev, ABS_X, coord.x); + input_report_abs(dev, ABS_Y, coord.y); + input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); + input_report_key(dev, BTN_TOUCH, coord.tsw); + input_sync(dev); + break; + case 10: + w8001->idx = 0; + memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN); + w8001->expected_packet = W8001_QUERY_PACKET; + complete(&w8001->cmd_done); + break; + } +} + + +static irqreturn_t w8001_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + + w8001_process_data(w8001, data); + + return IRQ_HANDLED; +} + +static int w8001_async_command(struct w8001 *w8001, unsigned char *packet, + int len) +{ + int rc = -1; + int i; + + mutex_lock(&w8001->cmd_mutex); + + for (i = 0; i < len; i++) { + if (serio_write(w8001->serio, packet[i])) + goto out; + } + rc = 0; + +out: + mutex_unlock(&w8001->cmd_mutex); + return rc; +} + +static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len) +{ + int rc = -1; + int i; + + mutex_lock(&w8001->cmd_mutex); + + serio_pause_rx(w8001->serio); + init_completion(&w8001->cmd_done); + serio_continue_rx(w8001->serio); + + for (i = 0; i < len; i++) { + if (serio_write(w8001->serio, packet[i])) + goto out; + } + + wait_for_completion_timeout(&w8001->cmd_done, HZ); + + if (w8001->expected_packet == W8001_QUERY_PACKET) { + /* We are back in reporting mode, the query was ACKed */ + memcpy(packet, w8001->response, W8001_PACKET_LEN); + rc = 0; + } + +out: + mutex_unlock(&w8001->cmd_mutex); + return rc; +} + +static int w8001_setup(struct w8001 *w8001) +{ + struct w8001_coord coord; + struct input_dev *dev = w8001->dev; + unsigned char start[1] = { '1' }; + unsigned char query[11] = { '*' }; + + if (w8001_command(w8001, query, 1)) + return -1; + + memset(&coord, 0, sizeof(coord)); + parse_data(query, &coord); + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + + if (w8001_async_command(w8001, start, 1)) + return -1; + + return 0; +} + +/* + * w8001_disconnect() is the opposite of w8001_connect() + */ + +static void w8001_disconnect(struct serio *serio) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + + input_get_device(w8001->dev); + input_unregister_device(w8001->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(w8001->dev); + kfree(w8001); +} + +/* + * w8001_connect() is the routine that is called when someone adds a + * new serio device that supports the w8001 protocol and registers it as + * an input device. + */ + +static int w8001_connect(struct serio *serio, struct serio_driver *drv) +{ + struct w8001 *w8001; + struct input_dev *input_dev; + int err; + + w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!w8001 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + w8001->serio = serio; + w8001->id = serio->id.id; + w8001->dev = input_dev; + mutex_init(&w8001->cmd_mutex); + init_completion(&w8001->cmd_done); + snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); + + input_dev->name = "Wacom W8001 Penabled Serial TouchScreen"; + input_dev->phys = w8001->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_W8001; + input_dev->id.product = w8001->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + serio_set_drvdata(serio, w8001); + err = serio_open(serio, drv); + if (err) + goto fail2; + + if (w8001_setup(w8001)) + goto fail3; + + err = input_register_device(w8001->dev); + if (err) + goto fail3; + + return 0; + +fail3: + serio_close(serio); +fail2: + serio_set_drvdata(serio, NULL); +fail1: + input_free_device(input_dev); + kfree(w8001); + return err; +} + +static struct serio_device_id w8001_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_W8001, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, w8001_serio_ids); + +static struct serio_driver w8001_drv = { + .driver = { + .name = "w8001", + }, + .description = DRIVER_DESC, + .id_table = w8001_serio_ids, + .interrupt = w8001_interrupt, + .connect = w8001_connect, + .disconnect = w8001_disconnect, +}; + +static int __init w8001_init(void) +{ + return serio_register_driver(&w8001_drv); +} + +static void __exit w8001_exit(void) +{ + serio_unregister_driver(&w8001_drv); +} + +module_init(w8001_init); +module_exit(w8001_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 25641d9e0ea8..1bcb357a01a1 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_ZHENHUA 0x36 #define SERIO_INEXIO 0x37 #define SERIO_TOUCHIT213 0x37 +#define SERIO_W8001 0x39 #endif -- cgit v1.2.3-71-gd317 From a2d781fc8d9b16113dd9440107d73c0f21d7cbef Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Nov 2008 17:02:24 -0500 Subject: Input: libps2 - handle 0xfc responses from devices Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 20 ++++++++++++++++---- include/linux/libps2.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 2b304c22c200..67248c31e19a 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -262,9 +262,17 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) break; case PS2_RET_NAK: - ps2dev->nak = 1; + ps2dev->flags |= PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_NAK; break; + case PS2_RET_ERR: + if (ps2dev->flags & PS2_FLAG_NAK) { + ps2dev->flags &= ~PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_ERR; + break; + } + /* * Workaround for mice which don't ACK the Get ID command. * These are valid mouse IDs that we recognize. @@ -282,8 +290,11 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) } - if (!ps2dev->nak && ps2dev->cmdcnt) - ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + if (!ps2dev->nak) { + ps2dev->flags &= ~PS2_FLAG_NAK; + if (ps2dev->cmdcnt) + ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + } ps2dev->flags &= ~PS2_FLAG_ACK; wake_up(&ps2dev->wait); @@ -329,6 +340,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) wake_up(&ps2dev->wait); - ps2dev->flags = 0; + /* reset all flags except last nack */ + ps2dev->flags &= PS2_FLAG_NAK; } EXPORT_SYMBOL(ps2_cmd_aborted); diff --git a/include/linux/libps2.h b/include/linux/libps2.h index afc413369101..b94534b7e266 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -18,11 +18,13 @@ #define PS2_RET_ID 0x00 #define PS2_RET_ACK 0xfa #define PS2_RET_NAK 0xfe +#define PS2_RET_ERR 0xfc #define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */ #define PS2_FLAG_CMD 2 /* Waiting for command to finish */ #define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */ #define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */ +#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */ struct ps2dev { struct serio *serio; -- cgit v1.2.3-71-gd317 From 193da6092764ab693da7170c5badbf60d7758c1d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Nov 2008 12:03:54 +0100 Subject: fuse: move FUSE_MINOR to miscdevice.h Move FUSE_MINOR to miscdevice.h. While at it, de-uglify the file. Signed-off-by: Tejun Heo Signed-off-by: Miklos Szeredi --- include/linux/fuse.h | 3 --- include/linux/miscdevice.h | 42 +++++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 350fe9767bbc..7caa473306e4 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -40,9 +40,6 @@ /** The major number of the fuse character device */ #define FUSE_MAJOR MISC_MAJOR -/** The minor number of the fuse character device */ -#define FUSE_MINOR 229 - /* Make sure all structures are padded to 64bit boundary, so 32bit userspace works under 64bit kernels */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 26433ec520b3..a820f816a49e 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -3,33 +3,33 @@ #include #include -#define PSMOUSE_MINOR 1 -#define MS_BUSMOUSE_MINOR 2 -#define ATIXL_BUSMOUSE_MINOR 3 -/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */ -#define ATARIMOUSE_MINOR 5 -#define SUN_MOUSE_MINOR 6 -#define APOLLO_MOUSE_MINOR 7 -#define PC110PAD_MINOR 9 -/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ +#define PSMOUSE_MINOR 1 +#define MS_BUSMOUSE_MINOR 2 +#define ATIXL_BUSMOUSE_MINOR 3 +/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */ +#define ATARIMOUSE_MINOR 5 +#define SUN_MOUSE_MINOR 6 +#define APOLLO_MOUSE_MINOR 7 +#define PC110PAD_MINOR 9 +/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ -#define RTC_MINOR 135 +#define RTC_MINOR 135 #define EFI_RTC_MINOR 136 /* EFI Time services */ -#define SUN_OPENPROM_MINOR 139 +#define SUN_OPENPROM_MINOR 139 #define DMAPI_MINOR 140 /* DMAPI */ -#define NVRAM_MINOR 144 -#define SGI_MMTIMER 153 +#define NVRAM_MINOR 144 +#define SGI_MMTIMER 153 #define STORE_QUEUE_MINOR 155 -#define I2O_MINOR 166 +#define I2O_MINOR 166 #define MICROCODE_MINOR 184 -#define MWAVE_MINOR 219 /* ACP/Mwave Modem */ -#define MPT_MINOR 220 -#define MISC_DYNAMIC_MINOR 255 - -#define TUN_MINOR 200 -#define HPET_MINOR 228 -#define KVM_MINOR 232 +#define TUN_MINOR 200 +#define MWAVE_MINOR 219 /* ACP/Mwave Modem */ +#define MPT_MINOR 220 +#define HPET_MINOR 228 +#define FUSE_MINOR 229 +#define KVM_MINOR 232 +#define MISC_DYNAMIC_MINOR 255 struct device; -- cgit v1.2.3-71-gd317 From 59efec7b903987dcb60b9ebc85c7acd4443a11a1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Nov 2008 12:03:55 +0100 Subject: fuse: implement ioctl support Generic ioctl support is tricky to implement because only the ioctl implementation itself knows which memory regions need to be read and/or written. To support this, fuse client can request retry of ioctl specifying memory regions to read and write. Deep copying (nested pointers) can be implemented by retrying multiple times resolving one depth of dereference at a time. For security and cleanliness considerations, ioctl implementation has restricted mode where the kernel determines data transfer directions and sizes using the _IOC_*() macros on the ioctl command. In this mode, retry is not allowed. For all FUSE servers, restricted mode is enforced. Unrestricted ioctl will be used by CUSE. Plese read the comment on top of fs/fuse/file.c::fuse_file_do_ioctl() for more information. Signed-off-by: Tejun Heo Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fuse.h | 32 ++++++ 2 files changed, 312 insertions(+) (limited to 'include/linux') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 617269803913..baed06ea7622 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1469,6 +1469,282 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin) return retval; } +static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, + unsigned int nr_segs, size_t bytes, bool to_user) +{ + struct iov_iter ii; + int page_idx = 0; + + if (!bytes) + return 0; + + iov_iter_init(&ii, iov, nr_segs, bytes, 0); + + while (iov_iter_count(&ii)) { + struct page *page = pages[page_idx++]; + size_t todo = min_t(size_t, PAGE_SIZE, iov_iter_count(&ii)); + void *kaddr, *map; + + kaddr = map = kmap(page); + + while (todo) { + char __user *uaddr = ii.iov->iov_base + ii.iov_offset; + size_t iov_len = ii.iov->iov_len - ii.iov_offset; + size_t copy = min(todo, iov_len); + size_t left; + + if (!to_user) + left = copy_from_user(kaddr, uaddr, copy); + else + left = copy_to_user(uaddr, kaddr, copy); + + if (unlikely(left)) + return -EFAULT; + + iov_iter_advance(&ii, copy); + todo -= copy; + kaddr += copy; + } + + kunmap(map); + } + + return 0; +} + +/* + * For ioctls, there is no generic way to determine how much memory + * needs to be read and/or written. Furthermore, ioctls are allowed + * to dereference the passed pointer, so the parameter requires deep + * copying but FUSE has no idea whatsoever about what to copy in or + * out. + * + * This is solved by allowing FUSE server to retry ioctl with + * necessary in/out iovecs. Let's assume the ioctl implementation + * needs to read in the following structure. + * + * struct a { + * char *buf; + * size_t buflen; + * } + * + * On the first callout to FUSE server, inarg->in_size and + * inarg->out_size will be NULL; then, the server completes the ioctl + * with FUSE_IOCTL_RETRY set in out->flags, out->in_iovs set to 1 and + * the actual iov array to + * + * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) } } + * + * which tells FUSE to copy in the requested area and retry the ioctl. + * On the second round, the server has access to the structure and + * from that it can tell what to look for next, so on the invocation, + * it sets FUSE_IOCTL_RETRY, out->in_iovs to 2 and iov array to + * + * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) }, + * { .iov_base = a.buf, .iov_len = a.buflen } } + * + * FUSE will copy both struct a and the pointed buffer from the + * process doing the ioctl and retry ioctl with both struct a and the + * buffer. + * + * This time, FUSE server has everything it needs and completes ioctl + * without FUSE_IOCTL_RETRY which finishes the ioctl call. + * + * Copying data out works the same way. + * + * Note that if FUSE_IOCTL_UNRESTRICTED is clear, the kernel + * automatically initializes in and out iovs by decoding @cmd with + * _IOC_* macros and the server is not allowed to request RETRY. This + * limits ioctl data transfers to well-formed ioctls and is the forced + * behavior for all FUSE servers. + */ +static long fuse_file_do_ioctl(struct file *file, unsigned int cmd, + unsigned long arg, unsigned int flags) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_ioctl_in inarg = { + .fh = ff->fh, + .cmd = cmd, + .arg = arg, + .flags = flags + }; + struct fuse_ioctl_out outarg; + struct fuse_req *req = NULL; + struct page **pages = NULL; + struct page *iov_page = NULL; + struct iovec *in_iov = NULL, *out_iov = NULL; + unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages; + size_t in_size, out_size, transferred; + int err; + + /* assume all the iovs returned by client always fits in a page */ + BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); + + if (!fuse_allow_task(fc, current)) + return -EACCES; + + err = -EIO; + if (is_bad_inode(inode)) + goto out; + + err = -ENOMEM; + pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); + iov_page = alloc_page(GFP_KERNEL); + if (!pages || !iov_page) + goto out; + + /* + * If restricted, initialize IO parameters as encoded in @cmd. + * RETRY from server is not allowed. + */ + if (!(flags & FUSE_IOCTL_UNRESTRICTED)) { + struct iovec *iov = page_address(iov_page); + + iov->iov_base = (void *)arg; + iov->iov_len = _IOC_SIZE(cmd); + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + in_iov = iov; + in_iovs = 1; + } + + if (_IOC_DIR(cmd) & _IOC_READ) { + out_iov = iov; + out_iovs = 1; + } + } + + retry: + inarg.in_size = in_size = iov_length(in_iov, in_iovs); + inarg.out_size = out_size = iov_length(out_iov, out_iovs); + + /* + * Out data can be used either for actual out data or iovs, + * make sure there always is at least one page. + */ + out_size = max_t(size_t, out_size, PAGE_SIZE); + max_pages = DIV_ROUND_UP(max(in_size, out_size), PAGE_SIZE); + + /* make sure there are enough buffer pages and init request with them */ + err = -ENOMEM; + if (max_pages > FUSE_MAX_PAGES_PER_REQ) + goto out; + while (num_pages < max_pages) { + pages[num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); + if (!pages[num_pages]) + goto out; + num_pages++; + } + + req = fuse_get_req(fc); + if (IS_ERR(req)) { + err = PTR_ERR(req); + req = NULL; + goto out; + } + memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages); + req->num_pages = num_pages; + + /* okay, let's send it to the client */ + req->in.h.opcode = FUSE_IOCTL; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 1; + req->in.args[0].size = sizeof(inarg); + req->in.args[0].value = &inarg; + if (in_size) { + req->in.numargs++; + req->in.args[1].size = in_size; + req->in.argpages = 1; + + err = fuse_ioctl_copy_user(pages, in_iov, in_iovs, in_size, + false); + if (err) + goto out; + } + + req->out.numargs = 2; + req->out.args[0].size = sizeof(outarg); + req->out.args[0].value = &outarg; + req->out.args[1].size = out_size; + req->out.argpages = 1; + req->out.argvar = 1; + + request_send(fc, req); + err = req->out.h.error; + transferred = req->out.args[1].size; + fuse_put_request(fc, req); + req = NULL; + if (err) + goto out; + + /* did it ask for retry? */ + if (outarg.flags & FUSE_IOCTL_RETRY) { + char *vaddr; + + /* no retry if in restricted mode */ + err = -EIO; + if (!(flags & FUSE_IOCTL_UNRESTRICTED)) + goto out; + + in_iovs = outarg.in_iovs; + out_iovs = outarg.out_iovs; + + /* + * Make sure things are in boundary, separate checks + * are to protect against overflow. + */ + err = -ENOMEM; + if (in_iovs > FUSE_IOCTL_MAX_IOV || + out_iovs > FUSE_IOCTL_MAX_IOV || + in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) + goto out; + + err = -EIO; + if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred) + goto out; + + /* okay, copy in iovs and retry */ + vaddr = kmap_atomic(pages[0], KM_USER0); + memcpy(page_address(iov_page), vaddr, transferred); + kunmap_atomic(vaddr, KM_USER0); + + in_iov = page_address(iov_page); + out_iov = in_iov + in_iovs; + + goto retry; + } + + err = -EIO; + if (transferred > inarg.out_size) + goto out; + + err = fuse_ioctl_copy_user(pages, out_iov, out_iovs, transferred, true); + out: + if (req) + fuse_put_request(fc, req); + if (iov_page) + __free_page(iov_page); + while (num_pages) + __free_page(pages[--num_pages]); + kfree(pages); + + return err ? err : outarg.result; +} + +static long fuse_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return fuse_file_do_ioctl(file, cmd, arg, 0); +} + +static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT); +} + static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, .read = do_sync_read, @@ -1483,6 +1759,8 @@ static const struct file_operations fuse_file_operations = { .lock = fuse_file_lock, .flock = fuse_file_flock, .splice_read = generic_file_splice_read, + .unlocked_ioctl = fuse_file_ioctl, + .compat_ioctl = fuse_file_compat_ioctl, }; static const struct file_operations fuse_direct_io_file_operations = { @@ -1495,6 +1773,8 @@ static const struct file_operations fuse_direct_io_file_operations = { .fsync = fuse_fsync, .lock = fuse_file_lock, .flock = fuse_file_flock, + .unlocked_ioctl = fuse_file_ioctl, + .compat_ioctl = fuse_file_compat_ioctl, /* no mmap and splice_read */ }; diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 7caa473306e4..608e300ae883 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -148,6 +148,21 @@ struct fuse_file_lock { */ #define FUSE_READ_LOCKOWNER (1 << 1) +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) + +#define FUSE_IOCTL_MAX_IOV 256 + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -185,6 +200,7 @@ enum fuse_opcode { FUSE_INTERRUPT = 36, FUSE_BMAP = 37, FUSE_DESTROY = 38, + FUSE_IOCTL = 39, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -385,6 +401,22 @@ struct fuse_bmap_out { __u64 block; }; +struct fuse_ioctl_in { + __u64 fh; + __u32 flags; + __u32 cmd; + __u64 arg; + __u32 in_size; + __u32 out_size; +}; + +struct fuse_ioctl_out { + __s32 result; + __u32 flags; + __u32 in_iovs; + __u32 out_iovs; +}; + struct fuse_in_header { __u32 len; __u32 opcode; -- cgit v1.2.3-71-gd317 From 8599396b5062bf6bd2a0b433503849e2322df1c2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Nov 2008 12:03:55 +0100 Subject: fuse: implement unsolicited notification Clients always used to write only in response to read requests. To implement poll efficiently, clients should be able to issue unsolicited notifications. This patch implements basic notification support. Zero fuse_out_header.unique is now accepted and considered unsolicited notification and the error field contains notification code. This patch doesn't implement any actual notification. Signed-off-by: Tejun Heo Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 27 +++++++++++++++++++++++++-- include/linux/fuse.h | 4 ++++ 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 225388f54ae7..ffd670bb8c8c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -816,6 +816,15 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, return err; } +static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, + unsigned int size, struct fuse_copy_state *cs) +{ + switch (code) { + default: + return -EINVAL; + } +} + /* Look up request on processing list by unique ID */ static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) { @@ -879,9 +888,23 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, err = fuse_copy_one(&cs, &oh, sizeof(oh)); if (err) goto err_finish; + + err = -EINVAL; + if (oh.len != nbytes) + goto err_finish; + + /* + * Zero oh.unique indicates unsolicited notification message + * and error contains notification code. + */ + if (!oh.unique) { + err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), &cs); + fuse_copy_finish(&cs); + return err ? err : nbytes; + } + err = -EINVAL; - if (!oh.unique || oh.error <= -1000 || oh.error > 0 || - oh.len != nbytes) + if (oh.error <= -1000 || oh.error > 0) goto err_finish; spin_lock(&fc->lock); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 608e300ae883..abde9949e2c0 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -203,6 +203,10 @@ enum fuse_opcode { FUSE_IOCTL = 39, }; +enum fuse_notify_code { + FUSE_NOTIFY_CODE_MAX, +}; + /* The read buffer is required to be at least 8k, but may be much larger */ #define FUSE_MIN_READ_BUFFER 8192 -- cgit v1.2.3-71-gd317 From 95668a69a4bb862063c4d28a746e55107dee7b98 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Nov 2008 12:03:55 +0100 Subject: fuse: implement poll support Implement poll support. Polled files are indexed using kh in a RB tree rooted at fuse_conn->polled_files. Client should send FUSE_NOTIFY_POLL notification once after processing FUSE_POLL which has FUSE_POLL_SCHEDULE_NOTIFY set. Sending notification unconditionally after the latest poll or everytime file content might have changed is inefficient but won't cause malfunction. fuse_file_poll() can sleep and requires patches from the following thread which allows f_op->poll() to sleep. http://thread.gmane.org/gmane.linux.kernel/726176 Signed-off-by: Tejun Heo Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 19 ++++++++ fs/fuse/file.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 20 ++++++++ fs/fuse/inode.c | 1 + include/linux/fuse.h | 25 ++++++++++ 5 files changed, 197 insertions(+) (limited to 'include/linux') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ffd670bb8c8c..6176e444c76e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -816,10 +816,29 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, return err; } +static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_poll_wakeup_out outarg; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + return fuse_notify_poll_wakeup(fc, &outarg); +} + static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { switch (code) { + case FUSE_NOTIFY_POLL: + return fuse_notify_poll(fc, size, cs); + default: return -EINVAL; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a28ced678d38..b3a944e4bb9c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -62,6 +62,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc) ff->kh = ++fc->khctr; spin_unlock(&fc->lock); } + RB_CLEAR_NODE(&ff->polled_node); + init_waitqueue_head(&ff->poll_wait); } return ff; } @@ -170,7 +172,11 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir) spin_lock(&fc->lock); list_del(&ff->write_entry); + if (!RB_EMPTY_NODE(&ff->polled_node)) + rb_erase(&ff->polled_node, &fc->polled_files); spin_unlock(&fc->lock); + + wake_up_interruptible_sync(&ff->poll_wait); /* * Normally this will send the RELEASE request, * however if some asynchronous READ or WRITE requests @@ -1749,6 +1755,130 @@ static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd, return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT); } +/* + * All files which have been polled are linked to RB tree + * fuse_conn->polled_files which is indexed by kh. Walk the tree and + * find the matching one. + */ +static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh, + struct rb_node **parent_out) +{ + struct rb_node **link = &fc->polled_files.rb_node; + struct rb_node *last = NULL; + + while (*link) { + struct fuse_file *ff; + + last = *link; + ff = rb_entry(last, struct fuse_file, polled_node); + + if (kh < ff->kh) + link = &last->rb_left; + else if (kh > ff->kh) + link = &last->rb_right; + else + return link; + } + + if (parent_out) + *parent_out = last; + return link; +} + +/* + * The file is about to be polled. Make sure it's on the polled_files + * RB tree. Note that files once added to the polled_files tree are + * not removed before the file is released. This is because a file + * polled once is likely to be polled again. + */ +static void fuse_register_polled_file(struct fuse_conn *fc, + struct fuse_file *ff) +{ + spin_lock(&fc->lock); + if (RB_EMPTY_NODE(&ff->polled_node)) { + struct rb_node **link, *parent; + + link = fuse_find_polled_node(fc, ff->kh, &parent); + BUG_ON(*link); + rb_link_node(&ff->polled_node, parent, link); + rb_insert_color(&ff->polled_node, &fc->polled_files); + } + spin_unlock(&fc->lock); +} + +static unsigned fuse_file_poll(struct file *file, poll_table *wait) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh }; + struct fuse_poll_out outarg; + struct fuse_req *req; + int err; + + if (fc->no_poll) + return DEFAULT_POLLMASK; + + poll_wait(file, &ff->poll_wait, wait); + + /* + * Ask for notification iff there's someone waiting for it. + * The client may ignore the flag and always notify. + */ + if (waitqueue_active(&ff->poll_wait)) { + inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY; + fuse_register_polled_file(fc, ff); + } + + req = fuse_get_req(fc); + if (IS_ERR(req)) + return PTR_ERR(req); + + req->in.h.opcode = FUSE_POLL; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 1; + req->in.args[0].size = sizeof(inarg); + req->in.args[0].value = &inarg; + req->out.numargs = 1; + req->out.args[0].size = sizeof(outarg); + req->out.args[0].value = &outarg; + request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + + if (!err) + return outarg.revents; + if (err == -ENOSYS) { + fc->no_poll = 1; + return DEFAULT_POLLMASK; + } + return POLLERR; +} + +/* + * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and + * wakes up the poll waiters. + */ +int fuse_notify_poll_wakeup(struct fuse_conn *fc, + struct fuse_notify_poll_wakeup_out *outarg) +{ + u64 kh = outarg->kh; + struct rb_node **link; + + spin_lock(&fc->lock); + + link = fuse_find_polled_node(fc, kh, NULL); + if (*link) { + struct fuse_file *ff; + + ff = rb_entry(*link, struct fuse_file, polled_node); + wake_up_interruptible_sync(&ff->poll_wait); + } + + spin_unlock(&fc->lock); + return 0; +} + static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, .read = do_sync_read, @@ -1765,6 +1895,7 @@ static const struct file_operations fuse_file_operations = { .splice_read = generic_file_splice_read, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, + .poll = fuse_file_poll, }; static const struct file_operations fuse_direct_io_file_operations = { @@ -1779,6 +1910,7 @@ static const struct file_operations fuse_direct_io_file_operations = { .flock = fuse_file_flock, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, + .poll = fuse_file_poll, /* no mmap and splice_read */ }; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 86f013303828..986fbd4c1ff5 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include /** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 @@ -111,6 +113,12 @@ struct fuse_file { /** Entry on inode's write_files list */ struct list_head write_entry; + + /** RB node to be linked on fuse_conn->polled_files */ + struct rb_node polled_node; + + /** Wait queue head for poll */ + wait_queue_head_t poll_wait; }; /** One input argument of a request */ @@ -328,6 +336,9 @@ struct fuse_conn { /** The next unique kernel file handle */ u64 khctr; + /** rbtree of fuse_files waiting for poll events indexed by ph */ + struct rb_root polled_files; + /** Number of requests currently in the background */ unsigned num_background; @@ -416,6 +427,9 @@ struct fuse_conn { /** Is bmap not implemented by fs? */ unsigned no_bmap:1; + /** Is poll not implemented by fs? */ + unsigned no_poll:1; + /** Do multi-page cached writes */ unsigned big_writes:1; @@ -524,6 +538,12 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir); int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, int isdir); +/** + * Notify poll wakeup + */ +int fuse_notify_poll_wakeup(struct fuse_conn *fc, + struct fuse_notify_poll_wakeup_out *outarg); + /** * Initialize file operations on a regular file */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 0e15bc221d23..ba7256128084 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -486,6 +486,7 @@ static struct fuse_conn *new_conn(struct super_block *sb) /* fuse does it's own writeback accounting */ fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB; fc->khctr = 0; + fc->polled_files = RB_ROOT; fc->dev = sb->s_dev; err = bdi_init(&fc->bdi); if (err) diff --git a/include/linux/fuse.h b/include/linux/fuse.h index abde9949e2c0..5650cf033e73 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -163,6 +163,13 @@ struct fuse_file_lock { #define FUSE_IOCTL_MAX_IOV 256 +/** + * Poll flags + * + * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify + */ +#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -201,9 +208,11 @@ enum fuse_opcode { FUSE_BMAP = 37, FUSE_DESTROY = 38, FUSE_IOCTL = 39, + FUSE_POLL = 40, }; enum fuse_notify_code { + FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_CODE_MAX, }; @@ -421,6 +430,22 @@ struct fuse_ioctl_out { __u32 out_iovs; }; +struct fuse_poll_in { + __u64 fh; + __u64 kh; + __u32 flags; + __u32 padding; +}; + +struct fuse_poll_out { + __u32 revents; + __u32 padding; +}; + +struct fuse_notify_poll_wakeup_out { + __u64 kh; +}; + struct fuse_in_header { __u32 len; __u32 opcode; -- cgit v1.2.3-71-gd317 From 1f55ed06cf0c361b293b32e5947d35d173eff2aa Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 1 Dec 2008 19:14:02 +0100 Subject: fuse: update interface version Change interface version to 7.11 after adding the IOCTL and POLL messages. Also clean up the header a bit: - update copyright date to 2008 - fix checkpatch warning: WARNING: Use #include instead of - remove FUSE_MAJOR define, which is not being used any more Signed-off-by: Miklos Szeredi --- include/linux/fuse.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 5650cf033e73..162e5defe683 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -1,6 +1,6 @@ /* FUSE: Filesystem in Userspace - Copyright (C) 2001-2006 Miklos Szeredi + Copyright (C) 2001-2008 Miklos Szeredi This program can be distributed under the terms of the GNU GPL. See the file COPYING. @@ -20,26 +20,27 @@ * * 7.10 * - add nonseekable open flag + * + * 7.11 + * - add IOCTL message + * - add unsolicited notification support + * - add POLL message and NOTIFY_POLL notification */ #ifndef _LINUX_FUSE_H #define _LINUX_FUSE_H -#include -#include +#include /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 10 +#define FUSE_KERNEL_MINOR_VERSION 11 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 -/** The major number of the fuse character device */ -#define FUSE_MAJOR MISC_MAJOR - /* Make sure all structures are padded to 64bit boundary, so 32bit userspace works under 64bit kernels */ -- cgit v1.2.3-71-gd317 From 50b6f1f4a430608f7345f66ecd68a129bff11649 Mon Sep 17 00:00:00 2001 From: Kwangwoo Lee Date: Sat, 20 Dec 2008 04:26:01 -0500 Subject: Input: add tsc2007 based touchscreen driver This drive has been tested on ARM9 based SoC - MV86XX. Signed-off-by: Kwangwoo Lee Acked-by: Jean Delvare Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/tsc2007.c | 381 ++++++++++++++++++++++++++++++++++++ include/linux/i2c/tsc2007.h | 17 ++ 4 files changed, 410 insertions(+) create mode 100644 drivers/input/touchscreen/tsc2007.c create mode 100644 include/linux/i2c/tsc2007.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 20eb52ed176d..83747dc9ba05 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -389,4 +389,15 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TSC2007 + tristate "TSC2007 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2007 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2007. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3dc84d3846c1..127f87cc2e2e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c new file mode 100644 index 000000000000..b75dc2990574 --- /dev/null +++ b/drivers/input/touchscreen/tsc2007.c @@ -0,0 +1,381 @@ +/* + * drivers/input/touchscreen/tsc2007.c + * + * Copyright (c) 2008 MtekVision Co., Ltd. + * Kwangwoo Lee + * + * Using code from: + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */ + +#define TSC2007_MEASURE_TEMP0 (0x0 << 4) +#define TSC2007_MEASURE_AUX (0x2 << 4) +#define TSC2007_MEASURE_TEMP1 (0x4 << 4) +#define TSC2007_ACTIVATE_XN (0x8 << 4) +#define TSC2007_ACTIVATE_YN (0x9 << 4) +#define TSC2007_ACTIVATE_YP_XN (0xa << 4) +#define TSC2007_SETUP (0xb << 4) +#define TSC2007_MEASURE_X (0xc << 4) +#define TSC2007_MEASURE_Y (0xd << 4) +#define TSC2007_MEASURE_Z1 (0xe << 4) +#define TSC2007_MEASURE_Z2 (0xf << 4) + +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define TSC2007_12BIT (0x0 << 1) +#define TSC2007_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tsc2007 { + struct input_dev *input; + char phys[32]; + struct hrtimer timer; + struct ts_event tc; + + struct i2c_client *client; + + spinlock_t lock; + + u16 model; + u16 x_plate_ohms; + + unsigned pendown; + int irq; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); +}; + +static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static void tsc2007_send_event(void *tsc) +{ + struct tsc2007 *ts = tsc; + u32 rt; + u16 x, y, z1, z2; + + x = ts->tc.x; + y = ts->tc.y; + z1 = ts->tc.z1; + z2 = ts->tc.z2; + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = z2; + rt -= z1; + rt *= x; + rt *= ts->x_plate_ohms; + rt /= z1; + rt = (rt + 2047) >> 12; + } else + rt = 0; + + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ + if (rt > MAX_12BIT) { + dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); + return; + } + + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. + * + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (rt) { + struct input_dev *input = ts->input; + + if (!ts->pendown) { + dev_dbg(&ts->client->dev, "DOWN\n"); + + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; + } + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", + x, y, rt); + } + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); +} + +static int tsc2007_read_values(struct tsc2007 *tsc) +{ + /* y- still on; turn on only y+ (and ADC) */ + tsc->tc.y = tsc2007_xfer(tsc, READ_Y); + + /* turn y- off, x+ on, then leave in lowpower */ + tsc->tc.x = tsc2007_xfer(tsc, READ_X); + + /* turn y+ off, x- on; we'll use formula #1 */ + tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1); + tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2); + + /* power down */ + tsc2007_xfer(tsc, PWRDOWN); + + return 0; +} + +static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle) +{ + struct tsc2007 *ts = container_of(handle, struct tsc2007, timer); + + spin_lock_irq(&ts->lock); + + if (unlikely(!ts->get_pendown_state() && ts->pendown)) { + struct input_dev *input = ts->input; + + dev_dbg(&ts->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; + enable_irq(ts->irq); + } else { + /* pen is still down, continue with the measurement */ + dev_dbg(&ts->client->dev, "pen is still down\n"); + + tsc2007_read_values(ts); + tsc2007_send_event(ts); + } + + spin_unlock_irq(&ts->lock); + + return HRTIMER_NORESTART; +} + +static irqreturn_t tsc2007_irq(int irq, void *handle) +{ + struct tsc2007 *ts = handle; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + if (likely(ts->get_pendown_state())) { + disable_irq(ts->irq); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); + } + + if (ts->clear_penirq) + ts->clear_penirq(); + + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + +static int tsc2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2007 *ts; + struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->input = input_dev; + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = tsc2007_timer; + + spin_lock_init(&ts->lock); + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + ts->get_pendown_state = pdata->get_pendown_state; + ts->clear_penirq = pdata->clear_penirq; + + pdata->init_platform_hw(); + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", client->dev.bus_id); + + input_dev->name = "TSC2007 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + tsc2007_read_values(ts); + + ts->irq = client->irq; + + err = request_irq(ts->irq, tsc2007_irq, 0, + client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + dev_info(&client->dev, "registered with irq (%d)\n", ts->irq); + + return 0; + + err_free_irq: + free_irq(ts->irq, ts); + hrtimer_cancel(&ts->timer); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int tsc2007_remove(struct i2c_client *client) +{ + struct tsc2007 *ts = i2c_get_clientdata(client); + struct tsc2007_platform_data *pdata; + + pdata = client->dev.platform_data; + pdata->exit_platform_hw(); + + free_irq(ts->irq, ts); + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id tsc2007_idtable[] = { + { "tsc2007", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); + +static struct i2c_driver tsc2007_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tsc2007" + }, + .id_table = tsc2007_idtable, + .probe = tsc2007_probe, + .remove = tsc2007_remove, +}; + +static int __init tsc2007_init(void) +{ + return i2c_add_driver(&tsc2007_driver); +} + +static void __exit tsc2007_exit(void) +{ + i2c_del_driver(&tsc2007_driver); +} + +module_init(tsc2007_init); +module_exit(tsc2007_exit); + +MODULE_AUTHOR("Kwangwoo Lee "); +MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/tsc2007.h b/include/linux/i2c/tsc2007.h new file mode 100644 index 000000000000..c6361fbb7bf9 --- /dev/null +++ b/include/linux/i2c/tsc2007.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_I2C_TSC2007_H +#define __LINUX_I2C_TSC2007_H + +/* linux/i2c/tsc2007.h */ + +struct tsc2007_platform_data { + u16 model; /* 2007. */ + u16 x_plate_ohms; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); /* If needed, clear 2nd level + interrupt source */ + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; + +#endif -- cgit v1.2.3-71-gd317 From 70a7d3cc1308a55104fbe505d76f2aca8a4cf53e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 22 Dec 2008 10:26:05 -0800 Subject: swiotlb: add hwdev to swiotlb_phys_to_bus() / swiotlb_sg_to_bus() Impact: extend functions with a (yet unused) parameter, update callsites Some architectures need it - in preparation for highmem swiotlb. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- arch/x86/kernel/pci-swiotlb_64.c | 2 +- include/linux/swiotlb.h | 3 ++- lib/swiotlb.c | 53 +++++++++++++++++----------------------- 3 files changed, 25 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/pci-swiotlb_64.c b/arch/x86/kernel/pci-swiotlb_64.c index 242c3440687f..6cf8a816dc29 100644 --- a/arch/x86/kernel/pci-swiotlb_64.c +++ b/arch/x86/kernel/pci-swiotlb_64.c @@ -23,7 +23,7 @@ void *swiotlb_alloc(unsigned order, unsigned long nslabs) return (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, order); } -dma_addr_t swiotlb_phys_to_bus(phys_addr_t paddr) +dma_addr_t swiotlb_phys_to_bus(struct device *hwdev, phys_addr_t paddr) { return paddr; } diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h index 325af1de0351..dedd3c0cfe30 100644 --- a/include/linux/swiotlb.h +++ b/include/linux/swiotlb.h @@ -27,7 +27,8 @@ swiotlb_init(void); extern void *swiotlb_alloc_boot(size_t bytes, unsigned long nslabs); extern void *swiotlb_alloc(unsigned order, unsigned long nslabs); -extern dma_addr_t swiotlb_phys_to_bus(phys_addr_t address); +extern dma_addr_t swiotlb_phys_to_bus(struct device *hwdev, + phys_addr_t address); extern phys_addr_t swiotlb_bus_to_phys(dma_addr_t address); extern int swiotlb_arch_range_needs_mapping(void *ptr, size_t size); diff --git a/lib/swiotlb.c b/lib/swiotlb.c index fa2dc4e5f9ba..3657da8ebbc3 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -126,7 +126,7 @@ void * __weak swiotlb_alloc(unsigned order, unsigned long nslabs) return (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, order); } -dma_addr_t __weak swiotlb_phys_to_bus(phys_addr_t paddr) +dma_addr_t __weak swiotlb_phys_to_bus(struct device *hwdev, phys_addr_t paddr) { return paddr; } @@ -136,9 +136,10 @@ phys_addr_t __weak swiotlb_bus_to_phys(dma_addr_t baddr) return baddr; } -static dma_addr_t swiotlb_virt_to_bus(volatile void *address) +static dma_addr_t swiotlb_virt_to_bus(struct device *hwdev, + volatile void *address) { - return swiotlb_phys_to_bus(virt_to_phys(address)); + return swiotlb_phys_to_bus(hwdev, virt_to_phys(address)); } static void *swiotlb_bus_to_virt(dma_addr_t address) @@ -151,35 +152,23 @@ int __weak swiotlb_arch_range_needs_mapping(void *ptr, size_t size) return 0; } -static dma_addr_t swiotlb_sg_to_bus(struct scatterlist *sg) +static dma_addr_t swiotlb_sg_to_bus(struct device *hwdev, struct scatterlist *sg) { - return swiotlb_phys_to_bus(page_to_phys(sg_page(sg)) + sg->offset); + return swiotlb_phys_to_bus(hwdev, page_to_phys(sg_page(sg)) + sg->offset); } static void swiotlb_print_info(unsigned long bytes) { phys_addr_t pstart, pend; - dma_addr_t bstart, bend; pstart = virt_to_phys(io_tlb_start); pend = virt_to_phys(io_tlb_end); - bstart = swiotlb_phys_to_bus(pstart); - bend = swiotlb_phys_to_bus(pend); - printk(KERN_INFO "Placing %luMB software IO TLB between %p - %p\n", bytes >> 20, io_tlb_start, io_tlb_end); - if (pstart != bstart || pend != bend) - printk(KERN_INFO "software IO TLB at phys %#llx - %#llx" - " bus %#llx - %#llx\n", - (unsigned long long)pstart, - (unsigned long long)pend, - (unsigned long long)bstart, - (unsigned long long)bend); - else - printk(KERN_INFO "software IO TLB at phys %#llx - %#llx\n", - (unsigned long long)pstart, - (unsigned long long)pend); + printk(KERN_INFO "software IO TLB at phys %#llx - %#llx\n", + (unsigned long long)pstart, + (unsigned long long)pend); } /* @@ -406,7 +395,7 @@ map_single(struct device *hwdev, struct swiotlb_phys_addr buffer, size_t size, i struct swiotlb_phys_addr slot_buf; mask = dma_get_seg_boundary(hwdev); - start_dma_addr = swiotlb_virt_to_bus(io_tlb_start) & mask; + start_dma_addr = swiotlb_virt_to_bus(hwdev, io_tlb_start) & mask; offset_slots = ALIGN(start_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; @@ -585,7 +574,9 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size, dma_mask = hwdev->coherent_dma_mask; ret = (void *)__get_free_pages(flags, order); - if (ret && !is_buffer_dma_capable(dma_mask, swiotlb_virt_to_bus(ret), size)) { + if (ret && + !is_buffer_dma_capable(dma_mask, swiotlb_virt_to_bus(hwdev, ret), + size)) { /* * The allocated memory isn't reachable by the device. * Fall back on swiotlb_map_single(). @@ -609,7 +600,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size, } memset(ret, 0, size); - dev_addr = swiotlb_virt_to_bus(ret); + dev_addr = swiotlb_virt_to_bus(hwdev, ret); /* Confirm address can be DMA'd by device */ if (!is_buffer_dma_capable(dma_mask, dev_addr, size)) { @@ -669,7 +660,7 @@ dma_addr_t swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size, int dir, struct dma_attrs *attrs) { - dma_addr_t dev_addr = swiotlb_virt_to_bus(ptr); + dma_addr_t dev_addr = swiotlb_virt_to_bus(hwdev, ptr); void *map; struct swiotlb_phys_addr buffer; @@ -694,7 +685,7 @@ swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size, map = io_tlb_overflow_buffer; } - dev_addr = swiotlb_virt_to_bus(map); + dev_addr = swiotlb_virt_to_bus(hwdev, map); /* * Ensure that the address returned is DMA'ble @@ -840,7 +831,7 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, BUG_ON(dir == DMA_NONE); for_each_sg(sgl, sg, nelems, i) { - dev_addr = swiotlb_sg_to_bus(sg); + dev_addr = swiotlb_sg_to_bus(hwdev, sg); if (range_needs_mapping(sg_virt(sg), sg->length) || address_needs_mapping(hwdev, dev_addr, sg->length)) { void *map; @@ -856,7 +847,7 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, sgl[0].dma_length = 0; return 0; } - sg->dma_address = swiotlb_virt_to_bus(map); + sg->dma_address = swiotlb_virt_to_bus(hwdev, map); } else sg->dma_address = dev_addr; sg->dma_length = sg->length; @@ -886,7 +877,7 @@ swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, BUG_ON(dir == DMA_NONE); for_each_sg(sgl, sg, nelems, i) { - if (sg->dma_address != swiotlb_sg_to_bus(sg)) + if (sg->dma_address != swiotlb_sg_to_bus(hwdev, sg)) unmap_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), sg->dma_length, dir); else if (dir == DMA_FROM_DEVICE) @@ -919,7 +910,7 @@ swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl, BUG_ON(dir == DMA_NONE); for_each_sg(sgl, sg, nelems, i) { - if (sg->dma_address != swiotlb_sg_to_bus(sg)) + if (sg->dma_address != swiotlb_sg_to_bus(hwdev, sg)) sync_single(hwdev, swiotlb_bus_to_virt(sg->dma_address), sg->dma_length, dir, target); else if (dir == DMA_FROM_DEVICE) @@ -944,7 +935,7 @@ swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, int swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) { - return (dma_addr == swiotlb_virt_to_bus(io_tlb_overflow_buffer)); + return (dma_addr == swiotlb_virt_to_bus(hwdev, io_tlb_overflow_buffer)); } /* @@ -956,7 +947,7 @@ swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) int swiotlb_dma_supported(struct device *hwdev, u64 mask) { - return swiotlb_virt_to_bus(io_tlb_end - 1) <= mask; + return swiotlb_virt_to_bus(hwdev, io_tlb_end - 1) <= mask; } EXPORT_SYMBOL(swiotlb_map_single); -- cgit v1.2.3-71-gd317 From ea319518ba3de282c13ae1cf4bf2215c5e03e67e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 26 Dec 2008 15:08:55 +0100 Subject: locking, percpu counters: introduce separate lock classes Impact: fix lockdep false positives Classify percpu_counter instances similar to regular lock objects -- that is, per instantiation site. The networking code has increased its use of percpu_counters, which leads to false positives if they are treated as a single class. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/percpu_counter.h | 14 ++++++++++---- lib/percpu_counter.c | 18 ++++-------------- lib/proportions.c | 6 +++--- mm/backing-dev.c | 2 +- 4 files changed, 18 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 9007ccdfc112..96bdde36599f 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -30,8 +30,16 @@ struct percpu_counter { #define FBC_BATCH (NR_CPUS*4) #endif -int percpu_counter_init(struct percpu_counter *fbc, s64 amount); -int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount); +int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, + struct lock_class_key *key); + +#define percpu_counter_init(fbc, value) \ + ({ \ + static struct lock_class_key __key; \ + \ + __percpu_counter_init(fbc, value, &__key); \ + }) + void percpu_counter_destroy(struct percpu_counter *fbc); void percpu_counter_set(struct percpu_counter *fbc, s64 amount); void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch); @@ -85,8 +93,6 @@ static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount) return 0; } -#define percpu_counter_init_irq percpu_counter_init - static inline void percpu_counter_destroy(struct percpu_counter *fbc) { } diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index a8663890a88c..c7fe2e4e8ed1 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -71,11 +71,11 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc) } EXPORT_SYMBOL(__percpu_counter_sum); -static struct lock_class_key percpu_counter_irqsafe; - -int percpu_counter_init(struct percpu_counter *fbc, s64 amount) +int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, + struct lock_class_key *key) { spin_lock_init(&fbc->lock); + lockdep_set_class(&fbc->lock, key); fbc->count = amount; fbc->counters = alloc_percpu(s32); if (!fbc->counters) @@ -87,17 +87,7 @@ int percpu_counter_init(struct percpu_counter *fbc, s64 amount) #endif return 0; } -EXPORT_SYMBOL(percpu_counter_init); - -int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount) -{ - int err; - - err = percpu_counter_init(fbc, amount); - if (!err) - lockdep_set_class(&fbc->lock, &percpu_counter_irqsafe); - return err; -} +EXPORT_SYMBOL(__percpu_counter_init); void percpu_counter_destroy(struct percpu_counter *fbc) { diff --git a/lib/proportions.c b/lib/proportions.c index 4f387a643d72..7367f2b727d0 100644 --- a/lib/proportions.c +++ b/lib/proportions.c @@ -83,11 +83,11 @@ int prop_descriptor_init(struct prop_descriptor *pd, int shift) pd->index = 0; pd->pg[0].shift = shift; mutex_init(&pd->mutex); - err = percpu_counter_init_irq(&pd->pg[0].events, 0); + err = percpu_counter_init(&pd->pg[0].events, 0); if (err) goto out; - err = percpu_counter_init_irq(&pd->pg[1].events, 0); + err = percpu_counter_init(&pd->pg[1].events, 0); if (err) percpu_counter_destroy(&pd->pg[0].events); @@ -191,7 +191,7 @@ int prop_local_init_percpu(struct prop_local_percpu *pl) spin_lock_init(&pl->lock); pl->shift = 0; pl->period = 0; - return percpu_counter_init_irq(&pl->events, 0); + return percpu_counter_init(&pl->events, 0); } void prop_local_destroy_percpu(struct prop_local_percpu *pl) diff --git a/mm/backing-dev.c b/mm/backing-dev.c index f2e574dbc300..f3b125857827 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -220,7 +220,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->max_prop_frac = PROP_FRAC_BASE; for (i = 0; i < NR_BDI_STAT_ITEMS; i++) { - err = percpu_counter_init_irq(&bdi->bdi_stat[i], 0); + err = percpu_counter_init(&bdi->bdi_stat[i], 0); if (err) goto err; } -- cgit v1.2.3-71-gd317 From 34a4c5eb421dab6fe8381aa12c990f9d6f645b17 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 29 Dec 2008 04:00:23 -0800 Subject: Input: map_to_7segment.h - convert to __inline__ for userspace Use __inline__ rather than inline for map_to_seg7() since it is exported to userspace. Signed-off-by: Mike Frysinger Signed-off-by: Dmitry Torokhov --- include/linux/map_to_7segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/map_to_7segment.h b/include/linux/map_to_7segment.h index 7df8432c4402..12d62a54d470 100644 --- a/include/linux/map_to_7segment.h +++ b/include/linux/map_to_7segment.h @@ -75,7 +75,7 @@ struct seg7_conversion_map { unsigned char table[128]; }; -static inline int map_to_seg7(struct seg7_conversion_map *map, int c) +static __inline__ int map_to_seg7(struct seg7_conversion_map *map, int c) { return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL; } -- cgit v1.2.3-71-gd317 From 47fea2adfc9e16846bc57c2f64ff233b354fef39 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Mon, 29 Dec 2008 23:39:17 +0530 Subject: sched: sched.c declare variables before they get used Impact: cleanup, avoid sparse warnings In linux/sched.h moved out sysctl_sched_latency, sysctl_sched_min_granularity, sysctl_sched_wakeup_granularity, sysctl_sched_shares_ratelimit and sysctl_sched_shares_thresh from #ifdef CONFIG_SCHED_DEBUG as these variables are common for both. Fixes these sparse warnings: kernel/sched.c:825:14: warning: symbol 'sysctl_sched_shares_ratelimit' was not declared. Should it be static? kernel/sched.c:832:14: warning: symbol 'sysctl_sched_shares_thresh' was not declared. Should it be static? kernel/sched_fair.c:37:14: warning: symbol 'sysctl_sched_latency' was not declared. Should it be static? kernel/sched_fair.c:43:14: warning: symbol 'sysctl_sched_min_granularity' was not declared. Should it be static? kernel/sched_fair.c:72:14: warning: symbol 'sysctl_sched_wakeup_granularity' was not declared. Should it be static? Signed-off-by: Jaswinder Singh Rajput Signed-off-by: Ingo Molnar --- include/linux/sched.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 8395e715809d..01d9fd268eb0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1651,16 +1651,16 @@ extern void wake_up_idle_cpu(int cpu); static inline void wake_up_idle_cpu(int cpu) { } #endif -#ifdef CONFIG_SCHED_DEBUG extern unsigned int sysctl_sched_latency; extern unsigned int sysctl_sched_min_granularity; extern unsigned int sysctl_sched_wakeup_granularity; +extern unsigned int sysctl_sched_shares_ratelimit; +extern unsigned int sysctl_sched_shares_thresh; +#ifdef CONFIG_SCHED_DEBUG extern unsigned int sysctl_sched_child_runs_first; extern unsigned int sysctl_sched_features; extern unsigned int sysctl_sched_migration_cost; extern unsigned int sysctl_sched_nr_migrate; -extern unsigned int sysctl_sched_shares_ratelimit; -extern unsigned int sysctl_sched_shares_thresh; int sched_nr_latency_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, -- cgit v1.2.3-71-gd317 From 1c5745aa380efb6417b5681104b007c8612fb496 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 22 Dec 2008 23:05:28 +0100 Subject: sched_clock: prevent scd->clock from moving backwards, take #2 Redo: 5b7dba4: sched_clock: prevent scd->clock from moving backwards which had to be reverted due to s2ram hangs: ca7e716: Revert "sched_clock: prevent scd->clock from moving backwards" ... this time with resume restoring GTOD later in the sequence taken into account as well. The "timekeeping_suspended" flag is not very nice but we cannot call into GTOD before it has been properly resumed and the scheduler will run very early in the resume sequence. Cc: Signed-off-by: Ingo Molnar --- include/linux/time.h | 1 + kernel/sched_clock.c | 5 ++++- kernel/time/timekeeping.c | 7 +++++-- 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/time.h b/include/linux/time.h index ce321ac5c8f8..fbbd2a1c92ba 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -105,6 +105,7 @@ extern unsigned long read_persistent_clock(void); extern int update_persistent_clock(struct timespec now); extern int no_sync_cmos_clock __read_mostly; void timekeeping_init(void); +extern int timekeeping_suspended; unsigned long get_seconds(void); struct timespec current_kernel_time(void); diff --git a/kernel/sched_clock.c b/kernel/sched_clock.c index e8ab096ddfe3..a0b0852414cc 100644 --- a/kernel/sched_clock.c +++ b/kernel/sched_clock.c @@ -124,7 +124,7 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now) clock = scd->tick_gtod + delta; min_clock = wrap_max(scd->tick_gtod, scd->clock); - max_clock = scd->tick_gtod + TICK_NSEC; + max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC); clock = wrap_max(clock, min_clock); clock = wrap_min(clock, max_clock); @@ -227,6 +227,9 @@ EXPORT_SYMBOL_GPL(sched_clock_idle_sleep_event); */ void sched_clock_idle_wakeup_event(u64 delta_ns) { + if (timekeeping_suspended) + return; + sched_clock_tick(); touch_softlockup_watchdog(); } diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index fa05e88aa76f..900f1b6598d1 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -46,6 +46,9 @@ struct timespec xtime __attribute__ ((aligned (16))); struct timespec wall_to_monotonic __attribute__ ((aligned (16))); static unsigned long total_sleep_time; /* seconds */ +/* flag for if timekeeping is suspended */ +int __read_mostly timekeeping_suspended; + static struct timespec xtime_cache __attribute__ ((aligned (16))); void update_xtime_cache(u64 nsec) { @@ -92,6 +95,8 @@ void getnstimeofday(struct timespec *ts) unsigned long seq; s64 nsecs; + WARN_ON(timekeeping_suspended); + do { seq = read_seqbegin(&xtime_lock); @@ -299,8 +304,6 @@ void __init timekeeping_init(void) write_sequnlock_irqrestore(&xtime_lock, flags); } -/* flag for if timekeeping is suspended */ -static int timekeeping_suspended; /* time in seconds when suspend began */ static unsigned long timekeeping_suspend_time; -- cgit v1.2.3-71-gd317 From 14eaddc967b16017d4a1a24d2be6c28ecbe06ed8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 31 Dec 2008 15:15:42 +0000 Subject: CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #2] Fix a regression in cap_capable() due to: commit 5ff7711e635b32f0a1e558227d030c7e45b4a465 Author: David Howells Date: Wed Dec 31 02:52:28 2008 +0000 CRED: Differentiate objective and effective subjective credentials on a task The problem is that the above patch allows a process to have two sets of credentials, and for the most part uses the subjective credentials when accessing current's creds. There is, however, one exception: cap_capable(), and thus capable(), uses the real/objective credentials of the target task, whether or not it is the current task. Ordinarily this doesn't matter, since usually the two cred pointers in current point to the same set of creds. However, sys_faccessat() makes use of this facility to override the credentials of the calling process to make its test, without affecting the creds as seen from other processes. One of the things sys_faccessat() does is to make an adjustment to the effective capabilities mask, which cap_capable(), as it stands, then ignores. The affected capability check is in generic_permission(): if (!(mask & MAY_EXEC) || execute_ok(inode)) if (capable(CAP_DAC_OVERRIDE)) return 0; This change splits capable() from has_capability() down into the commoncap and SELinux code. The capable() security op now only deals with the current process, and uses the current process's subjective creds. A new security op - task_capable() - is introduced that can check any task's objective creds. strictly the capable() security op is superfluous with the presence of the task_capable() op, however it should be faster to call the capable() op since two fewer arguments need be passed down through the various layers. This can be tested by compiling the following program from the XFS testsuite: /* * t_access_root.c - trivial test program to show permission bug. * * Written by Michael Kerrisk - copyright ownership not pursued. * Sourced from: http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-10/6030.html */ #include #include #include #include #include #include #define UID 500 #define GID 100 #define PERM 0 #define TESTPATH "/tmp/t_access" static void errExit(char *msg) { perror(msg); exit(EXIT_FAILURE); } /* errExit */ static void accessTest(char *file, int mask, char *mstr) { printf("access(%s, %s) returns %d\n", file, mstr, access(file, mask)); } /* accessTest */ int main(int argc, char *argv[]) { int fd, perm, uid, gid; char *testpath; char cmd[PATH_MAX + 20]; testpath = (argc > 1) ? argv[1] : TESTPATH; perm = (argc > 2) ? strtoul(argv[2], NULL, 8) : PERM; uid = (argc > 3) ? atoi(argv[3]) : UID; gid = (argc > 4) ? atoi(argv[4]) : GID; unlink(testpath); fd = open(testpath, O_RDWR | O_CREAT, 0); if (fd == -1) errExit("open"); if (fchown(fd, uid, gid) == -1) errExit("fchown"); if (fchmod(fd, perm) == -1) errExit("fchmod"); close(fd); snprintf(cmd, sizeof(cmd), "ls -l %s", testpath); system(cmd); if (seteuid(uid) == -1) errExit("seteuid"); accessTest(testpath, 0, "0"); accessTest(testpath, R_OK, "R_OK"); accessTest(testpath, W_OK, "W_OK"); accessTest(testpath, X_OK, "X_OK"); accessTest(testpath, R_OK | W_OK, "R_OK | W_OK"); accessTest(testpath, R_OK | X_OK, "R_OK | X_OK"); accessTest(testpath, W_OK | X_OK, "W_OK | X_OK"); accessTest(testpath, R_OK | W_OK | X_OK, "R_OK | W_OK | X_OK"); exit(EXIT_SUCCESS); } /* main */ This can be run against an Ext3 filesystem as well as against an XFS filesystem. If successful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 03:00 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns 0 access(/tmp/xxx, W_OK) returns 0 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns 0 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 If unsuccessful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 02:56 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns -1 access(/tmp/xxx, W_OK) returns -1 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns -1 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 I've also tested the fix with the SELinux and syscalls LTP testsuites. Signed-off-by: David Howells Signed-off-by: James Morris --- include/linux/capability.h | 17 ++++++++++++++-- include/linux/security.h | 49 +++++++++++++++++++++++++++++++++++++--------- kernel/capability.c | 2 +- security/capability.c | 1 + security/commoncap.c | 42 +++++++++++++++++++++++++++------------ security/root_plug.c | 1 + security/security.c | 25 +++++++++++++++++++---- security/selinux/hooks.c | 26 ++++++++++++++++++------ security/smack/smack_lsm.c | 1 + 9 files changed, 129 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/include/linux/capability.h b/include/linux/capability.h index e22f48c2a46f..5b8a13214451 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -529,8 +529,21 @@ extern const kernel_cap_t __cap_init_eff_set; * * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability(t, cap) (security_capable((t), (cap)) == 0) -#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0) +#define has_capability(t, cap) (security_task_capable((t), (cap)) == 0) + +/** + * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect, false if not, but don't write an audit message for the + * check. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +#define has_capability_noaudit(t, cap) \ + (security_task_capable_noaudit((t), (cap)) == 0) extern int capable(int cap); diff --git a/include/linux/security.h b/include/linux/security.h index 3416cb85e77b..76989b8bc34f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -48,7 +48,9 @@ struct audit_krule; * These functions are in security/capability.c and are used * as the default capabilities functions */ -extern int cap_capable(struct task_struct *tsk, int cap, int audit); +extern int cap_capable(int cap, int audit); +extern int cap_task_capable(struct task_struct *tsk, const struct cred *cred, + int cap, int audit); extern int cap_settime(struct timespec *ts, struct timezone *tz); extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); @@ -1195,9 +1197,18 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @permitted contains the permitted capability set. * Return 0 and update @new if permission is granted. * @capable: - * Check whether the @tsk process has the @cap capability. + * Check whether the current process has the @cap capability in its + * subjective/effective credentials. + * @cap contains the capability . + * @audit: Whether to write an audit message or not + * Return 0 if the capability is granted for @tsk. + * @task_capable: + * Check whether the @tsk process has the @cap capability in its + * objective/real credentials. * @tsk contains the task_struct for the process. + * @cred contains the credentials to use. * @cap contains the capability . + * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. * @acct: * Check permission before enabling or disabling process accounting. If @@ -1290,7 +1301,9 @@ struct security_operations { const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); - int (*capable) (struct task_struct *tsk, int cap, int audit); + int (*capable) (int cap, int audit); + int (*task_capable) (struct task_struct *tsk, const struct cred *cred, + int cap, int audit); int (*acct) (struct file *file); int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); @@ -1556,8 +1569,9 @@ int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable(struct task_struct *tsk, int cap); -int security_capable_noaudit(struct task_struct *tsk, int cap); +int security_capable(int cap); +int security_task_capable(struct task_struct *tsk, int cap); +int security_task_capable_noaudit(struct task_struct *tsk, int cap); int security_acct(struct file *file); int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); @@ -1754,14 +1768,31 @@ static inline int security_capset(struct cred *new, return cap_capset(new, old, effective, inheritable, permitted); } -static inline int security_capable(struct task_struct *tsk, int cap) +static inline int security_capable(int cap) { - return cap_capable(tsk, cap, SECURITY_CAP_AUDIT); + return cap_capable(cap, SECURITY_CAP_AUDIT); } -static inline int security_capable_noaudit(struct task_struct *tsk, int cap) +static inline int security_task_capable(struct task_struct *tsk, int cap) { - return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT); + int ret; + + rcu_read_lock(); + ret = cap_task_capable(tsk, __task_cred(tsk), cap, SECURITY_CAP_AUDIT); + rcu_read_unlock(); + return ret; +} + +static inline +int security_task_capable_noaudit(struct task_struct *tsk, int cap) +{ + int ret; + + rcu_read_lock(); + ret = cap_task_capable(tsk, __task_cred(tsk), cap, + SECURITY_CAP_NOAUDIT); + rcu_read_unlock(); + return ret; } static inline int security_acct(struct file *file) diff --git a/kernel/capability.c b/kernel/capability.c index 36b4b4daebec..df62f53f84ac 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -308,7 +308,7 @@ int capable(int cap) BUG(); } - if (has_capability(current, cap)) { + if (security_capable(cap) == 0) { current->flags |= PF_SUPERPRIV; return 1; } diff --git a/security/capability.c b/security/capability.c index 2dce66fcb992..fd1493da4f8d 100644 --- a/security/capability.c +++ b/security/capability.c @@ -826,6 +826,7 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, capset); set_to_cap_if_null(ops, acct); set_to_cap_if_null(ops, capable); + set_to_cap_if_null(ops, task_capable); set_to_cap_if_null(ops, quotactl); set_to_cap_if_null(ops, quota_on); set_to_cap_if_null(ops, sysctl); diff --git a/security/commoncap.c b/security/commoncap.c index 79713545cd63..7f0b2a68717d 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -43,28 +43,44 @@ int cap_netlink_recv(struct sk_buff *skb, int cap) EXPORT_SYMBOL(cap_netlink_recv); /** - * cap_capable - Determine whether a task has a particular effective capability - * @tsk: The task to query + * cap_capable - Determine whether current has a particular effective capability * @cap: The capability to check for * @audit: Whether to write an audit message or not * * Determine whether the nominated task has the specified capability amongst - * its effective set, returning 0 if it does, -ve if it does not. + * its effective set, returning 0 if it does, -ve if it does not. Note that + * this uses current's subjective/effective credentials. * * NOTE WELL: cap_capable() cannot be used like the kernel's capable() * function. That is, it has the reverse semantics: cap_capable() returns 0 * when a task has a capability, but the kernel's capable() returns 1 for this * case. */ -int cap_capable(struct task_struct *tsk, int cap, int audit) +int cap_capable(int cap, int audit) { - __u32 cap_raised; + return cap_raised(current_cap(), cap) ? 0 : -EPERM; +} - /* Derived from include/linux/sched.h:capable. */ - rcu_read_lock(); - cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); - rcu_read_unlock(); - return cap_raised ? 0 : -EPERM; +/** + * cap_has_capability - Determine whether a task has a particular effective capability + * @tsk: The task to query + * @cred: The credentials to use + * @cap: The capability to check for + * @audit: Whether to write an audit message or not + * + * Determine whether the nominated task has the specified capability amongst + * its effective set, returning 0 if it does, -ve if it does not. Note that + * this uses the task's objective/real credentials. + * + * NOTE WELL: cap_has_capability() cannot be used like the kernel's + * has_capability() function. That is, it has the reverse semantics: + * cap_has_capability() returns 0 when a task has a capability, but the + * kernel's has_capability() returns 1 for this case. + */ +int cap_task_capable(struct task_struct *tsk, const struct cred *cred, int cap, + int audit) +{ + return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; } /** @@ -160,7 +176,7 @@ static inline int cap_inh_is_capped(void) /* they are so limited unless the current task has the CAP_SETPCAP * capability */ - if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + if (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) return 0; #endif return 1; @@ -869,7 +885,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ - || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ + || (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ /* * [1] no changing of bits that are locked * [2] no unlocking of locks @@ -950,7 +966,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { int cap_sys_admin = 0; - if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) + if (cap_capable(CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) cap_sys_admin = 1; return __vm_enough_memory(mm, pages, cap_sys_admin); } diff --git a/security/root_plug.c b/security/root_plug.c index 40fb4f15e27b..559578f8ac66 100644 --- a/security/root_plug.c +++ b/security/root_plug.c @@ -77,6 +77,7 @@ static struct security_operations rootplug_security_ops = { .capget = cap_capget, .capset = cap_capset, .capable = cap_capable, + .task_capable = cap_task_capable, .bprm_set_creds = cap_bprm_set_creds, diff --git a/security/security.c b/security/security.c index d85dbb37c972..9bbc8e57b8c6 100644 --- a/security/security.c +++ b/security/security.c @@ -154,14 +154,31 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable(struct task_struct *tsk, int cap) +int security_capable(int cap) { - return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT); + return security_ops->capable(cap, SECURITY_CAP_AUDIT); } -int security_capable_noaudit(struct task_struct *tsk, int cap) +int security_task_capable(struct task_struct *tsk, int cap) { - return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT); + const struct cred *cred; + int ret; + + cred = get_task_cred(tsk); + ret = security_ops->task_capable(tsk, cred, cap, SECURITY_CAP_AUDIT); + put_cred(cred); + return ret; +} + +int security_task_capable_noaudit(struct task_struct *tsk, int cap) +{ + const struct cred *cred; + int ret; + + cred = get_task_cred(tsk); + ret = security_ops->task_capable(tsk, cred, cap, SECURITY_CAP_NOAUDIT); + put_cred(cred); + return ret; } int security_acct(struct file *file) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index df30a7555d8a..eb6c45107a05 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1433,12 +1433,13 @@ static int current_has_perm(const struct task_struct *tsk, /* Check whether a task is allowed to use a capability. */ static int task_has_capability(struct task_struct *tsk, + const struct cred *cred, int cap, int audit) { struct avc_audit_data ad; struct av_decision avd; u16 sclass; - u32 sid = task_sid(tsk); + u32 sid = cred_sid(cred); u32 av = CAP_TO_MASK(cap); int rc; @@ -1865,15 +1866,27 @@ static int selinux_capset(struct cred *new, const struct cred *old, return cred_has_perm(old, new, PROCESS__SETCAP); } -static int selinux_capable(struct task_struct *tsk, int cap, int audit) +static int selinux_capable(int cap, int audit) +{ + int rc; + + rc = secondary_ops->capable(cap, audit); + if (rc) + return rc; + + return task_has_capability(current, current_cred(), cap, audit); +} + +static int selinux_task_capable(struct task_struct *tsk, + const struct cred *cred, int cap, int audit) { int rc; - rc = secondary_ops->capable(tsk, cap, audit); + rc = secondary_ops->task_capable(tsk, cred, cap, audit); if (rc) return rc; - return task_has_capability(tsk, cap, audit); + return task_has_capability(tsk, cred, cap, audit); } static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -2037,7 +2050,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) { int rc, cap_sys_admin = 0; - rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); + rc = selinux_capable(CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); if (rc == 0) cap_sys_admin = 1; @@ -2880,7 +2893,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name * and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); + error = selinux_capable(CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); if (!error) error = security_sid_to_context_force(isec->sid, &context, &size); @@ -5568,6 +5581,7 @@ static struct security_operations selinux_ops = { .capset = selinux_capset, .sysctl = selinux_sysctl, .capable = selinux_capable, + .task_capable = selinux_task_capable, .quotactl = selinux_quotactl, .quota_on = selinux_quota_on, .syslog = selinux_syslog, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 6bfaba6177c2..7f12cc7015b6 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2827,6 +2827,7 @@ struct security_operations smack_ops = { .capget = cap_capget, .capset = cap_capset, .capable = cap_capable, + .task_capable = cap_task_capable, .syslog = smack_syslog, .settime = cap_settime, .vm_enough_memory = cap_vm_enough_memory, -- cgit v1.2.3-71-gd317 From ea7d3fef4222cd98556a0b386598268d4dbf6670 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 4 Jan 2009 13:03:02 -0800 Subject: rcu: eliminate synchronize_rcu_xxx macro Impact: cleanup Expand macro into two files. The synchronize_rcu_xxx macro is quite ugly and it's only used by two callers, so expand it instead. This makes this code easier to change. Signed-off-by: Andi Kleen Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar --- include/linux/rcupdate.h | 12 ------------ kernel/rcupdate.c | 11 +++++++++-- kernel/rcupreempt.c | 11 ++++++++++- 3 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 1168fbcea8d4..921340a7b71c 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -204,18 +204,6 @@ struct rcu_synchronize { extern void wakeme_after_rcu(struct rcu_head *head); -#define synchronize_rcu_xxx(name, func) \ -void name(void) \ -{ \ - struct rcu_synchronize rcu; \ - \ - init_completion(&rcu.completion); \ - /* Will wake me after RCU finished. */ \ - func(&rcu.head, wakeme_after_rcu); \ - /* Wait for it. */ \ - wait_for_completion(&rcu.completion); \ -} - /** * synchronize_sched - block until all CPUs have exited any non-preemptive * kernel code sequences. diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index ad63af8b2521..d92a76a881aa 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -77,8 +77,15 @@ void wakeme_after_rcu(struct rcu_head *head) * sections are delimited by rcu_read_lock() and rcu_read_unlock(), * and may be nested. */ -void synchronize_rcu(void); /* Makes kernel-doc tools happy */ -synchronize_rcu_xxx(synchronize_rcu, call_rcu) +void synchronize_rcu(void) +{ + struct rcu_synchronize rcu; + init_completion(&rcu.completion); + /* Will wake me after RCU finished. */ + call_rcu(&rcu.head, wakeme_after_rcu); + /* Wait for it. */ + wait_for_completion(&rcu.completion); +} EXPORT_SYMBOL_GPL(synchronize_rcu); static void rcu_barrier_callback(struct rcu_head *notused) diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index f9dc8f3720f6..33cfc50781f9 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -1177,7 +1177,16 @@ EXPORT_SYMBOL_GPL(call_rcu_sched); * in -rt this does -not- necessarily result in all currently executing * interrupt -handlers- having completed. */ -synchronize_rcu_xxx(__synchronize_sched, call_rcu_sched) +void __synchronize_sched(void) +{ + struct rcu_synchronize rcu; + + init_completion(&rcu.completion); + /* Will wake me after RCU finished. */ + call_rcu_sched(&rcu.head, wakeme_after_rcu); + /* Wait for it. */ + wait_for_completion(&rcu.completion); +} EXPORT_SYMBOL_GPL(__synchronize_sched); /* -- cgit v1.2.3-71-gd317 From a6037b61c2f5fc99c57c15b26d7cfa58bbb34008 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 5 Jan 2009 11:28:22 +0100 Subject: hrtimer: fix recursion deadlock by re-introducing the softirq Impact: fix rare runtime deadlock There are a few sites that do: spin_lock_irq(&foo) hrtimer_start(&bar) __run_hrtimer(&bar) func() spin_lock(&foo) which obviously deadlocks. In order to avoid this, never call __run_hrtimer() from hrtimer_start*() context, but instead defer this to softirq context. Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/interrupt.h | 3 ++- kernel/hrtimer.c | 60 +++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 0702c4d7bdf0..2062833f5f7a 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -253,7 +253,8 @@ enum BLOCK_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, - RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ + HRTIMER_SOFTIRQ, + RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS }; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 8010a67cead0..b68e98f4e4c1 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -634,7 +634,6 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { } -static void __run_hrtimer(struct hrtimer *timer); /* * When High resolution timers are active, try to reprogram. Note, that in case @@ -646,13 +645,9 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, struct hrtimer_clock_base *base) { if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) { - /* - * XXX: recursion check? - * hrtimer_forward() should round up with timer granularity - * so that we never get into inf recursion here, - * it doesn't do that though - */ - __run_hrtimer(timer); + spin_unlock(&base->cpu_base->lock); + raise_softirq_irqoff(HRTIMER_SOFTIRQ); + spin_lock(&base->cpu_base->lock); return 1; } return 0; @@ -705,11 +700,6 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, } static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { } static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { } -static inline int hrtimer_reprogram(struct hrtimer *timer, - struct hrtimer_clock_base *base) -{ - return 0; -} #endif /* CONFIG_HIGH_RES_TIMERS */ @@ -780,9 +770,11 @@ EXPORT_SYMBOL_GPL(hrtimer_forward); * * The timer is inserted in expiry order. Insertion into the * red black tree is O(log(n)). Must hold the base lock. + * + * Returns 1 when the new timer is the leftmost timer in the tree. */ -static void enqueue_hrtimer(struct hrtimer *timer, - struct hrtimer_clock_base *base, int reprogram) +static int enqueue_hrtimer(struct hrtimer *timer, + struct hrtimer_clock_base *base) { struct rb_node **link = &base->active.rb_node; struct rb_node *parent = NULL; @@ -814,20 +806,8 @@ static void enqueue_hrtimer(struct hrtimer *timer, * Insert the timer to the rbtree and check whether it * replaces the first pending timer */ - if (leftmost) { - /* - * Reprogram the clock event device. When the timer is already - * expired hrtimer_enqueue_reprogram has either called the - * callback or added it to the pending list and raised the - * softirq. - * - * This is a NOP for !HIGHRES - */ - if (reprogram && hrtimer_enqueue_reprogram(timer, base)) - return; - + if (leftmost) base->first = &timer->node; - } rb_link_node(&timer->node, parent, link); rb_insert_color(&timer->node, &base->active); @@ -836,6 +816,8 @@ static void enqueue_hrtimer(struct hrtimer *timer, * state of a possibly running callback. */ timer->state |= HRTIMER_STATE_ENQUEUED; + + return leftmost; } /* @@ -912,7 +894,7 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n { struct hrtimer_clock_base *base, *new_base; unsigned long flags; - int ret; + int ret, leftmost; base = lock_hrtimer_base(timer, &flags); @@ -940,12 +922,16 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n timer_stats_hrtimer_set_start_info(timer); + leftmost = enqueue_hrtimer(timer, new_base); + /* * Only allow reprogramming if the new base is on this CPU. * (it might still be on another CPU if the timer was pending) + * + * XXX send_remote_softirq() ? */ - enqueue_hrtimer(timer, new_base, - new_base->cpu_base == &__get_cpu_var(hrtimer_bases)); + if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) + hrtimer_enqueue_reprogram(timer, new_base); unlock_hrtimer_base(timer, &flags); @@ -1163,7 +1149,7 @@ static void __run_hrtimer(struct hrtimer *timer) */ if (restart != HRTIMER_NORESTART) { BUG_ON(timer->state != HRTIMER_STATE_CALLBACK); - enqueue_hrtimer(timer, base, 0); + enqueue_hrtimer(timer, base); } timer->state &= ~HRTIMER_STATE_CALLBACK; } @@ -1277,6 +1263,11 @@ void hrtimer_peek_ahead_timers(void) local_irq_restore(flags); } +static void run_hrtimer_softirq(struct softirq_action *h) +{ + hrtimer_peek_ahead_timers(); +} + #endif /* CONFIG_HIGH_RES_TIMERS */ /* @@ -1532,7 +1523,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, * is done, which will run all expired timers and re-programm * the timer device. */ - enqueue_hrtimer(timer, new_base, 0); + enqueue_hrtimer(timer, new_base); /* Clear the migration state bit */ timer->state &= ~HRTIMER_STATE_MIGRATE; @@ -1610,6 +1601,9 @@ void __init hrtimers_init(void) hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, (void *)(long)smp_processor_id()); register_cpu_notifier(&hrtimers_nb); +#ifdef CONFIG_HIGH_RES_TIMERS + open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); +#endif } /** -- cgit v1.2.3-71-gd317 From c70f22d203fc02c805b6ed4a3483b740dc36786b Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 5 Jan 2009 19:07:50 +0800 Subject: sched: clean up arch_reinit_sched_domains() - Make arch_reinit_sched_domains() static. It was exported to be used in s390, but now rebuild_sched_domains() is used instead. - Make it return void. Signed-off-by: Li Zefan Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 - kernel/sched.c | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 38a3f4b15394..91207df702e8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -912,7 +912,6 @@ static inline struct cpumask *sched_domain_span(struct sched_domain *sd) extern void partition_sched_domains(int ndoms_new, struct cpumask *doms_new, struct sched_domain_attr *dattr_new); -extern int arch_reinit_sched_domains(void); /* Test a flag in parent sched domain */ static inline int test_sd_parent(struct sched_domain *sd, int flag) diff --git a/kernel/sched.c b/kernel/sched.c index 9a8e296959c1..c5019a5dcaa4 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7987,7 +7987,7 @@ match2: } #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) -int arch_reinit_sched_domains(void) +static void arch_reinit_sched_domains(void) { get_online_cpus(); @@ -7996,13 +7996,10 @@ int arch_reinit_sched_domains(void) rebuild_sched_domains(); put_online_cpus(); - - return 0; } static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) { - int ret; unsigned int level = 0; if (sscanf(buf, "%u", &level) != 1) @@ -8023,9 +8020,9 @@ static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) else sched_mc_power_savings = level; - ret = arch_reinit_sched_domains(); + arch_reinit_sched_domains(); - return ret ? ret : count; + return count; } #ifdef CONFIG_SCHED_MC -- cgit v1.2.3-71-gd317 From be92d7af38fb8a91f8575ab2272e00f2e51667ff Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 5 Jan 2009 14:34:42 +0100 Subject: genirq: provide irq_to_desc() to non-genirq architectures too Impact: build fix on non-genirq architectures Sam Ravnborg reported this build failure on sparc32 allmodconfig, the GPIO drivers assume the presence of irq_to_desc(): drivers/gpio/gpiolib.c: In function `gpiolib_dbg_show': drivers/gpio/gpiolib.c:1146: error: implicit declaration of function 'irq_to_desc' Add it in the !genirq case too. Reported-by: Sam Ravnborg Signed-off-by: Ingo Molnar Tested-by: Sam Ravnborg --- include/linux/irqnr.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/irqnr.h b/include/linux/irqnr.h index 5504a5c97836..86af92e9e84c 100644 --- a/include/linux/irqnr.h +++ b/include/linux/irqnr.h @@ -8,7 +8,12 @@ #ifndef CONFIG_GENERIC_HARDIRQS #include -# define nr_irqs NR_IRQS + +/* + * Wrappers for non-genirq architectures: + */ +#define nr_irqs NR_IRQS +#define irq_to_desc(irq) (&irq_desc[irq]) # define for_each_irq_desc(irq, desc) \ for (irq = 0; irq < nr_irqs; irq++) -- cgit v1.2.3-71-gd317 From bd53cbcce501b61921a1af2dddfa87c5b9923dfd Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:48 +0100 Subject: ide: add ->cur_port to struct ide_host and use it for serialized hosts * Pass 'ide_hwif_t *' instead of 'ide_hwgroup_t *' to unexpected_intr(). * Cache pointer to the port currently being serviced in ->cur_port and use it instead of hwif->hwgroup on serialized hosts. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 25 +++++++++++++++---------- include/linux/ide.h | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 1d63159fc97d..43bf43d802c3 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -726,7 +726,7 @@ void do_ide_request(struct request_queue *q) if (!ide_lock_hwgroup(hwgroup)) { ide_hwif_t *prev_port; repeat: - prev_port = hwgroup->hwif; + prev_port = hwif->host->cur_port; hwgroup->rq = NULL; if (drive->dev_flags & IDE_DFLAG_SLEEPING) { @@ -736,15 +736,17 @@ repeat: } } - if (hwif != prev_port) { + if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) && + hwif != prev_port) { /* * set nIEN for previous port, drives in the * quirk_list may not like intr setups/cleanups */ - if (hwgroup->drive->quirk_list == 0) + if (prev_port && hwgroup->drive->quirk_list == 0) prev_port->tp_ops->set_irq(prev_port, 0); + + hwif->host->cur_port = hwif; } - hwgroup->hwif = hwif; hwgroup->drive = drive; drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); @@ -976,7 +978,7 @@ void ide_timer_expiry (unsigned long data) /** * unexpected_intr - handle an unexpected IDE interrupt * @irq: interrupt line - * @hwgroup: hwgroup being processed + * @hwif: port being processed * * There's nothing really useful we can do with an unexpected interrupt, * other than reading the status register (to clear it), and logging it. @@ -1005,11 +1007,11 @@ void ide_timer_expiry (unsigned long data) * is doing the current command, but we don't know which hwif burped * mysteriously. */ - -static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) + +static void unexpected_intr(int irq, ide_hwif_t *hwif) { + ide_hwgroup_t *hwgroup = hwif->hwgroup; u8 stat; - ide_hwif_t *hwif = hwgroup->hwif; /* * handle the unexpected interrupt @@ -1044,7 +1046,7 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) * not need to override it. If you do be aware it is subtle in * places * - * hwgroup->hwif is the interface in the group currently performing + * hwif is the interface in the group currently performing * a command. hwgroup->drive is the drive and hwgroup->handler is * the IRQ handler to call. As we issue a command the handlers * step through multiple states, reassigning the handler to the @@ -1070,6 +1072,9 @@ irqreturn_t ide_intr (int irq, void *dev_id) irqreturn_t irq_ret = IRQ_NONE; int plug_device = 0; + if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) + hwif = hwif->host->cur_port; + spin_lock_irqsave(&hwgroup->lock, flags); if (!ide_ack_intr(hwif)) @@ -1099,7 +1104,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Probably not a shared PCI interrupt, * so we can safely try to do something about it: */ - unexpected_intr(irq, hwgroup); + unexpected_intr(irq, hwif); #ifdef CONFIG_BLK_DEV_IDEPCI } else { /* diff --git a/include/linux/ide.h b/include/linux/ide.h index db5ef8ae1ab9..3de13df8bcef 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -852,6 +852,7 @@ struct ide_host { unsigned int (*init_chipset)(struct pci_dev *); unsigned long host_flags; void *host_priv; + ide_hwif_t *cur_port; /* for hosts requiring serialization */ }; /* -- cgit v1.2.3-71-gd317 From ae86afaee6a1c77c7a06d81dcc3bf872204d3bec Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:48 +0100 Subject: ide: use per-port IRQ handlers Use hwif instead of hwgroup as {request,free}_irq()'s cookie, teach ide_intr() to return early for non-active serialized ports, modify unexpected_intr() accordingly and then use per-port IRQ handlers instead of per-hwgroup ones. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 58 ++++++++++++++++++++----------------------------- drivers/ide/ide-probe.c | 21 +++++++----------- drivers/ide/ide.c | 17 +-------------- include/linux/ide.h | 4 ++-- 4 files changed, 34 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 43bf43d802c3..1fc739f44154 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -723,7 +723,7 @@ void do_ide_request(struct request_queue *q) spin_unlock_irq(q->queue_lock); spin_lock_irq(&hwgroup->lock); - if (!ide_lock_hwgroup(hwgroup)) { + if (!ide_lock_hwgroup(hwgroup, hwif)) { ide_hwif_t *prev_port; repeat: prev_port = hwif->host->cur_port; @@ -1002,44 +1002,30 @@ void ide_timer_expiry (unsigned long data) * before completing the issuance of any new drive command, so we will not * be accidentally invoked as a result of any valid command completion * interrupt. - * - * Note that we must walk the entire hwgroup here. We know which hwif - * is doing the current command, but we don't know which hwif burped - * mysteriously. */ static void unexpected_intr(int irq, ide_hwif_t *hwif) { - ide_hwgroup_t *hwgroup = hwif->hwgroup; - u8 stat; - - /* - * handle the unexpected interrupt - */ - do { - if (hwif->irq == irq) { - stat = hwif->tp_ops->read_status(hwif); - - if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { - /* Try to not flood the console with msgs */ - static unsigned long last_msgtime, count; - ++count; - if (time_after(jiffies, last_msgtime + HZ)) { - last_msgtime = jiffies; - printk(KERN_ERR "%s%s: unexpected interrupt, " - "status=0x%02x, count=%ld\n", - hwif->name, - (hwif->next==hwgroup->hwif) ? "" : "(?)", stat, count); - } - } + u8 stat = hwif->tp_ops->read_status(hwif); + + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { + /* Try to not flood the console with msgs */ + static unsigned long last_msgtime, count; + ++count; + + if (time_after(jiffies, last_msgtime + HZ)) { + last_msgtime = jiffies; + printk(KERN_ERR "%s: unexpected interrupt, " + "status=0x%02x, count=%ld\n", + hwif->name, stat, count); } - } while ((hwif = hwif->next) != hwgroup->hwif); + } } /** * ide_intr - default IDE interrupt handler * @irq: interrupt number - * @dev_id: hwif group + * @dev_id: hwif * @regs: unused weirdness from the kernel irq layer * * This is the default IRQ handler for the IDE layer. You should @@ -1063,17 +1049,19 @@ static void unexpected_intr(int irq, ide_hwif_t *hwif) irqreturn_t ide_intr (int irq, void *dev_id) { - unsigned long flags; - ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id; - ide_hwif_t *hwif = hwgroup->hwif; + ide_hwif_t *hwif = (ide_hwif_t *)dev_id; + ide_hwgroup_t *hwgroup = hwif->hwgroup; ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; + unsigned long flags; ide_startstop_t startstop; irqreturn_t irq_ret = IRQ_NONE; int plug_device = 0; - if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) - hwif = hwif->host->cur_port; + if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) { + if (hwif != hwif->host->cur_port) + goto out_early; + } spin_lock_irqsave(&hwgroup->lock, flags); @@ -1172,7 +1160,7 @@ out_handled: irq_ret = IRQ_HANDLED; out: spin_unlock_irqrestore(&hwgroup->lock, flags); - +out_early: if (plug_device) ide_plug_device(drive); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index c5adb7b9c5b5..2752509531b7 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1022,6 +1022,7 @@ static int init_irq (ide_hwif_t *hwif) unsigned int index; ide_hwgroup_t *hwgroup; ide_hwif_t *match = NULL; + int sa = 0; mutex_lock(&ide_cfg_mtx); hwif->hwgroup = NULL; @@ -1076,24 +1077,18 @@ static int init_irq (ide_hwif_t *hwif) ide_ports[hwif->index] = hwif; - /* - * Allocate the irq, if not already obtained for another hwif - */ - if (!match || match->irq != hwif->irq) { - int sa = 0; #if defined(__mc68000__) - sa = IRQF_SHARED; + sa = IRQF_SHARED; #endif /* __mc68000__ */ - if (hwif->chipset == ide_pci) - sa = IRQF_SHARED; + if (hwif->chipset == ide_pci) + sa = IRQF_SHARED; - if (io_ports->ctl_addr) - hwif->tp_ops->set_irq(hwif, 1); + if (io_ports->ctl_addr) + hwif->tp_ops->set_irq(hwif, 1); - if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) - goto out_unlink; - } + if (request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwif)) + goto out_unlink; if (!hwif->rqsize) { if ((hwif->host_flags & IDE_HFLAG_NO_LBA48) || diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 46a2d4ca812b..5bc2e4782a55 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -175,10 +175,6 @@ EXPORT_SYMBOL_GPL(ide_port_unregister_devices); void ide_unregister(ide_hwif_t *hwif) { - ide_hwif_t *g; - ide_hwgroup_t *hwgroup; - int irq_count = 0; - BUG_ON(in_interrupt()); BUG_ON(irqs_disabled()); @@ -191,18 +187,7 @@ void ide_unregister(ide_hwif_t *hwif) ide_proc_unregister_port(hwif); - hwgroup = hwif->hwgroup; - /* - * free the irq if we were the only hwif using it - */ - g = hwgroup->hwif; - do { - if (g->irq == hwif->irq) - ++irq_count; - g = g->next; - } while (g != hwgroup->hwif); - if (irq_count == 1) - free_irq(hwif->irq, hwgroup); + free_irq(hwif->irq, hwif); ide_remove_port_from_hwgroup(hwif); diff --git a/include/linux/ide.h b/include/linux/ide.h index 3de13df8bcef..f5382ad0bd4c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1274,14 +1274,14 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout); extern void ide_timer_expiry(unsigned long); extern irqreturn_t ide_intr(int irq, void *dev_id); -static inline int ide_lock_hwgroup(ide_hwgroup_t *hwgroup) +static inline int ide_lock_hwgroup(ide_hwgroup_t *hwgroup, ide_hwif_t *hwif) { if (hwgroup->busy) return 1; hwgroup->busy = 1; /* for atari only */ - ide_get_lock(ide_intr, hwgroup); + ide_get_lock(ide_intr, hwif); return 0; } -- cgit v1.2.3-71-gd317 From efe0397eef544ac4bcca23d39aa8d5db154952e0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:49 +0100 Subject: ide: remove hwgroup->hwif and {drive,hwif}->next * Add 'int port_count' field to ide_hwgroup_t to keep the track of the number of ports in the hwgroup. Then update init_irq() and ide_remove_port_from_hwgroup() to use it. * Remove no longer needed hwgroup->hwif, {drive,hwif}->next, ide_add_drive_to_hwgroup() and ide_remove_drive_from_hwgroup() (hwgroup->drive now only denotes the currently active device in the hwgroup). * Update locking documentation in . While at it: * Rename ->drive field in ide_hwgroup_t to ->cur_dev. * Use __func__ in ide_timer_expiry(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 12 +++---- drivers/ide/ide-probe.c | 90 ++++--------------------------------------------- include/linux/ide.h | 13 ++----- 3 files changed, 15 insertions(+), 100 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 1fc739f44154..4ce793c05629 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -742,12 +742,12 @@ repeat: * set nIEN for previous port, drives in the * quirk_list may not like intr setups/cleanups */ - if (prev_port && hwgroup->drive->quirk_list == 0) + if (prev_port && hwgroup->cur_dev->quirk_list == 0) prev_port->tp_ops->set_irq(prev_port, 0); hwif->host->cur_port = hwif; } - hwgroup->drive = drive; + hwgroup->cur_dev = drive; drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); spin_unlock_irq(&hwgroup->lock); @@ -913,9 +913,9 @@ void ide_timer_expiry (unsigned long data) * Either way, we don't really want to complain about anything. */ } else { - drive = hwgroup->drive; + drive = hwgroup->cur_dev; if (!drive) { - printk(KERN_ERR "ide_timer_expiry: hwgroup->drive was NULL\n"); + printk(KERN_ERR "%s: ->cur_dev was NULL\n", __func__); hwgroup->handler = NULL; } else { ide_hwif_t *hwif; @@ -1033,7 +1033,7 @@ static void unexpected_intr(int irq, ide_hwif_t *hwif) * places * * hwif is the interface in the group currently performing - * a command. hwgroup->drive is the drive and hwgroup->handler is + * a command. hwgroup->cur_dev is the drive and hwgroup->handler is * the IRQ handler to call. As we issue a command the handlers * step through multiple states, reassigning the handler to the * next step in the process. Unlike a smart SCSI controller IDE @@ -1105,7 +1105,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) goto out; } - drive = hwgroup->drive; + drive = hwgroup->cur_dev; if (!drive) { /* * This should NEVER happen, and there isn't much diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 2752509531b7..68f3c87b8284 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -918,27 +918,9 @@ static int ide_init_queue(ide_drive_t *drive) return 0; } -static void ide_add_drive_to_hwgroup(ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; - - spin_lock_irq(&hwgroup->lock); - if (!hwgroup->drive) { - /* first drive for hwgroup. */ - drive->next = drive; - hwgroup->drive = drive; - hwgroup->hwif = HWIF(hwgroup->drive); - } else { - drive->next = hwgroup->drive->next; - hwgroup->drive->next = drive; - } - spin_unlock_irq(&hwgroup->lock); -} - /* * For any present drive: * - allocate the block device queue - * - link drive into the hwgroup */ static int ide_port_setup_devices(ide_hwif_t *hwif) { @@ -961,8 +943,6 @@ static int ide_port_setup_devices(ide_hwif_t *hwif) } j++; - - ide_add_drive_to_hwgroup(drive); } mutex_unlock(&ide_cfg_mtx); @@ -978,33 +958,9 @@ void ide_remove_port_from_hwgroup(ide_hwif_t *hwif) ide_ports[hwif->index] = NULL; spin_lock_irq(&hwgroup->lock); - /* - * Remove us from the hwgroup, and free - * the hwgroup if we were the only member - */ - if (hwif->next == hwif) { - BUG_ON(hwgroup->hwif != hwif); + /* Free the hwgroup if we were the only member. */ + if (--hwgroup->port_count == 0) kfree(hwgroup); - } else { - /* There is another interface in hwgroup. - * Unlink us, and set hwgroup->drive and ->hwif to - * something sane. - */ - ide_hwif_t *g = hwgroup->hwif; - - while (g->next != hwif) - g = g->next; - g->next = hwif->next; - if (hwgroup->hwif == hwif) { - /* Chose a random hwif for hwgroup->hwif. - * It's guaranteed that there are no drives - * left in the hwgroup. - */ - BUG_ON(hwgroup->drive != NULL); - hwgroup->hwif = g; - } - BUG_ON(hwgroup->hwif == hwif); - } spin_unlock_irq(&hwgroup->lock); } @@ -1044,20 +1000,9 @@ static int init_irq (ide_hwif_t *hwif) if (match) { hwgroup = match->hwgroup; hwif->hwgroup = hwgroup; - /* - * Link us into the hwgroup. - * This must be done early, do ensure that unexpected_intr - * can find the hwif and prevent irq storms. - * No drives are attached to the new hwif, choose_drive - * can't do anything stupid (yet). - * Add ourself as the 2nd entry to the hwgroup->hwif - * linked list, the first entry is the hwif that owns - * hwgroup->handler - do not change that. - */ + spin_lock_irq(&hwgroup->lock); - hwif->next = hwgroup->hwif->next; - hwgroup->hwif->next = hwif; - BUG_ON(hwif->next == hwif); + hwgroup->port_count++; spin_unlock_irq(&hwgroup->lock); } else { hwgroup = kmalloc_node(sizeof(*hwgroup), GFP_KERNEL|__GFP_ZERO, @@ -1068,7 +1013,8 @@ static int init_irq (ide_hwif_t *hwif) spin_lock_init(&hwgroup->lock); hwif->hwgroup = hwgroup; - hwgroup->hwif = hwif->next = hwif; + + hwgroup->port_count = 1; init_timer(&hwgroup->timer); hwgroup->timer.function = &ide_timer_expiry; @@ -1191,29 +1137,6 @@ void ide_init_disk(struct gendisk *disk, ide_drive_t *drive) EXPORT_SYMBOL_GPL(ide_init_disk); -static void ide_remove_drive_from_hwgroup(ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; - - if (drive == drive->next) { - /* special case: last drive from hwgroup. */ - BUG_ON(hwgroup->drive != drive); - hwgroup->drive = NULL; - } else { - ide_drive_t *walk; - - walk = hwgroup->drive; - while (walk->next != drive) - walk = walk->next; - walk->next = drive->next; - if (hwgroup->drive == drive) { - hwgroup->drive = drive->next; - hwgroup->hwif = hwgroup->drive->hwif; - } - } - BUG_ON(hwgroup->drive == drive); -} - static void drive_release_dev (struct device *dev) { ide_drive_t *drive = container_of(dev, ide_drive_t, gendev); @@ -1222,7 +1145,6 @@ static void drive_release_dev (struct device *dev) ide_proc_unregister_device(drive); spin_lock_irq(&hwgroup->lock); - ide_remove_drive_from_hwgroup(drive); kfree(drive->id); drive->id = NULL; drive->dev_flags &= ~IDE_DFLAG_PRESENT; diff --git a/include/linux/ide.h b/include/linux/ide.h index f5382ad0bd4c..8b74ccdd221c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -588,7 +588,6 @@ struct ide_drive_s { struct request_queue *queue; /* request queue */ struct request *rq; /* current request */ - struct ide_drive_s *next; /* circular list of hwgroup drives */ void *driver_data; /* extra driver data */ u16 *id; /* identification info */ #ifdef CONFIG_IDE_PROC_FS @@ -750,7 +749,6 @@ struct ide_dma_ops { struct ide_host; typedef struct hwif_s { - struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwif_s *mate; /* other hwif from same PCI chip */ struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ @@ -874,9 +872,7 @@ typedef struct hwgroup_s { unsigned int polling : 1; /* current drive */ - ide_drive_t *drive; - /* ptr to current hwif in linked-list */ - ide_hwif_t *hwif; + ide_drive_t *cur_dev; /* current request */ struct request *rq; @@ -892,6 +888,8 @@ typedef struct hwgroup_s { int req_gen_timer; spinlock_t lock; + + int port_count; } ide_hwgroup_t; typedef struct ide_driver_s ide_driver_t; @@ -1622,12 +1620,7 @@ extern struct mutex ide_cfg_mtx; /* * Structure locking: * - * ide_cfg_mtx and hwgroup->lock together protect changes to - * ide_hwif_t->next - * ide_drive_t->next - * * ide_hwgroup_t->busy: hwgroup->lock - * ide_hwgroup_t->hwif: hwgroup->lock * ide_hwif_t->{hwgroup,mate}: constant, no locking * ide_drive_t->hwif: constant, no locking */ -- cgit v1.2.3-71-gd317 From 5b31f855f10d0053e738baa6d91fb6a3fad35119 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:49 +0100 Subject: ide: use lock bitops for ports serialization (v2) * Add ->host_busy field to struct ide_host and use it's first bit together with lock bitops to provide new ports serialization method. * Convert core IDE code to use new ide_[un]lock_host() helpers. This removes the need for taking hwgroup->lock if host is already busy on serialized hosts and makes it possible to merge ide_hwgroup_t into ide_hwif_t (done in the later patch). * Remove no longer needed ide_hwgroup_t.busy and ide_[un]lock_hwgroup(). * Update do_ide_request() documentation. v2: * ide_release_lock() should be called inside IDE_HFLAG_SERIALIZE check. * Add ide_hwif_t.busy flag and ide_[un]lock_port() for serializing devices on a port. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 105 ++++++++++++++++++++++++++++++--------------------- include/linux/ide.h | 35 +++-------------- 2 files changed, 69 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 4ce793c05629..8f371821c614 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -666,45 +666,54 @@ void ide_stall_queue (ide_drive_t *drive, unsigned long timeout) } EXPORT_SYMBOL(ide_stall_queue); +static inline int ide_lock_port(ide_hwif_t *hwif) +{ + if (hwif->busy) + return 1; + + hwif->busy = 1; + + return 0; +} + +static inline void ide_unlock_port(ide_hwif_t *hwif) +{ + hwif->busy = 0; +} + +static inline int ide_lock_host(struct ide_host *host, ide_hwif_t *hwif) +{ + int rc = 0; + + if (host->host_flags & IDE_HFLAG_SERIALIZE) { + rc = test_and_set_bit_lock(IDE_HOST_BUSY, &host->host_busy); + if (rc == 0) { + /* for atari only */ + ide_get_lock(ide_intr, hwif); + } + } + return rc; +} + +static inline void ide_unlock_host(struct ide_host *host) +{ + if (host->host_flags & IDE_HFLAG_SERIALIZE) { + /* for atari only */ + ide_release_lock(); + clear_bit_unlock(IDE_HOST_BUSY, &host->host_busy); + } +} + /* * Issue a new request to a drive from hwgroup - * - * A hwgroup is a serialized group of IDE interfaces. Usually there is - * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640) - * may have both interfaces in a single hwgroup to "serialize" access. - * Or possibly multiple ISA interfaces can share a common IRQ by being grouped - * together into one hwgroup for serialized access. - * - * Note also that several hwgroups can end up sharing a single IRQ, - * possibly along with many other devices. This is especially common in - * PCI-based systems with off-board IDE controller cards. - * - * The IDE driver uses a per-hwgroup lock to protect the hwgroup->busy flag. - * - * The first thread into the driver for a particular hwgroup sets the - * hwgroup->busy flag to indicate that this hwgroup is now active, - * and then initiates processing of the top request from the request queue. - * - * Other threads attempting entry notice the busy setting, and will simply - * queue their new requests and exit immediately. Note that hwgroup->busy - * remains set even when the driver is merely awaiting the next interrupt. - * Thus, the meaning is "this hwgroup is busy processing a request". - * - * When processing of a request completes, the completing thread or IRQ-handler - * will start the next request from the queue. If no more work remains, - * the driver will clear the hwgroup->busy flag and exit. - * - * The per-hwgroup spinlock is used to protect all access to the - * hwgroup->busy flag, but is otherwise not needed for most processing in - * the driver. This makes the driver much more friendlier to shared IRQs - * than previous designs, while remaining 100% (?) SMP safe and capable. */ void do_ide_request(struct request_queue *q) { ide_drive_t *drive = q->queuedata; ide_hwif_t *hwif = drive->hwif; + struct ide_host *host = hwif->host; ide_hwgroup_t *hwgroup = hwif->hwgroup; - struct request *rq; + struct request *rq = NULL; ide_startstop_t startstop; /* @@ -721,9 +730,13 @@ void do_ide_request(struct request_queue *q) blk_remove_plug(q); spin_unlock_irq(q->queue_lock); + + if (ide_lock_host(host, hwif)) + goto plug_device_2; + spin_lock_irq(&hwgroup->lock); - if (!ide_lock_hwgroup(hwgroup, hwif)) { + if (!ide_lock_port(hwif)) { ide_hwif_t *prev_port; repeat: prev_port = hwif->host->cur_port; @@ -731,7 +744,7 @@ repeat: if (drive->dev_flags & IDE_DFLAG_SLEEPING) { if (time_before(drive->sleep, jiffies)) { - ide_unlock_hwgroup(hwgroup); + ide_unlock_port(hwif); goto plug_device; } } @@ -761,7 +774,7 @@ repeat: spin_lock_irq(&hwgroup->lock); if (!rq) { - ide_unlock_hwgroup(hwgroup); + ide_unlock_port(hwif); goto out; } @@ -782,7 +795,7 @@ repeat: blk_pm_request(rq) == 0 && (rq->cmd_flags & REQ_PREEMPT) == 0) { /* there should be no pending command at this point */ - ide_unlock_hwgroup(hwgroup); + ide_unlock_port(hwif); goto plug_device; } @@ -798,11 +811,15 @@ repeat: goto plug_device; out: spin_unlock_irq(&hwgroup->lock); + if (rq == NULL) + ide_unlock_host(host); spin_lock_irq(q->queue_lock); return; plug_device: spin_unlock_irq(&hwgroup->lock); + ide_unlock_host(host); +plug_device_2: spin_lock_irq(q->queue_lock); if (!elv_queue_empty(q)) @@ -844,9 +861,9 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) ide_dma_off_quietly(drive); /* - * un-busy drive etc (hwgroup->busy is cleared on return) and - * make sure request is sane + * un-busy drive etc and make sure request is sane */ + rq = HWGROUP(drive)->rq; if (!rq) @@ -895,6 +912,7 @@ static void ide_plug_device(ide_drive_t *drive) void ide_timer_expiry (unsigned long data) { ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; + ide_hwif_t *uninitialized_var(hwif); ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; ide_expiry_t *expiry; @@ -918,7 +936,6 @@ void ide_timer_expiry (unsigned long data) printk(KERN_ERR "%s: ->cur_dev was NULL\n", __func__); hwgroup->handler = NULL; } else { - ide_hwif_t *hwif; ide_startstop_t startstop = ide_stopped; if ((expiry = hwgroup->expiry) != NULL) { @@ -964,15 +981,17 @@ void ide_timer_expiry (unsigned long data) spin_lock_irq(&hwgroup->lock); enable_irq(hwif->irq); if (startstop == ide_stopped) { - ide_unlock_hwgroup(hwgroup); + ide_unlock_port(hwif); plug_device = 1; } } } spin_unlock_irqrestore(&hwgroup->lock, flags); - if (plug_device) + if (plug_device) { + ide_unlock_host(hwif->host); ide_plug_device(drive); + } } /** @@ -1150,7 +1169,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) */ if (startstop == ide_stopped) { if (hwgroup->handler == NULL) { /* paranoia */ - ide_unlock_hwgroup(hwgroup); + ide_unlock_port(hwif); plug_device = 1; } else printk(KERN_ERR "%s: %s: huh? expected NULL handler " @@ -1161,8 +1180,10 @@ out_handled: out: spin_unlock_irqrestore(&hwgroup->lock, flags); out_early: - if (plug_device) + if (plug_device) { + ide_unlock_host(hwif->host); ide_plug_device(drive); + } return irq_ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 8b74ccdd221c..00df155b5a02 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -828,6 +828,7 @@ typedef struct hwif_s { unsigned present : 1; /* this interface exists */ unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */ + unsigned busy : 1; /* serializes devices on a port */ struct device gendev; struct device *portdev; @@ -851,8 +852,13 @@ struct ide_host { unsigned long host_flags; void *host_priv; ide_hwif_t *cur_port; /* for hosts requiring serialization */ + + /* used for hosts requiring serialization */ + volatile long host_busy; }; +#define IDE_HOST_BUSY 0 + /* * internal ide interrupt handler type */ @@ -866,8 +872,6 @@ typedef struct hwgroup_s { /* irq handler, if active */ ide_startstop_t (*handler)(ide_drive_t *); - /* BOOL: protects all fields below */ - volatile int busy; /* BOOL: polling active & poll_timeout field valid */ unsigned int polling : 1; @@ -1271,26 +1275,6 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout); extern void ide_timer_expiry(unsigned long); extern irqreturn_t ide_intr(int irq, void *dev_id); - -static inline int ide_lock_hwgroup(ide_hwgroup_t *hwgroup, ide_hwif_t *hwif) -{ - if (hwgroup->busy) - return 1; - - hwgroup->busy = 1; - /* for atari only */ - ide_get_lock(ide_intr, hwif); - - return 0; -} - -static inline void ide_unlock_hwgroup(ide_hwgroup_t *hwgroup) -{ - /* for atari only */ - ide_release_lock(); - hwgroup->busy = 0; -} - extern void do_ide_request(struct request_queue *); void ide_init_disk(struct gendisk *, ide_drive_t *); @@ -1617,13 +1601,6 @@ static inline void ide_set_max_pio(ide_drive_t *drive) extern spinlock_t ide_lock; extern struct mutex ide_cfg_mtx; -/* - * Structure locking: - * - * ide_hwgroup_t->busy: hwgroup->lock - * ide_hwif_t->{hwgroup,mate}: constant, no locking - * ide_drive_t->hwif: constant, no locking - */ #define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) -- cgit v1.2.3-71-gd317 From b65fac32cfe3b2f98cd472fef400bd1c1340de23 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:50 +0100 Subject: ide: merge ide_hwgroup_t with ide_hwif_t (v2) * Merge ide_hwgroup_t with ide_hwif_t. * Cleanup init_irq() accordingly, then remove no longer needed ide_remove_port_from_hwgroup() and ide_ports[]. * Remove now unused HWGROUP() macro. While at it: * ide_dump_ata_error() fixups v2: * Fix ->quirk_list check in do_ide_request() (s/hwif->cur_dev/prev_port->cur_dev). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/alim15x3.c | 2 +- drivers/ide/au1xxx-ide.c | 6 +-- drivers/ide/icside.c | 2 +- drivers/ide/ide-atapi.c | 8 ++-- drivers/ide/ide-cd.c | 16 +++---- drivers/ide/ide-dma-sff.c | 4 +- drivers/ide/ide-dma.c | 2 +- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-io.c | 117 ++++++++++++++++++++++----------------------- drivers/ide/ide-iops.c | 69 +++++++++++++------------- drivers/ide/ide-lib.c | 9 ++-- drivers/ide/ide-park.c | 16 +++---- drivers/ide/ide-pm.c | 2 +- drivers/ide/ide-probe.c | 87 +++++---------------------------- drivers/ide/ide-tape.c | 9 ++-- drivers/ide/ide-taskfile.c | 4 +- drivers/ide/ide.c | 7 +-- drivers/ide/pdc202xx_old.c | 2 +- drivers/ide/pmac.c | 2 +- drivers/ide/scc_pata.c | 4 +- drivers/ide/sgiioc4.c | 2 +- drivers/ide/tc86c001.c | 10 ++-- drivers/ide/trm290.c | 2 +- drivers/ide/tx4939ide.c | 2 +- drivers/ide/umc8672.c | 13 +++-- include/linux/ide.h | 55 ++++++++++----------- 26 files changed, 188 insertions(+), 266 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index 45d2356bb725..290204e89eaf 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -198,7 +198,7 @@ static void ali_set_dma_mode(ide_drive_t *drive, const u8 speed) static int ali15x3_dma_setup(ide_drive_t *drive) { if (m5229_revision < 0xC2 && drive->media != ide_disk) { - if (rq_data_dir(drive->hwif->hwgroup->rq)) + if (rq_data_dir(drive->hwif->rq)) return 1; /* try PIO instead of DMA */ } return ide_dma_setup(drive); diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 0ec8fd1e4dcb..db412bc772d1 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -213,7 +213,7 @@ static int auide_build_dmatable(ide_drive_t *drive) { int i, iswrite, count = 0; ide_hwif_t *hwif = HWIF(drive); - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = hwif->rq; _auide_hwif *ahwif = &auide_hwif; struct scatterlist *sg; @@ -309,8 +309,8 @@ static void auide_dma_exec_cmd(ide_drive_t *drive, u8 command) } static int auide_dma_setup(ide_drive_t *drive) -{ - struct request *rq = HWGROUP(drive)->rq; +{ + struct request *rq = drive->hwif->rq; if (!auide_build_dmatable(drive)) { ide_map_sg(drive, rq); diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 81f70caeb40f..72d95c99f147 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -312,7 +312,7 @@ static int icside_dma_setup(ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); struct expansion_card *ec = ECARD_DEV(hwif->dev); struct icside_state *state = ecard_get_drvdata(ec); - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; unsigned int dma_mode; if (rq_data_dir(rq)) diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index e8688c0f8645..d6a50d67b7db 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -243,7 +243,7 @@ EXPORT_SYMBOL_GPL(ide_retry_pc); int ide_cd_expiry(ide_drive_t *drive) { - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = drive->hwif->rq; unsigned long wait = 0; debug_log("%s: rq->cmd[0]: 0x%x\n", __func__, rq->cmd[0]); @@ -294,7 +294,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; unsigned int timeout, temp; @@ -491,7 +491,7 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) { struct ide_atapi_pc *uninitialized_var(pc); ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; ide_expiry_t *expiry; unsigned int timeout; int cmd_len; @@ -580,7 +580,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive) if (dev_is_idecd(drive)) { tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; - bcount = ide_cd_get_xferlen(hwif->hwgroup->rq); + bcount = ide_cd_get_xferlen(hwif->rq); expiry = ide_cd_expiry; timeout = ATAPI_WAIT_PC; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 1a7410f88249..c35b64d495f5 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -239,7 +239,7 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, static void cdrom_end_request(ide_drive_t *drive, int uptodate) { - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = drive->hwif->rq; int nsectors = rq->hard_cur_sectors; ide_debug_log(IDE_DBG_FUNC, "Call %s, cmd: 0x%x, uptodate: 0x%x, " @@ -306,8 +306,7 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; - struct request *rq = hwgroup->rq; + struct request *rq = hwif->rq; int stat, err, sense_key; /* check for errors */ @@ -502,7 +501,7 @@ end_request: blkdev_dequeue_request(rq); spin_unlock_irqrestore(q->queue_lock, flags); - hwgroup->rq = NULL; + hwif->rq = NULL; cdrom_queue_request_sense(drive, rq->sense, rq); } else @@ -525,7 +524,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *); static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; int xferlen; xferlen = ide_cd_get_xferlen(rq); @@ -567,7 +566,7 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive) static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; int cmd_len; ide_startstop_t startstop; @@ -854,8 +853,7 @@ static int cdrom_newpc_intr_dummy_cb(struct request *rq) static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; - struct request *rq = hwgroup->rq; + struct request *rq = hwif->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; int dma_error = 0, dma, stat, thislen, uptodate = 0; @@ -1061,7 +1059,7 @@ end_request: if (blk_end_request(rq, 0, dlen)) BUG(); - hwgroup->rq = NULL; + hwif->rq = NULL; } else { if (!uptodate) rq->cmd_flags |= REQ_FAILED; diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index f6d2d44d8a9a..623a82d1535d 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -175,7 +175,7 @@ EXPORT_SYMBOL_GPL(ide_build_dmatable); int ide_dma_setup(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; unsigned int reading = rq_data_dir(rq) ? 0 : ATA_DMA_WR; u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; u8 dma_stat; @@ -240,7 +240,7 @@ static int dma_timer_expiry(ide_drive_t *drive) if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ return WAIT_CMD; - hwif->hwgroup->expiry = NULL; /* one free ride for now */ + hwif->expiry = NULL; /* one free ride for now */ if (dma_stat & ATA_DMA_ERR) /* ERROR */ return -1; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index fffd11717b2d..72ebab0bc755 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -96,7 +96,7 @@ ide_startstop_t ide_dma_intr(ide_drive_t *drive) if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) { if (!dma_stat) { - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; task_end_request(drive, rq, stat); return ide_stopped; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 0a48e2dc53a2..3eab1c6c9b31 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -71,7 +71,7 @@ static int ide_floppy_end_request(ide_drive_t *drive, int uptodate, int nsecs) { struct ide_disk_obj *floppy = drive->driver_data; - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = drive->hwif->rq; int error; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 8f371821c614..6ff82d7055b9 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -88,7 +88,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq, ret = 0; if (ret == 0 && dequeue) - drive->hwif->hwgroup->rq = NULL; + drive->hwif->rq = NULL; return ret; } @@ -107,7 +107,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq, int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors) { unsigned int nr_bytes = nr_sectors << 9; - struct request *rq = drive->hwif->hwgroup->rq; + struct request *rq = drive->hwif->rq; if (!nr_bytes) { if (blk_pc_request(rq)) @@ -160,8 +160,8 @@ EXPORT_SYMBOL_GPL(ide_end_dequeued_request); void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) { - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; - struct request *rq = hwgroup->rq; + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->rq; if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *task = (ide_task_t *)rq->special; @@ -186,7 +186,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) return; } - hwgroup->rq = NULL; + hwif->rq = NULL; rq->errors = err; @@ -321,7 +321,8 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) err = ide_dump_status(drive, msg, stat); - if ((rq = HWGROUP(drive)->rq) == NULL) + rq = drive->hwif->rq; + if (rq == NULL) return ide_stopped; /* retry only "normal" I/O: */ @@ -654,7 +655,7 @@ kill_rq: * @timeout: time to stall for (jiffies) * * ide_stall_queue() can be used by a drive to give excess bandwidth back - * to the hwgroup by sleeping for timeout jiffies. + * to the port by sleeping for timeout jiffies. */ void ide_stall_queue (ide_drive_t *drive, unsigned long timeout) @@ -705,14 +706,13 @@ static inline void ide_unlock_host(struct ide_host *host) } /* - * Issue a new request to a drive from hwgroup + * Issue a new request to a device. */ void do_ide_request(struct request_queue *q) { ide_drive_t *drive = q->queuedata; ide_hwif_t *hwif = drive->hwif; struct ide_host *host = hwif->host; - ide_hwgroup_t *hwgroup = hwif->hwgroup; struct request *rq = NULL; ide_startstop_t startstop; @@ -734,13 +734,13 @@ void do_ide_request(struct request_queue *q) if (ide_lock_host(host, hwif)) goto plug_device_2; - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); if (!ide_lock_port(hwif)) { ide_hwif_t *prev_port; repeat: prev_port = hwif->host->cur_port; - hwgroup->rq = NULL; + hwif->rq = NULL; if (drive->dev_flags & IDE_DFLAG_SLEEPING) { if (time_before(drive->sleep, jiffies)) { @@ -755,15 +755,15 @@ repeat: * set nIEN for previous port, drives in the * quirk_list may not like intr setups/cleanups */ - if (prev_port && hwgroup->cur_dev->quirk_list == 0) + if (prev_port && prev_port->cur_dev->quirk_list == 0) prev_port->tp_ops->set_irq(prev_port, 0); hwif->host->cur_port = hwif; } - hwgroup->cur_dev = drive; + hwif->cur_dev = drive; drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); spin_lock_irq(q->queue_lock); /* * we know that the queue isn't empty, but this can happen @@ -771,7 +771,7 @@ repeat: */ rq = elv_next_request(drive->queue); spin_unlock_irq(q->queue_lock); - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); if (!rq) { ide_unlock_port(hwif); @@ -799,25 +799,25 @@ repeat: goto plug_device; } - hwgroup->rq = rq; + hwif->rq = rq; - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); startstop = start_request(drive, rq); - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); if (startstop == ide_stopped) goto repeat; } else goto plug_device; out: - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); if (rq == NULL) ide_unlock_host(host); spin_lock_irq(q->queue_lock); return; plug_device: - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); ide_unlock_host(host); plug_device_2: spin_lock_irq(q->queue_lock); @@ -827,7 +827,7 @@ plug_device_2: } /* - * un-busy the hwgroup etc, and clear any pending DMA status. we want to + * un-busy the port etc, and clear any pending DMA status. we want to * retry the current request in pio mode instead of risking tossing it * all away */ @@ -864,12 +864,11 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) * un-busy drive etc and make sure request is sane */ - rq = HWGROUP(drive)->rq; - + rq = hwif->rq; if (!rq) goto out; - HWGROUP(drive)->rq = NULL; + hwif->rq = NULL; rq->errors = 0; @@ -897,7 +896,7 @@ static void ide_plug_device(ide_drive_t *drive) /** * ide_timer_expiry - handle lack of an IDE interrupt - * @data: timer callback magic (hwgroup) + * @data: timer callback magic (hwif) * * An IDE command has timed out before the expected drive return * occurred. At this point we attempt to clean up the current @@ -911,19 +910,18 @@ static void ide_plug_device(ide_drive_t *drive) void ide_timer_expiry (unsigned long data) { - ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; - ide_hwif_t *uninitialized_var(hwif); + ide_hwif_t *hwif = (ide_hwif_t *)data; ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; - ide_expiry_t *expiry; unsigned long flags; unsigned long wait = -1; int plug_device = 0; - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); + + handler = hwif->handler; - if (((handler = hwgroup->handler) == NULL) || - (hwgroup->req_gen != hwgroup->req_gen_timer)) { + if (handler == NULL || hwif->req_gen != hwif->req_gen_timer) { /* * Either a marginal timeout occurred * (got the interrupt just as timer expired), @@ -931,38 +929,39 @@ void ide_timer_expiry (unsigned long data) * Either way, we don't really want to complain about anything. */ } else { - drive = hwgroup->cur_dev; + drive = hwif->cur_dev; if (!drive) { printk(KERN_ERR "%s: ->cur_dev was NULL\n", __func__); - hwgroup->handler = NULL; + hwif->handler = NULL; } else { + ide_expiry_t *expiry = hwif->expiry; ide_startstop_t startstop = ide_stopped; - if ((expiry = hwgroup->expiry) != NULL) { + if (expiry) { /* continue */ if ((wait = expiry(drive)) > 0) { /* reset timer */ - hwgroup->timer.expires = jiffies + wait; - hwgroup->req_gen_timer = hwgroup->req_gen; - add_timer(&hwgroup->timer); - spin_unlock_irqrestore(&hwgroup->lock, flags); + hwif->timer.expires = jiffies + wait; + hwif->req_gen_timer = hwif->req_gen; + add_timer(&hwif->timer); + spin_unlock_irqrestore(&hwif->lock, flags); return; } } - hwgroup->handler = NULL; + hwif->handler = NULL; /* * We need to simulate a real interrupt when invoking * the handler() function, which means we need to * globally mask the specific IRQ: */ - spin_unlock(&hwgroup->lock); + spin_unlock(&hwif->lock); hwif = HWIF(drive); /* disable_irq_nosync ?? */ disable_irq(hwif->irq); /* local CPU only, * as if we were handling an interrupt */ local_irq_disable(); - if (hwgroup->polling) { + if (hwif->polling) { startstop = handler(drive); } else if (drive_is_ready(drive)) { if (drive->waiting_for_dma) @@ -978,7 +977,7 @@ void ide_timer_expiry (unsigned long data) ide_error(drive, "irq timeout", hwif->tp_ops->read_status(hwif)); } - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); enable_irq(hwif->irq); if (startstop == ide_stopped) { ide_unlock_port(hwif); @@ -986,7 +985,7 @@ void ide_timer_expiry (unsigned long data) } } } - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); if (plug_device) { ide_unlock_host(hwif->host); @@ -1052,7 +1051,7 @@ static void unexpected_intr(int irq, ide_hwif_t *hwif) * places * * hwif is the interface in the group currently performing - * a command. hwgroup->cur_dev is the drive and hwgroup->handler is + * a command. hwif->cur_dev is the drive and hwif->handler is * the IRQ handler to call. As we issue a command the handlers * step through multiple states, reassigning the handler to the * next step in the process. Unlike a smart SCSI controller IDE @@ -1063,13 +1062,12 @@ static void unexpected_intr(int irq, ide_hwif_t *hwif) * * The handler eventually returns ide_stopped to indicate the * request completed. At this point we issue the next request - * on the hwgroup and the process begins again. + * on the port and the process begins again. */ - + irqreturn_t ide_intr (int irq, void *dev_id) { ide_hwif_t *hwif = (ide_hwif_t *)dev_id; - ide_hwgroup_t *hwgroup = hwif->hwgroup; ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; unsigned long flags; @@ -1082,12 +1080,14 @@ irqreturn_t ide_intr (int irq, void *dev_id) goto out_early; } - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); if (!ide_ack_intr(hwif)) goto out; - if ((handler = hwgroup->handler) == NULL || hwgroup->polling) { + handler = hwif->handler; + + if (handler == NULL || hwif->polling) { /* * Not expecting an interrupt from this drive. * That means this could be: @@ -1124,7 +1124,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) goto out; } - drive = hwgroup->cur_dev; + drive = hwif->cur_dev; if (!drive) { /* * This should NEVER happen, and there isn't much @@ -1145,10 +1145,10 @@ irqreturn_t ide_intr (int irq, void *dev_id) */ goto out; - hwgroup->handler = NULL; - hwgroup->req_gen++; - del_timer(&hwgroup->timer); - spin_unlock(&hwgroup->lock); + hwif->handler = NULL; + hwif->req_gen++; + del_timer(&hwif->timer); + spin_unlock(&hwif->lock); if (hwif->port_ops && hwif->port_ops->clear_irq) hwif->port_ops->clear_irq(drive); @@ -1159,7 +1159,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) /* service this interrupt, may set handler for next interrupt */ startstop = handler(drive); - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); /* * Note that handler() may have set things up for another * interrupt to occur soon, but it cannot happen until @@ -1168,7 +1168,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * won't allow another of the same (on any CPU) until we return. */ if (startstop == ide_stopped) { - if (hwgroup->handler == NULL) { /* paranoia */ + if (hwif->handler == NULL) { /* paranoia */ ide_unlock_port(hwif); plug_device = 1; } else @@ -1178,7 +1178,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) out_handled: irq_ret = IRQ_HANDLED; out: - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); out_early: if (plug_device) { ide_unlock_host(hwif->host); @@ -1205,11 +1205,10 @@ out_early: void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq) { - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; struct request_queue *q = drive->queue; unsigned long flags; - hwgroup->rq = NULL; + drive->hwif->rq = NULL; spin_lock_irqsave(q->queue_lock, flags); __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index ad8bd6539283..b92304d0e79a 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -822,25 +822,25 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry) { - ide_hwgroup_t *hwgroup = HWGROUP(drive); - - BUG_ON(hwgroup->handler); - hwgroup->handler = handler; - hwgroup->expiry = expiry; - hwgroup->timer.expires = jiffies + timeout; - hwgroup->req_gen_timer = hwgroup->req_gen; - add_timer(&hwgroup->timer); + ide_hwif_t *hwif = drive->hwif; + + BUG_ON(hwif->handler); + hwif->handler = handler; + hwif->expiry = expiry; + hwif->timer.expires = jiffies + timeout; + hwif->req_gen_timer = hwif->req_gen; + add_timer(&hwif->timer); } void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry) { - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; + ide_hwif_t *hwif = drive->hwif; unsigned long flags; - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); } EXPORT_SYMBOL(ide_set_handler); @@ -863,10 +863,9 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, unsigned timeout, ide_expiry_t *expiry) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; unsigned long flags; - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); __ide_set_handler(drive, handler, timeout, expiry); hwif->tp_ops->exec_command(hwif, cmd); /* @@ -876,26 +875,25 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, * FIXME: we could skip this delay with care on non shared devices */ ndelay(400); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); } EXPORT_SYMBOL(ide_execute_command); void ide_execute_pkt_cmd(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; unsigned long flags; - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); hwif->tp_ops->exec_command(hwif, ATA_CMD_PACKET); ndelay(400); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); } EXPORT_SYMBOL_GPL(ide_execute_pkt_cmd); static inline void ide_complete_drive_reset(ide_drive_t *drive, int err) { - struct request *rq = drive->hwif->hwgroup->rq; + struct request *rq = drive->hwif->rq; if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET) ide_end_request(drive, err ? err : 1, 0); @@ -913,7 +911,6 @@ static ide_startstop_t do_reset1 (ide_drive_t *, int); static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; u8 stat; SELECT_DRIVE(drive); @@ -923,20 +920,20 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) if (OK_STAT(stat, 0, ATA_BUSY)) printk("%s: ATAPI reset complete\n", drive->name); else { - if (time_before(jiffies, hwgroup->poll_timeout)) { + if (time_before(jiffies, hwif->poll_timeout)) { ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL); /* continue polling */ return ide_started; } /* end of polling */ - hwgroup->polling = 0; + hwif->polling = 0; printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); /* do it the old fashioned way */ return do_reset1(drive, 1); } /* done polling */ - hwgroup->polling = 0; + hwif->polling = 0; ide_complete_drive_reset(drive, 0); return ide_stopped; } @@ -968,7 +965,6 @@ static void ide_reset_report_error(ide_hwif_t *hwif, u8 err) */ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) { - ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); const struct ide_port_ops *port_ops = hwif->port_ops; u8 tmp; @@ -986,7 +982,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) tmp = hwif->tp_ops->read_status(hwif); if (!OK_STAT(tmp, 0, ATA_BUSY)) { - if (time_before(jiffies, hwgroup->poll_timeout)) { + if (time_before(jiffies, hwif->poll_timeout)) { ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL); /* continue polling */ return ide_started; @@ -1007,7 +1003,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } out: - hwgroup->polling = 0; /* done polling */ + hwif->polling = 0; /* done polling */ ide_complete_drive_reset(drive, err); return ide_stopped; } @@ -1081,7 +1077,6 @@ static void pre_reset(ide_drive_t *drive) static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; struct ide_io_ports *io_ports = &hwif->io_ports; const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_port_ops *port_ops; @@ -1089,10 +1084,10 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) unsigned int unit; DEFINE_WAIT(wait); - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); /* We must not reset with running handlers */ - BUG_ON(hwgroup->handler != NULL); + BUG_ON(hwif->handler != NULL); /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { @@ -1101,10 +1096,10 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) udelay (20); tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); ndelay(400); - hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - hwgroup->polling = 1; + hwif->poll_timeout = jiffies + WAIT_WORSTCASE; + hwif->polling = 1; __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); return ide_started; } @@ -1127,9 +1122,9 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) if (time_before_eq(timeout, now)) break; - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); timeout = schedule_timeout_uninterruptible(timeout - now); - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); } while (timeout); finish_wait(&ide_park_wq, &wait); @@ -1141,7 +1136,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(&hwif->drives[unit]); if (io_ports->ctl_addr == 0) { - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); ide_complete_drive_reset(drive, -ENXIO); return ide_stopped; } @@ -1164,8 +1159,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) tp_ops->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); - hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - hwgroup->polling = 1; + hwif->poll_timeout = jiffies + WAIT_WORSTCASE; + hwif->polling = 1; __ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL); /* @@ -1177,7 +1172,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) if (port_ops && port_ops->resetproc) port_ops->resetproc(drive); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); return ide_started; } diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 9f6e33d8a8b2..09526a0de734 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -273,7 +273,7 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) static void ide_dump_opcode(ide_drive_t *drive) { - struct request *rq = drive->hwif->hwgroup->rq; + struct request *rq = drive->hwif->rq; ide_task_t *task = NULL; if (!rq) @@ -346,10 +346,13 @@ static void ide_dump_ata_error(ide_drive_t *drive, u8 err) printk(KERN_CONT "}"); if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK || (err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) { + struct request *rq = drive->hwif->rq; + ide_dump_sector(drive); - if (HWGROUP(drive) && HWGROUP(drive)->rq) + + if (rq) printk(KERN_CONT ", sector=%llu", - (unsigned long long)HWGROUP(drive)->rq->sector); + (unsigned long long)rq->sector); } printk(KERN_CONT "\n"); } diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c index 678454ac2483..c875a957596c 100644 --- a/drivers/ide/ide-park.c +++ b/drivers/ide/ide-park.c @@ -7,22 +7,22 @@ DECLARE_WAIT_QUEUE_HEAD(ide_park_wq); static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) { - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; + ide_hwif_t *hwif = drive->hwif; struct request_queue *q = drive->queue; struct request *rq; int rc; timeout += jiffies; - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); if (drive->dev_flags & IDE_DFLAG_PARKED) { int reset_timer = time_before(timeout, drive->sleep); int start_queue = 0; drive->sleep = timeout; wake_up_all(&ide_park_wq); - if (reset_timer && del_timer(&hwgroup->timer)) + if (reset_timer && del_timer(&hwif->timer)) start_queue = 1; - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); if (start_queue) { spin_lock_irq(q->queue_lock); @@ -31,7 +31,7 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) } return; } - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); rq = blk_get_request(q, READ, __GFP_WAIT); rq->cmd[0] = REQ_PARK_HEADS; @@ -64,21 +64,21 @@ ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, char *buf) { ide_drive_t *drive = to_ide_device(dev); - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; + ide_hwif_t *hwif = drive->hwif; unsigned long now; unsigned int msecs; if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) return -EOPNOTSUPP; - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); now = jiffies; if (drive->dev_flags & IDE_DFLAG_PARKED && time_after(drive->sleep, now)) msecs = jiffies_to_msecs(drive->sleep - now); else msecs = 0; - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); return snprintf(buf, 20, "%u\n", msecs); } diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index 8282c6086e6a..abb84a2dd821 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -194,7 +194,7 @@ void ide_complete_pm_request(ide_drive_t *drive, struct request *rq) } spin_unlock_irqrestore(q->queue_lock, flags); - drive->hwif->hwgroup->rq = NULL; + drive->hwif->rq = NULL; if (blk_end_request(rq, 0, 0)) BUG(); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 68f3c87b8284..e953b70706c2 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -949,79 +949,20 @@ static int ide_port_setup_devices(ide_hwif_t *hwif) return j; } -static ide_hwif_t *ide_ports[MAX_HWIFS]; - -void ide_remove_port_from_hwgroup(ide_hwif_t *hwif) -{ - ide_hwgroup_t *hwgroup = hwif->hwgroup; - - ide_ports[hwif->index] = NULL; - - spin_lock_irq(&hwgroup->lock); - /* Free the hwgroup if we were the only member. */ - if (--hwgroup->port_count == 0) - kfree(hwgroup); - spin_unlock_irq(&hwgroup->lock); -} - /* - * This routine sets up the irq for an ide interface, and creates a new - * hwgroup for the irq/hwif if none was previously assigned. - * - * Much of the code is for correctly detecting/handling irq sharing - * and irq serialization situations. This is somewhat complex because - * it handles static as well as dynamic (PCMCIA) IDE interfaces. + * This routine sets up the IRQ for an IDE interface. */ static int init_irq (ide_hwif_t *hwif) { struct ide_io_ports *io_ports = &hwif->io_ports; - unsigned int index; - ide_hwgroup_t *hwgroup; - ide_hwif_t *match = NULL; int sa = 0; mutex_lock(&ide_cfg_mtx); - hwif->hwgroup = NULL; - - for (index = 0; index < MAX_HWIFS; index++) { - ide_hwif_t *h = ide_ports[index]; + spin_lock_init(&hwif->lock); - if (h && h->hwgroup) { /* scan only initialized ports */ - if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) { - if (hwif->host == h->host) - match = h; - } - } - } - - /* - * If we are still without a hwgroup, then form a new one - */ - if (match) { - hwgroup = match->hwgroup; - hwif->hwgroup = hwgroup; - - spin_lock_irq(&hwgroup->lock); - hwgroup->port_count++; - spin_unlock_irq(&hwgroup->lock); - } else { - hwgroup = kmalloc_node(sizeof(*hwgroup), GFP_KERNEL|__GFP_ZERO, - hwif_to_node(hwif)); - if (hwgroup == NULL) - goto out_up; - - spin_lock_init(&hwgroup->lock); - - hwif->hwgroup = hwgroup; - - hwgroup->port_count = 1; - - init_timer(&hwgroup->timer); - hwgroup->timer.function = &ide_timer_expiry; - hwgroup->timer.data = (unsigned long) hwgroup; - } - - ide_ports[hwif->index] = hwif; + init_timer(&hwif->timer); + hwif->timer.function = &ide_timer_expiry; + hwif->timer.data = (unsigned long)hwif; #if defined(__mc68000__) sa = IRQF_SHARED; @@ -1034,7 +975,7 @@ static int init_irq (ide_hwif_t *hwif) hwif->tp_ops->set_irq(hwif, 1); if (request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwif)) - goto out_unlink; + goto out_up; if (!hwif->rqsize) { if ((hwif->host_flags & IDE_HFLAG_NO_LBA48) || @@ -1052,14 +993,12 @@ static int init_irq (ide_hwif_t *hwif) printk(KERN_INFO "%s at 0x%08lx on irq %d", hwif->name, io_ports->data_addr, hwif->irq); #endif /* __mc68000__ */ - if (match) - printk(KERN_CONT " (serialized with %s)", match->name); + if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) + printk(KERN_CONT " (serialized)"); printk(KERN_CONT "\n"); mutex_unlock(&ide_cfg_mtx); return 0; -out_unlink: - ide_remove_port_from_hwgroup(hwif); out_up: mutex_unlock(&ide_cfg_mtx); return 1; @@ -1140,20 +1079,20 @@ EXPORT_SYMBOL_GPL(ide_init_disk); static void drive_release_dev (struct device *dev) { ide_drive_t *drive = container_of(dev, ide_drive_t, gendev); - ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; + ide_hwif_t *hwif = drive->hwif; ide_proc_unregister_device(drive); - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); kfree(drive->id); drive->id = NULL; drive->dev_flags &= ~IDE_DFLAG_PRESENT; /* Messed up locking ... */ - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); blk_cleanup_queue(drive->queue); - spin_lock_irq(&hwgroup->lock); + spin_lock_irq(&hwif->lock); drive->queue = NULL; - spin_unlock_irq(&hwgroup->lock); + spin_unlock_irq(&hwif->lock); complete(&drive->gendev_rel_comp); } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 5d2aa22cd6e4..e39f2f46d982 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -479,7 +479,7 @@ static void ide_tape_kfree_buffer(idetape_tape_t *tape) static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) { - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = drive->hwif->rq; idetape_tape_t *tape = drive->driver_data; unsigned long flags; int error; @@ -531,7 +531,7 @@ static void ide_tape_callback(ide_drive_t *drive, int dsc) printk(KERN_ERR "ide-tape: Error in REQUEST SENSE " "itself - Aborting request!\n"); } else if (pc->c[0] == READ_6 || pc->c[0] == WRITE_6) { - struct request *rq = drive->hwif->hwgroup->rq; + struct request *rq = drive->hwif->rq; int blocks = pc->xferred / tape->blk_size; tape->avg_size += blocks * tape->blk_size; @@ -576,7 +576,7 @@ static void ide_tape_callback(ide_drive_t *drive, int dsc) /* * Postpone the current request so that ide.c will be able to service requests - * from another device on the same hwgroup while we are polling for DSC. + * from another device on the same port while we are polling for DSC. */ static void idetape_postpone_request(ide_drive_t *drive) { @@ -584,7 +584,8 @@ static void idetape_postpone_request(ide_drive_t *drive) debug_log(DBG_PROCS, "Enter %s\n", __func__); - tape->postponed_rq = HWGROUP(drive)->rq; + tape->postponed_rq = drive->hwif->rq; + ide_stall_queue(drive, tape->dsc_poll_freq); } diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index bf4fb9d8d176..693e8d15fb78 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -361,7 +361,7 @@ static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; u8 stat = hwif->tp_ops->read_status(hwif); /* Error? */ @@ -395,7 +395,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = hwif->rq; u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 5bc2e4782a55..9e403282dfa7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -189,8 +189,6 @@ void ide_unregister(ide_hwif_t *hwif) free_irq(hwif->irq, hwif); - ide_remove_port_from_hwgroup(hwif); - device_unregister(hwif->portdev); device_unregister(&hwif->gendev); wait_for_completion(&hwif->gendev_rel_comp); @@ -315,7 +313,6 @@ static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio) static int set_pio_mode(ide_drive_t *drive, int arg) { ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *hwgroup = hwif->hwgroup; const struct ide_port_ops *port_ops = hwif->port_ops; if (arg < 0 || arg > 255) @@ -330,9 +327,9 @@ static int set_pio_mode(ide_drive_t *drive, int arg) unsigned long flags; /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */ - spin_lock_irqsave(&hwgroup->lock, flags); + spin_lock_irqsave(&hwif->lock, flags); port_ops->set_pio_mode(drive, arg); - spin_unlock_irqrestore(&hwgroup->lock, flags); + spin_unlock_irqrestore(&hwif->lock, flags); } else port_ops->set_pio_mode(drive, arg); } else { diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c index 624e62e5cc9a..072ef70bf061 100644 --- a/drivers/ide/pdc202xx_old.c +++ b/drivers/ide/pdc202xx_old.c @@ -169,8 +169,8 @@ static void pdc202xx_dma_start(ide_drive_t *drive) if (drive->current_speed > XFER_UDMA_2) pdc_old_enable_66MHz_clock(drive->hwif); if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { - struct request *rq = HWGROUP(drive)->rq; ide_hwif_t *hwif = HWIF(drive); + struct request *rq = hwif->rq; unsigned long high_16 = hwif->extra_base - 16; unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); u32 word_count = 0; diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 7c481bb56fab..899b96baf215 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1516,7 +1516,7 @@ pmac_ide_dma_setup(ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = hwif->rq; u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4); if (!pmac_ide_build_dmatable(drive, rq)) { diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 0f48f9dacfa5..e966113fd569 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -316,7 +316,7 @@ static void scc_dma_host_set(ide_drive_t *drive, int on) static int scc_dma_setup(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = hwif->rq; unsigned int reading; u8 dma_stat; @@ -405,7 +405,7 @@ static int scc_dma_end(ide_drive_t *drive) drive->name); data_loss = 1; if (retry++) { - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = hwif->rq; int unit; /* ERROR_RESET and drive->crc_count are needed * to reduce DMA transfer mode in retry process. diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index a687a7dfea6f..c68b71b1087b 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -492,7 +492,7 @@ use_pio_instead: static int sgiioc4_dma_setup(ide_drive_t *drive) { - struct request *rq = HWGROUP(drive)->rq; + struct request *rq = drive->hwif->rq; unsigned int count = 0; int ddir; diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c index 93e2cce4b296..accb379bcad6 100644 --- a/drivers/ide/tc86c001.c +++ b/drivers/ide/tc86c001.c @@ -64,11 +64,10 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); ide_expiry_t *expiry = ide_get_hwifdata(hwif); - ide_hwgroup_t *hwgroup = HWGROUP(drive); u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Restore a higher level driver's expiry handler first. */ - hwgroup->expiry = expiry; + hwif->expiry = expiry; if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ unsigned long sc_base = hwif->config_data; @@ -111,10 +110,9 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) static void tc86c001_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned long sc_base = hwif->config_data; unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); - unsigned long nsectors = hwgroup->rq->nr_sectors; + unsigned long nsectors = hwif->rq->nr_sectors; /* * We have to manually load the sector count and size into @@ -125,8 +123,8 @@ static void tc86c001_dma_start(ide_drive_t *drive) outw(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */ /* Install our timeout expiry hook, saving the current handler... */ - ide_set_hwifdata(hwif, hwgroup->expiry); - hwgroup->expiry = &tc86c001_timer_expiry; + ide_set_hwifdata(hwif, hwif->expiry); + hwif->expiry = &tc86c001_timer_expiry; ide_dma_start(drive); } diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index 2a5ea90cf8b8..79a03df118d8 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -184,7 +184,7 @@ static void trm290_dma_exec_cmd(ide_drive_t *drive, u8 command) static int trm290_dma_setup(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; unsigned int count, rw; if (rq_data_dir(rq)) { diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 4a8c5a21bd4c..1ac27ac7283b 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -293,7 +293,7 @@ static int tx4939ide_dma_setup(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; void __iomem *base = TX4939IDE_BASE(hwif); - struct request *rq = hwif->hwgroup->rq; + struct request *rq = hwif->rq; u8 reading; int nent; diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c index e29978cf6197..0608d41fb6d0 100644 --- a/drivers/ide/umc8672.c +++ b/drivers/ide/umc8672.c @@ -106,22 +106,21 @@ static void umc_set_speeds(u8 speeds[]) static void umc_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = drive->hwif; - ide_hwgroup_t *mate_hwgroup = hwif->mate ? hwif->mate->hwgroup : NULL; + ide_hwif_t *hwif = drive->hwif, *mate = hwif->mate; unsigned long uninitialized_var(flags); printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]); - if (mate_hwgroup) - spin_lock_irqsave(&mate_hwgroup->lock, flags); - if (mate_hwgroup && mate_hwgroup->handler) { + if (mate) + spin_lock_irqsave(&mate->lock, flags); + if (mate && mate->handler) { printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n"); } else { current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio]; umc_set_speeds(current_speeds); } - if (mate_hwgroup) - spin_unlock_irqrestore(&mate_hwgroup->lock, flags); + if (mate) + spin_unlock_irqrestore(&mate->lock, flags); } static const struct ide_port_ops umc8672_port_ops = { diff --git a/include/linux/ide.h b/include/linux/ide.h index 00df155b5a02..f27f130ba000 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -42,7 +42,6 @@ typedef unsigned char byte; /* used everywhere */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ #define HWIF(drive) ((ide_hwif_t *)((drive)->hwif)) -#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup)) /* * Definitions for accessing IDE controller registers @@ -750,7 +749,6 @@ struct ide_host; typedef struct hwif_s { struct hwif_s *mate; /* other hwif from same PCI chip */ - struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ struct ide_host *host; @@ -840,6 +838,30 @@ typedef struct hwif_s { #ifdef CONFIG_BLK_DEV_IDEACPI struct ide_acpi_hwif_link *acpidata; #endif + + /* IRQ handler, if active */ + ide_startstop_t (*handler)(ide_drive_t *); + + /* BOOL: polling active & poll_timeout field valid */ + unsigned int polling : 1; + + /* current drive */ + ide_drive_t *cur_dev; + + /* current request */ + struct request *rq; + + /* failsafe timer */ + struct timer_list timer; + /* timeout value during long polls */ + unsigned long poll_timeout; + /* queried upon timeouts */ + int (*expiry)(ide_drive_t *); + + int req_gen; + int req_gen_timer; + + spinlock_t lock; } ____cacheline_internodealigned_in_smp ide_hwif_t; #define MAX_HOST_PORTS 4 @@ -868,34 +890,6 @@ typedef int (ide_expiry_t)(ide_drive_t *); /* used by ide-cd, ide-floppy, etc. */ typedef void (xfer_func_t)(ide_drive_t *, struct request *rq, void *, unsigned); -typedef struct hwgroup_s { - /* irq handler, if active */ - ide_startstop_t (*handler)(ide_drive_t *); - - /* BOOL: polling active & poll_timeout field valid */ - unsigned int polling : 1; - - /* current drive */ - ide_drive_t *cur_dev; - - /* current request */ - struct request *rq; - - /* failsafe timer */ - struct timer_list timer; - /* timeout value during long polls */ - unsigned long poll_timeout; - /* queried upon timeouts */ - int (*expiry)(ide_drive_t *); - - int req_gen; - int req_gen_timer; - - spinlock_t lock; - - int port_count; -} ide_hwgroup_t; - typedef struct ide_driver_s ide_driver_t; extern struct mutex ide_setting_mtx; @@ -1512,7 +1506,6 @@ static inline void ide_acpi_port_init_devices(ide_hwif_t *hwif) { ; } static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {} #endif -void ide_remove_port_from_hwgroup(ide_hwif_t *); void ide_unregister(ide_hwif_t *); void ide_register_region(struct gendisk *); -- cgit v1.2.3-71-gd317 From b40d1b88f1001f0224c63fa2c008914514bcef33 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:51 +0100 Subject: ide: move ide_init_port_data() and friends to ide-probe.c * Move IDE_DEFAULT_MAX_FAILURES to . * Move ide_cfg_mtx, ide_hwif_to_major[], ide_port_init_devices_data(), ide_init_port_data(), ide_init_port_hw() and ide_unregister() to ide-probe.c from ide.c. * Make ide_unregister(), ide_init_port_data(), ide_init_port_hw() and ide_cfg_mtx static. While at it: * Remove stale ide_init_port_data() documentation and ide_lock extern. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 135 ++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide.c | 146 ------------------------------------------------ include/linux/ide.h | 9 +-- 3 files changed, 136 insertions(+), 154 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index e953b70706c2..09ea50118e63 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -918,6 +918,8 @@ static int ide_init_queue(ide_drive_t *drive) return 0; } +static DEFINE_MUTEX(ide_cfg_mtx); + /* * For any present drive: * - allocate the block device queue @@ -1273,6 +1275,69 @@ static void ide_port_cable_detect(ide_hwif_t *hwif) } } +static const u8 ide_hwif_to_major[] = + { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, + IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR }; + +static void ide_port_init_devices_data(ide_hwif_t *hwif) +{ + int unit; + + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + u8 j = (hwif->index * MAX_DRIVES) + unit; + + memset(drive, 0, sizeof(*drive)); + + drive->media = ide_disk; + drive->select = (unit << 4) | ATA_DEVICE_OBS; + drive->hwif = hwif; + drive->ready_stat = ATA_DRDY; + drive->bad_wstat = BAD_W_STAT; + drive->special.b.recalibrate = 1; + drive->special.b.set_geometry = 1; + drive->name[0] = 'h'; + drive->name[1] = 'd'; + drive->name[2] = 'a' + j; + drive->max_failures = IDE_DEFAULT_MAX_FAILURES; + + INIT_LIST_HEAD(&drive->list); + init_completion(&drive->gendev_rel_comp); + } +} + +static void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) +{ + /* bulk initialize hwif & drive info with zeros */ + memset(hwif, 0, sizeof(ide_hwif_t)); + + /* fill in any non-zero initial values */ + hwif->index = index; + hwif->major = ide_hwif_to_major[index]; + + hwif->name[0] = 'i'; + hwif->name[1] = 'd'; + hwif->name[2] = 'e'; + hwif->name[3] = '0' + index; + + init_completion(&hwif->gendev_rel_comp); + + hwif->tp_ops = &default_tp_ops; + + ide_port_init_devices_data(hwif); +} + +static void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) +{ + memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); + hwif->irq = hw->irq; + hwif->chipset = hw->chipset; + hwif->dev = hw->dev; + hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; + hwif->ack_intr = hw->ack_intr; + hwif->config_data = hw->config; +} + static unsigned int ide_indexes; /** @@ -1503,6 +1568,76 @@ int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, } EXPORT_SYMBOL_GPL(ide_host_add); +static void __ide_port_unregister_devices(ide_hwif_t *hwif) +{ + int i; + + for (i = 0; i < MAX_DRIVES; i++) { + ide_drive_t *drive = &hwif->drives[i]; + + if (drive->dev_flags & IDE_DFLAG_PRESENT) { + device_unregister(&drive->gendev); + wait_for_completion(&drive->gendev_rel_comp); + } + } +} + +void ide_port_unregister_devices(ide_hwif_t *hwif) +{ + mutex_lock(&ide_cfg_mtx); + __ide_port_unregister_devices(hwif); + hwif->present = 0; + ide_port_init_devices_data(hwif); + mutex_unlock(&ide_cfg_mtx); +} +EXPORT_SYMBOL_GPL(ide_port_unregister_devices); + +/** + * ide_unregister - free an IDE interface + * @hwif: IDE interface + * + * Perform the final unregister of an IDE interface. + * + * Locking: + * The caller must not hold the IDE locks. + * + * It is up to the caller to be sure there is no pending I/O here, + * and that the interface will not be reopened (present/vanishing + * locking isn't yet done BTW). + */ + +static void ide_unregister(ide_hwif_t *hwif) +{ + BUG_ON(in_interrupt()); + BUG_ON(irqs_disabled()); + + mutex_lock(&ide_cfg_mtx); + + if (hwif->present) { + __ide_port_unregister_devices(hwif); + hwif->present = 0; + } + + ide_proc_unregister_port(hwif); + + free_irq(hwif->irq, hwif); + + device_unregister(hwif->portdev); + device_unregister(&hwif->gendev); + wait_for_completion(&hwif->gendev_rel_comp); + + /* + * Remove us from the kernel's knowledge + */ + blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<sg_table); + unregister_blkdev(hwif->major, hwif->name); + + ide_release_dma_engine(hwif); + + mutex_unlock(&ide_cfg_mtx); +} + void ide_host_free(struct ide_host *host) { ide_hwif_t *hwif; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 6538b63158bf..c1bb0f6784a9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -60,154 +60,8 @@ #include #include - -/* default maximum number of failures */ -#define IDE_DEFAULT_MAX_FAILURES 1 - struct class *ide_port_class; -static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, - IDE2_MAJOR, IDE3_MAJOR, - IDE4_MAJOR, IDE5_MAJOR, - IDE6_MAJOR, IDE7_MAJOR, - IDE8_MAJOR, IDE9_MAJOR }; - -DEFINE_MUTEX(ide_cfg_mtx); - -static void ide_port_init_devices_data(ide_hwif_t *); - -/* - * Do not even *think* about calling this! - */ -void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) -{ - /* bulk initialize hwif & drive info with zeros */ - memset(hwif, 0, sizeof(ide_hwif_t)); - - /* fill in any non-zero initial values */ - hwif->index = index; - hwif->major = ide_hwif_to_major[index]; - - hwif->name[0] = 'i'; - hwif->name[1] = 'd'; - hwif->name[2] = 'e'; - hwif->name[3] = '0' + index; - - init_completion(&hwif->gendev_rel_comp); - - hwif->tp_ops = &default_tp_ops; - - ide_port_init_devices_data(hwif); -} - -static void ide_port_init_devices_data(ide_hwif_t *hwif) -{ - int unit; - - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; - u8 j = (hwif->index * MAX_DRIVES) + unit; - - memset(drive, 0, sizeof(*drive)); - - drive->media = ide_disk; - drive->select = (unit << 4) | ATA_DEVICE_OBS; - drive->hwif = hwif; - drive->ready_stat = ATA_DRDY; - drive->bad_wstat = BAD_W_STAT; - drive->special.b.recalibrate = 1; - drive->special.b.set_geometry = 1; - drive->name[0] = 'h'; - drive->name[1] = 'd'; - drive->name[2] = 'a' + j; - drive->max_failures = IDE_DEFAULT_MAX_FAILURES; - - INIT_LIST_HEAD(&drive->list); - init_completion(&drive->gendev_rel_comp); - } -} - -static void __ide_port_unregister_devices(ide_hwif_t *hwif) -{ - int i; - - for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = &hwif->drives[i]; - - if (drive->dev_flags & IDE_DFLAG_PRESENT) { - device_unregister(&drive->gendev); - wait_for_completion(&drive->gendev_rel_comp); - } - } -} - -void ide_port_unregister_devices(ide_hwif_t *hwif) -{ - mutex_lock(&ide_cfg_mtx); - __ide_port_unregister_devices(hwif); - hwif->present = 0; - ide_port_init_devices_data(hwif); - mutex_unlock(&ide_cfg_mtx); -} -EXPORT_SYMBOL_GPL(ide_port_unregister_devices); - -/** - * ide_unregister - free an IDE interface - * @hwif: IDE interface - * - * Perform the final unregister of an IDE interface. - * - * Locking: - * The caller must not hold the IDE locks. - * - * It is up to the caller to be sure there is no pending I/O here, - * and that the interface will not be reopened (present/vanishing - * locking isn't yet done BTW). - */ - -void ide_unregister(ide_hwif_t *hwif) -{ - BUG_ON(in_interrupt()); - BUG_ON(irqs_disabled()); - - mutex_lock(&ide_cfg_mtx); - - if (hwif->present) { - __ide_port_unregister_devices(hwif); - hwif->present = 0; - } - - ide_proc_unregister_port(hwif); - - free_irq(hwif->irq, hwif); - - device_unregister(hwif->portdev); - device_unregister(&hwif->gendev); - wait_for_completion(&hwif->gendev_rel_comp); - - /* - * Remove us from the kernel's knowledge - */ - blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<sg_table); - unregister_blkdev(hwif->major, hwif->name); - - ide_release_dma_engine(hwif); - - mutex_unlock(&ide_cfg_mtx); -} - -void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) -{ - memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); - hwif->irq = hw->irq; - hwif->chipset = hw->chipset; - hwif->dev = hw->dev; - hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; - hwif->ack_intr = hw->ack_intr; - hwif->config_data = hw->config; -} - /* * Locks for IDE setting functionality */ diff --git a/include/linux/ide.h b/include/linux/ide.h index f27f130ba000..ee2f461882ad 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -37,6 +37,7 @@ typedef unsigned char byte; /* used everywhere */ /* * Probably not wise to fiddle with these */ +#define IDE_DEFAULT_MAX_FAILURES 1 #define ERROR_MAX 8 /* Max read/write errors per sector */ #define ERROR_RESET 3 /* Reset controller every 4th retry */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ @@ -184,9 +185,6 @@ typedef struct hw_regs_s { unsigned long config; } hw_regs_t; -void ide_init_port_data(struct hwif_s *, unsigned int); -void ide_init_port_hw(struct hwif_s *, hw_regs_t *); - static inline void ide_std_init_ports(hw_regs_t *hw, unsigned long io_addr, unsigned long ctl_addr) @@ -1506,8 +1504,6 @@ static inline void ide_acpi_port_init_devices(ide_hwif_t *hwif) { ; } static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {} #endif -void ide_unregister(ide_hwif_t *); - void ide_register_region(struct gendisk *); void ide_unregister_region(struct gendisk *); @@ -1592,9 +1588,6 @@ static inline void ide_set_max_pio(ide_drive_t *drive) ide_set_pio(drive, 255); } -extern spinlock_t ide_lock; -extern struct mutex ide_cfg_mtx; - #define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) char *ide_media_string(ide_drive_t *); -- cgit v1.2.3-71-gd317 From 898ec223fea2a2df88035e58dbf50f493577e225 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:52 +0100 Subject: ide: remove HWIF() macro Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/aec62xx.c | 4 ++-- drivers/ide/alim15x3.c | 4 ++-- drivers/ide/au1xxx-ide.c | 4 ++-- drivers/ide/cmd64x.c | 14 +++++++------- drivers/ide/cs5520.c | 2 +- drivers/ide/cy82c693.c | 2 +- drivers/ide/hpt366.c | 18 +++++++++--------- drivers/ide/icside.c | 10 +++++----- drivers/ide/ide-acpi.c | 4 ++-- drivers/ide/ide-disk.c | 4 ++-- drivers/ide/ide-io.c | 8 ++++---- drivers/ide/ide-iops.c | 4 ++-- drivers/ide/ide-pm.c | 4 ++-- drivers/ide/ide-probe.c | 12 ++++++------ drivers/ide/ide-taskfile.c | 2 +- drivers/ide/it8213.c | 4 ++-- drivers/ide/ns87415.c | 6 +++--- drivers/ide/pdc202xx_new.c | 4 ++-- drivers/ide/pdc202xx_old.c | 10 +++++----- drivers/ide/piix.c | 6 +++--- drivers/ide/pmac.c | 4 ++-- drivers/ide/qd65xx.c | 7 ++++--- drivers/ide/sc1200.c | 6 +++--- drivers/ide/scc_pata.c | 8 ++++---- drivers/ide/serverworks.c | 2 +- drivers/ide/sgiioc4.c | 12 ++++++------ drivers/ide/siimage.c | 10 +++++----- drivers/ide/sis5513.c | 2 +- drivers/ide/sl82c105.c | 4 ++-- drivers/ide/slc90e66.c | 4 ++-- drivers/ide/tc86c001.c | 6 +++--- drivers/ide/triflex.c | 2 +- drivers/ide/trm290.c | 8 ++++---- drivers/ide/via82cxxx.c | 2 +- include/linux/ide.h | 2 -- 35 files changed, 102 insertions(+), 103 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c index 4142c698e0d3..4485b9c6f0e6 100644 --- a/drivers/ide/aec62xx.c +++ b/drivers/ide/aec62xx.c @@ -83,7 +83,7 @@ static u8 pci_bus_clock_list_ultra (u8 speed, struct chipset_bus_clock_list_entr static void aec6210_set_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); struct ide_host *host = pci_get_drvdata(dev); struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; @@ -111,7 +111,7 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed) static void aec6260_set_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); struct ide_host *host = pci_get_drvdata(dev); struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index 290204e89eaf..8d217430c997 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -68,7 +68,7 @@ static struct pci_dev *isa_dev; static void ali_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); int s_time = t->setup, a_time = t->active, c_time = t->cycle; @@ -150,7 +150,7 @@ static u8 ali_udma_filter(ide_drive_t *drive) static void ali_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 speed1 = speed; u8 unit = drive->dn & 1; diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index db412bc772d1..11f2c8f3db48 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -212,7 +212,7 @@ static void auide_set_dma_mode(ide_drive_t *drive, const u8 speed) static int auide_build_dmatable(ide_drive_t *drive) { int i, iswrite, count = 0; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->rq; _auide_hwif *ahwif = &auide_hwif; struct scatterlist *sg; @@ -286,7 +286,7 @@ static int auide_build_dmatable(ide_drive_t *drive) static int auide_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; if (hwif->sg_nents) { ide_destroy_dmatable(drive); diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index 3623bf013bcf..d1fc198719b2 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -115,7 +115,7 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_ */ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); unsigned int cycle_time; @@ -180,7 +180,7 @@ static void cmd64x_set_pio_mode(ide_drive_t *drive, const u8 pio) static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 unit = drive->dn & 0x01; u8 regU = 0, pciU = hwif->channel ? UDIDETCR1 : UDIDETCR0; @@ -226,7 +226,7 @@ static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed) static int cmd648_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long base = hwif->dma_base - (hwif->channel * 8); int err = ide_dma_end(drive); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : @@ -242,7 +242,7 @@ static int cmd648_dma_end(ide_drive_t *drive) static int cmd64x_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int irq_reg = hwif->channel ? ARTTIM23 : CFR; u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : @@ -259,7 +259,7 @@ static int cmd64x_dma_end(ide_drive_t *drive) static int cmd648_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long base = hwif->dma_base - (hwif->channel * 8); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; @@ -282,7 +282,7 @@ static int cmd648_dma_test_irq(ide_drive_t *drive) static int cmd64x_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int irq_reg = hwif->channel ? ARTTIM23 : CFR; u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : @@ -313,7 +313,7 @@ static int cmd64x_dma_test_irq(ide_drive_t *drive) static int cmd646_1_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; diff --git a/drivers/ide/cs5520.c b/drivers/ide/cs5520.c index 5efb467f8fa0..d003bec56ff9 100644 --- a/drivers/ide/cs5520.c +++ b/drivers/ide/cs5520.c @@ -59,7 +59,7 @@ static struct pio_clocks cs5520_pio_clocks[]={ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *pdev = to_pci_dev(hwif->dev); int controller = drive->dn > 1 ? 1 : 0; diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c index d37baf8ecc5f..74fc5401f407 100644 --- a/drivers/ide/cy82c693.c +++ b/drivers/ide/cy82c693.c @@ -203,7 +203,7 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode) static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); pio_clocks_t pclk; unsigned int addrCtrl; diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index b18e10d99d2e..a18a02a96977 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -626,7 +626,7 @@ static struct hpt_info *hpt3xx_get_info(struct device *dev) static u8 hpt3xx_udma_filter(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct hpt_info *info = hpt3xx_get_info(hwif->dev); u8 mask = hwif->ultra_mask; @@ -665,7 +665,7 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive) static u8 hpt3xx_mdma_filter(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct hpt_info *info = hpt3xx_get_info(hwif->dev); switch (info->chip_type) { @@ -743,7 +743,7 @@ static void hpt3xx_quirkproc(ide_drive_t *drive) static void hpt3xx_maskproc(ide_drive_t *drive, int mask) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); struct hpt_info *info = hpt3xx_get_info(hwif->dev); @@ -788,7 +788,7 @@ static void hpt366_dma_lost_irq(ide_drive_t *drive) static void hpt370_clear_engine(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); pci_write_config_byte(dev, hwif->select_data, 0x37); @@ -797,7 +797,7 @@ static void hpt370_clear_engine(ide_drive_t *drive) static void hpt370_irq_timeout(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u16 bfifo = 0; u8 dma_cmd; @@ -822,7 +822,7 @@ static void hpt370_dma_start(ide_drive_t *drive) static int hpt370_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) { @@ -844,7 +844,7 @@ static void hpt370_dma_timeout(ide_drive_t *drive) /* returns 1 if DMA IRQ issued, 0 otherwise */ static int hpt374_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u16 bfifo = 0; u8 dma_stat; @@ -865,7 +865,7 @@ static int hpt374_dma_test_irq(ide_drive_t *drive) static int hpt374_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 mcr = 0, mcr_addr = hwif->select_data; u8 bwsr = 0, mask = hwif->channel ? 0x02 : 0x01; @@ -927,7 +927,7 @@ static void hpt3xxn_set_clock(ide_hwif_t *hwif, u8 mode) static void hpt3xxn_rw_disk(ide_drive_t *drive, struct request *rq) { - hpt3xxn_set_clock(HWIF(drive), rq_data_dir(rq) ? 0x23 : 0x21); + hpt3xxn_set_clock(drive->hwif, rq_data_dir(rq) ? 0x23 : 0x21); } /** diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 72d95c99f147..97a35c667aee 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -166,7 +166,7 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = { */ static void icside_maskproc(ide_drive_t *drive, int mask) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); struct icside_state *state = ecard_get_drvdata(ec); unsigned long flags; @@ -284,7 +284,7 @@ static void icside_dma_host_set(ide_drive_t *drive, int on) static int icside_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); drive->waiting_for_dma = 0; @@ -299,7 +299,7 @@ static int icside_dma_end(ide_drive_t *drive) static void icside_dma_start(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); /* We can not enable DMA on both channels simultaneously. */ @@ -309,7 +309,7 @@ static void icside_dma_start(ide_drive_t *drive) static int icside_dma_setup(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); struct icside_state *state = ecard_get_drvdata(ec); struct request *rq = hwif->rq; @@ -362,7 +362,7 @@ static void icside_dma_exec_cmd(ide_drive_t *drive, u8 cmd) static int icside_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); struct icside_state *state = ecard_get_drvdata(ec); diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index fd4a36433050..f89b6ecf7d1a 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -218,7 +218,7 @@ static acpi_handle ide_acpi_hwif_get_handle(ide_hwif_t *hwif) */ static acpi_handle ide_acpi_drive_get_handle(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; int port; acpi_handle drive_handle; @@ -263,7 +263,7 @@ static int do_drive_get_GTF(ide_drive_t *drive, acpi_status status; struct acpi_buffer output; union acpi_object *out_obj; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct device *dev = hwif->gendev.parent; int err = -ENODEV; int port; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index eb9fac4d0f0c..4088a622873e 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -89,7 +89,7 @@ static void ide_tf_set_cmd(ide_drive_t *drive, ide_task_t *task, u8 dma) static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u16 nsectors = (u16)rq->nr_sectors; u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48); u8 dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); @@ -187,7 +187,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 6ff82d7055b9..0e2b95ec08a5 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -463,7 +463,7 @@ EXPORT_SYMBOL_GPL(ide_init_sg_cmd); static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; ide_task_t *task = rq->special; if (task) { @@ -587,7 +587,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) #ifdef DEBUG printk("%s: start_request: current=0x%08lx\n", - HWIF(drive)->name, (unsigned long) rq); + drive->hwif->name, (unsigned long) rq); #endif /* bail early if we've exceeded max_failures */ @@ -833,7 +833,7 @@ plug_device_2: */ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct request *rq; ide_startstop_t ret = ide_stopped; @@ -955,7 +955,7 @@ void ide_timer_expiry (unsigned long data) * globally mask the specific IRQ: */ spin_unlock(&hwif->lock); - hwif = HWIF(drive); + hwif = drive->hwif; /* disable_irq_nosync ?? */ disable_irq(hwif->irq); /* local CPU only, diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index b92304d0e79a..386080ee4608 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -451,7 +451,7 @@ EXPORT_SYMBOL(ide_fixstring); */ int drive_is_ready (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u8 stat = 0; if (drive->waiting_for_dma) @@ -965,7 +965,7 @@ static void ide_reset_report_error(ide_hwif_t *hwif, u8 err) */ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; const struct ide_port_ops *port_ops = hwif->port_ops; u8 tmp; int err = 0; diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index abb84a2dd821..0c206c68bbb1 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -5,7 +5,7 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg) { ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive); - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct request *rq; struct request_pm_state rqpm; ide_task_t args; @@ -39,7 +39,7 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg) int generic_ide_resume(struct device *dev) { ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive); - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct request *rq; struct request_pm_state rqpm; ide_task_t args; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 09ea50118e63..9dfa99f062aa 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -189,7 +189,7 @@ static void ide_classify_atapi_dev(ide_drive_t *drive) static void do_identify(ide_drive_t *drive, u8 cmd) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u16 *id = drive->id; char *m = (char *)&id[ATA_ID_PROD]; unsigned long flags; @@ -266,7 +266,7 @@ err_misc: static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; const struct ide_tp_ops *tp_ops = hwif->tp_ops; int use_altstatus = 0, rc; @@ -341,7 +341,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) static int try_to_identify (ide_drive_t *drive, u8 cmd) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; int retval; int autoprobe = 0; @@ -438,7 +438,7 @@ static u8 ide_read_device(ide_drive_t *drive) static int do_probe (ide_drive_t *drive, u8 cmd) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; int rc; u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat; @@ -522,7 +522,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) */ static void enable_nest (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; @@ -869,7 +869,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) static int ide_init_queue(ide_drive_t *drive) { struct request_queue *q; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; int max_sectors = 256; int max_sg_entries = PRD_ENTRIES; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 693e8d15fb78..55d451ce9b4f 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -58,7 +58,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *); ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; const struct ide_tp_ops *tp_ops = hwif->tp_ops; diff --git a/drivers/ide/it8213.c b/drivers/ide/it8213.c index 7c2feeb3c5ec..d7969b6d139e 100644 --- a/drivers/ide/it8213.c +++ b/drivers/ide/it8213.c @@ -25,7 +25,7 @@ static void it8213_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int is_slave = drive->dn & 1; int master_port = 0x40; @@ -82,7 +82,7 @@ static void it8213_set_pio_mode(ide_drive_t *drive, const u8 pio) static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 maslave = 0x40; int a_speed = 3 << (drive->dn * 4); diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 13789060f407..aceb2fcbe1d1 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -138,12 +138,12 @@ static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; /* * This routine either enables/disables (according to IDE_DFLAG_PRESENT) - * the IRQ associated with the port (HWIF(drive)), + * the IRQ associated with the port, * and selects either PIO or DMA handshaking for the next I/O operation. */ static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data; unsigned long flags; @@ -197,7 +197,7 @@ static void ns87415_selectproc (ide_drive_t *drive) static int ns87415_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; diff --git a/drivers/ide/pdc202xx_new.c b/drivers/ide/pdc202xx_new.c index 211ae46e3e0c..f21290c4b447 100644 --- a/drivers/ide/pdc202xx_new.c +++ b/drivers/ide/pdc202xx_new.c @@ -143,7 +143,7 @@ static struct udma_timing { static void pdcnew_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 adj = (drive->dn & 1) ? 0x08 : 0x00; @@ -219,7 +219,7 @@ static void pdcnew_reset(ide_drive_t *drive) * Deleted this because it is redundant from the caller. */ printk(KERN_WARNING "pdc202xx_new: %s channel reset.\n", - HWIF(drive)->channel ? "Secondary" : "Primary"); + drive->hwif->channel ? "Secondary" : "Primary"); } /** diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c index 072ef70bf061..e8e6b29d9e41 100644 --- a/drivers/ide/pdc202xx_old.c +++ b/drivers/ide/pdc202xx_old.c @@ -39,7 +39,7 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *); static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 drive_pci = 0x60 + (drive->dn << 2); @@ -169,7 +169,7 @@ static void pdc202xx_dma_start(ide_drive_t *drive) if (drive->current_speed > XFER_UDMA_2) pdc_old_enable_66MHz_clock(drive->hwif); if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->rq; unsigned long high_16 = hwif->extra_base - 16; unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); @@ -189,7 +189,7 @@ static void pdc202xx_dma_start(ide_drive_t *drive) static int pdc202xx_dma_end(ide_drive_t *drive) { if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long high_16 = hwif->extra_base - 16; unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); u8 clock = 0; @@ -205,7 +205,7 @@ static int pdc202xx_dma_end(ide_drive_t *drive) static int pdc202xx_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long high_16 = hwif->extra_base - 16; u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 sc1d = inb(high_16 + 0x001d); @@ -243,7 +243,7 @@ static void pdc202xx_reset_host (ide_hwif_t *hwif) static void pdc202xx_reset (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; ide_hwif_t *mate = hwif->mate; pdc202xx_reset_host(hwif); diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c index 61d2d920a5cd..e57b6c3ac78e 100644 --- a/drivers/ide/piix.c +++ b/drivers/ide/piix.c @@ -67,7 +67,7 @@ static int no_piix_dma; static void piix_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int is_slave = drive->dn & 1; int master_port = hwif->channel ? 0x42 : 0x40; @@ -136,7 +136,7 @@ static void piix_set_pio_mode(ide_drive_t *drive, const u8 pio) static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 maslave = hwif->channel ? 0x42 : 0x40; int a_speed = 3 << (drive->dn * 4); @@ -224,7 +224,7 @@ static unsigned int init_chipset_ich(struct pci_dev *dev) */ static void ich_clear_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u8 dma_stat; /* diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 899b96baf215..ee52a21af1be 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1513,7 +1513,7 @@ use_pio_instead: static int pmac_ide_dma_setup(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct request *rq = hwif->rq; @@ -1637,7 +1637,7 @@ pmac_ide_dma_test_irq (ide_drive_t *drive) break; if (++timeout > 100) { printk(KERN_WARNING "ide%d, ide_dma_test_irq \ - timeout flushing channel\n", HWIF(drive)->index); + timeout flushing channel\n", hwif->index); break; } } diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c index bc27c7aba936..5b2e3af43c4b 100644 --- a/drivers/ide/qd65xx.c +++ b/drivers/ide/qd65xx.c @@ -202,7 +202,8 @@ static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio) recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120; } - qd_set_timing(drive, qd6500_compute_timing(HWIF(drive), active_time, recovery_time)); + qd_set_timing(drive, qd6500_compute_timing(drive->hwif, + active_time, recovery_time)); } static void qd6580_set_pio_mode(ide_drive_t *drive, const u8 pio) @@ -245,11 +246,11 @@ static void qd6580_set_pio_mode(ide_drive_t *drive, const u8 pio) printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio); } - if (!HWIF(drive)->channel && drive->media != ide_disk) { + if (!hwif->channel && drive->media != ide_disk) { outb(0x5f, QD_CONTROL_PORT); printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO " "and post-write buffer on %s.\n", - drive->name, HWIF(drive)->name); + drive->name, hwif->name); } qd_set_timing(drive, qd6580_compute_timing(active_time, recovery_time)); diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index ec7f766ef5e4..6f68fe984bfb 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -125,7 +125,7 @@ out: static void sc1200_set_dma_mode(ide_drive_t *drive, const u8 mode) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned int reg, timings; unsigned short pci_clock; @@ -170,7 +170,7 @@ static void sc1200_set_dma_mode(ide_drive_t *drive, const u8 mode) */ static int sc1200_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long dma_base = hwif->dma_base; byte dma_stat; @@ -199,7 +199,7 @@ static int sc1200_dma_end(ide_drive_t *drive) static void sc1200_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; int mode = -1; /* diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index e966113fd569..90574ab76345 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -217,7 +217,7 @@ scc_ide_outsl(unsigned long port, void *addr, u32 count) static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct scc_ports *ports = ide_get_hwifdata(hwif); unsigned long ctl_base = ports->ctl; unsigned long cckctrl_port = ctl_base + 0xff0; @@ -249,7 +249,7 @@ static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio) static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct scc_ports *ports = ide_get_hwifdata(hwif); unsigned long ctl_base = ports->ctl; unsigned long cckctrl_port = ctl_base + 0xff0; @@ -387,7 +387,7 @@ static int __scc_dma_end(ide_drive_t *drive) static int scc_dma_end(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; void __iomem *dma_base = (void __iomem *)hwif->dma_base; unsigned long intsts_port = hwif->dma_base + 0x014; u32 reg; @@ -496,7 +496,7 @@ static int scc_dma_end(ide_drive_t *drive) /* returns 1 if dma irq issued, 0 otherwise */ static int scc_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u32 int_stat = in_be32((void __iomem *)hwif->dma_base + 0x014); /* SCC errata A252,A308 workaround: Step4 */ diff --git a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c index 437bc919dafd..382102ba467b 100644 --- a/drivers/ide/serverworks.c +++ b/drivers/ide/serverworks.c @@ -151,7 +151,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) static const u8 dma_modes[] = { 0x77, 0x21, 0x20 }; static const u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 }; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 unit = drive->dn & 1; diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index c68b71b1087b..8e1ffd57a86d 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -123,7 +123,7 @@ static int sgiioc4_clearirq(ide_drive_t * drive) { u32 intr_reg; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; unsigned long other_ir = io_ports->irq_addr + (IOC4_INTR_REG << 2); @@ -181,7 +181,7 @@ sgiioc4_clearirq(ide_drive_t * drive) static void sgiioc4_dma_start(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long ioc4_dma_addr = hwif->dma_base + IOC4_DMA_CTRL * 4; unsigned int reg = readl((void __iomem *)ioc4_dma_addr); unsigned int temp_reg = reg | IOC4_S_DMA_START; @@ -209,7 +209,7 @@ sgiioc4_ide_dma_stop(ide_hwif_t *hwif, u64 dma_base) static int sgiioc4_dma_end(ide_drive_t *drive) { u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long dma_base = hwif->dma_base; int dma_stat = 0; unsigned long *ending_dma = ide_get_hwifdata(hwif); @@ -271,7 +271,7 @@ static void sgiioc4_set_dma_mode(ide_drive_t *drive, const u8 speed) /* returns 1 if dma irq issued, 0 otherwise */ static int sgiioc4_dma_test_irq(ide_drive_t *drive) { - return sgiioc4_checkirq(HWIF(drive)); + return sgiioc4_checkirq(drive->hwif); } static void sgiioc4_dma_host_set(ide_drive_t *drive, int on) @@ -367,7 +367,7 @@ static void sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) { u32 ioc4_dma; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long dma_base = hwif->dma_base; unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4; u32 dma_addr, ending_dma_addr; @@ -427,7 +427,7 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) static unsigned int sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned int *table = hwif->dmatable_cpu; unsigned int count = 0, i = 1; struct scatterlist *sg; diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c index 7d622d20bc4c..652b3a04620f 100644 --- a/drivers/ide/siimage.c +++ b/drivers/ide/siimage.c @@ -114,7 +114,7 @@ static unsigned long siimage_selreg(ide_hwif_t *hwif, int r) static inline unsigned long siimage_seldev(ide_drive_t *drive, int r) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long base = (unsigned long)hwif->hwif_data; u8 unit = drive->dn & 1; @@ -243,7 +243,7 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio) static const u16 tf_speed[] = { 0x328a, 0x2283, 0x1281, 0x10c3, 0x10c1 }; static const u16 data_speed[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 }; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); ide_drive_t *pair = ide_get_pair_dev(drive); u32 speedt = 0; @@ -300,7 +300,7 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed) static const u8 ultra5[] = { 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01 }; static const u16 dma[] = { 0x2208, 0x10C2, 0x10C1 }; - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long base = (unsigned long)hwif->hwif_data; u16 ultra = 0, multi = 0; @@ -340,7 +340,7 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed) /* returns 1 if dma irq issued, 0 otherwise */ static int siimage_io_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 dma_altstat = 0; unsigned long addr = siimage_selreg(hwif, 1); @@ -367,7 +367,7 @@ static int siimage_io_dma_test_irq(ide_drive_t *drive) static int siimage_mmio_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long addr = siimage_selreg(hwif, 0x1); void __iomem *sata_error_addr = (void __iomem *)hwif->sata_scr[SATA_ERROR_OFFSET]; diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c index ad32e18c5ba3..9ec1a4a4432c 100644 --- a/drivers/ide/sis5513.c +++ b/drivers/ide/sis5513.c @@ -274,7 +274,7 @@ static void sis_program_timings(ide_drive_t *drive, const u8 mode) static void config_drive_art_rwp(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 reg4bh = 0; u8 rw_prefetch = 0; diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c index 84dc33602ff8..1ded01d81ab3 100644 --- a/drivers/ide/sl82c105.c +++ b/drivers/ide/sl82c105.c @@ -140,7 +140,7 @@ static inline void sl82c105_reset_host(struct pci_dev *dev) */ static void sl82c105_dma_lost_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; u8 dma_cmd; @@ -177,7 +177,7 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive) */ static void sl82c105_dma_start(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int reg = 0x44 + drive->dn * 4; diff --git a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c index 0f759e4ed779..40b4b94a4288 100644 --- a/drivers/ide/slc90e66.c +++ b/drivers/ide/slc90e66.c @@ -20,7 +20,7 @@ static DEFINE_SPINLOCK(slc90e66_lock); static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); int is_slave = drive->dn & 1; int master_port = hwif->channel ? 0x42 : 0x40; @@ -73,7 +73,7 @@ static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio) static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u8 maslave = hwif->channel ? 0x42 : 0x40; int sitre = 0, a_speed = 7 << (drive->dn * 4); diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c index accb379bcad6..d2c00fb928e4 100644 --- a/drivers/ide/tc86c001.c +++ b/drivers/ide/tc86c001.c @@ -15,7 +15,7 @@ static void tc86c001_set_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long scr_port = hwif->config_data + (drive->dn ? 0x02 : 0x00); u16 mode, scr = inw(scr_port); @@ -62,7 +62,7 @@ static void tc86c001_set_pio_mode(ide_drive_t *drive, const u8 pio) */ static int tc86c001_timer_expiry(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; ide_expiry_t *expiry = ide_get_hwifdata(hwif); u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); @@ -109,7 +109,7 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) static void tc86c001_dma_start(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; unsigned long sc_base = hwif->config_data; unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); unsigned long nsectors = hwif->rq->nr_sectors; diff --git a/drivers/ide/triflex.c b/drivers/ide/triflex.c index b6ff40336aa9..8773c3ba7462 100644 --- a/drivers/ide/triflex.c +++ b/drivers/ide/triflex.c @@ -36,7 +36,7 @@ static void triflex_set_mode(ide_drive_t *drive, const u8 speed) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); u32 triflex_timings = 0; u16 timing = 0; diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index 79a03df118d8..b6a1285a4021 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -144,7 +144,7 @@ static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; u16 reg = 0; unsigned long flags; @@ -222,15 +222,15 @@ static int trm290_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* purge DMA mappings */ ide_destroy_dmatable(drive); - status = inw(HWIF(drive)->dma_base + 2); + status = inw(drive->hwif->dma_base + 2); + return status != 0x00ff; } static int trm290_dma_test_irq(ide_drive_t *drive) { - u16 status; + u16 status = inw(drive->hwif->dma_base + 2); - status = inw(HWIF(drive)->dma_base + 2); return status == 0x00ff; } diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c index 2a812d3207e9..fecc0e03c3fc 100644 --- a/drivers/ide/via82cxxx.c +++ b/drivers/ide/via82cxxx.c @@ -178,7 +178,7 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed) ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); } - via_set_speed(HWIF(drive), drive->dn, &t); + via_set_speed(hwif, drive->dn, &t); } /** diff --git a/include/linux/ide.h b/include/linux/ide.h index ee2f461882ad..58b9c99482cd 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -42,8 +42,6 @@ typedef unsigned char byte; /* used everywhere */ #define ERROR_RESET 3 /* Reset controller every 4th retry */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ -#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif)) - /* * Definitions for accessing IDE controller registers */ -- cgit v1.2.3-71-gd317 From 54cc1428cfa619e16d75baae8cb041a2eff015f0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:52 +0100 Subject: ide: remove local_irq_set() macro Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 3 ++- drivers/ide/ide-probe.c | 3 ++- include/linux/ide.h | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 386080ee4608..44ad03bf9cb4 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -503,7 +503,8 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti stat = tp_ops->read_status(hwif); if (stat & ATA_BUSY) { - local_irq_set(flags); + local_irq_save(flags); + local_irq_enable_in_hardirq(); timeout += jiffies; while ((stat = tp_ops->read_status(hwif)) & ATA_BUSY) { if (time_after(jiffies, timeout)) { diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9dfa99f062aa..2fca32ed576f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -796,7 +796,8 @@ static int ide_probe_port(ide_hwif_t *hwif) if (irqd) disable_irq(hwif->irq); - local_irq_set(flags); + local_irq_save(flags); + local_irq_enable_in_hardirq(); if (ide_port_wait_ready(hwif) == -EBUSY) printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name); diff --git a/include/linux/ide.h b/include/linux/ide.h index 58b9c99482cd..82d500c5a847 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1586,8 +1586,6 @@ static inline void ide_set_max_pio(ide_drive_t *drive) ide_set_pio(drive, 255); } -#define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) - char *ide_media_string(ide_drive_t *); extern struct device_attribute ide_dev_attrs[]; -- cgit v1.2.3-71-gd317 From c0ae50234771684ae0cbac5dfb70e1a09c22aa89 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:52 +0100 Subject: ide: remove ide_pci_enablebit_t typedef Remove needless parens while at it. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/setup-pci.c | 2 +- include/linux/ide.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 9f1f9163a136..09f820af379d 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -471,7 +471,7 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, */ for (port = 0; port < channels; ++port) { - const ide_pci_enablebit_t *e = &(d->enablebits[port]); + const struct ide_pci_enablebit *e = &d->enablebits[port]; if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val)) { diff --git a/include/linux/ide.h b/include/linux/ide.h index 82d500c5a847..cca6cfb299bc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1300,11 +1300,11 @@ static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, } #endif -typedef struct ide_pci_enablebit_s { +struct ide_pci_enablebit { u8 reg; /* byte pci reg holding the enable-bit */ u8 mask; /* mask to isolate the enable-bit */ u8 val; /* value of masked reg when "enabled" */ -} ide_pci_enablebit_t; +}; enum { /* Uses ISA control ports not PCI ones. */ @@ -1393,7 +1393,8 @@ struct ide_port_info { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - ide_pci_enablebit_t enablebits[2]; + struct ide_pci_enablebit enablebits[2]; + hwif_chipset_t chipset; u16 max_sectors; /* if < than the default one */ -- cgit v1.2.3-71-gd317 From 9892ec5497af1ec38c46974b5a370ba11c636b19 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:53 +0100 Subject: ide: remove 'byte' typedef Just use u8 instead, also s/__u8/u8/ in ide-cd.h while at it. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.h | 26 +++++++++++++------------- drivers/ide/it821x.c | 4 ++-- drivers/ide/qd65xx.h | 4 ++-- drivers/ide/sc1200.c | 2 +- include/linux/ide.h | 4 +--- 5 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index bf676b262181..d5ae036af00c 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -33,33 +33,33 @@ /* Structure of a MSF cdrom address. */ struct atapi_msf { - byte reserved; - byte minute; - byte second; - byte frame; + u8 reserved; + u8 minute; + u8 second; + u8 frame; }; /* Space to hold the disk TOC. */ #define MAX_TRACKS 99 struct atapi_toc_header { unsigned short toc_length; - byte first_track; - byte last_track; + u8 first_track; + u8 last_track; }; struct atapi_toc_entry { - byte reserved1; + u8 reserved1; #if defined(__BIG_ENDIAN_BITFIELD) - __u8 adr : 4; - __u8 control : 4; + u8 adr : 4; + u8 control : 4; #elif defined(__LITTLE_ENDIAN_BITFIELD) - __u8 control : 4; - __u8 adr : 4; + u8 control : 4; + u8 adr : 4; #else #error "Please fix " #endif - byte track; - byte reserved2; + u8 track; + u8 reserved2; union { unsigned lba; struct atapi_msf msf; diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c index ef004089761b..a34f3e19b4a4 100644 --- a/drivers/ide/it821x.c +++ b/drivers/ide/it821x.c @@ -279,7 +279,7 @@ static void it821x_set_pio_mode(ide_drive_t *drive, const u8 pio) * the shared MWDMA/PIO timing register. */ -static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted) +static void it821x_tune_mwdma(ide_drive_t *drive, u8 mode_wanted) { ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); @@ -316,7 +316,7 @@ static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted) * controller when doing UDMA modes in pass through. */ -static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted) +static void it821x_tune_udma(ide_drive_t *drive, u8 mode_wanted) { ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = to_pci_dev(hwif->dev); diff --git a/drivers/ide/qd65xx.h b/drivers/ide/qd65xx.h index c83dea85e621..6636f9665d16 100644 --- a/drivers/ide/qd65xx.h +++ b/drivers/ide/qd65xx.h @@ -31,8 +31,8 @@ #define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff) -#define QD_TIMING(drive) (byte)(((drive)->drive_data) & 0x00ff) -#define QD_TIMREG(drive) (byte)((((drive)->drive_data) & 0xff00) >> 8) +#define QD_TIMING(drive) (u8)(((drive)->drive_data) & 0x00ff) +#define QD_TIMREG(drive) (u8)((((drive)->drive_data) & 0xff00) >> 8) #define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08)) #define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index 6f68fe984bfb..1cf477aaae36 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -172,7 +172,7 @@ static int sc1200_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; unsigned long dma_base = hwif->dma_base; - byte dma_stat; + u8 dma_stat; dma_stat = inb(dma_base+2); /* get DMA status */ diff --git a/include/linux/ide.h b/include/linux/ide.h index cca6cfb299bc..545a67f1f6b5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -32,8 +32,6 @@ # define SUPPORT_VLB_SYNC 1 #endif -typedef unsigned char byte; /* used everywhere */ - /* * Probably not wise to fiddle with these */ @@ -1161,7 +1159,7 @@ void ide_pad_transfer(ide_drive_t *, int, int); ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8); -ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat); +ide_startstop_t ide_error(ide_drive_t *, const char *, u8); void ide_fix_driveid(u16 *); -- cgit v1.2.3-71-gd317 From 7f3c868ba78e486bd9d7569f884dd46d8f59bb18 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:53 +0100 Subject: ide: remove ide_driver_t typedef While at it: - s/struct ide_driver_s/struct ide_driver/ - use to_ide_driver() macro in ide-proc.c Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 2 +- drivers/ide/ide-cd.h | 8 ++++---- drivers/ide/ide-gd.c | 2 +- drivers/ide/ide-gd.h | 10 +++++----- drivers/ide/ide-io.c | 12 ++++++------ drivers/ide/ide-pm.c | 2 +- drivers/ide/ide-proc.c | 16 ++++++++-------- drivers/ide/ide-tape.c | 10 +++++----- drivers/ide/ide-taskfile.c | 8 ++++---- drivers/ide/ide.c | 6 +++--- include/linux/ide.h | 18 +++++++++--------- 11 files changed, 47 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index c35b64d495f5..bc982dfacc36 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1914,7 +1914,7 @@ static void ide_cd_release(struct kref *kref) static int ide_cd_probe(ide_drive_t *); -static ide_driver_t ide_cdrom_driver = { +static struct ide_driver ide_cdrom_driver = { .gen_driver = { .owner = THIS_MODULE, .name = "ide-cdrom", diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index d5ae036af00c..ac40d6cb90a2 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -77,10 +77,10 @@ struct atapi_toc { /* Extra per-device info for cdrom drives. */ struct cdrom_info { - ide_drive_t *drive; - ide_driver_t *driver; - struct gendisk *disk; - struct kref kref; + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct kref kref; /* Buffer for table of contents. NULL if we haven't allocated a TOC buffer for this device yet. */ diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index b8078b3231f7..cae82b3c432a 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -149,7 +149,7 @@ static int ide_gd_end_request(ide_drive_t *drive, int uptodate, int nrsecs) return drive->disk_ops->end_request(drive, uptodate, nrsecs); } -static ide_driver_t ide_gd_driver = { +static struct ide_driver ide_gd_driver = { .gen_driver = { .owner = THIS_MODULE, .name = "ide-gd", diff --git a/drivers/ide/ide-gd.h b/drivers/ide/ide-gd.h index 7d3d101713e0..a86779f0756b 100644 --- a/drivers/ide/ide-gd.h +++ b/drivers/ide/ide-gd.h @@ -14,11 +14,11 @@ #endif struct ide_disk_obj { - ide_drive_t *drive; - ide_driver_t *driver; - struct gendisk *disk; - struct kref kref; - unsigned int openers; /* protected by BKL for now */ + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct kref kref; + unsigned int openers; /* protected by BKL for now */ /* Last failed packet command */ struct ide_atapi_pc *failed_pc; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 0e2b95ec08a5..e788b3622f2e 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -199,9 +199,9 @@ EXPORT_SYMBOL(ide_end_drive_cmd); static void ide_kill_rq(ide_drive_t *drive, struct request *rq) { if (rq->rq_disk) { - ide_driver_t *drv; + struct ide_driver *drv; - drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv = *(struct ide_driver **)rq->rq_disk->private_data; drv->end_request(drive, 0, 0); } else ide_end_request(drive, 0, 0); @@ -333,9 +333,9 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) } if (rq->rq_disk) { - ide_driver_t *drv; + struct ide_driver *drv; - drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv = *(struct ide_driver **)rq->rq_disk->private_data; return drv->error(drive, rq, stat, err); } else return __ide_error(drive, rq, stat, err); @@ -606,7 +606,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) return startstop; } if (!drive->special.all) { - ide_driver_t *drv; + struct ide_driver *drv; /* * We reset the drive so we need to issue a SETFEATURES. @@ -639,7 +639,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) */ return ide_special_rq(drive, rq); - drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv = *(struct ide_driver **)rq->rq_disk->private_data; return drv->do_request(drive, rq, rq->sector); } diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index 0c206c68bbb1..4b3bf6a06b70 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -67,7 +67,7 @@ int generic_ide_resume(struct device *dev) blk_put_request(rq); if (err == 0 && dev->driver) { - ide_driver_t *drv = to_ide_driver(dev->driver); + struct ide_driver *drv = to_ide_driver(dev->driver); if (drv->resume) drv->resume(drive); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index a14e2938e4f3..d985a9ec6bef 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -439,13 +439,13 @@ static int proc_ide_read_dmodel static int proc_ide_read_driver (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; - struct device *dev = &drive->gendev; - ide_driver_t *ide_drv; - int len; + ide_drive_t *drive = (ide_drive_t *)data; + struct device *dev = &drive->gendev; + struct ide_driver *ide_drv; + int len; if (dev->driver) { - ide_drv = container_of(dev->driver, ide_driver_t, gen_driver); + ide_drv = to_ide_driver(dev->driver); len = sprintf(page, "%s version %s\n", dev->driver->name, ide_drv->version); } else @@ -555,7 +555,7 @@ static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t } } -void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) +void ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver) { mutex_lock(&ide_setting_mtx); drive->settings = driver->proc_devsets(drive); @@ -577,7 +577,7 @@ EXPORT_SYMBOL(ide_proc_register_driver); * Takes ide_setting_mtx. */ -void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) +void ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver) { ide_remove_proc_entries(drive->proc, driver->proc_entries(drive)); @@ -653,7 +653,7 @@ void ide_proc_unregister_port(ide_hwif_t *hwif) static int proc_print_driver(struct device_driver *drv, void *data) { - ide_driver_t *ide_drv = container_of(drv, ide_driver_t, gen_driver); + struct ide_driver *ide_drv = to_ide_driver(drv); struct seq_file *s = data; seq_printf(s, "%s version %s\n", drv->name, ide_drv->version); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index e39f2f46d982..fd865039bfdb 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -166,10 +166,10 @@ struct idetape_bh { * to an interrupt or a timer event is stored in the struct defined below. */ typedef struct ide_tape_obj { - ide_drive_t *drive; - ide_driver_t *driver; - struct gendisk *disk; - struct kref kref; + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct kref kref; /* * failed_pc points to the last failed packet command, or contains @@ -2313,7 +2313,7 @@ static const struct ide_proc_devset *ide_tape_proc_devsets(ide_drive_t *drive) static int ide_tape_probe(ide_drive_t *); -static ide_driver_t idetape_driver = { +static struct ide_driver idetape_driver = { .gen_driver = { .owner = THIS_MODULE, .name = "ide-tape", diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 55d451ce9b4f..16138bce84a7 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -309,9 +309,9 @@ static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, } if (sectors > 0) { - ide_driver_t *drv; + struct ide_driver *drv; - drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv = *(struct ide_driver **)rq->rq_disk->private_data; drv->end_request(drive, 1, sectors); } } @@ -328,9 +328,9 @@ void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) } if (rq->rq_disk) { - ide_driver_t *drv; + struct ide_driver *drv; - drv = *(ide_driver_t **)rq->rq_disk->private_data;; + drv = *(struct ide_driver **)rq->rq_disk->private_data;; drv->end_request(drive, 1, rq->nr_sectors); } else ide_end_request(drive, 1, rq->nr_sectors); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index c1bb0f6784a9..6971c285a212 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -281,7 +281,7 @@ static int ide_uevent(struct device *dev, struct kobj_uevent_env *env) static int generic_ide_probe(struct device *dev) { ide_drive_t *drive = to_ide_device(dev); - ide_driver_t *drv = to_ide_driver(dev->driver); + struct ide_driver *drv = to_ide_driver(dev->driver); return drv->probe ? drv->probe(drive) : -ENODEV; } @@ -289,7 +289,7 @@ static int generic_ide_probe(struct device *dev) static int generic_ide_remove(struct device *dev) { ide_drive_t *drive = to_ide_device(dev); - ide_driver_t *drv = to_ide_driver(dev->driver); + struct ide_driver *drv = to_ide_driver(dev->driver); if (drv->remove) drv->remove(drive); @@ -300,7 +300,7 @@ static int generic_ide_remove(struct device *dev) static void generic_ide_shutdown(struct device *dev) { ide_drive_t *drive = to_ide_device(dev); - ide_driver_t *drv = to_ide_driver(dev->driver); + struct ide_driver *drv = to_ide_driver(dev->driver); if (dev->driver && drv->shutdown) drv->shutdown(drive); diff --git a/include/linux/ide.h b/include/linux/ide.h index 545a67f1f6b5..fcbcfa2cbe75 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -437,7 +437,7 @@ struct ide_atapi_pc { }; struct ide_devset; -struct ide_driver_s; +struct ide_driver; #ifdef CONFIG_BLK_DEV_IDEACPI struct ide_acpi_drive_link; @@ -884,8 +884,6 @@ typedef int (ide_expiry_t)(ide_drive_t *); /* used by ide-cd, ide-floppy, etc. */ typedef void (xfer_func_t)(ide_drive_t *, struct request *rq, void *, unsigned); -typedef struct ide_driver_s ide_driver_t; - extern struct mutex ide_setting_mtx; /* @@ -1011,8 +1009,8 @@ void ide_proc_register_port(ide_hwif_t *); void ide_proc_port_register_devices(ide_hwif_t *); void ide_proc_unregister_device(ide_drive_t *); void ide_proc_unregister_port(ide_hwif_t *); -void ide_proc_register_driver(ide_drive_t *, ide_driver_t *); -void ide_proc_unregister_driver(ide_drive_t *, ide_driver_t *); +void ide_proc_register_driver(ide_drive_t *, struct ide_driver *); +void ide_proc_unregister_driver(ide_drive_t *, struct ide_driver *); read_proc_t proc_ide_read_capacity; read_proc_t proc_ide_read_geometry; @@ -1039,8 +1037,10 @@ static inline void ide_proc_register_port(ide_hwif_t *hwif) { ; } static inline void ide_proc_port_register_devices(ide_hwif_t *hwif) { ; } static inline void ide_proc_unregister_device(ide_drive_t *drive) { ; } static inline void ide_proc_unregister_port(ide_hwif_t *hwif) { ; } -static inline void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) { ; } -static inline void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) { ; } +static inline void ide_proc_register_driver(ide_drive_t *drive, + struct ide_driver *driver) { ; } +static inline void ide_proc_unregister_driver(ide_drive_t *drive, + struct ide_driver *driver) { ; } #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0; #endif @@ -1109,7 +1109,7 @@ void ide_check_pm_state(ide_drive_t *, struct request *); * The gendriver.owner field should be set to the module owner of this driver. * The gendriver.name field should be set to the name of this driver */ -struct ide_driver_s { +struct ide_driver { const char *version; ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t); int (*end_request)(ide_drive_t *, int, int); @@ -1125,7 +1125,7 @@ struct ide_driver_s { #endif }; -#define to_ide_driver(drv) container_of(drv, ide_driver_t, gen_driver) +#define to_ide_driver(drv) container_of(drv, struct ide_driver, gen_driver) int ide_device_get(ide_drive_t *); void ide_device_put(ide_drive_t *); -- cgit v1.2.3-71-gd317 From 627e05daa10896a8f012fa78e8434c07e9e55ea7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:54 +0100 Subject: ide: remove ->error method from struct ide_driver * Remove (now superfluous) ->error method from struct ide_driver. * Unexport __ide_error() and make it static. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 1 - drivers/ide/ide-gd.c | 1 - drivers/ide/ide-io.c | 13 ++----------- drivers/ide/ide-tape.c | 1 - include/linux/ide.h | 3 --- 5 files changed, 2 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index bc982dfacc36..6c7dd8fd8638 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1925,7 +1925,6 @@ static struct ide_driver ide_cdrom_driver = { .version = IDECD_VERSION, .do_request = ide_cd_do_request, .end_request = ide_end_request, - .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc_entries = ide_cd_proc_entries, .proc_devsets = ide_cd_proc_devsets, diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index cae82b3c432a..7857b209c6df 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -162,7 +162,6 @@ static struct ide_driver ide_gd_driver = { .version = IDE_GD_VERSION, .do_request = ide_gd_do_request, .end_request = ide_gd_end_request, - .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc_entries = ide_disk_proc_entries, .proc_devsets = ide_disk_proc_devsets, diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index e788b3622f2e..9d363a1b5573 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -291,7 +291,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u return ide_stopped; } -ide_startstop_t +static ide_startstop_t __ide_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { if (drive->media == ide_disk) @@ -299,8 +299,6 @@ __ide_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) return ide_atapi_error(drive, rq, stat, err); } -EXPORT_SYMBOL_GPL(__ide_error); - /** * ide_error - handle an error on the IDE * @drive: drive the error occurred on @@ -332,15 +330,8 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) return ide_stopped; } - if (rq->rq_disk) { - struct ide_driver *drv; - - drv = *(struct ide_driver **)rq->rq_disk->private_data; - return drv->error(drive, rq, stat, err); - } else - return __ide_error(drive, rq, stat, err); + return __ide_error(drive, rq, stat, err); } - EXPORT_SYMBOL_GPL(ide_error); static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index fd865039bfdb..d7ecd3c79757 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2324,7 +2324,6 @@ static struct ide_driver idetape_driver = { .version = IDETAPE_VERSION, .do_request = idetape_do_request, .end_request = idetape_end_request, - .error = __ide_error, #ifdef CONFIG_IDE_PROC_FS .proc_entries = ide_tape_proc_entries, .proc_devsets = ide_tape_proc_devsets, diff --git a/include/linux/ide.h b/include/linux/ide.h index fcbcfa2cbe75..9f6fe1fe7a6c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1113,7 +1113,6 @@ struct ide_driver { const char *version; ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t); int (*end_request)(ide_drive_t *, int, int); - ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8); struct device_driver gen_driver; int (*probe)(ide_drive_t *); void (*remove)(ide_drive_t *); @@ -1157,8 +1156,6 @@ void ide_execute_pkt_cmd(ide_drive_t *); void ide_pad_transfer(ide_drive_t *, int, int); -ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8); - ide_startstop_t ide_error(ide_drive_t *, const char *, u8); void ide_fix_driveid(u16 *); -- cgit v1.2.3-71-gd317 From 5e7f3a46690f7f6c9f2781c700ab4370874aa0e8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:56 +0100 Subject: ide: dynamic allocation of device structures Allocate device structures dynamically instead of having them embedded in ide_hwif_t: * Remove needless zeroing of port structure from ide_init_port_data(). * Add ide_hwif_t.devices[MAX_DRIVES] (table of pointers to the devices). * Add ide_port_{alloc,free}_devices() helpers and use them respectively in ide_{host,free}_alloc(). * Convert all users of ->drives[] to use ->devices[] instead. While at it: * Use drive->dn for the slave device check in scc_pata.c. As a nice side-effect this patch cuts ~1kB (x86-32) from the resulting code size: text data bss dec hex filename 53963 1244 237 55444 d894 drivers/ide/ide-core.o.before 52981 1244 237 54462 d4be drivers/ide/ide-core.o.after Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-acpi.c | 10 +++---- drivers/ide/ide-iops.c | 4 +-- drivers/ide/ide-probe.c | 71 +++++++++++++++++++++++++++++++++++-------------- drivers/ide/ide-proc.c | 2 +- drivers/ide/ide.c | 2 +- drivers/ide/scc_pata.c | 5 ++-- include/linux/ide.h | 4 +-- 7 files changed, 65 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index f89b6ecf7d1a..fd155b8a256c 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -656,7 +656,7 @@ void ide_acpi_set_state(ide_hwif_t *hwif, int on) if (on) acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; if (!drive->acpidata->obj_handle) drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive); @@ -711,14 +711,14 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) * for both drives, regardless whether they are connected * or not. */ - hwif->drives[0].acpidata = &hwif->acpidata->master; - hwif->drives[1].acpidata = &hwif->acpidata->slave; + hwif->devices[0]->acpidata = &hwif->acpidata->master; + hwif->devices[1]->acpidata = &hwif->acpidata->slave; /* * Send IDENTIFY for each drive */ for (i = 0; i < MAX_DRIVES; i++) { - drive = &hwif->drives[i]; + drive = hwif->devices[i]; memset(drive->acpidata, 0, sizeof(*drive->acpidata)); @@ -745,7 +745,7 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) ide_acpi_push_timing(hwif); for (i = 0; i < MAX_DRIVES; i++) { - drive = &hwif->drives[i]; + drive = hwif->devices[i]; if (drive->dev_flags & IDE_DFLAG_PRESENT) /* Execute ACPI startup code */ diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 1bcb9484f49e..26b58d15c4e6 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -1111,7 +1111,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE); timeout = jiffies; for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *tdrive = &hwif->drives[unit]; + ide_drive_t *tdrive = hwif->devices[unit]; if (tdrive->dev_flags & IDE_DFLAG_PRESENT && tdrive->dev_flags & IDE_DFLAG_PARKED && @@ -1134,7 +1134,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * for any of the drives on this interface. */ for (unit = 0; unit < MAX_DRIVES; ++unit) - pre_reset(&hwif->drives[unit]); + pre_reset(hwif->devices[unit]); if (io_ports->ctl_addr == 0) { spin_unlock_irqrestore(&hwif->lock, flags); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 00c7e5a67bd1..006e601cafb8 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -463,7 +463,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) if (ide_read_device(drive) != drive->select && present == 0) { if (drive->dn & 1) { /* exit with drive0 selected */ - SELECT_DRIVE(&hwif->drives[0]); + SELECT_DRIVE(hwif->devices[0]); /* allow ATA_BUSY to assert & clear */ msleep(50); } @@ -509,7 +509,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) } if (drive->dn & 1) { /* exit with drive0 selected */ - SELECT_DRIVE(&hwif->drives[0]); + SELECT_DRIVE(hwif->devices[0]); msleep(50); /* ensure drive irq is clear */ (void)tp_ops->read_status(hwif); @@ -715,7 +715,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Now make sure both master & slave are ready */ for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; /* Ignore disks that we will not probe for later. */ if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || @@ -733,7 +733,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) out: /* Exit function with master reselected (let's be sane) */ if (unit) - SELECT_DRIVE(&hwif->drives[0]); + SELECT_DRIVE(hwif->devices[0]); return rc; } @@ -749,7 +749,7 @@ out: void ide_undecoded_slave(ide_drive_t *dev1) { - ide_drive_t *dev0 = &dev1->hwif->drives[0]; + ide_drive_t *dev0 = dev1->hwif->devices[0]; if ((dev1->dn & 1) == 0 || (dev0->dev_flags & IDE_DFLAG_PRESENT) == 0) return; @@ -784,8 +784,8 @@ static int ide_probe_port(ide_hwif_t *hwif) BUG_ON(hwif->present); - if ((hwif->drives[0].dev_flags & IDE_DFLAG_NOPROBE) && - (hwif->drives[1].dev_flags & IDE_DFLAG_NOPROBE)) + if ((hwif->devices[0]->dev_flags & IDE_DFLAG_NOPROBE) && + (hwif->devices[1]->dev_flags & IDE_DFLAG_NOPROBE)) return -EACCES; /* @@ -807,7 +807,7 @@ static int ide_probe_port(ide_hwif_t *hwif) * but a lot of cdrom drives are configured as single slaves. */ for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; (void) probe_for_drive(drive); if (drive->dev_flags & IDE_DFLAG_PRESENT) @@ -832,7 +832,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) int unit; for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; if (drive->dev_flags & IDE_DFLAG_PRESENT) { if (port_ops && port_ops->quirkproc) @@ -841,7 +841,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) } for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; if (drive->dev_flags & IDE_DFLAG_PRESENT) { ide_set_max_pio(drive); @@ -854,7 +854,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) } for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; if ((hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) || drive->id[ATA_ID_DWORD_IO]) @@ -931,7 +931,7 @@ static int ide_port_setup_devices(ide_hwif_t *hwif) mutex_lock(&ide_cfg_mtx); for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = &hwif->drives[i]; + ide_drive_t *drive = hwif->devices[i]; if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) continue; @@ -1017,7 +1017,7 @@ static struct kobject *ata_probe(dev_t dev, int *part, void *data) { ide_hwif_t *hwif = data; int unit = *part >> PARTN_BITS; - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) return NULL; @@ -1164,7 +1164,7 @@ static void hwif_register_devices(ide_hwif_t *hwif) unsigned int i; for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = &hwif->drives[i]; + ide_drive_t *drive = hwif->devices[i]; struct device *dev = &drive->gendev; int ret; @@ -1190,7 +1190,7 @@ static void ide_port_init_devices(ide_hwif_t *hwif) int i; for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = &hwif->drives[i]; + ide_drive_t *drive = hwif->devices[i]; drive->dn = i + hwif->channel * 2; @@ -1285,7 +1285,7 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) int unit; for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; u8 j = (hwif->index * MAX_DRIVES) + unit; memset(drive, 0, sizeof(*drive)); @@ -1309,9 +1309,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) static void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) { - /* bulk initialize hwif & drive info with zeros */ - memset(hwif, 0, sizeof(ide_hwif_t)); - /* fill in any non-zero initial values */ hwif->index = index; hwif->major = ide_hwif_to_major[index]; @@ -1388,6 +1385,34 @@ static void ide_free_port_slot(int idx) mutex_unlock(&ide_cfg_mtx); } +static void ide_port_free_devices(ide_hwif_t *hwif) +{ + int i; + + for (i = 0; i < MAX_DRIVES; i++) + kfree(hwif->devices[i]); +} + +static int ide_port_alloc_devices(ide_hwif_t *hwif, int node) +{ + int i; + + for (i = 0; i < MAX_DRIVES; i++) { + ide_drive_t *drive; + + drive = kzalloc_node(sizeof(*drive), GFP_KERNEL, node); + if (drive == NULL) + goto out_nomem; + + hwif->devices[i] = drive; + } + return 0; + +out_nomem: + ide_port_free_devices(hwif); + return -ENOMEM; +} + struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) { struct ide_host *host; @@ -1410,6 +1435,11 @@ struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) if (hwif == NULL) continue; + if (ide_port_alloc_devices(hwif, node) < 0) { + kfree(hwif); + continue; + } + idx = ide_find_port_slot(d); if (idx < 0) { printk(KERN_ERR "%s: no free slot for interface\n", @@ -1575,7 +1605,7 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif) int i; for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = &hwif->drives[i]; + ide_drive_t *drive = hwif->devices[i]; if (drive->dev_flags & IDE_DFLAG_PRESENT) { device_unregister(&drive->gendev); @@ -1651,6 +1681,7 @@ void ide_host_free(struct ide_host *host) if (hwif == NULL) continue; + ide_port_free_devices(hwif); ide_free_port_slot(hwif->index); kfree(hwif); } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index d985a9ec6bef..1dc827fa7061 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -599,7 +599,7 @@ void ide_proc_port_register_devices(ide_hwif_t *hwif) char name[64]; for (d = 0; d < MAX_DRIVES; d++) { - ide_drive_t *drive = &hwif->drives[d]; + ide_drive_t *drive = hwif->devices[d]; if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc) continue; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 6971c285a212..8a6f893d127e 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -497,7 +497,7 @@ void ide_port_apply_params(ide_hwif_t *hwif) } for (i = 0; i < MAX_DRIVES; i++) - ide_dev_apply_params(&hwif->drives[i], i); + ide_dev_apply_params(hwif->devices[i], i); } /* diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 90574ab76345..841164d7d3b9 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -259,7 +259,7 @@ static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed) unsigned long scrcst_port = ctl_base + 0x014; unsigned long udenvt_port = ctl_base + 0x018; unsigned long tdvhsel_port = ctl_base + 0x020; - int is_slave = (&hwif->drives[1] == drive); + int is_slave = drive->dn & 1; int offset, idx; unsigned long reg; unsigned long jcactsel; @@ -413,7 +413,8 @@ static int scc_dma_end(ide_drive_t *drive) if (rq) rq->errors |= ERROR_RESET; for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = &hwif->drives[unit]; + ide_drive_t *drive = hwif->devices[unit]; + drive->crc_count++; } } diff --git a/include/linux/ide.h b/include/linux/ide.h index 9f6fe1fe7a6c..f00086b10be3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -753,7 +753,7 @@ typedef struct hwif_s { unsigned long sata_scr[SATA_NR_PORTS]; - ide_drive_t drives[MAX_DRIVES]; /* drive info */ + ide_drive_t *devices[MAX_DRIVES]; u8 major; /* our major number */ u8 index; /* 0 for ide0; 1 for ide1; ... */ @@ -1600,7 +1600,7 @@ static inline int hwif_to_node(ide_hwif_t *hwif) static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive) { - ide_drive_t *peer = &drive->hwif->drives[(drive->dn ^ 1) & 1]; + ide_drive_t *peer = drive->hwif->devices[(drive->dn ^ 1) & 1]; return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL; } -- cgit v1.2.3-71-gd317 From 2bd24a1cfc99d242c2cff9a6b74ca49fcaac3fb6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:56 +0100 Subject: ide: add port and host iterators Add ide_port_for_each_dev() / ide_host_for_each_port() iterators and update IDE code to use them. While at it: - s/unit/i/ variable in ide_port_wait_ready(), ide_probe_port(), ide_port_tune_devices(), ide_port_init_devices_data(), do_reset1(), ide_acpi_set_state() and scc_dma_end() - s/d/i/ variable in ide_proc_port_register_devices() There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-acpi.c | 14 +++----- drivers/ide/ide-iops.c | 11 +++--- drivers/ide/ide-probe.c | 96 ++++++++++++++++++++----------------------------- drivers/ide/ide-proc.c | 7 ++-- drivers/ide/ide.c | 5 +-- drivers/ide/scc_pata.c | 8 ++--- include/linux/ide.h | 11 ++++-- 7 files changed, 67 insertions(+), 85 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index fd155b8a256c..2f9e941968d6 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -641,7 +641,8 @@ void ide_acpi_push_timing(ide_hwif_t *hwif) */ void ide_acpi_set_state(ide_hwif_t *hwif, int on) { - int unit; + ide_drive_t *drive; + int i; if (ide_noacpi || ide_noacpi_psx) return; @@ -655,9 +656,8 @@ void ide_acpi_set_state(ide_hwif_t *hwif, int on) /* channel first and then drives for power on and verse versa for power off */ if (on) acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = hwif->devices[unit]; + ide_port_for_each_dev(i, drive, hwif) { if (!drive->acpidata->obj_handle) drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive); @@ -717,9 +717,7 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) /* * Send IDENTIFY for each drive */ - for (i = 0; i < MAX_DRIVES; i++) { - drive = hwif->devices[i]; - + ide_port_for_each_dev(i, drive, hwif) { memset(drive->acpidata, 0, sizeof(*drive->acpidata)); if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) @@ -744,9 +742,7 @@ void ide_acpi_port_init_devices(ide_hwif_t *hwif) ide_acpi_get_timing(hwif); ide_acpi_push_timing(hwif); - for (i = 0; i < MAX_DRIVES; i++) { - drive = hwif->devices[i]; - + ide_port_for_each_dev(i, drive, hwif) { if (drive->dev_flags & IDE_DFLAG_PRESENT) /* Execute ACPI startup code */ ide_acpi_exec_tfs(drive); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 26b58d15c4e6..1a22c31ce7ac 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -1081,8 +1081,9 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) struct ide_io_ports *io_ports = &hwif->io_ports; const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_port_ops *port_ops; + ide_drive_t *tdrive; unsigned long flags, timeout; - unsigned int unit; + int i; DEFINE_WAIT(wait); spin_lock_irqsave(&hwif->lock, flags); @@ -1110,9 +1111,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE); timeout = jiffies; - for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *tdrive = hwif->devices[unit]; - + ide_port_for_each_dev(i, tdrive, hwif) { if (tdrive->dev_flags & IDE_DFLAG_PRESENT && tdrive->dev_flags & IDE_DFLAG_PARKED && time_after(tdrive->sleep, timeout)) @@ -1133,8 +1132,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * First, reset any device state data we were maintaining * for any of the drives on this interface. */ - for (unit = 0; unit < MAX_DRIVES; ++unit) - pre_reset(hwif->devices[unit]); + ide_port_for_each_dev(i, tdrive, hwif) + pre_reset(tdrive); if (io_ports->ctl_addr == 0) { spin_unlock_irqrestore(&hwif->lock, flags); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 006e601cafb8..e688ca1c967c 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -697,7 +697,8 @@ out: static int ide_port_wait_ready(ide_hwif_t *hwif) { - int unit, rc; + ide_drive_t *drive; + int i, rc; printk(KERN_DEBUG "Probing IDE interface %s...\n", hwif->name); @@ -714,9 +715,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) return rc; /* Now make sure both master & slave are ready */ - for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = hwif->devices[unit]; - + ide_port_for_each_dev(i, drive, hwif) { /* Ignore disks that we will not probe for later. */ if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || (drive->dev_flags & IDE_DFLAG_PRESENT)) { @@ -732,7 +731,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) } out: /* Exit function with master reselected (let's be sane) */ - if (unit) + if (i) SELECT_DRIVE(hwif->devices[0]); return rc; @@ -778,9 +777,10 @@ EXPORT_SYMBOL_GPL(ide_undecoded_slave); static int ide_probe_port(ide_hwif_t *hwif) { + ide_drive_t *drive; unsigned long flags; unsigned int irqd; - int unit, rc = -ENODEV; + int i, rc = -ENODEV; BUG_ON(hwif->present); @@ -806,9 +806,7 @@ static int ide_probe_port(ide_hwif_t *hwif) * Second drive should only exist if first drive was found, * but a lot of cdrom drives are configured as single slaves. */ - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = hwif->devices[unit]; - + ide_port_for_each_dev(i, drive, hwif) { (void) probe_for_drive(drive); if (drive->dev_flags & IDE_DFLAG_PRESENT) rc = 0; @@ -829,20 +827,17 @@ static int ide_probe_port(ide_hwif_t *hwif) static void ide_port_tune_devices(ide_hwif_t *hwif) { const struct ide_port_ops *port_ops = hwif->port_ops; - int unit; - - for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = hwif->devices[unit]; + ide_drive_t *drive; + int i; + ide_port_for_each_dev(i, drive, hwif) { if (drive->dev_flags & IDE_DFLAG_PRESENT) { if (port_ops && port_ops->quirkproc) port_ops->quirkproc(drive); } } - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = hwif->devices[unit]; - + ide_port_for_each_dev(i, drive, hwif) { if (drive->dev_flags & IDE_DFLAG_PRESENT) { ide_set_max_pio(drive); @@ -853,9 +848,7 @@ static void ide_port_tune_devices(ide_hwif_t *hwif) } } - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = hwif->devices[unit]; - + ide_port_for_each_dev(i, drive, hwif) { if ((hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) || drive->id[ATA_ID_DWORD_IO]) drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT; @@ -927,12 +920,11 @@ static DEFINE_MUTEX(ide_cfg_mtx); */ static int ide_port_setup_devices(ide_hwif_t *hwif) { + ide_drive_t *drive; int i, j = 0; mutex_lock(&ide_cfg_mtx); - for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = hwif->devices[i]; - + ide_port_for_each_dev(i, drive, hwif) { if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) continue; @@ -1161,10 +1153,10 @@ out: static void hwif_register_devices(ide_hwif_t *hwif) { + ide_drive_t *drive; unsigned int i; - for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = hwif->devices[i]; + ide_port_for_each_dev(i, drive, hwif) { struct device *dev = &drive->gendev; int ret; @@ -1187,11 +1179,10 @@ static void hwif_register_devices(ide_hwif_t *hwif) static void ide_port_init_devices(ide_hwif_t *hwif) { const struct ide_port_ops *port_ops = hwif->port_ops; + ide_drive_t *drive; int i; - for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = hwif->devices[i]; - + ide_port_for_each_dev(i, drive, hwif) { drive->dn = i + hwif->channel * 2; if (hwif->host_flags & IDE_HFLAG_IO_32BIT) @@ -1282,16 +1273,16 @@ static const u8 ide_hwif_to_major[] = static void ide_port_init_devices_data(ide_hwif_t *hwif) { - int unit; + ide_drive_t *drive; + int i; - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = hwif->devices[unit]; - u8 j = (hwif->index * MAX_DRIVES) + unit; + ide_port_for_each_dev(i, drive, hwif) { + u8 j = (hwif->index * MAX_DRIVES) + i; memset(drive, 0, sizeof(*drive)); drive->media = ide_disk; - drive->select = (unit << 4) | ATA_DEVICE_OBS; + drive->select = (i << 4) | ATA_DEVICE_OBS; drive->hwif = hwif; drive->ready_stat = ATA_DRDY; drive->bad_wstat = BAD_W_STAT; @@ -1387,10 +1378,11 @@ static void ide_free_port_slot(int idx) static void ide_port_free_devices(ide_hwif_t *hwif) { + ide_drive_t *drive; int i; - for (i = 0; i < MAX_DRIVES; i++) - kfree(hwif->devices[i]); + ide_port_for_each_dev(i, drive, hwif) + kfree(drive); } static int ide_port_alloc_devices(ide_hwif_t *hwif, int node) @@ -1478,9 +1470,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_hwif_t *hwif, *mate = NULL; int i, j = 0; - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) { mate = NULL; continue; @@ -1506,9 +1496,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_port_init_devices(hwif); } - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) continue; @@ -1523,9 +1511,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_port_tune_devices(hwif); } - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) continue; @@ -1550,9 +1536,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, ide_acpi_port_init_devices(hwif); } - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) continue; @@ -1560,9 +1544,7 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, hwif_register_devices(hwif); } - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) continue; @@ -1602,11 +1584,10 @@ EXPORT_SYMBOL_GPL(ide_host_add); static void __ide_port_unregister_devices(ide_hwif_t *hwif) { + ide_drive_t *drive; int i; - for (i = 0; i < MAX_DRIVES; i++) { - ide_drive_t *drive = hwif->devices[i]; - + ide_port_for_each_dev(i, drive, hwif) { if (drive->dev_flags & IDE_DFLAG_PRESENT) { device_unregister(&drive->gendev); wait_for_completion(&drive->gendev_rel_comp); @@ -1675,9 +1656,7 @@ void ide_host_free(struct ide_host *host) ide_hwif_t *hwif; int i; - for (i = 0; i < MAX_HOST_PORTS; i++) { - hwif = host->ports[i]; - + ide_host_for_each_port(i, hwif, host) { if (hwif == NULL) continue; @@ -1692,11 +1671,12 @@ EXPORT_SYMBOL_GPL(ide_host_free); void ide_host_remove(struct ide_host *host) { + ide_hwif_t *hwif; int i; - for (i = 0; i < MAX_HOST_PORTS; i++) { - if (host->ports[i]) - ide_unregister(host->ports[i]); + ide_host_for_each_port(i, hwif, host) { + if (hwif) + ide_unregister(hwif); } ide_host_free(host); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 1dc827fa7061..1d8978b3314a 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -593,14 +593,13 @@ EXPORT_SYMBOL(ide_proc_unregister_driver); void ide_proc_port_register_devices(ide_hwif_t *hwif) { - int d; struct proc_dir_entry *ent; struct proc_dir_entry *parent = hwif->proc; + ide_drive_t *drive; char name[64]; + int i; - for (d = 0; d < MAX_DRIVES; d++) { - ide_drive_t *drive = hwif->devices[d]; - + ide_port_for_each_dev(i, drive, hwif) { if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc) continue; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 8a6f893d127e..258805da15c3 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -488,6 +488,7 @@ MODULE_PARM_DESC(ignore_cable, "ignore cable detection"); void ide_port_apply_params(ide_hwif_t *hwif) { + ide_drive_t *drive; int i; if (ide_ignore_cable & (1 << hwif->index)) { @@ -496,8 +497,8 @@ void ide_port_apply_params(ide_hwif_t *hwif) hwif->cbl = ATA_CBL_PATA40_SHORT; } - for (i = 0; i < MAX_DRIVES; i++) - ide_dev_apply_params(hwif->devices[i], i); + ide_port_for_each_dev(i, drive, hwif) + ide_dev_apply_params(drive, i); } /* diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 841164d7d3b9..5d53850c79d7 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -406,17 +406,17 @@ static int scc_dma_end(ide_drive_t *drive) data_loss = 1; if (retry++) { struct request *rq = hwif->rq; - int unit; + ide_drive_t *drive; + int i; + /* ERROR_RESET and drive->crc_count are needed * to reduce DMA transfer mode in retry process. */ if (rq) rq->errors |= ERROR_RESET; - for (unit = 0; unit < MAX_DRIVES; unit++) { - ide_drive_t *drive = hwif->devices[unit]; + ide_port_for_each_dev(i, drive, hwif) drive->crc_count++; - } } } } diff --git a/include/linux/ide.h b/include/linux/ide.h index f00086b10be3..4cecd923fc79 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -753,7 +753,7 @@ typedef struct hwif_s { unsigned long sata_scr[SATA_NR_PORTS]; - ide_drive_t *devices[MAX_DRIVES]; + ide_drive_t *devices[MAX_DRIVES + 1]; u8 major; /* our major number */ u8 index; /* 0 for ide0; 1 for ide1; ... */ @@ -861,7 +861,7 @@ typedef struct hwif_s { #define MAX_HOST_PORTS 4 struct ide_host { - ide_hwif_t *ports[MAX_HOST_PORTS]; + ide_hwif_t *ports[MAX_HOST_PORTS + 1]; unsigned int n_ports; struct device *dev[2]; unsigned int (*init_chipset)(struct pci_dev *); @@ -1604,4 +1604,11 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive) return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL; } + +#define ide_port_for_each_dev(i, dev, port) \ + for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++) + +#define ide_host_for_each_port(i, port, host) \ + for ((i) = 0; ((port) = (host)->ports[i]) || (i) < MAX_HOST_PORTS; (i)++) + #endif /* _IDE_H */ -- cgit v1.2.3-71-gd317 From d6251d4488a361c93da2398818e1ec69cffb6073 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 6 Jan 2009 17:20:58 +0100 Subject: ide-cd: convert to ide-atapi facilities ... and remove no longer needed cdrom_start_packet_command and cdrom_transfer_packet_command. Tested lightly with ide-cd and ide-floppy. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 5 ++- drivers/ide/ide-cd.c | 96 ++----------------------------------------------- include/linux/ide.h | 2 ++ 3 files changed, 8 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index d6a50d67b7db..e96c01260598 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -549,7 +549,10 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) } /* Set the interrupt routine */ - ide_set_handler(drive, ide_pc_intr, timeout, expiry); + ide_set_handler(drive, + (dev_is_idecd(drive) ? drive->irq_handler + : ide_pc_intr), + timeout, expiry); /* Begin DMA, if necessary */ if (dev_is_idecd(drive)) { diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 2cb301dccd21..cae69372cf45 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -510,99 +510,6 @@ end_request: return 1; } -static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *); -static ide_startstop_t cdrom_newpc_intr(ide_drive_t *); - -/* - * Set up the device registers for transferring a packet command on DEV, - * expecting to later transfer XFERLEN bytes. HANDLER is the routine - * which actually transfers the command to the drive. If this is a - * drq_interrupt device, this routine will arrange for HANDLER to be - * called when the interrupt from the drive arrives. Otherwise, HANDLER - * will be called immediately after the drive is prepared for the transfer. - */ -static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->rq; - int xferlen; - - xferlen = ide_cd_get_xferlen(rq); - - ide_debug_log(IDE_DBG_PC, "Call %s, xferlen: %d\n", __func__, xferlen); - - /* FIXME: for Virtual DMA we must check harder */ - if (drive->dma) - drive->dma = !hwif->dma_ops->dma_setup(drive); - - /* set up the controller registers */ - ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL, - xferlen, drive->dma); - - if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { - /* waiting for CDB interrupt, not DMA yet. */ - if (drive->dma) - drive->waiting_for_dma = 0; - - /* packet command */ - ide_execute_command(drive, ATA_CMD_PACKET, - cdrom_transfer_packet_command, - ATAPI_WAIT_PC, ide_cd_expiry); - return ide_started; - } else { - ide_execute_pkt_cmd(drive); - - return cdrom_transfer_packet_command(drive); - } -} - -/* - * Send a packet command to DRIVE described by CMD_BUF and CMD_LEN. The device - * registers must have already been prepared by cdrom_start_packet_command. - * HANDLER is the interrupt handler to call when the command completes or - * there's data ready. - */ -#define ATAPI_MIN_CDB_BYTES 12 -static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - struct request *rq = hwif->rq; - int cmd_len; - ide_startstop_t startstop; - - ide_debug_log(IDE_DBG_PC, "Call %s\n", __func__); - - /* we must wait for DRQ to get set */ - if (ide_wait_stat(&startstop, drive, ATA_DRQ, ATA_BUSY, WAIT_READY)) { - printk(KERN_ERR "%s: timeout while waiting for DRQ to assert\n", - drive->name); - return startstop; - } - - if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { - /* ok, next interrupt will be DMA interrupt */ - if (drive->dma) - drive->waiting_for_dma = 1; - } - - /* arm the interrupt handler */ - ide_set_handler(drive, cdrom_newpc_intr, rq->timeout, ide_cd_expiry); - - /* ATAPI commands get padded out to 12 bytes minimum */ - cmd_len = COMMAND_SIZE(rq->cmd[0]); - if (cmd_len < ATAPI_MIN_CDB_BYTES) - cmd_len = ATAPI_MIN_CDB_BYTES; - - /* start the DMA if need be */ - if (drive->dma) - hwif->dma_ops->dma_start(drive); - - /* send the command to the device */ - hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); - - return ide_started; -} - /* * Check the contents of the interrupt reason register from the cdrom * and attempt to recover if there are problems. Returns 0 if everything's @@ -1174,7 +1081,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, return ide_stopped; } - return cdrom_start_packet_command(drive); + return ide_issue_pc(drive); } /* @@ -2072,6 +1979,7 @@ static int ide_cd_probe(ide_drive_t *drive) } drive->debug_mask = debug_mask; + drive->irq_handler = cdrom_newpc_intr; info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL); if (info == NULL) { diff --git a/include/linux/ide.h b/include/linux/ide.h index 4cecd923fc79..13deba5e0157 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -654,6 +654,8 @@ struct ide_drive_s { int (*pc_io_buffers)(struct ide_drive_s *, struct ide_atapi_pc *, unsigned int, int); + ide_startstop_t (*irq_handler)(struct ide_drive_s *); + unsigned long atapi_flags; struct ide_atapi_pc request_sense_pc; -- cgit v1.2.3-71-gd317 From 906ef986a71d541a726550fa40dcbc5c356f810e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:59 +0100 Subject: ide: struct ide_atapi_pc - remove unused fields and update documentation Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 13deba5e0157..a7dbfd857115 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -426,13 +426,9 @@ struct ide_atapi_pc { struct idetape_bh *bh; char *b_data; - /* idescsi only for now */ struct scatterlist *sg; unsigned int sg_cnt; - struct scsi_cmnd *scsi_cmd; - void (*done) (struct scsi_cmnd *); - unsigned long timeout; }; -- cgit v1.2.3-71-gd317 From 94c96445f32c16cfdc398b20b7e78945ab7e35f9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 6 Jan 2009 17:20:59 +0100 Subject: ide: remove unused ide_hwif_t.sg_mapped field Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index a7dbfd857115..ebc22a836520 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -817,7 +817,6 @@ typedef struct hwif_s { unsigned extra_ports; /* number of extra dma ports */ unsigned present : 1; /* this interface exists */ - unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */ unsigned busy : 1; /* serializes devices on a port */ struct device gendev; -- cgit v1.2.3-71-gd317 From 391ad1908a9c13d457ea12ce1508d6b8a7ba72ad Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Tue, 6 Jan 2009 17:21:01 +0100 Subject: Resurrect IT8172 IDE controller driver Support for the IT8172 IDE controller was removed from the kernel sometime after 2.6.18. Support for the only boards that used the IT8172 was removed from the kernel after 2.6.18, as they had never compiled since 2.6.0. However, there are a couple of platforms that use this chip: the PMC-Sierra Xiao Hu thin-client computer, which is no longer in production, and the Linksys NSS4000 Network Attached Storage box, which is based on the Xiao Hu board. I am attempting to add support for the Xiao Hu to the kernel, and this IT8172 IDE controller is the first bit of code in this effort. This patch resurrects the IT8172 IDE controller code. I began with the 2.6.18 version of the it8172.c file, and have moved it forward so that it works with the latest version of the kernel. I have run this driver on a PMC-Sierra Xiao Hu board with the 2.6.28 kernel, and I have had no problems with it in my configuration. The attached patch applies cleanly against 2.6.28. Signed-off-by: Shane McDonald Acked-by: Sergei Shtylyov Cc: alan@lxorguk.ukuu.org.uk [bart: s/HWIF(drive)/drive->hwif/] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 7 ++ drivers/ide/Makefile | 1 + drivers/ide/it8172.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 1 + 4 files changed, 175 insertions(+) create mode 100644 drivers/ide/it8172.c (limited to 'include/linux') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 4ee85fcf9aaf..3f9503867e6b 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -511,6 +511,13 @@ config BLK_DEV_PIIX This allows the kernel to change PIO, DMA and UDMA speeds and to configure the chip to optimum performance. +config BLK_DEV_IT8172 + tristate "IT8172 IDE support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for the IDE controller on the + IT8172 System Controller. + config BLK_DEV_IT8213 tristate "IT8213 IDE support" select BLK_DEV_IDEDMA_PCI diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 410728992e6a..c2b9c93f0095 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_BLK_DEV_SC1200) += sc1200.o obj-$(CONFIG_BLK_DEV_CY82C693) += cy82c693.o obj-$(CONFIG_BLK_DEV_DELKIN) += delkin_cb.o obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o +obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o obj-$(CONFIG_BLK_DEV_IT8213) += it8213.o obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o obj-$(CONFIG_BLK_DEV_JMICRON) += jmicron.o diff --git a/drivers/ide/it8172.c b/drivers/ide/it8172.c new file mode 100644 index 000000000000..e021078cd06b --- /dev/null +++ b/drivers/ide/it8172.c @@ -0,0 +1,166 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 IDE controller support + * + * Copyright (C) 2000 MontaVista Software Inc. + * Copyright (C) 2008 Shane McDonald + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "IT8172" + +static void it8172_set_pio_mode(ide_drive_t *drive, const u8 pio) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u16 drive_enables; + u32 drive_timing; + + /* + * The highest value of DIOR/DIOW pulse width and recovery time + * that can be set in the IT8172 is 8 PCI clock cycles. As a result, + * it cannot be configured for PIO mode 0. This table sets these + * parameters to the maximum supported by the IT8172. + */ + static const u8 timings[] = { 0x3f, 0x3c, 0x1b, 0x12, 0x0a }; + + pci_read_config_word(dev, 0x40, &drive_enables); + pci_read_config_dword(dev, 0x44, &drive_timing); + + /* + * Enable port 0x44. The IT8172 spec is confused; it calls + * this register the "Slave IDE Timing Register", but in fact, + * it controls timing for both master and slave drives. + */ + drive_enables |= 0x4000; + + drive_enables &= drive->dn ? 0xc006 : 0xc060; + if (drive->media == ide_disk) + /* enable prefetch */ + drive_enables |= 0x0004 << (drive->dn * 4); + if (ata_id_has_iordy(drive->id)) + /* enable IORDY sample-point */ + drive_enables |= 0x0002 << (drive->dn * 4); + + drive_timing &= drive->dn ? 0x00003f00 : 0x000fc000; + drive_timing |= timings[pio] << (drive->dn * 6 + 8); + + pci_write_config_word(dev, 0x40, drive_enables); + pci_write_config_dword(dev, 0x44, drive_timing); +} + +static void it8172_set_dma_mode(ide_drive_t *drive, const u8 speed) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + u8 reg48, reg4a; + + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x4a, ®4a); + + if (speed >= XFER_UDMA_0) { + u8 udma = speed - XFER_UDMA_0; + u_speed = udma << (drive->dn * 4); + + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + reg4a &= ~a_speed; + pci_write_config_byte(dev, 0x4a, reg4a | u_speed); + } else { + const u8 mwdma_to_pio[] = { 0, 3, 4 }; + u8 pio; + + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + + pio = mwdma_to_pio[speed - XFER_MW_DMA_0]; + + it8172_set_pio_mode(drive, pio); + } +} + + +static const struct ide_port_ops it8172_port_ops = { + .set_pio_mode = it8172_set_pio_mode, + .set_dma_mode = it8172_set_dma_mode, +}; + +static const struct ide_port_info it8172_port_info __devinitdata = { + .name = DRV_NAME, + .port_ops = &it8172_port_ops, + .enablebits = { {0x41, 0x80, 0x80}, {0x00, 0x00, 0x00} }, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4 & ~ATA_PIO0, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, +}; + +static int __devinit it8172_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return -ENODEV; /* IT8172 is more than an IDE controller */ + return ide_pci_init_one(dev, &it8172_port_info, NULL); +} + +static struct pci_device_id it8172_pci_tbl[] = { + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8172), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, it8172_pci_tbl); + +static struct pci_driver it8172_pci_driver = { + .name = "IT8172_IDE", + .id_table = it8172_pci_tbl, + .probe = it8172_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init it8172_ide_init(void) +{ + return ide_pci_register_driver(&it8172_pci_driver); +} + +static void __exit it8172_ide_exit(void) +{ + pci_unregister_driver(&it8172_pci_driver); +} + +module_init(it8172_ide_init); +module_exit(it8172_ide_exit); + +MODULE_AUTHOR("Steve Longerbeam"); +MODULE_DESCRIPTION("PCI driver module for ITE 8172 IDE"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 218c73b1e6d4..d543365518ab 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1658,6 +1658,7 @@ #define PCI_VENDOR_ID_ROCKWELL 0x127A #define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_DEVICE_ID_ITE_8172 0x8172 #define PCI_DEVICE_ID_ITE_8211 0x8211 #define PCI_DEVICE_ID_ITE_8212 0x8212 #define PCI_DEVICE_ID_ITE_8213 0x8213 -- cgit v1.2.3-71-gd317 From 592b5315219881c6c0af4785f96456ad2043193a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 6 Jan 2009 17:21:02 +0100 Subject: ide: move read_sff_dma_status() method to 'struct ide_dma_ops' Move apparently misplaced read_sff_dma_status() method from 'struct ide_tp_ops' to 'struct ide_dma_ops', renaming it to dma_sff_read_status() and making only required for SFF-8038i compatible IDE controller drivers (greatly cutting down the number of initializers) as its only user (outside ide-dma-sff.c and such drivers) appears to be ide_pci_check_simplex() which is only called for such controllers... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/alim15x3.c | 1 + drivers/ide/au1xxx-ide.c | 1 - drivers/ide/cmd64x.c | 3 +++ drivers/ide/falconide.c | 1 - drivers/ide/hpt366.c | 3 +++ drivers/ide/ide-dma-sff.c | 22 +++++++++++++++++----- drivers/ide/ide-h8300.c | 1 - drivers/ide/ide-iops.c | 10 ---------- drivers/ide/ide-probe.c | 7 +++++-- drivers/ide/it821x.c | 1 + drivers/ide/ns87415.c | 8 +++++--- drivers/ide/pdc202xx_old.c | 2 ++ drivers/ide/pmac.c | 1 - drivers/ide/q40ide.c | 1 - drivers/ide/sc1200.c | 1 + drivers/ide/scc_pata.c | 4 ++-- drivers/ide/setup-pci.c | 7 ++++--- drivers/ide/sgiioc4.c | 1 - drivers/ide/siimage.c | 1 + drivers/ide/sl82c105.c | 1 + drivers/ide/tc86c001.c | 1 + drivers/ide/tx4939ide.c | 21 ++++++++++++--------- include/linux/ide.h | 8 ++++++-- 23 files changed, 65 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index f1c817240501..66f43083408b 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -509,6 +509,7 @@ static const struct ide_dma_ops ali_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info ali15x3_chipset __devinitdata = { diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 11f2c8f3db48..79a2dfed8eb7 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -502,7 +502,6 @@ static const struct ide_tp_ops au1xxx_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index 265cf9268c63..2f9688d87ecd 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -385,6 +385,7 @@ static const struct ide_dma_ops cmd64x_dma_ops = { .dma_test_irq = cmd64x_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_dma_ops cmd646_rev1_dma_ops = { @@ -396,6 +397,7 @@ static const struct ide_dma_ops cmd646_rev1_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_dma_ops cmd648_dma_ops = { @@ -407,6 +409,7 @@ static const struct ide_dma_ops cmd648_dma_ops = { .dma_test_irq = cmd648_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c index 39d500d84b07..a5ba820d69bb 100644 --- a/drivers/ide/falconide.c +++ b/drivers/ide/falconide.c @@ -70,7 +70,6 @@ static const struct ide_tp_ops falconide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index 208614f4dc3c..3eb9b5c63a0f 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -1424,6 +1424,7 @@ static const struct ide_dma_ops hpt37x_dma_ops = { .dma_test_irq = hpt374_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_dma_ops hpt370_dma_ops = { @@ -1435,6 +1436,7 @@ static const struct ide_dma_ops hpt370_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = hpt370_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_dma_ops hpt36x_dma_ops = { @@ -1446,6 +1448,7 @@ static const struct ide_dma_ops hpt36x_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = hpt366_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info hpt366_chipsets[] __devinitdata = { diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index 623a82d1535d..bcdadc777564 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -50,6 +50,17 @@ int config_drive_for_dma(ide_drive_t *drive) return 0; } +u8 ide_dma_sff_read_status(ide_hwif_t *hwif) +{ + unsigned long addr = hwif->dma_base + ATA_DMA_STATUS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)addr); + else + return inb(addr); +} +EXPORT_SYMBOL_GPL(ide_dma_sff_read_status); + /** * ide_dma_host_set - Enable/disable DMA on a host * @drive: drive to control @@ -62,7 +73,7 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; u8 unit = drive->dn & 1; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); @@ -200,7 +211,7 @@ int ide_dma_setup(ide_drive_t *drive) outb(reading, hwif->dma_base + ATA_DMA_CMD); /* read DMA status for INTR & ERROR flags */ - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); /* clear INTR & ERROR flags */ if (mmio) @@ -232,7 +243,7 @@ EXPORT_SYMBOL_GPL(ide_dma_setup); static int dma_timer_expiry(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); printk(KERN_WARNING "%s: %s: DMA status (0x%02x)\n", drive->name, __func__, dma_stat); @@ -305,7 +316,7 @@ int ide_dma_end(ide_drive_t *drive) } /* get DMA status */ - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); if (mmio) /* clear the INTR & ERROR bits */ @@ -331,7 +342,7 @@ EXPORT_SYMBOL_GPL(ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); return (dma_stat & ATA_DMA_INTR) ? 1 : 0; } @@ -346,5 +357,6 @@ const struct ide_dma_ops sff_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, + .dma_sff_read_status = ide_dma_sff_read_status, }; EXPORT_SYMBOL_GPL(sff_dma_ops); diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index e2cdd2e9cdec..9270d3255ee0 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -159,7 +159,6 @@ static const struct ide_tp_ops h8300_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 1a22c31ce7ac..e728cfe7273f 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -105,15 +105,6 @@ u8 ide_read_altstatus(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_read_altstatus); -u8 ide_read_sff_dma_status(ide_hwif_t *hwif) -{ - if (hwif->host_flags & IDE_HFLAG_MMIO) - return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); - else - return inb(hwif->dma_base + ATA_DMA_STATUS); -} -EXPORT_SYMBOL_GPL(ide_read_sff_dma_status); - void ide_set_irq(ide_hwif_t *hwif, int on) { u8 ctl = ATA_DEVCTL_OBS; @@ -388,7 +379,6 @@ const struct ide_tp_ops default_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index ebb1b7f863f4..0ccbb4459fb9 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1229,6 +1229,8 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) { int rc; + hwif->dma_ops = d->dma_ops; + if (d->init_dma) rc = d->init_dma(hwif, d); else @@ -1236,12 +1238,13 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, if (rc < 0) { printk(KERN_INFO "%s: DMA disabled\n", hwif->name); + + hwif->dma_ops = NULL; hwif->dma_base = 0; hwif->swdma_mask = 0; hwif->mwdma_mask = 0; hwif->ultra_mask = 0; - } else if (d->dma_ops) - hwif->dma_ops = d->dma_ops; + } } if ((d->host_flags & IDE_HFLAG_SERIALIZE) || diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c index 879056645352..0be27ac1f077 100644 --- a/drivers/ide/it821x.c +++ b/drivers/ide/it821x.c @@ -512,6 +512,7 @@ static struct ide_dma_ops it821x_pass_through_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, + .dma_sff_read_status = ide_dma_sff_read_status, }; /** diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index aceb2fcbe1d1..83643ed9a426 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -56,7 +56,7 @@ static u8 superio_read_status(ide_hwif_t *hwif) return superio_ide_inb(hwif->io_ports.status_addr); } -static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) +static u8 superio_dma_sff_read_status(ide_hwif_t *hwif) { return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); } @@ -109,7 +109,6 @@ static const struct ide_tp_ops superio_tp_ops = { .exec_command = ide_exec_command, .read_status = superio_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = superio_read_sff_dma_status, .set_irq = ide_set_irq, @@ -132,6 +131,8 @@ static void __devinit superio_init_iops(struct hwif_s *hwif) tmp = superio_ide_inb(dma_stat); outb(tmp | 0x66, dma_stat); } +#else +#define superio_dma_sff_read_status ide_dma_sff_read_status #endif static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; @@ -201,7 +202,7 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); /* get DMA command mode */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ @@ -308,6 +309,7 @@ static const struct ide_dma_ops ns87415_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = superio_dma_sff_read_status, }; static const struct ide_port_info ns87415_chipset __devinitdata = { diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c index e8e6b29d9e41..97193323aebf 100644 --- a/drivers/ide/pdc202xx_old.c +++ b/drivers/ide/pdc202xx_old.c @@ -337,6 +337,7 @@ static const struct ide_dma_ops pdc20246_dma_ops = { .dma_test_irq = pdc202xx_dma_test_irq, .dma_lost_irq = pdc202xx_dma_lost_irq, .dma_timeout = pdc202xx_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_dma_ops pdc2026x_dma_ops = { @@ -348,6 +349,7 @@ static const struct ide_dma_ops pdc2026x_dma_ops = { .dma_test_irq = pdc202xx_dma_test_irq, .dma_lost_irq = pdc202xx_dma_lost_irq, .dma_timeout = pdc202xx_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; #define DECLARE_PDC2026X_DEV(udma, sectors) \ diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index ee52a21af1be..74625e821a43 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -955,7 +955,6 @@ static const struct ide_tp_ops pmac_tp_ops = { .exec_command = pmac_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = pmac_set_irq, diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c index 4af4a8ce4cdf..9f9c0b3cc3a3 100644 --- a/drivers/ide/q40ide.c +++ b/drivers/ide/q40ide.c @@ -99,7 +99,6 @@ static const struct ide_tp_ops q40ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index 1cf477aaae36..dbdd2985a0d8 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -292,6 +292,7 @@ static const struct ide_dma_ops sc1200_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info sc1200_chipset __devinitdata = { diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 5d53850c79d7..1cb43068455a 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -143,7 +143,7 @@ static u8 scc_read_altstatus(ide_hwif_t *hwif) return (u8)in_be32((void *)hwif->io_ports.ctl_addr); } -static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) +static u8 scc_dma_sff_read_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); } @@ -853,7 +853,6 @@ static const struct ide_tp_ops scc_tp_ops = { .exec_command = scc_exec_command, .read_status = scc_read_status, .read_altstatus = scc_read_altstatus, - .read_sff_dma_status = scc_read_sff_dma_status, .set_irq = scc_set_irq, @@ -880,6 +879,7 @@ static const struct ide_dma_ops scc_dma_ops = { .dma_test_irq = scc_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = scc_dma_sff_read_status, }; #define DECLARE_SCC_DEV(name_str) \ diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index bc37dff8c675..e85d1ed29c2a 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -130,7 +130,7 @@ int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { printk(KERN_INFO "%s %s: simplex device: DMA disabled\n", d->name, pci_name(dev)); @@ -377,6 +377,9 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) hwif->dma_base = base; + if (hwif->dma_ops == NULL) + hwif->dma_ops = &sff_dma_ops; + if (ide_pci_check_simplex(hwif, d) < 0) return -1; @@ -393,8 +396,6 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - - hwif->dma_ops = &sff_dma_ops; } return 0; diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index 8e1ffd57a86d..fdb9d7037694 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -523,7 +523,6 @@ static const struct ide_tp_ops sgiioc4_tp_ops = { .exec_command = ide_exec_command, .read_status = sgiioc4_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c index 652b3a04620f..cb2b352b876b 100644 --- a/drivers/ide/siimage.c +++ b/drivers/ide/siimage.c @@ -717,6 +717,7 @@ static const struct ide_dma_ops sil_dma_ops = { .dma_test_irq = siimage_dma_test_irq, .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, + .dma_sff_read_status = ide_dma_sff_read_status, }; #define DECLARE_SII_DEV(p_ops) \ diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c index 1ded01d81ab3..48cc748c5043 100644 --- a/drivers/ide/sl82c105.c +++ b/drivers/ide/sl82c105.c @@ -299,6 +299,7 @@ static const struct ide_dma_ops sl82c105_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = sl82c105_dma_lost_irq, .dma_timeout = sl82c105_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info sl82c105_chipset __devinitdata = { diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c index d2c00fb928e4..84109f5a1632 100644 --- a/drivers/ide/tc86c001.c +++ b/drivers/ide/tc86c001.c @@ -188,6 +188,7 @@ static const struct ide_dma_ops tc86c001_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, }; static const struct ide_port_info tc86c001_chipset __devinitdata = { diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 1ac27ac7283b..882f6f07c476 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -397,6 +397,17 @@ static int tx4939ide_dma_test_irq(ide_drive_t *drive) return found; } +#ifdef __BIG_ENDIAN +static u8 tx4939ide_dma_sff_read_status(ide_hwif_t *hwif) +{ + void __iomem *base = TX4939IDE_BASE(hwif); + + return tx4939ide_readb(base, TX4939IDE_DMA_Stat); +} +#else +#define tx4939ide_dma_sff_read_status ide_dma_sff_read_status +#endif + static void tx4939ide_init_hwif(ide_hwif_t *hwif) { void __iomem *base = TX4939IDE_BASE(hwif); @@ -443,13 +454,6 @@ static void tx4939ide_tf_load_fixup(ide_drive_t *drive, ide_task_t *task) #ifdef __BIG_ENDIAN -static u8 tx4939ide_read_sff_dma_status(ide_hwif_t *hwif) -{ - void __iomem *base = TX4939IDE_BASE(hwif); - - return tx4939ide_readb(base, TX4939IDE_DMA_Stat); -} - /* custom iops (independent from SWAP_IO_SPACE) */ static u8 tx4939ide_inb(unsigned long port) { @@ -585,7 +589,6 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = tx4939ide_read_sff_dma_status, .set_irq = ide_set_irq, @@ -609,7 +612,6 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .read_sff_dma_status = ide_read_sff_dma_status, .set_irq = ide_set_irq, @@ -638,6 +640,7 @@ static const struct ide_dma_ops tx4939ide_dma_ops = { .dma_test_irq = tx4939ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timeout = ide_dma_timeout, + .dma_sff_read_status = tx4939ide_dma_sff_read_status, }; static const struct ide_port_info tx4939ide_port_info __initdata = { diff --git a/include/linux/ide.h b/include/linux/ide.h index ebc22a836520..3644f6323384 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -674,7 +674,6 @@ struct ide_tp_ops { void (*exec_command)(struct hwif_s *, u8); u8 (*read_status)(struct hwif_s *); u8 (*read_altstatus)(struct hwif_s *); - u8 (*read_sff_dma_status)(struct hwif_s *); void (*set_irq)(struct hwif_s *, int); @@ -735,6 +734,11 @@ struct ide_dma_ops { int (*dma_test_irq)(struct ide_drive_s *); void (*dma_lost_irq)(struct ide_drive_s *); void (*dma_timeout)(struct ide_drive_s *); + /* + * The following method is optional and only required to be + * implemented for the SFF-8038i compatible controllers. + */ + u8 (*dma_sff_read_status)(struct hwif_s *); }; struct ide_host; @@ -1177,7 +1181,6 @@ void ide_tf_dump(const char *, struct ide_taskfile *); void ide_exec_command(ide_hwif_t *, u8); u8 ide_read_status(ide_hwif_t *); u8 ide_read_altstatus(ide_hwif_t *); -u8 ide_read_sff_dma_status(ide_hwif_t *); void ide_set_irq(ide_hwif_t *, int); @@ -1458,6 +1461,7 @@ void ide_dma_exec_cmd(ide_drive_t *, u8); extern void ide_dma_start(ide_drive_t *); int ide_dma_end(ide_drive_t *); int ide_dma_test_irq(ide_drive_t *); +u8 ide_dma_sff_read_status(ide_hwif_t *); extern const struct ide_dma_ops sff_dma_ops; #else static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; } -- cgit v1.2.3-71-gd317 From adf094931ffb25ef4b381559918f1a34181a5273 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Oct 2008 22:46:05 +0200 Subject: PM: Simplify the new suspend/hibernation framework for devices PM: Simplify the new suspend/hibernation framework for devices Following the discussion at the Kernel Summit, simplify the new device PM framework by merging 'struct pm_ops' and 'struct pm_ext_ops' and removing pointers to 'struct pm_ext_ops' from 'struct platform_driver' and 'struct pci_driver'. After this change, the suspend/hibernation callbacks will only reside in 'struct device_driver' as well as at the bus type/ device class/device type level. Accordingly, PCI and platform device drivers are now expected to put their suspend/hibernation callbacks into the 'struct device_driver' embedded in 'struct pci_driver' or 'struct platform_driver', respectively. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Cc: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 115 +++++++++++++++++++++------------------- drivers/base/power/main.c | 19 +++---- drivers/pci/pci-driver.c | 46 +++++++--------- drivers/usb/core/usb.c | 4 +- include/linux/device.h | 8 +-- include/linux/pci.h | 1 - include/linux/platform_device.h | 1 - include/linux/pm.h | 76 +++++++++----------------- 8 files changed, 119 insertions(+), 151 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index dfcbfe504867..6c743b6008d9 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -503,8 +503,6 @@ int platform_driver_register(struct platform_driver *drv) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; - if (drv->pm) - drv->driver.pm = &drv->pm->base; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register); @@ -686,7 +684,10 @@ static int platform_pm_suspend(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->suspend) ret = drv->pm->suspend(dev); } else { @@ -698,16 +699,15 @@ static int platform_pm_suspend(struct device *dev) static int platform_pm_suspend_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->suspend_noirq) - ret = pdrv->pm->suspend_noirq(dev); + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); } @@ -720,7 +720,10 @@ static int platform_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->resume) ret = drv->pm->resume(dev); } else { @@ -732,16 +735,15 @@ static int platform_pm_resume(struct device *dev) static int platform_pm_resume_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->resume_noirq) - ret = pdrv->pm->resume_noirq(dev); + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -780,16 +782,15 @@ static int platform_pm_freeze(struct device *dev) static int platform_pm_freeze_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->freeze_noirq) - ret = pdrv->pm->freeze_noirq(dev); + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); } @@ -802,7 +803,10 @@ static int platform_pm_thaw(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->thaw) ret = drv->pm->thaw(dev); } else { @@ -814,16 +818,15 @@ static int platform_pm_thaw(struct device *dev) static int platform_pm_thaw_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->thaw_noirq) - ret = pdrv->pm->thaw_noirq(dev); + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -836,7 +839,10 @@ static int platform_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->poweroff) ret = drv->pm->poweroff(dev); } else { @@ -848,16 +854,15 @@ static int platform_pm_poweroff(struct device *dev) static int platform_pm_poweroff_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->poweroff_noirq) - ret = pdrv->pm->poweroff_noirq(dev); + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); } @@ -870,7 +875,10 @@ static int platform_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->restore) ret = drv->pm->restore(dev); } else { @@ -882,16 +890,15 @@ static int platform_pm_restore(struct device *dev) static int platform_pm_restore_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->restore_noirq) - ret = pdrv->pm->restore_noirq(dev); + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -912,17 +919,15 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -static struct pm_ext_ops platform_pm_ops = { - .base = { - .prepare = platform_pm_prepare, - .complete = platform_pm_complete, - .suspend = platform_pm_suspend, - .resume = platform_pm_resume, - .freeze = platform_pm_freeze, - .thaw = platform_pm_thaw, - .poweroff = platform_pm_poweroff, - .restore = platform_pm_restore, - }, +static struct dev_pm_ops platform_dev_pm_ops = { + .prepare = platform_pm_prepare, + .complete = platform_pm_complete, + .suspend = platform_pm_suspend, + .resume = platform_pm_resume, + .freeze = platform_pm_freeze, + .thaw = platform_pm_thaw, + .poweroff = platform_pm_poweroff, + .restore = platform_pm_restore, .suspend_noirq = platform_pm_suspend_noirq, .resume_noirq = platform_pm_resume_noirq, .freeze_noirq = platform_pm_freeze_noirq, @@ -931,7 +936,7 @@ static struct pm_ext_ops platform_pm_ops = { .restore_noirq = platform_pm_restore_noirq, }; -#define PLATFORM_PM_OPS_PTR &platform_pm_ops +#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops) #else /* !CONFIG_PM_SLEEP */ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 692c20ba5144..a8e4dcbcaf7a 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -112,7 +112,8 @@ void device_pm_remove(struct device *dev) * @ops: PM operations to choose from. * @state: PM transition of the system being carried out. */ -static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) +static int pm_op(struct device *dev, struct dev_pm_ops *ops, + pm_message_t state) { int error = 0; @@ -174,7 +175,7 @@ static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) * The operation is executed with interrupts disabled by the only remaining * functional CPU in the system. */ -static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops, +static int pm_noirq_op(struct device *dev, struct dev_pm_ops *ops, pm_message_t state) { int error = 0; @@ -354,7 +355,7 @@ static int resume_device(struct device *dev, pm_message_t state) if (dev->bus) { if (dev->bus->pm) { pm_dev_dbg(dev, state, ""); - error = pm_op(dev, &dev->bus->pm->base, state); + error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->resume) { pm_dev_dbg(dev, state, "legacy "); error = dev->bus->resume(dev); @@ -451,9 +452,9 @@ static void complete_device(struct device *dev, pm_message_t state) dev->type->pm->complete(dev); } - if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) { + if (dev->bus && dev->bus->pm && dev->bus->pm->complete) { pm_dev_dbg(dev, state, "completing "); - dev->bus->pm->base.complete(dev); + dev->bus->pm->complete(dev); } up(&dev->sem); @@ -624,7 +625,7 @@ static int suspend_device(struct device *dev, pm_message_t state) if (dev->bus) { if (dev->bus->pm) { pm_dev_dbg(dev, state, ""); - error = pm_op(dev, &dev->bus->pm->base, state); + error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->suspend) { pm_dev_dbg(dev, state, "legacy "); error = dev->bus->suspend(dev, state); @@ -685,10 +686,10 @@ static int prepare_device(struct device *dev, pm_message_t state) down(&dev->sem); - if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) { + if (dev->bus && dev->bus->pm && dev->bus->pm->prepare) { pm_dev_dbg(dev, state, "preparing "); - error = dev->bus->pm->base.prepare(dev); - suspend_report_result(dev->bus->pm->base.prepare, error); + error = dev->bus->pm->prepare(dev); + suspend_report_result(dev->bus->pm->prepare, error); if (error) goto End; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index b4cdd690ae71..4042d211c3e5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -433,8 +433,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -469,11 +468,10 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_resume_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); if (drv && drv->pm) { if (drv->pm->resume_noirq) @@ -519,8 +517,7 @@ static int pci_pm_freeze(struct device *dev) static int pci_pm_freeze_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -553,15 +550,14 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_thaw_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { if (drv->pm->thaw_noirq) error = drv->pm->thaw_noirq(dev); } else { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); error = pci_legacy_resume_early(dev); } @@ -589,8 +585,7 @@ static int pci_pm_poweroff(struct device *dev) static int pci_pm_poweroff_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -625,7 +620,7 @@ static int pci_pm_restore(struct device *dev) static int pci_pm_restore_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; pci_fixup_device(pci_fixup_resume, pci_dev); @@ -654,17 +649,15 @@ static int pci_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct pm_ext_ops pci_pm_ops = { - .base = { - .prepare = pci_pm_prepare, - .complete = pci_pm_complete, - .suspend = pci_pm_suspend, - .resume = pci_pm_resume, - .freeze = pci_pm_freeze, - .thaw = pci_pm_thaw, - .poweroff = pci_pm_poweroff, - .restore = pci_pm_restore, - }, +struct dev_pm_ops pci_dev_pm_ops = { + .prepare = pci_pm_prepare, + .complete = pci_pm_complete, + .suspend = pci_pm_suspend, + .resume = pci_pm_resume, + .freeze = pci_pm_freeze, + .thaw = pci_pm_thaw, + .poweroff = pci_pm_poweroff, + .restore = pci_pm_restore, .suspend_noirq = pci_pm_suspend_noirq, .resume_noirq = pci_pm_resume_noirq, .freeze_noirq = pci_pm_freeze_noirq, @@ -673,7 +666,7 @@ struct pm_ext_ops pci_pm_ops = { .restore_noirq = pci_pm_restore_noirq, }; -#define PCI_PM_OPS_PTR &pci_pm_ops +#define PCI_PM_OPS_PTR (&pci_dev_pm_ops) #else /* !CONFIG_PM_SLEEP */ @@ -703,9 +696,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.owner = owner; drv->driver.mod_name = mod_name; - if (drv->pm) - drv->driver.pm = &drv->pm->base; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index be1fa0723f2c..399e15fc5052 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -286,7 +286,7 @@ static int usb_dev_restore(struct device *dev) return usb_resume(dev); } -static struct pm_ops usb_device_pm_ops = { +static struct dev_pm_ops usb_device_pm_ops = { .prepare = usb_dev_prepare, .complete = usb_dev_complete, .suspend = usb_dev_suspend, @@ -301,7 +301,7 @@ static struct pm_ops usb_device_pm_ops = { #define ksuspend_usb_init() 0 #define ksuspend_usb_cleanup() do {} while (0) -#define usb_device_pm_ops (*(struct pm_ops *)0) +#define usb_device_pm_ops (*(struct dev_pm_ops *)0) #endif /* CONFIG_PM */ diff --git a/include/linux/device.h b/include/linux/device.h index 1a3686d15f98..4a520051c315 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -65,7 +65,7 @@ struct bus_type { int (*resume_early)(struct device *dev); int (*resume)(struct device *dev); - struct pm_ext_ops *pm; + struct dev_pm_ops *pm; struct bus_type_private *p; }; @@ -133,7 +133,7 @@ struct device_driver { int (*resume) (struct device *dev); struct attribute_group **groups; - struct pm_ops *pm; + struct dev_pm_ops *pm; struct driver_private *p; }; @@ -198,7 +198,7 @@ struct class { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); - struct pm_ops *pm; + struct dev_pm_ops *pm; struct class_private *p; }; @@ -291,7 +291,7 @@ struct device_type { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); - struct pm_ops *pm; + struct dev_pm_ops *pm; }; /* interface for exporting device attributes */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 03b0b8c3c81b..4bb156ba854a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -421,7 +421,6 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); - struct pm_ext_ops *pm; struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 4b8cc6a32479..9a342699c607 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -55,7 +55,6 @@ struct platform_driver { int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); - struct pm_ext_ops *pm; struct device_driver driver; }; diff --git a/include/linux/pm.h b/include/linux/pm.h index 42de4003c4ee..5785666d0cc4 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -41,7 +41,7 @@ typedef struct pm_message { } pm_message_t; /** - * struct pm_ops - device PM callbacks + * struct dev_pm_ops - device PM callbacks * * Several driver power state transitions are externally visible, affecting * the state of pending I/O queues and (for drivers that touch hardware) @@ -126,46 +126,6 @@ typedef struct pm_message { * On most platforms, there are no restrictions on availability of * resources like clocks during @restore(). * - * All of the above callbacks, except for @complete(), return error codes. - * However, the error codes returned by the resume operations, @resume(), - * @thaw(), and @restore(), do not cause the PM core to abort the resume - * transition during which they are returned. The error codes returned in - * that cases are only printed by the PM core to the system logs for debugging - * purposes. Still, it is recommended that drivers only return error codes - * from their resume methods in case of an unrecoverable failure (i.e. when the - * device being handled refuses to resume and becomes unusable) to allow us to - * modify the PM core in the future, so that it can avoid attempting to handle - * devices that failed to resume and their children. - * - * It is allowed to unregister devices while the above callbacks are being - * executed. However, it is not allowed to unregister a device from within any - * of its own callbacks. - */ - -struct pm_ops { - int (*prepare)(struct device *dev); - void (*complete)(struct device *dev); - int (*suspend)(struct device *dev); - int (*resume)(struct device *dev); - int (*freeze)(struct device *dev); - int (*thaw)(struct device *dev); - int (*poweroff)(struct device *dev); - int (*restore)(struct device *dev); -}; - -/** - * struct pm_ext_ops - extended device PM callbacks - * - * Some devices require certain operations related to suspend and hibernation - * to be carried out with interrupts disabled. Thus, 'struct pm_ext_ops' below - * is defined, adding callbacks to be executed with interrupts disabled to - * 'struct pm_ops'. - * - * The following callbacks included in 'struct pm_ext_ops' are executed with - * the nonboot CPUs switched off and with interrupts disabled on the only - * functional CPU. They also are executed with the PM core list of devices - * locked, so they must NOT unregister any devices. - * * @suspend_noirq: Complete the operations of ->suspend() by carrying out any * actions required for suspending the device that need interrupts to be * disabled @@ -190,18 +150,32 @@ struct pm_ops { * actions required for restoring the operations of the device that need * interrupts to be disabled * - * All of the above callbacks return error codes, but the error codes returned - * by the resume operations, @resume_noirq(), @thaw_noirq(), and - * @restore_noirq(), do not cause the PM core to abort the resume transition - * during which they are returned. The error codes returned in that cases are - * only printed by the PM core to the system logs for debugging purposes. - * Still, as stated above, it is recommended that drivers only return error - * codes from their resume methods if the device being handled fails to resume - * and is not usable any more. + * All of the above callbacks, except for @complete(), return error codes. + * However, the error codes returned by the resume operations, @resume(), + * @thaw(), @restore(), @resume_noirq(), @thaw_noirq(), and @restore_noirq() do + * not cause the PM core to abort the resume transition during which they are + * returned. The error codes returned in that cases are only printed by the PM + * core to the system logs for debugging purposes. Still, it is recommended + * that drivers only return error codes from their resume methods in case of an + * unrecoverable failure (i.e. when the device being handled refuses to resume + * and becomes unusable) to allow us to modify the PM core in the future, so + * that it can avoid attempting to handle devices that failed to resume and + * their children. + * + * It is allowed to unregister devices while the above callbacks are being + * executed. However, it is not allowed to unregister a device from within any + * of its own callbacks. */ -struct pm_ext_ops { - struct pm_ops base; +struct dev_pm_ops { + int (*prepare)(struct device *dev); + void (*complete)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*freeze)(struct device *dev); + int (*thaw)(struct device *dev); + int (*poweroff)(struct device *dev); + int (*restore)(struct device *dev); int (*suspend_noirq)(struct device *dev); int (*resume_noirq)(struct device *dev); int (*freeze_noirq)(struct device *dev); -- cgit v1.2.3-71-gd317 From 7f4f5d4516b441d712fa0ffe5380618fb7fc545e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 17 Nov 2008 11:14:19 -0500 Subject: Fix misspellings in pm.h macros This patch (as1167) fixes some misspellings in various recently-added macros in pm.h. Fortunately these macros are not yet used anywhere. Signed-off-by: Alan Stern Acked-by: Rafael J. Wysocki --- include/linux/pm.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index 5785666d0cc4..de2e0a8f6728 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -252,7 +252,7 @@ struct dev_pm_ops { #define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) #define PM_EVENT_USER_SUSPEND (PM_EVENT_USER | PM_EVENT_SUSPEND) #define PM_EVENT_USER_RESUME (PM_EVENT_USER | PM_EVENT_RESUME) -#define PM_EVENT_REMOTE_WAKEUP (PM_EVENT_REMOTE | PM_EVENT_RESUME) +#define PM_EVENT_REMOTE_RESUME (PM_EVENT_REMOTE | PM_EVENT_RESUME) #define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) #define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) @@ -265,15 +265,15 @@ struct dev_pm_ops { #define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) #define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) #define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) -#define PMSG_USER_SUSPEND ((struct pm_messge) \ +#define PMSG_USER_SUSPEND ((struct pm_message) \ { .event = PM_EVENT_USER_SUSPEND, }) -#define PMSG_USER_RESUME ((struct pm_messge) \ +#define PMSG_USER_RESUME ((struct pm_message) \ { .event = PM_EVENT_USER_RESUME, }) -#define PMSG_REMOTE_RESUME ((struct pm_messge) \ +#define PMSG_REMOTE_RESUME ((struct pm_message) \ { .event = PM_EVENT_REMOTE_RESUME, }) -#define PMSG_AUTO_SUSPEND ((struct pm_messge) \ +#define PMSG_AUTO_SUSPEND ((struct pm_message) \ { .event = PM_EVENT_AUTO_SUSPEND, }) -#define PMSG_AUTO_RESUME ((struct pm_messge) \ +#define PMSG_AUTO_RESUME ((struct pm_message) \ { .event = PM_EVENT_AUTO_RESUME, }) /** -- cgit v1.2.3-71-gd317 From 929d2fa5955ab27aa21fac615b23e0e92e8dc3a0 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 16 Oct 2008 15:51:35 -0600 Subject: driver core: Rearrange struct device for better packing This minor rearrangement saves 16 bytes from sizeof(struct device) according to pahole. Signed-off-by: Matthew Wilcox Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 4a520051c315..4e14fad41430 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -373,9 +373,9 @@ struct device { struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ + unsigned uevent_suppress:1; const char *init_name; /* initial name of the device */ struct device_type *type; - unsigned uevent_suppress:1; struct semaphore sem; /* semaphore to synchronize calls to * its driver. @@ -408,12 +408,13 @@ struct device { /* arch specific additions */ struct dev_archdata archdata; + dev_t devt; /* dev_t, creates the sysfs "dev" */ + spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; - dev_t devt; /* dev_t, creates the sysfs "dev" */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); -- cgit v1.2.3-71-gd317 From 210272a28465a7a31bcd580d2f9529f924965aa5 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 16 Oct 2008 14:57:54 -0600 Subject: driver core: Remove completion from struct klist_node Removing the completion from klist_node reduces its size from 64 bytes to 28 on x86-64. To maintain the semantics of klist_remove(), we add a single list of klist nodes which are pending deletion and scan them. Signed-off-by: Matthew Wilcox Signed-off-by: Greg Kroah-Hartman --- include/linux/klist.h | 2 -- lib/klist.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/klist.h b/include/linux/klist.h index 8ea98db223e5..d5a27af9dba5 100644 --- a/include/linux/klist.h +++ b/include/linux/klist.h @@ -13,7 +13,6 @@ #define _LINUX_KLIST_H #include -#include #include #include @@ -41,7 +40,6 @@ struct klist_node { void *n_klist; /* never access directly */ struct list_head n_node; struct kref n_ref; - struct completion n_removed; }; extern void klist_add_tail(struct klist_node *n, struct klist *k); diff --git a/lib/klist.c b/lib/klist.c index bbdd3015c2c7..573d6068a42e 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -36,6 +36,7 @@ #include #include +#include /* * Use the lowest bit of n_klist to mark deleted nodes and exclude @@ -108,7 +109,6 @@ static void add_tail(struct klist *k, struct klist_node *n) static void klist_node_init(struct klist *k, struct klist_node *n) { INIT_LIST_HEAD(&n->n_node); - init_completion(&n->n_removed); kref_init(&n->n_ref); knode_set_klist(n, k); if (k->get) @@ -171,13 +171,34 @@ void klist_add_before(struct klist_node *n, struct klist_node *pos) } EXPORT_SYMBOL_GPL(klist_add_before); +struct klist_waiter { + struct list_head list; + struct klist_node *node; + struct task_struct *process; + int woken; +}; + +static DEFINE_SPINLOCK(klist_remove_lock); +static LIST_HEAD(klist_remove_waiters); + static void klist_release(struct kref *kref) { + struct klist_waiter *waiter, *tmp; struct klist_node *n = container_of(kref, struct klist_node, n_ref); WARN_ON(!knode_dead(n)); list_del(&n->n_node); - complete(&n->n_removed); + spin_lock(&klist_remove_lock); + list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) { + if (waiter->node != n) + continue; + + waiter->woken = 1; + mb(); + wake_up_process(waiter->process); + list_del(&waiter->list); + } + spin_unlock(&klist_remove_lock); knode_set_klist(n, NULL); } @@ -217,8 +238,24 @@ EXPORT_SYMBOL_GPL(klist_del); */ void klist_remove(struct klist_node *n) { + struct klist_waiter waiter; + + waiter.node = n; + waiter.process = current; + waiter.woken = 0; + spin_lock(&klist_remove_lock); + list_add(&waiter.list, &klist_remove_waiters); + spin_unlock(&klist_remove_lock); + klist_del(n); - wait_for_completion(&n->n_removed); + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (waiter.woken) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); } EXPORT_SYMBOL_GPL(klist_remove); -- cgit v1.2.3-71-gd317 From 2831fe6f9cc4e16c103504ee09a47a084297c0f3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 16 Dec 2008 12:23:36 -0800 Subject: driver core: create a private portion of struct device This is to be used to move things out of struct device that no code outside of the driver core should ever touch. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 12 ++++++++++++ drivers/base/core.c | 8 ++++++++ include/linux/device.h | 3 +++ 3 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 0a5f055dffba..6b20809b5fd4 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -63,6 +63,18 @@ struct class_private { #define to_class(obj) \ container_of(obj, struct class_private, class_subsys.kobj) +/** + * struct device_private - structure to hold the private to the driver core portions of the device structure. + * + * @device - pointer back to the struct class that this structure is + * associated with. + * + * Nothing outside of the driver core should ever touch these fields. + */ +struct device_private { + struct device *device; +}; + /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/core.c b/drivers/base/core.c index 14aa2b6953c9..6657787e64f8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -109,6 +109,7 @@ static struct sysfs_ops dev_sysfs_ops = { static void device_release(struct kobject *kobj) { struct device *dev = to_dev(kobj); + struct device_private *p = dev->p; if (dev->release) dev->release(dev); @@ -120,6 +121,7 @@ static void device_release(struct kobject *kobj) WARN(1, KERN_ERR "Device '%s' does not have a release() " "function, it is broken and must be fixed.\n", dev_name(dev)); + kfree(p); } static struct kobj_type device_ktype = { @@ -536,6 +538,12 @@ static void klist_children_put(struct klist_node *n) */ void device_initialize(struct device *dev) { + dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); + if (!dev->p) { + WARN_ON(1); + return; + } + dev->p->device = dev; dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); klist_init(&dev->klist_children, klist_children_get, diff --git a/include/linux/device.h b/include/linux/device.h index 4e14fad41430..d6d34084fd37 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -28,6 +28,7 @@ #define BUS_ID_SIZE 20 struct device; +struct device_private; struct device_driver; struct driver_private; struct class; @@ -371,6 +372,8 @@ struct device { struct klist_node knode_bus; struct device *parent; + struct device_private *p; + struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ unsigned uevent_suppress:1; -- cgit v1.2.3-71-gd317 From 11c3b5c3e08f4d855cbef52883c266b9ab9df879 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 16 Dec 2008 12:24:56 -0800 Subject: driver core: move klist_children into private structure Nothing outside of the driver core should ever touch klist_children, or knode_parent, so move them out of the public eye. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 6 ++++++ drivers/base/core.c | 37 ++++++++++++++++++++++++------------- include/linux/device.h | 2 -- 3 files changed, 30 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 6b20809b5fd4..f5cf31c664d7 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -66,14 +66,20 @@ struct class_private { /** * struct device_private - structure to hold the private to the driver core portions of the device structure. * + * @klist_children - klist containing all children of this device + * @knode_parent - node in sibling list * @device - pointer back to the struct class that this structure is * associated with. * * Nothing outside of the driver core should ever touch these fields. */ struct device_private { + struct klist klist_children; + struct klist_node knode_parent; struct device *device; }; +#define to_device_private_parent(obj) \ + container_of(obj, struct device_private, knode_parent) /* initialisation functions */ extern int devices_init(void); diff --git a/drivers/base/core.c b/drivers/base/core.c index 6657787e64f8..180ff84ea26c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -509,14 +509,16 @@ EXPORT_SYMBOL_GPL(device_schedule_callback_owner); static void klist_children_get(struct klist_node *n) { - struct device *dev = container_of(n, struct device, knode_parent); + struct device_private *p = to_device_private_parent(n); + struct device *dev = p->device; get_device(dev); } static void klist_children_put(struct klist_node *n) { - struct device *dev = container_of(n, struct device, knode_parent); + struct device_private *p = to_device_private_parent(n); + struct device *dev = p->device; put_device(dev); } @@ -546,7 +548,7 @@ void device_initialize(struct device *dev) dev->p->device = dev; dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); - klist_init(&dev->klist_children, klist_children_get, + klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); @@ -927,7 +929,8 @@ int device_add(struct device *dev) kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); if (parent) - klist_add_tail(&dev->knode_parent, &parent->klist_children); + klist_add_tail(&dev->p->knode_parent, + &parent->p->klist_children); if (dev->class) { mutex_lock(&dev->class->p->class_mutex); @@ -1038,7 +1041,7 @@ void device_del(struct device *dev) device_pm_remove(dev); dpm_sysfs_remove(dev); if (parent) - klist_del(&dev->knode_parent); + klist_del(&dev->p->knode_parent); if (MAJOR(dev->devt)) { device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); @@ -1102,7 +1105,14 @@ void device_unregister(struct device *dev) static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - return n ? container_of(n, struct device, knode_parent) : NULL; + struct device *dev = NULL; + struct device_private *p; + + if (n) { + p = to_device_private_parent(n); + dev = p->device; + } + return dev; } /** @@ -1124,7 +1134,7 @@ int device_for_each_child(struct device *parent, void *data, struct device *child; int error = 0; - klist_iter_init(&parent->klist_children, &i); + klist_iter_init(&parent->p->klist_children, &i); while ((child = next_device(&i)) && !error) error = fn(child, data); klist_iter_exit(&i); @@ -1155,7 +1165,7 @@ struct device *device_find_child(struct device *parent, void *data, if (!parent) return NULL; - klist_iter_init(&parent->klist_children, &i); + klist_iter_init(&parent->p->klist_children, &i); while ((child = next_device(&i))) if (match(child, data) && get_device(child)) break; @@ -1478,9 +1488,10 @@ int device_move(struct device *dev, struct device *new_parent) old_parent = dev->parent; dev->parent = new_parent; if (old_parent) - klist_remove(&dev->knode_parent); + klist_remove(&dev->p->knode_parent); if (new_parent) { - klist_add_tail(&dev->knode_parent, &new_parent->klist_children); + klist_add_tail(&dev->p->knode_parent, + &new_parent->p->klist_children); set_dev_node(dev, dev_to_node(new_parent)); } @@ -1492,11 +1503,11 @@ int device_move(struct device *dev, struct device *new_parent) device_move_class_links(dev, new_parent, old_parent); if (!kobject_move(&dev->kobj, &old_parent->kobj)) { if (new_parent) - klist_remove(&dev->knode_parent); + klist_remove(&dev->p->knode_parent); dev->parent = old_parent; if (old_parent) { - klist_add_tail(&dev->knode_parent, - &old_parent->klist_children); + klist_add_tail(&dev->p->knode_parent, + &old_parent->p->klist_children); set_dev_node(dev, dev_to_node(old_parent)); } } diff --git a/include/linux/device.h b/include/linux/device.h index d6d34084fd37..60423e687205 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -366,8 +366,6 @@ struct device_dma_parameters { }; struct device { - struct klist klist_children; - struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; -- cgit v1.2.3-71-gd317 From 93e746db183b3bdbbda67900f79b5835f9cb388f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 16 Dec 2008 12:25:49 -0800 Subject: driver core: move knode_driver into private structure Nothing outside of the driver core should ever touch knode_driver, so move it out of the public eye. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ++++ drivers/base/dd.c | 13 ++++++++----- drivers/base/driver.c | 13 ++++++++++--- include/linux/device.h | 1 - 4 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index f5cf31c664d7..8af0bb2c0aa8 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -68,6 +68,7 @@ struct class_private { * * @klist_children - klist containing all children of this device * @knode_parent - node in sibling list + * @knode_driver - node in driver list * @device - pointer back to the struct class that this structure is * associated with. * @@ -76,10 +77,13 @@ struct class_private { struct device_private { struct klist klist_children; struct klist_node knode_parent; + struct klist_node knode_driver; struct device *device; }; #define to_device_private_parent(obj) \ container_of(obj, struct device_private, knode_parent) +#define to_device_private_driver(obj) \ + container_of(obj, struct device_private, knode_driver) /* initialisation functions */ extern int devices_init(void); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 17a8e45cf9c6..bb5f1eb83c03 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -28,7 +28,7 @@ static void driver_bound(struct device *dev) { - if (klist_node_attached(&dev->knode_driver)) { + if (klist_node_attached(&dev->p->knode_driver)) { printk(KERN_WARNING "%s: device %s already bound\n", __func__, kobject_name(&dev->kobj)); return; @@ -41,7 +41,7 @@ static void driver_bound(struct device *dev) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); - klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices); + klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); } static int driver_sysfs_add(struct device *dev) @@ -311,7 +311,7 @@ static void __device_release_driver(struct device *dev) drv->remove(dev); devres_release_all(dev); dev->driver = NULL; - klist_remove(&dev->knode_driver); + klist_remove(&dev->p->knode_driver); } } @@ -341,6 +341,7 @@ EXPORT_SYMBOL_GPL(device_release_driver); */ void driver_detach(struct device_driver *drv) { + struct device_private *dev_prv; struct device *dev; for (;;) { @@ -349,8 +350,10 @@ void driver_detach(struct device_driver *drv) spin_unlock(&drv->p->klist_devices.k_lock); break; } - dev = list_entry(drv->p->klist_devices.k_list.prev, - struct device, knode_driver.n_node); + dev_prv = list_entry(drv->p->klist_devices.k_list.prev, + struct device_private, + knode_driver.n_node); + dev = dev_prv->device; get_device(dev); spin_unlock(&drv->p->klist_devices.k_lock); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 1e2bda780e48..b76cc69f1106 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -19,7 +19,14 @@ static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - return n ? container_of(n, struct device, knode_driver) : NULL; + struct device *dev = NULL; + struct device_private *dev_prv; + + if (n) { + dev_prv = to_device_private_driver(n); + dev = dev_prv->device; + } + return dev; } /** @@ -42,7 +49,7 @@ int driver_for_each_device(struct device_driver *drv, struct device *start, return -EINVAL; klist_iter_init_node(&drv->p->klist_devices, &i, - start ? &start->knode_driver : NULL); + start ? &start->p->knode_driver : NULL); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); @@ -76,7 +83,7 @@ struct device *driver_find_device(struct device_driver *drv, return NULL; klist_iter_init_node(&drv->p->klist_devices, &i, - (start ? &start->knode_driver : NULL)); + (start ? &start->p->knode_driver : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; diff --git a/include/linux/device.h b/include/linux/device.h index 60423e687205..e3630222c3c1 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -366,7 +366,6 @@ struct device_dma_parameters { }; struct device { - struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; -- cgit v1.2.3-71-gd317 From b9daa99ee533578e3f88231e7a16784dcb44ec42 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 16 Dec 2008 12:26:21 -0800 Subject: driver core: move knode_bus into private structure Nothing outside of the driver core should ever touch knode_bus, so move it out of the public eye. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ++++ drivers/base/bus.c | 40 +++++++++++++++++++++++++++------------- include/linux/device.h | 1 - 3 files changed, 31 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 8af0bb2c0aa8..b676f8f801f8 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -69,6 +69,7 @@ struct class_private { * @klist_children - klist containing all children of this device * @knode_parent - node in sibling list * @knode_driver - node in driver list + * @knode_bus - node in bus list * @device - pointer back to the struct class that this structure is * associated with. * @@ -78,12 +79,15 @@ struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; + struct klist_node knode_bus; struct device *device; }; #define to_device_private_parent(obj) \ container_of(obj, struct device_private, knode_parent) #define to_device_private_driver(obj) \ container_of(obj, struct device_private, knode_driver) +#define to_device_private_bus(obj) \ + container_of(obj, struct device_private, knode_bus) /* initialisation functions */ extern int devices_init(void); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 83f32b891fa9..0f0a50444672 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -253,7 +253,14 @@ static ssize_t store_drivers_probe(struct bus_type *bus, static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - return n ? container_of(n, struct device, knode_bus) : NULL; + struct device *dev = NULL; + struct device_private *dev_prv; + + if (n) { + dev_prv = to_device_private_bus(n); + dev = dev_prv->device; + } + return dev; } /** @@ -286,7 +293,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start, return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->knode_bus : NULL)); + (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); @@ -320,7 +327,7 @@ struct device *bus_find_device(struct bus_type *bus, return NULL; klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->knode_bus : NULL)); + (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; @@ -507,7 +514,8 @@ void bus_attach_device(struct device *dev) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) - klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); + klist_add_tail(&dev->p->knode_bus, + &bus->p->klist_devices); } } @@ -528,8 +536,8 @@ void bus_remove_device(struct device *dev) sysfs_remove_link(&dev->bus->p->devices_kset->kobj, dev_name(dev)); device_remove_attrs(dev->bus, dev); - if (klist_node_attached(&dev->knode_bus)) - klist_del(&dev->knode_bus); + if (klist_node_attached(&dev->p->knode_bus)) + klist_del(&dev->p->knode_bus); pr_debug("bus: '%s': remove device %s\n", dev->bus->name, dev_name(dev)); @@ -831,14 +839,16 @@ static void bus_remove_attrs(struct bus_type *bus) static void klist_devices_get(struct klist_node *n) { - struct device *dev = container_of(n, struct device, knode_bus); + struct device_private *dev_prv = to_device_private_bus(n); + struct device *dev = dev_prv->device; get_device(dev); } static void klist_devices_put(struct klist_node *n) { - struct device *dev = container_of(n, struct device, knode_bus); + struct device_private *dev_prv = to_device_private_bus(n); + struct device *dev = dev_prv->device; put_device(dev); } @@ -993,18 +1003,20 @@ static void device_insertion_sort_klist(struct device *a, struct list_head *list { struct list_head *pos; struct klist_node *n; + struct device_private *dev_prv; struct device *b; list_for_each(pos, list) { n = container_of(pos, struct klist_node, n_node); - b = container_of(n, struct device, knode_bus); + dev_prv = to_device_private_bus(n); + b = dev_prv->device; if (compare(a, b) <= 0) { - list_move_tail(&a->knode_bus.n_node, - &b->knode_bus.n_node); + list_move_tail(&a->p->knode_bus.n_node, + &b->p->knode_bus.n_node); return; } } - list_move_tail(&a->knode_bus.n_node, list); + list_move_tail(&a->p->knode_bus.n_node, list); } void bus_sort_breadthfirst(struct bus_type *bus, @@ -1014,6 +1026,7 @@ void bus_sort_breadthfirst(struct bus_type *bus, LIST_HEAD(sorted_devices); struct list_head *pos, *tmp; struct klist_node *n; + struct device_private *dev_prv; struct device *dev; struct klist *device_klist; @@ -1022,7 +1035,8 @@ void bus_sort_breadthfirst(struct bus_type *bus, spin_lock(&device_klist->k_lock); list_for_each_safe(pos, tmp, &device_klist->k_list) { n = container_of(pos, struct klist_node, n_node); - dev = container_of(n, struct device, knode_bus); + dev_prv = to_device_private_bus(n); + dev = dev_prv->device; device_insertion_sort_klist(dev, &sorted_devices, compare); } list_splice(&sorted_devices, &device_klist->k_list); diff --git a/include/linux/device.h b/include/linux/device.h index e3630222c3c1..e21b5d69d67c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -366,7 +366,6 @@ struct device_dma_parameters { }; struct device { - struct klist_node knode_bus; struct device *parent; struct device_private *p; -- cgit v1.2.3-71-gd317 From d0d85ff989222f08dd1fa66321fef5567bbc4a7b Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 4 Dec 2008 16:55:47 +0100 Subject: Make DEBUG take precedence over DYNAMIC_PRINTK_DEBUG Statically defined DEBUG should take precedence over dynamically enabled debugging; otherwise adding DEBUG (like, for example, via CONFIG_DEBUG_KOBJECT) does not have the expected result of printing pr_debug() and dev_dbg() messages unconditionally. Signed-off-by: Cornelia Huck Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 8 ++++---- include/linux/kernel.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index e21b5d69d67c..b97a0cf1eb05 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -553,13 +553,13 @@ extern const char *dev_driver_string(const struct device *dev); #define dev_info(dev, format, arg...) \ dev_printk(KERN_INFO , dev , format , ## arg) -#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#if defined(DEBUG) +#define dev_dbg(dev, format, arg...) \ + dev_printk(KERN_DEBUG , dev , format , ## arg) +#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG) #define dev_dbg(dev, format, ...) do { \ dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ } while (0) -#elif defined(DEBUG) -#define dev_dbg(dev, format, arg...) \ - dev_printk(KERN_DEBUG , dev , format , ## arg) #else #define dev_dbg(dev, format, arg...) \ ({ if (0) dev_printk(KERN_DEBUG, dev, format, ##arg); 0; }) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index ca9ff6411dfa..d242fe1381fd 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -349,13 +349,13 @@ static inline char *pack_hex_byte(char *buf, u8 byte) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) /* If you are writing a driver, please use dev_dbg instead */ -#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#if defined(DEBUG) +#define pr_debug(fmt, ...) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) +#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG) #define pr_debug(fmt, ...) do { \ dynamic_pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) -#elif defined(DEBUG) -#define pr_debug(fmt, ...) \ - printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; }) -- cgit v1.2.3-71-gd317 From 0aa0dc41bfd993491c2344870eee7a3b218551fb Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Mon, 15 Dec 2008 12:58:26 +0000 Subject: driver core: add root_device_register() Add support for allocating root device objects which group device objects under /sys/devices directories. Also add a sysfs 'module' symlink which points to the owner of the root device object. This symlink will be used in virtio to allow userspace to determine which virtio bus implementation a given device is associated with. [Includes suggestions from Cornelia Huck] Signed-off-by: Mark McLoughlin Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 11 ++++++ 2 files changed, 102 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index ee555d7d5c3f..61df508fa62b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1217,6 +1217,97 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); +struct root_device +{ + struct device dev; + struct module *owner; +}; + +#define to_root_device(dev) container_of(dev, struct root_device, dev) + +static void root_device_release(struct device *dev) +{ + kfree(to_root_device(dev)); +} + +/** + * __root_device_register - allocate and register a root device + * @name: root device name + * @owner: owner module of the root device, usually THIS_MODULE + * + * This function allocates a root device and registers it + * using device_register(). In order to free the returned + * device, use root_device_unregister(). + * + * Root devices are dummy devices which allow other devices + * to be grouped under /sys/devices. Use this function to + * allocate a root device and then use it as the parent of + * any device which should appear under /sys/devices/{name} + * + * The /sys/devices/{name} directory will also contain a + * 'module' symlink which points to the @owner directory + * in sysfs. + * + * Note: You probably want to use root_device_register(). + */ +struct device *__root_device_register(const char *name, struct module *owner) +{ + struct root_device *root; + int err = -ENOMEM; + + root = kzalloc(sizeof(struct root_device), GFP_KERNEL); + if (!root) + return ERR_PTR(err); + + err = dev_set_name(&root->dev, name); + if (err) { + kfree(root); + return ERR_PTR(err); + } + + root->dev.release = root_device_release; + + err = device_register(&root->dev); + if (err) { + put_device(&root->dev); + return ERR_PTR(err); + } + +#ifdef CONFIG_MODULE /* gotta find a "cleaner" way to do this */ + if (owner) { + struct module_kobject *mk = &owner->mkobj; + + err = sysfs_create_link(&root->dev.kobj, &mk->kobj, "module"); + if (err) { + device_unregister(&root->dev); + return ERR_PTR(err); + } + root->owner = owner; + } +#endif + + return &root->dev; +} +EXPORT_SYMBOL_GPL(__root_device_register); + +/** + * root_device_unregister - unregister and free a root device + * @root: device going away. + * + * This function unregisters and cleans up a device that was created by + * root_device_register(). + */ +void root_device_unregister(struct device *dev) +{ + struct root_device *root = to_root_device(dev); + + if (root->owner) + sysfs_remove_link(&root->dev.kobj, "module"); + + device_unregister(dev); +} +EXPORT_SYMBOL_GPL(root_device_unregister); + static void device_create_release(struct device *dev) { diff --git a/include/linux/device.h b/include/linux/device.h index b97a0cf1eb05..7d9da4b4993f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -482,6 +482,17 @@ extern struct device *device_find_child(struct device *dev, void *data, extern int device_rename(struct device *dev, char *new_name); extern int device_move(struct device *dev, struct device *new_parent); +/* + * Root device objects for grouping under /sys/devices + */ +extern struct device *__root_device_register(const char *name, + struct module *owner); +static inline struct device *root_device_register(const char *name) +{ + return __root_device_register(name, THIS_MODULE); +} +extern void root_device_unregister(struct device *root); + /* * Manual binding of a device to driver. See drivers/base/bus.c * for information on use. -- cgit v1.2.3-71-gd317 From 475b44c19913b877537c8bc19799f75b0b142641 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 6 Jan 2009 10:44:38 -0800 Subject: mtd: struct device - replace bus_id with dev_name(), dev_set_name() CC: David Woodhouse Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/m25p80.c | 16 ++++++++-------- drivers/mtd/devices/mtd_dataflash.c | 30 +++++++++++++++--------------- drivers/mtd/maps/integrator-flash.c | 2 +- drivers/mtd/maps/ixp2000.c | 4 ++-- drivers/mtd/maps/ixp4xx.c | 2 +- drivers/mtd/maps/omap_nor.c | 2 +- drivers/mtd/maps/physmap.c | 6 +++--- drivers/mtd/maps/physmap_of.c | 4 ++-- drivers/mtd/mtdconcat.c | 2 +- drivers/mtd/nand/fsl_upm.c | 2 +- drivers/mtd/nand/plat_nand.c | 2 +- drivers/mtd/nand/tmio_nand.c | 2 +- drivers/mtd/onenand/generic.c | 2 +- drivers/mtd/onenand/omap2.c | 2 +- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/vmt.c | 4 ++-- include/linux/mtd/concat.h | 2 +- 17 files changed, 43 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6659b2275c0c..5733f0643843 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -170,7 +170,7 @@ static int wait_till_ready(struct m25p *flash) static int erase_chip(struct m25p *flash) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n", - flash->spi->dev.bus_id, __func__, + dev_name(&flash->spi->dev), __func__, flash->mtd.size / 1024); /* Wait until finished previous write command. */ @@ -197,7 +197,7 @@ static int erase_chip(struct m25p *flash) static int erase_sector(struct m25p *flash, u32 offset) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", - flash->spi->dev.bus_id, __func__, + dev_name(&flash->spi->dev), __func__, flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ @@ -234,7 +234,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) u32 addr,len; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", - flash->spi->dev.bus_id, __func__, "at", + dev_name(&flash->spi->dev), __func__, "at", (u32)instr->addr, instr->len); /* sanity checks */ @@ -295,7 +295,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __func__, "from", + dev_name(&flash->spi->dev), __func__, "from", (u32)from, len); /* sanity checks */ @@ -367,7 +367,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __func__, "to", + dev_name(&flash->spi->dev), __func__, "to", (u32)to, len); if (retlen) @@ -563,7 +563,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) tmp = spi_write_then_read(spi, &code, 1, id, 5); if (tmp < 0) { DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", - spi->dev.bus_id, tmp); + dev_name(&spi->dev), tmp); return NULL; } jedec = id[0]; @@ -617,7 +617,7 @@ static int __devinit m25p_probe(struct spi_device *spi) /* unrecognized chip? */ if (i == ARRAY_SIZE(m25p_data)) { DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", - spi->dev.bus_id, data->type); + dev_name(&spi->dev), data->type); info = NULL; /* recognized; is that chip really what's there? */ @@ -658,7 +658,7 @@ static int __devinit m25p_probe(struct spi_device *spi) if (data && data->name) flash->mtd.name = data->name; else - flash->mtd.name = spi->dev.bus_id; + flash->mtd.name = dev_name(&spi->dev); flash->mtd.type = MTD_NORFLASH; flash->mtd.writesize = 1; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 6dd9aff8bb2d..65126cd668ff 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -128,7 +128,7 @@ static int dataflash_waitready(struct spi_device *spi) status = dataflash_status(spi); if (status < 0) { DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); status = 0; } @@ -154,7 +154,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", - spi->dev.bus_id, + dev_name(&spi->dev), instr->addr, instr->len); /* Sanity checks */ @@ -197,7 +197,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) if (status < 0) { printk(KERN_ERR "%s: erase %x, err %d\n", - spi->dev.bus_id, pageaddr, status); + dev_name(&spi->dev), pageaddr, status); /* REVISIT: can retry instr->retries times; or * giveup and instr->fail_addr = instr->addr; */ @@ -239,7 +239,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, int status; DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n", - priv->spi->dev.bus_id, (unsigned)from, (unsigned)(from + len)); + dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len)); *retlen = 0; @@ -288,7 +288,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, status = 0; } else DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n", - priv->spi->dev.bus_id, + dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len), status); return status; @@ -315,7 +315,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n", - spi->dev.bus_id, (unsigned)to, (unsigned)(to + len)); + dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len)); *retlen = 0; @@ -374,7 +374,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, status = spi_sync(spi, &msg); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n", - spi->dev.bus_id, addr, status); + dev_name(&spi->dev), addr, status); (void) dataflash_waitready(priv->spi); } @@ -396,7 +396,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, spi_transfer_del(x + 1); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n", - spi->dev.bus_id, addr, writelen, status); + dev_name(&spi->dev), addr, writelen, status); (void) dataflash_waitready(priv->spi); @@ -416,14 +416,14 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, status = spi_sync(spi, &msg); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n", - spi->dev.bus_id, addr, status); + dev_name(&spi->dev), addr, status); status = dataflash_waitready(priv->spi); /* Check result of the compare operation */ if (status & (1 << 6)) { printk(KERN_ERR "%s: compare page %u, err %d\n", - spi->dev.bus_id, pageaddr, status); + dev_name(&spi->dev), pageaddr, status); remaining = 0; status = -EIO; break; @@ -779,7 +779,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) tmp = spi_write_then_read(spi, &code, 1, id, 3); if (tmp < 0) { DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", - spi->dev.bus_id, tmp); + dev_name(&spi->dev), tmp); return ERR_PTR(tmp); } if (id[0] != 0x1f) @@ -869,7 +869,7 @@ static int __devinit dataflash_probe(struct spi_device *spi) status = dataflash_status(spi); if (status <= 0 || status == 0xff) { DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); if (status == 0 || status == 0xff) status = -ENODEV; return status; @@ -905,13 +905,13 @@ static int __devinit dataflash_probe(struct spi_device *spi) /* obsolete AT45DB1282 not (yet?) supported */ default: DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n", - spi->dev.bus_id, status & 0x3c); + dev_name(&spi->dev), status & 0x3c); status = -ENODEV; } if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); return status; } @@ -921,7 +921,7 @@ static int __devexit dataflash_remove(struct spi_device *spi) struct dataflash *flash = dev_get_drvdata(&spi->dev); int status; - DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); + DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev)); if (mtd_has_partitions() && flash->partitioned) status = del_mtd_partitions(&flash->mtd); diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 7100ee3c7b01..d2ec262666c7 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -105,7 +105,7 @@ static int armflash_probe(struct platform_device *dev) info->map.bankwidth = plat->width; info->map.phys = res->start; info->map.virt = base; - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.set_vpp = armflash_set_vpp; simple_map_init(&info->map); diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index 3ea1de9be720..d4fb9a3ab4df 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -188,7 +188,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) */ info->map.map_priv_2 = (unsigned long) ixp_data->bank_setup; - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.read = ixp2000_flash_read8; info->map.write = ixp2000_flash_write8; info->map.copy_from = ixp2000_flash_copy_from; @@ -196,7 +196,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) info->res = request_mem_region(dev->resource->start, dev->resource->end - dev->resource->start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (!info->res) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 16555cbeaea4..7214b876feba 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -218,7 +218,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) * handle that. */ info->map.bankwidth = 2; - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.read = ixp4xx_read16, info->map.write = ixp4xx_probe_write16, info->map.copy_from = ixp4xx_copy_from, diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index 05f276af15da..7e50e9b1b781 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -101,7 +101,7 @@ static int __init omapflash_probe(struct platform_device *pdev) err = -ENOMEM; goto out_release_mem_region; } - info->map.name = pdev->dev.bus_id; + info->map.name = dev_name(&pdev->dev); info->map.phys = res->start; info->map.size = size; info->map.bankwidth = pdata->width; diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index dfbf3f270cea..1db16e549e38 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -108,13 +108,13 @@ static int physmap_flash_probe(struct platform_device *dev) if (!devm_request_mem_region(&dev->dev, dev->resource[i].start, dev->resource[i].end - dev->resource[i].start + 1, - dev->dev.bus_id)) { + dev_name(&dev->dev))) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; goto err_out; } - info->map[i].name = dev->dev.bus_id; + info->map[i].name = dev_name(&dev->dev); info->map[i].phys = dev->resource[i].start; info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; info->map[i].bankwidth = physmap_data->width; @@ -150,7 +150,7 @@ static int physmap_flash_probe(struct platform_device *dev) * We detected multiple devices. Concatenate them together. */ #ifdef CONFIG_MTD_CONCAT - info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id); + info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev)); if (info->cmtd == NULL) err = -ENXIO; #else diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 5fcfec034a94..fbf0ca939d72 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -183,7 +183,7 @@ static int __devinit of_flash_probe(struct of_device *dev, err = -EBUSY; info->res = request_mem_region(res.start, res.end - res.start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (!info->res) goto err_out; @@ -194,7 +194,7 @@ static int __devinit of_flash_probe(struct of_device *dev, goto err_out; } - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.phys = res.start; info->map.size = res.end - res.start + 1; info->map.bankwidth = *width; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 789842d0e6f2..1a05cf37851e 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -691,7 +691,7 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) */ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ - char *name) + const char *name) { /* name for the new device */ int i; size_t size; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index a83192f80eba..7815a404a632 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -222,7 +222,7 @@ static int __devinit fun_probe(struct of_device *ofdev, fun->rnb_gpio = of_get_gpio(ofdev->node, 0); if (fun->rnb_gpio >= 0) { - ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id); + ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev)); if (ret) { dev_err(&ofdev->dev, "can't request RNB gpio\n"); goto err2; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index f674c5427b17..75f9f4874ecf 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -54,7 +54,7 @@ static int __init plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; data->mtd.owner = THIS_MODULE; - data->mtd.name = pdev->dev.bus_id; + data->mtd.name = dev_name(&pdev->dev); data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index edb1e322113d..daa6a4c3b8ce 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -433,7 +433,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->chip_delay = 15; retval = request_irq(irq, &tmio_irq, - IRQF_DISABLED, dev->dev.bus_id, tmio); + IRQF_DISABLED, dev_name(&dev->dev), tmio); if (retval) { dev_err(&dev->dev, "request_irq error %d\n", retval); goto err_irq; diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index ad81ab8e95e2..5b69e7773c6c 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -63,7 +63,7 @@ static int __devinit generic_onenand_probe(struct device *dev) info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.irq = platform_get_irq(pdev, 0); - info->mtd.name = pdev->dev.bus_id; + info->mtd.name = dev_name(&pdev->dev); info->mtd.priv = &info->onenand; info->mtd.owner = THIS_MODULE; diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index d1e0b8e7224b..96ecc1766fa8 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -668,7 +668,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) c->onenand.base); c->pdev = pdev; - c->mtd.name = pdev->dev.bus_id; + c->mtd.name = dev_name(&pdev->dev); c->mtd.priv = &c->onenand; c->mtd.owner = THIS_MODULE; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index ba0bd3d5775b..7caf22cd5ad0 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -280,7 +280,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi) ubi->dev.release = dev_release; ubi->dev.devt = ubi->cdev.dev; ubi->dev.class = ubi_class; - sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + dev_set_name(&ubi->dev, UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) return err; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3531ca9a1e24..22e1d7398fce 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -329,7 +329,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) { ubi_err("cannot register device"); @@ -678,7 +678,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) vol->dev.parent = &ubi->dev; vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) goto out_gluebi; diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h index c02f3d264ecf..e80c674daeb3 100644 --- a/include/linux/mtd/concat.h +++ b/include/linux/mtd/concat.h @@ -13,7 +13,7 @@ struct mtd_info *mtd_concat_create( struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ - char *name); /* name for the new device */ + const char *name); /* name for the new device */ void mtd_concat_destroy(struct mtd_info *mtd); -- cgit v1.2.3-71-gd317 From e70c412ee45332db2636a8f5a35a0685efb0e4aa Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Sat, 6 Dec 2008 02:23:13 +0100 Subject: UIO: Pass information about ioports to userspace (V2) Devices sometimes have memory where all or parts of it can not be mapped to userspace. But it might still be possible to access this memory from userspace by other means. An example are PCI cards that advertise not only mappable memory but also ioport ranges. On x86 architectures, these can be accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed) reported a similar problem on Blackfin arch where it doesn't seem to be easy to mmap non-cached memory but it can still be accessed from userspace. This patch allows kernel drivers to pass information about such ports to userspace. Similar to the existing mem[] array, it adds a port[] array to struct uio_info. Each port range is described by start, size, and porttype. If a driver fills in at least one such port range, the UIO core will simply pass this information to userspace by creating a new directory "portio" underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will contain a subdirectory (portX) for each port range given. Note that UIO simply passes this information to userspace, it performs no action whatsoever with this data. It's userspace's responsibility to obtain access to these ports and to solve arch dependent issues. The "porttype" attribute tells userspace what kind of port it is dealing with. This mechanism could also be used to give userspace information about GPIOs related to a device. You frequently find such hardware in embedded devices, so I added a UIO_PORT_GPIO definition. I'm not really sure if this is a good idea since there are other solutions to this problem, but it won't hurt much anyway. Signed-off-by: Hans J. Koch Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 159 ++++++++++++++++++++++++++++++++++++++++----- include/linux/uio_driver.h | 26 ++++++++ 2 files changed, 168 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 2d2440cd57a9..4ca85a113aa2 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -35,6 +35,7 @@ struct uio_device { int vma_count; struct uio_info *info; struct kobject *map_dir; + struct kobject *portio_dir; }; static int uio_major; @@ -75,17 +76,17 @@ static ssize_t map_offset_show(struct uio_mem *mem, char *buf) return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK); } -struct uio_sysfs_entry { +struct map_sysfs_entry { struct attribute attr; ssize_t (*show)(struct uio_mem *, char *); ssize_t (*store)(struct uio_mem *, const char *, size_t); }; -static struct uio_sysfs_entry addr_attribute = +static struct map_sysfs_entry addr_attribute = __ATTR(addr, S_IRUGO, map_addr_show, NULL); -static struct uio_sysfs_entry size_attribute = +static struct map_sysfs_entry size_attribute = __ATTR(size, S_IRUGO, map_size_show, NULL); -static struct uio_sysfs_entry offset_attribute = +static struct map_sysfs_entry offset_attribute = __ATTR(offset, S_IRUGO, map_offset_show, NULL); static struct attribute *attrs[] = { @@ -106,9 +107,9 @@ static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr, { struct uio_map *map = to_map(kobj); struct uio_mem *mem = map->mem; - struct uio_sysfs_entry *entry; + struct map_sysfs_entry *entry; - entry = container_of(attr, struct uio_sysfs_entry, attr); + entry = container_of(attr, struct map_sysfs_entry, attr); if (!entry->show) return -EIO; @@ -116,16 +117,93 @@ static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr, return entry->show(mem, buf); } -static struct sysfs_ops uio_sysfs_ops = { +static struct sysfs_ops map_sysfs_ops = { .show = map_type_show, }; static struct kobj_type map_attr_type = { .release = map_release, - .sysfs_ops = &uio_sysfs_ops, + .sysfs_ops = &map_sysfs_ops, .default_attrs = attrs, }; +struct uio_portio { + struct kobject kobj; + struct uio_port *port; +}; +#define to_portio(portio) container_of(portio, struct uio_portio, kobj) + +static ssize_t portio_start_show(struct uio_port *port, char *buf) +{ + return sprintf(buf, "0x%lx\n", port->start); +} + +static ssize_t portio_size_show(struct uio_port *port, char *buf) +{ + return sprintf(buf, "0x%lx\n", port->size); +} + +static ssize_t portio_porttype_show(struct uio_port *port, char *buf) +{ + const char *porttypes[] = {"none", "x86", "gpio", "other"}; + + if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) + return -EINVAL; + + return sprintf(buf, "port_%s\n", porttypes[port->porttype]); +} + +struct portio_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct uio_port *, char *); + ssize_t (*store)(struct uio_port *, const char *, size_t); +}; + +static struct portio_sysfs_entry portio_start_attribute = + __ATTR(start, S_IRUGO, portio_start_show, NULL); +static struct portio_sysfs_entry portio_size_attribute = + __ATTR(size, S_IRUGO, portio_size_show, NULL); +static struct portio_sysfs_entry portio_porttype_attribute = + __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL); + +static struct attribute *portio_attrs[] = { + &portio_start_attribute.attr, + &portio_size_attribute.attr, + &portio_porttype_attribute.attr, + NULL, +}; + +static void portio_release(struct kobject *kobj) +{ + struct uio_portio *portio = to_portio(kobj); + kfree(portio); +} + +static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uio_portio *portio = to_portio(kobj); + struct uio_port *port = portio->port; + struct portio_sysfs_entry *entry; + + entry = container_of(attr, struct portio_sysfs_entry, attr); + + if (!entry->show) + return -EIO; + + return entry->show(port, buf); +} + +static struct sysfs_ops portio_sysfs_ops = { + .show = portio_type_show, +}; + +static struct kobj_type portio_attr_type = { + .release = portio_release, + .sysfs_ops = &portio_sysfs_ops, + .default_attrs = portio_attrs, +}; + static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { @@ -177,10 +255,13 @@ static struct attribute_group uio_attr_grp = { static int uio_dev_add_attributes(struct uio_device *idev) { int ret; - int mi; + int mi, pi; int map_found = 0; + int portio_found = 0; struct uio_mem *mem; struct uio_map *map; + struct uio_port *port; + struct uio_portio *portio; ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp); if (ret) @@ -195,25 +276,58 @@ static int uio_dev_add_attributes(struct uio_device *idev) idev->map_dir = kobject_create_and_add("maps", &idev->dev->kobj); if (!idev->map_dir) - goto err; + goto err_map; } map = kzalloc(sizeof(*map), GFP_KERNEL); if (!map) - goto err; + goto err_map; kobject_init(&map->kobj, &map_attr_type); map->mem = mem; mem->map = map; ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi); if (ret) - goto err; + goto err_map; ret = kobject_uevent(&map->kobj, KOBJ_ADD); if (ret) - goto err; + goto err_map; + } + + for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) { + port = &idev->info->port[pi]; + if (port->size == 0) + break; + if (!portio_found) { + portio_found = 1; + idev->portio_dir = kobject_create_and_add("portio", + &idev->dev->kobj); + if (!idev->portio_dir) + goto err_portio; + } + portio = kzalloc(sizeof(*portio), GFP_KERNEL); + if (!portio) + goto err_portio; + kobject_init(&portio->kobj, &portio_attr_type); + portio->port = port; + port->portio = portio; + ret = kobject_add(&portio->kobj, idev->portio_dir, + "port%d", pi); + if (ret) + goto err_portio; + ret = kobject_uevent(&portio->kobj, KOBJ_ADD); + if (ret) + goto err_portio; } return 0; -err: +err_portio: + for (pi--; pi >= 0; pi--) { + port = &idev->info->port[pi]; + portio = port->portio; + kobject_put(&portio->kobj); + } + kobject_put(idev->portio_dir); +err_map: for (mi--; mi>=0; mi--) { mem = &idev->info->mem[mi]; map = mem->map; @@ -228,15 +342,26 @@ err_group: static void uio_dev_del_attributes(struct uio_device *idev) { - int mi; + int i; struct uio_mem *mem; - for (mi = 0; mi < MAX_UIO_MAPS; mi++) { - mem = &idev->info->mem[mi]; + struct uio_port *port; + + for (i = 0; i < MAX_UIO_MAPS; i++) { + mem = &idev->info->mem[i]; if (mem->size == 0) break; kobject_put(&mem->map->kobj); } kobject_put(idev->map_dir); + + for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) { + port = &idev->info->port[i]; + if (port->size == 0) + break; + kobject_put(&port->portio->kobj); + } + kobject_put(idev->portio_dir); + sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp); } diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index cdf338d94b7f..20be327bfbb4 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -38,6 +38,24 @@ struct uio_mem { #define MAX_UIO_MAPS 5 +struct uio_portio; + +/** + * struct uio_port - description of a UIO port region + * @start: start of port region + * @size: size of port region + * @porttype: type of port (see UIO_PORT_* below) + * @portio: for use by the UIO core only. + */ +struct uio_port { + unsigned long start; + unsigned long size; + int porttype; + struct uio_portio *portio; +}; + +#define MAX_UIO_PORT_REGIONS 5 + struct uio_device; /** @@ -46,6 +64,7 @@ struct uio_device; * @name: device name * @version: device driver version * @mem: list of mappable memory regions, size==0 for end of list + * @port: list of port regions, size==0 for end of list * @irq: interrupt number or UIO_IRQ_CUSTOM * @irq_flags: flags for request_irq() * @priv: optional private data @@ -60,6 +79,7 @@ struct uio_info { char *name; char *version; struct uio_mem mem[MAX_UIO_MAPS]; + struct uio_port port[MAX_UIO_PORT_REGIONS]; long irq; unsigned long irq_flags; void *priv; @@ -92,4 +112,10 @@ extern void uio_event_notify(struct uio_info *info); #define UIO_MEM_LOGICAL 2 #define UIO_MEM_VIRTUAL 3 +/* defines for uio_port->porttype */ +#define UIO_PORT_NONE 0 +#define UIO_PORT_X86 1 +#define UIO_PORT_GPIO 2 +#define UIO_PORT_OTHER 3 + #endif /* _LINUX_UIO_DRIVER_H_ */ -- cgit v1.2.3-71-gd317 From b8ac9fc0e8cda9f9776019c5b0464b0c6d2d4c90 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 12 Dec 2008 11:44:21 +0100 Subject: uio: make uio_info's name and version const These are only ever assigned constant strings and never modified. This was noticed because Wolfram Sang needed to cast the result of of_get_property() in order to assign it to the name field of a struct uio_info. Signed-off-by: Stephen Rothwell Signed-off-by: Hans J. Koch Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 4 ++-- include/linux/uio_driver.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index 6116b93608df..b787e4721c90 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -393,12 +393,12 @@ offset = N * getpagesize(); -char *name: Required. The name of your driver as +const char *name: Required. The name of your driver as it will appear in sysfs. I recommend using the name of your module for this. -char *version: Required. This string appears in +const char *version: Required. This string appears in /sys/class/uio/uioX/version. diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 20be327bfbb4..a0bb6bd2e5c1 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -76,8 +76,8 @@ struct uio_device; */ struct uio_info { struct uio_device *uio_dev; - char *name; - char *version; + const char *name; + const char *version; struct uio_mem mem[MAX_UIO_MAPS]; struct uio_port port[MAX_UIO_PORT_REGIONS]; long irq; -- cgit v1.2.3-71-gd317 From 29881c4502ba05f46bc12ae8053d4e08d7e2615c Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 7 Jan 2009 09:21:54 +1100 Subject: Revert "CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #2]" This reverts commit 14eaddc967b16017d4a1a24d2be6c28ecbe06ed8. David has a better version to come. --- include/linux/capability.h | 17 ++-------------- include/linux/security.h | 49 +++++++++------------------------------------- kernel/capability.c | 2 +- security/capability.c | 1 - security/commoncap.c | 42 ++++++++++++--------------------------- security/root_plug.c | 1 - security/security.c | 25 ++++------------------- security/selinux/hooks.c | 26 ++++++------------------ security/smack/smack_lsm.c | 1 - 9 files changed, 35 insertions(+), 129 deletions(-) (limited to 'include/linux') diff --git a/include/linux/capability.h b/include/linux/capability.h index 5b8a13214451..e22f48c2a46f 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -529,21 +529,8 @@ extern const kernel_cap_t __cap_init_eff_set; * * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability(t, cap) (security_task_capable((t), (cap)) == 0) - -/** - * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) - * @t: The task in question - * @cap: The capability to be tested for - * - * Return true if the specified task has the given superior capability - * currently in effect, false if not, but don't write an audit message for the - * check. - * - * Note that this does not set PF_SUPERPRIV on the task. - */ -#define has_capability_noaudit(t, cap) \ - (security_task_capable_noaudit((t), (cap)) == 0) +#define has_capability(t, cap) (security_capable((t), (cap)) == 0) +#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0) extern int capable(int cap); diff --git a/include/linux/security.h b/include/linux/security.h index 76989b8bc34f..3416cb85e77b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -48,9 +48,7 @@ struct audit_krule; * These functions are in security/capability.c and are used * as the default capabilities functions */ -extern int cap_capable(int cap, int audit); -extern int cap_task_capable(struct task_struct *tsk, const struct cred *cred, - int cap, int audit); +extern int cap_capable(struct task_struct *tsk, int cap, int audit); extern int cap_settime(struct timespec *ts, struct timezone *tz); extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); @@ -1197,18 +1195,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @permitted contains the permitted capability set. * Return 0 and update @new if permission is granted. * @capable: - * Check whether the current process has the @cap capability in its - * subjective/effective credentials. - * @cap contains the capability . - * @audit: Whether to write an audit message or not - * Return 0 if the capability is granted for @tsk. - * @task_capable: - * Check whether the @tsk process has the @cap capability in its - * objective/real credentials. + * Check whether the @tsk process has the @cap capability. * @tsk contains the task_struct for the process. - * @cred contains the credentials to use. * @cap contains the capability . - * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. * @acct: * Check permission before enabling or disabling process accounting. If @@ -1301,9 +1290,7 @@ struct security_operations { const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); - int (*capable) (int cap, int audit); - int (*task_capable) (struct task_struct *tsk, const struct cred *cred, - int cap, int audit); + int (*capable) (struct task_struct *tsk, int cap, int audit); int (*acct) (struct file *file); int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); @@ -1569,9 +1556,8 @@ int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable(int cap); -int security_task_capable(struct task_struct *tsk, int cap); -int security_task_capable_noaudit(struct task_struct *tsk, int cap); +int security_capable(struct task_struct *tsk, int cap); +int security_capable_noaudit(struct task_struct *tsk, int cap); int security_acct(struct file *file); int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); @@ -1768,31 +1754,14 @@ static inline int security_capset(struct cred *new, return cap_capset(new, old, effective, inheritable, permitted); } -static inline int security_capable(int cap) +static inline int security_capable(struct task_struct *tsk, int cap) { - return cap_capable(cap, SECURITY_CAP_AUDIT); + return cap_capable(tsk, cap, SECURITY_CAP_AUDIT); } -static inline int security_task_capable(struct task_struct *tsk, int cap) +static inline int security_capable_noaudit(struct task_struct *tsk, int cap) { - int ret; - - rcu_read_lock(); - ret = cap_task_capable(tsk, __task_cred(tsk), cap, SECURITY_CAP_AUDIT); - rcu_read_unlock(); - return ret; -} - -static inline -int security_task_capable_noaudit(struct task_struct *tsk, int cap) -{ - int ret; - - rcu_read_lock(); - ret = cap_task_capable(tsk, __task_cred(tsk), cap, - SECURITY_CAP_NOAUDIT); - rcu_read_unlock(); - return ret; + return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT); } static inline int security_acct(struct file *file) diff --git a/kernel/capability.c b/kernel/capability.c index df62f53f84ac..36b4b4daebec 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -308,7 +308,7 @@ int capable(int cap) BUG(); } - if (security_capable(cap) == 0) { + if (has_capability(current, cap)) { current->flags |= PF_SUPERPRIV; return 1; } diff --git a/security/capability.c b/security/capability.c index fd1493da4f8d..2dce66fcb992 100644 --- a/security/capability.c +++ b/security/capability.c @@ -826,7 +826,6 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, capset); set_to_cap_if_null(ops, acct); set_to_cap_if_null(ops, capable); - set_to_cap_if_null(ops, task_capable); set_to_cap_if_null(ops, quotactl); set_to_cap_if_null(ops, quota_on); set_to_cap_if_null(ops, sysctl); diff --git a/security/commoncap.c b/security/commoncap.c index 7f0b2a68717d..79713545cd63 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -43,44 +43,28 @@ int cap_netlink_recv(struct sk_buff *skb, int cap) EXPORT_SYMBOL(cap_netlink_recv); /** - * cap_capable - Determine whether current has a particular effective capability + * cap_capable - Determine whether a task has a particular effective capability + * @tsk: The task to query * @cap: The capability to check for * @audit: Whether to write an audit message or not * * Determine whether the nominated task has the specified capability amongst - * its effective set, returning 0 if it does, -ve if it does not. Note that - * this uses current's subjective/effective credentials. + * its effective set, returning 0 if it does, -ve if it does not. * * NOTE WELL: cap_capable() cannot be used like the kernel's capable() * function. That is, it has the reverse semantics: cap_capable() returns 0 * when a task has a capability, but the kernel's capable() returns 1 for this * case. */ -int cap_capable(int cap, int audit) +int cap_capable(struct task_struct *tsk, int cap, int audit) { - return cap_raised(current_cap(), cap) ? 0 : -EPERM; -} + __u32 cap_raised; -/** - * cap_has_capability - Determine whether a task has a particular effective capability - * @tsk: The task to query - * @cred: The credentials to use - * @cap: The capability to check for - * @audit: Whether to write an audit message or not - * - * Determine whether the nominated task has the specified capability amongst - * its effective set, returning 0 if it does, -ve if it does not. Note that - * this uses the task's objective/real credentials. - * - * NOTE WELL: cap_has_capability() cannot be used like the kernel's - * has_capability() function. That is, it has the reverse semantics: - * cap_has_capability() returns 0 when a task has a capability, but the - * kernel's has_capability() returns 1 for this case. - */ -int cap_task_capable(struct task_struct *tsk, const struct cred *cred, int cap, - int audit) -{ - return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + /* Derived from include/linux/sched.h:capable. */ + rcu_read_lock(); + cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); + rcu_read_unlock(); + return cap_raised ? 0 : -EPERM; } /** @@ -176,7 +160,7 @@ static inline int cap_inh_is_capped(void) /* they are so limited unless the current task has the CAP_SETPCAP * capability */ - if (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) return 0; #endif return 1; @@ -885,7 +869,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ - || (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ + || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ /* * [1] no changing of bits that are locked * [2] no unlocking of locks @@ -966,7 +950,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { int cap_sys_admin = 0; - if (cap_capable(CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) + if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) cap_sys_admin = 1; return __vm_enough_memory(mm, pages, cap_sys_admin); } diff --git a/security/root_plug.c b/security/root_plug.c index 559578f8ac66..40fb4f15e27b 100644 --- a/security/root_plug.c +++ b/security/root_plug.c @@ -77,7 +77,6 @@ static struct security_operations rootplug_security_ops = { .capget = cap_capget, .capset = cap_capset, .capable = cap_capable, - .task_capable = cap_task_capable, .bprm_set_creds = cap_bprm_set_creds, diff --git a/security/security.c b/security/security.c index 9bbc8e57b8c6..d85dbb37c972 100644 --- a/security/security.c +++ b/security/security.c @@ -154,31 +154,14 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable(int cap) +int security_capable(struct task_struct *tsk, int cap) { - return security_ops->capable(cap, SECURITY_CAP_AUDIT); + return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT); } -int security_task_capable(struct task_struct *tsk, int cap) +int security_capable_noaudit(struct task_struct *tsk, int cap) { - const struct cred *cred; - int ret; - - cred = get_task_cred(tsk); - ret = security_ops->task_capable(tsk, cred, cap, SECURITY_CAP_AUDIT); - put_cred(cred); - return ret; -} - -int security_task_capable_noaudit(struct task_struct *tsk, int cap) -{ - const struct cred *cred; - int ret; - - cred = get_task_cred(tsk); - ret = security_ops->task_capable(tsk, cred, cap, SECURITY_CAP_NOAUDIT); - put_cred(cred); - return ret; + return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT); } int security_acct(struct file *file) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index eb6c45107a05..df30a7555d8a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1433,13 +1433,12 @@ static int current_has_perm(const struct task_struct *tsk, /* Check whether a task is allowed to use a capability. */ static int task_has_capability(struct task_struct *tsk, - const struct cred *cred, int cap, int audit) { struct avc_audit_data ad; struct av_decision avd; u16 sclass; - u32 sid = cred_sid(cred); + u32 sid = task_sid(tsk); u32 av = CAP_TO_MASK(cap); int rc; @@ -1866,27 +1865,15 @@ static int selinux_capset(struct cred *new, const struct cred *old, return cred_has_perm(old, new, PROCESS__SETCAP); } -static int selinux_capable(int cap, int audit) -{ - int rc; - - rc = secondary_ops->capable(cap, audit); - if (rc) - return rc; - - return task_has_capability(current, current_cred(), cap, audit); -} - -static int selinux_task_capable(struct task_struct *tsk, - const struct cred *cred, int cap, int audit) +static int selinux_capable(struct task_struct *tsk, int cap, int audit) { int rc; - rc = secondary_ops->task_capable(tsk, cred, cap, audit); + rc = secondary_ops->capable(tsk, cap, audit); if (rc) return rc; - return task_has_capability(tsk, cred, cap, audit); + return task_has_capability(tsk, cap, audit); } static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -2050,7 +2037,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) { int rc, cap_sys_admin = 0; - rc = selinux_capable(CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); + rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); if (rc == 0) cap_sys_admin = 1; @@ -2893,7 +2880,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name * and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error = selinux_capable(CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); + error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); if (!error) error = security_sid_to_context_force(isec->sid, &context, &size); @@ -5581,7 +5568,6 @@ static struct security_operations selinux_ops = { .capset = selinux_capset, .sysctl = selinux_sysctl, .capable = selinux_capable, - .task_capable = selinux_task_capable, .quotactl = selinux_quotactl, .quota_on = selinux_quota_on, .syslog = selinux_syslog, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7f12cc7015b6..6bfaba6177c2 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2827,7 +2827,6 @@ struct security_operations smack_ops = { .capget = cap_capget, .capset = cap_capset, .capable = cap_capable, - .task_capable = cap_task_capable, .syslog = smack_syslog, .settime = cap_settime, .vm_enough_memory = cap_vm_enough_memory, -- cgit v1.2.3-71-gd317 From 3699c53c485bf0168e6500d0ed18bf931584dd7c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 6 Jan 2009 22:27:01 +0000 Subject: CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #3] Fix a regression in cap_capable() due to: commit 3b11a1decef07c19443d24ae926982bc8ec9f4c0 Author: David Howells Date: Fri Nov 14 10:39:26 2008 +1100 CRED: Differentiate objective and effective subjective credentials on a task The problem is that the above patch allows a process to have two sets of credentials, and for the most part uses the subjective credentials when accessing current's creds. There is, however, one exception: cap_capable(), and thus capable(), uses the real/objective credentials of the target task, whether or not it is the current task. Ordinarily this doesn't matter, since usually the two cred pointers in current point to the same set of creds. However, sys_faccessat() makes use of this facility to override the credentials of the calling process to make its test, without affecting the creds as seen from other processes. One of the things sys_faccessat() does is to make an adjustment to the effective capabilities mask, which cap_capable(), as it stands, then ignores. The affected capability check is in generic_permission(): if (!(mask & MAY_EXEC) || execute_ok(inode)) if (capable(CAP_DAC_OVERRIDE)) return 0; This change passes the set of credentials to be tested down into the commoncap and SELinux code. The security functions called by capable() and has_capability() select the appropriate set of credentials from the process being checked. This can be tested by compiling the following program from the XFS testsuite: /* * t_access_root.c - trivial test program to show permission bug. * * Written by Michael Kerrisk - copyright ownership not pursued. * Sourced from: http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-10/6030.html */ #include #include #include #include #include #include #define UID 500 #define GID 100 #define PERM 0 #define TESTPATH "/tmp/t_access" static void errExit(char *msg) { perror(msg); exit(EXIT_FAILURE); } /* errExit */ static void accessTest(char *file, int mask, char *mstr) { printf("access(%s, %s) returns %d\n", file, mstr, access(file, mask)); } /* accessTest */ int main(int argc, char *argv[]) { int fd, perm, uid, gid; char *testpath; char cmd[PATH_MAX + 20]; testpath = (argc > 1) ? argv[1] : TESTPATH; perm = (argc > 2) ? strtoul(argv[2], NULL, 8) : PERM; uid = (argc > 3) ? atoi(argv[3]) : UID; gid = (argc > 4) ? atoi(argv[4]) : GID; unlink(testpath); fd = open(testpath, O_RDWR | O_CREAT, 0); if (fd == -1) errExit("open"); if (fchown(fd, uid, gid) == -1) errExit("fchown"); if (fchmod(fd, perm) == -1) errExit("fchmod"); close(fd); snprintf(cmd, sizeof(cmd), "ls -l %s", testpath); system(cmd); if (seteuid(uid) == -1) errExit("seteuid"); accessTest(testpath, 0, "0"); accessTest(testpath, R_OK, "R_OK"); accessTest(testpath, W_OK, "W_OK"); accessTest(testpath, X_OK, "X_OK"); accessTest(testpath, R_OK | W_OK, "R_OK | W_OK"); accessTest(testpath, R_OK | X_OK, "R_OK | X_OK"); accessTest(testpath, W_OK | X_OK, "W_OK | X_OK"); accessTest(testpath, R_OK | W_OK | X_OK, "R_OK | W_OK | X_OK"); exit(EXIT_SUCCESS); } /* main */ This can be run against an Ext3 filesystem as well as against an XFS filesystem. If successful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 03:00 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns 0 access(/tmp/xxx, W_OK) returns 0 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns 0 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 If unsuccessful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 02:56 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns -1 access(/tmp/xxx, W_OK) returns -1 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns -1 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 I've also tested the fix with the SELinux and syscalls LTP testsuites. Signed-off-by: David Howells Tested-by: J. Bruce Fields Acked-by: Serge Hallyn Signed-off-by: James Morris --- include/linux/capability.h | 17 +++++++++++++++-- include/linux/security.h | 41 ++++++++++++++++++++++++++++++++--------- kernel/capability.c | 2 +- security/commoncap.c | 29 ++++++++++++++--------------- security/security.c | 26 ++++++++++++++++++++++---- security/selinux/hooks.c | 16 ++++++++++------ 6 files changed, 94 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/capability.h b/include/linux/capability.h index e22f48c2a46f..02bdb768d43b 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -529,8 +529,21 @@ extern const kernel_cap_t __cap_init_eff_set; * * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability(t, cap) (security_capable((t), (cap)) == 0) -#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0) +#define has_capability(t, cap) (security_real_capable((t), (cap)) == 0) + +/** + * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect, false if not, but don't write an audit message for the + * check. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +#define has_capability_noaudit(t, cap) \ + (security_real_capable_noaudit((t), (cap)) == 0) extern int capable(int cap); diff --git a/include/linux/security.h b/include/linux/security.h index 3416cb85e77b..f9c390494f18 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -48,7 +48,8 @@ struct audit_krule; * These functions are in security/capability.c and are used * as the default capabilities functions */ -extern int cap_capable(struct task_struct *tsk, int cap, int audit); +extern int cap_capable(struct task_struct *tsk, const struct cred *cred, + int cap, int audit); extern int cap_settime(struct timespec *ts, struct timezone *tz); extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); @@ -1195,9 +1196,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @permitted contains the permitted capability set. * Return 0 and update @new if permission is granted. * @capable: - * Check whether the @tsk process has the @cap capability. + * Check whether the @tsk process has the @cap capability in the indicated + * credentials. * @tsk contains the task_struct for the process. + * @cred contains the credentials to use. * @cap contains the capability . + * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. * @acct: * Check permission before enabling or disabling process accounting. If @@ -1290,7 +1294,8 @@ struct security_operations { const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); - int (*capable) (struct task_struct *tsk, int cap, int audit); + int (*capable) (struct task_struct *tsk, const struct cred *cred, + int cap, int audit); int (*acct) (struct file *file); int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); @@ -1556,8 +1561,9 @@ int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable(struct task_struct *tsk, int cap); -int security_capable_noaudit(struct task_struct *tsk, int cap); +int security_capable(int cap); +int security_real_capable(struct task_struct *tsk, int cap); +int security_real_capable_noaudit(struct task_struct *tsk, int cap); int security_acct(struct file *file); int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); @@ -1754,14 +1760,31 @@ static inline int security_capset(struct cred *new, return cap_capset(new, old, effective, inheritable, permitted); } -static inline int security_capable(struct task_struct *tsk, int cap) +static inline int security_capable(int cap) { - return cap_capable(tsk, cap, SECURITY_CAP_AUDIT); + return cap_capable(current, current_cred(), cap, SECURITY_CAP_AUDIT); } -static inline int security_capable_noaudit(struct task_struct *tsk, int cap) +static inline int security_real_capable(struct task_struct *tsk, int cap) { - return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT); + int ret; + + rcu_read_lock(); + ret = cap_capable(tsk, __task_cred(tsk), cap, SECURITY_CAP_AUDIT); + rcu_read_unlock(); + return ret; +} + +static inline +int security_real_capable_noaudit(struct task_struct *tsk, int cap) +{ + int ret; + + rcu_read_lock(); + ret = cap_capable(tsk, __task_cred(tsk), cap, + SECURITY_CAP_NOAUDIT); + rcu_read_unlock(); + return ret; } static inline int security_acct(struct file *file) diff --git a/kernel/capability.c b/kernel/capability.c index 36b4b4daebec..df62f53f84ac 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -308,7 +308,7 @@ int capable(int cap) BUG(); } - if (has_capability(current, cap)) { + if (security_capable(cap) == 0) { current->flags |= PF_SUPERPRIV; return 1; } diff --git a/security/commoncap.c b/security/commoncap.c index 79713545cd63..f0e671dcfff0 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -45,26 +45,22 @@ EXPORT_SYMBOL(cap_netlink_recv); /** * cap_capable - Determine whether a task has a particular effective capability * @tsk: The task to query + * @cred: The credentials to use * @cap: The capability to check for * @audit: Whether to write an audit message or not * * Determine whether the nominated task has the specified capability amongst * its effective set, returning 0 if it does, -ve if it does not. * - * NOTE WELL: cap_capable() cannot be used like the kernel's capable() - * function. That is, it has the reverse semantics: cap_capable() returns 0 - * when a task has a capability, but the kernel's capable() returns 1 for this - * case. + * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() + * and has_capability() functions. That is, it has the reverse semantics: + * cap_has_capability() returns 0 when a task has a capability, but the + * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable(struct task_struct *tsk, int cap, int audit) +int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, + int audit) { - __u32 cap_raised; - - /* Derived from include/linux/sched.h:capable. */ - rcu_read_lock(); - cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); - rcu_read_unlock(); - return cap_raised ? 0 : -EPERM; + return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; } /** @@ -160,7 +156,8 @@ static inline int cap_inh_is_capped(void) /* they are so limited unless the current task has the CAP_SETPCAP * capability */ - if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + if (cap_capable(current, current_cred(), CAP_SETPCAP, + SECURITY_CAP_AUDIT) == 0) return 0; #endif return 1; @@ -869,7 +866,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ - || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ + || (cap_capable(current, current_cred(), CAP_SETPCAP, + SECURITY_CAP_AUDIT) != 0) /*[4]*/ /* * [1] no changing of bits that are locked * [2] no unlocking of locks @@ -950,7 +948,8 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { int cap_sys_admin = 0; - if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) + if (cap_capable(current, current_cred(), CAP_SYS_ADMIN, + SECURITY_CAP_NOAUDIT) == 0) cap_sys_admin = 1; return __vm_enough_memory(mm, pages, cap_sys_admin); } diff --git a/security/security.c b/security/security.c index d85dbb37c972..a02f243f09c0 100644 --- a/security/security.c +++ b/security/security.c @@ -154,14 +154,32 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable(struct task_struct *tsk, int cap) +int security_capable(int cap) { - return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT); + return security_ops->capable(current, current_cred(), cap, + SECURITY_CAP_AUDIT); } -int security_capable_noaudit(struct task_struct *tsk, int cap) +int security_real_capable(struct task_struct *tsk, int cap) { - return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT); + const struct cred *cred; + int ret; + + cred = get_task_cred(tsk); + ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_AUDIT); + put_cred(cred); + return ret; +} + +int security_real_capable_noaudit(struct task_struct *tsk, int cap) +{ + const struct cred *cred; + int ret; + + cred = get_task_cred(tsk); + ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_NOAUDIT); + put_cred(cred); + return ret; } int security_acct(struct file *file) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index df30a7555d8a..00815973d412 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1433,12 +1433,13 @@ static int current_has_perm(const struct task_struct *tsk, /* Check whether a task is allowed to use a capability. */ static int task_has_capability(struct task_struct *tsk, + const struct cred *cred, int cap, int audit) { struct avc_audit_data ad; struct av_decision avd; u16 sclass; - u32 sid = task_sid(tsk); + u32 sid = cred_sid(cred); u32 av = CAP_TO_MASK(cap); int rc; @@ -1865,15 +1866,16 @@ static int selinux_capset(struct cred *new, const struct cred *old, return cred_has_perm(old, new, PROCESS__SETCAP); } -static int selinux_capable(struct task_struct *tsk, int cap, int audit) +static int selinux_capable(struct task_struct *tsk, const struct cred *cred, + int cap, int audit) { int rc; - rc = secondary_ops->capable(tsk, cap, audit); + rc = secondary_ops->capable(tsk, cred, cap, audit); if (rc) return rc; - return task_has_capability(tsk, cap, audit); + return task_has_capability(tsk, cred, cap, audit); } static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -2037,7 +2039,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) { int rc, cap_sys_admin = 0; - rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); + rc = selinux_capable(current, current_cred(), CAP_SYS_ADMIN, + SECURITY_CAP_NOAUDIT); if (rc == 0) cap_sys_admin = 1; @@ -2880,7 +2883,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name * and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); + error = selinux_capable(current, current_cred(), CAP_MAC_ADMIN, + SECURITY_CAP_NOAUDIT); if (!error) error = security_sid_to_context_force(isec->sid, &context, &size); -- cgit v1.2.3-71-gd317 From 08fba69986e20c1c9e5fe2e6064d146cc4f42480 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 6 Jan 2009 14:38:53 -0800 Subject: mm: report the pagesize backing a VMA in /proc/pid/smaps It is useful to verify a hugepage-aware application is using the expected pagesizes for its memory regions. This patch creates an entry called KernelPageSize in /proc/pid/smaps that is the size of page used by the kernel to back a VMA. The entry is not called PageSize as it is possible the MMU uses a different size. This extension should not break any sensible parser that skips lines containing unrecognised information. Signed-off-by: Mel Gorman Acked-by: "KOSAKI Motohiro" Cc: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 6 ++++-- include/linux/hugetlb.h | 3 +++ mm/hugetlb.c | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 3a8bdd7f5756..41ef5f23e779 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -396,7 +396,8 @@ static int show_smap(struct seq_file *m, void *v) "Private_Clean: %8lu kB\n" "Private_Dirty: %8lu kB\n" "Referenced: %8lu kB\n" - "Swap: %8lu kB\n", + "Swap: %8lu kB\n" + "KernelPageSize: %8lu kB\n", (vma->vm_end - vma->vm_start) >> 10, mss.resident >> 10, (unsigned long)(mss.pss >> (10 + PSS_SHIFT)), @@ -405,7 +406,8 @@ static int show_smap(struct seq_file *m, void *v) mss.private_clean >> 10, mss.private_dirty >> 10, mss.referenced >> 10, - mss.swap >> 10); + mss.swap >> 10, + vma_kernel_pagesize(vma) >> 10); if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index e1c8afc002c0..648e1e25979e 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -233,6 +233,8 @@ static inline unsigned long huge_page_size(struct hstate *h) return (unsigned long)PAGE_SIZE << h->order; } +extern unsigned long vma_kernel_pagesize(struct vm_area_struct *vma); + static inline unsigned long huge_page_mask(struct hstate *h) { return h->mask; @@ -273,6 +275,7 @@ struct hstate {}; #define hstate_inode(i) NULL #define huge_page_size(h) PAGE_SIZE #define huge_page_mask(h) PAGE_MASK +#define vma_kernel_pagesize(v) PAGE_SIZE #define huge_page_order(h) 0 #define huge_page_shift(h) PAGE_SHIFT static inline unsigned int pages_per_huge_page(struct hstate *h) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 6058b53dcb89..5cb8bc7c80f7 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -219,6 +219,22 @@ static pgoff_t vma_hugecache_offset(struct hstate *h, (vma->vm_pgoff >> huge_page_order(h)); } +/* + * Return the size of the pages allocated when backing a VMA. In the majority + * cases this will be same size as used by the page table entries. + */ +unsigned long vma_kernel_pagesize(struct vm_area_struct *vma) +{ + struct hstate *hstate; + + if (!is_vm_hugetlb_page(vma)) + return PAGE_SIZE; + + hstate = hstate_vma(vma); + + return 1UL << (hstate->order + PAGE_SHIFT); +} + /* * Flags for MAP_PRIVATE reservations. These are stored in the bottom * bits of the reservation map pointer, which are always clear due to -- cgit v1.2.3-71-gd317 From 3340289ddf29ca75c3acfb3a6b72f234b2f74d5c Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 6 Jan 2009 14:38:54 -0800 Subject: mm: report the MMU pagesize in /proc/pid/smaps The KernelPageSize entry in /proc/pid/smaps is the pagesize used by the kernel to back a VMA. This matches the size used by the MMU in the majority of cases. However, one counter-example occurs on PPC64 kernels whereby a kernel using 64K as a base pagesize may still use 4K pages for the MMU on older processor. To distinguish, this patch reports MMUPageSize as the pagesize used by the MMU in /proc/pid/smaps. Signed-off-by: Mel Gorman Cc: "KOSAKI Motohiro" Cc: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/include/asm/hugetlb.h | 6 ++++++ arch/powerpc/mm/hugetlbpage.c | 7 +++++++ fs/proc/task_mmu.c | 6 ++++-- include/linux/hugetlb.h | 3 +++ mm/hugetlb.c | 13 +++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/include/asm/hugetlb.h b/arch/powerpc/include/asm/hugetlb.h index 26f0d0ab27a5..b1dafb6a9743 100644 --- a/arch/powerpc/include/asm/hugetlb.h +++ b/arch/powerpc/include/asm/hugetlb.h @@ -17,6 +17,12 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep); +/* + * The version of vma_mmu_pagesize() in arch/powerpc/mm/hugetlbpage.c needs + * to override the version in mm/hugetlb.c + */ +#define vma_mmu_pagesize vma_mmu_pagesize + /* * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 201c7a5486cb..9920d6a7cf29 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -512,6 +512,13 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return slice_get_unmapped_area(addr, len, flags, mmu_psize, 1, 0); } +unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) +{ + unsigned int psize = get_slice_psize(vma->vm_mm, vma->vm_start); + + return 1UL << mmu_psize_to_shift(psize); +} + /* * Called by asm hashtable.S for doing lazy icache flush */ diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 41ef5f23e779..94063840832a 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -397,7 +397,8 @@ static int show_smap(struct seq_file *m, void *v) "Private_Dirty: %8lu kB\n" "Referenced: %8lu kB\n" "Swap: %8lu kB\n" - "KernelPageSize: %8lu kB\n", + "KernelPageSize: %8lu kB\n" + "MMUPageSize: %8lu kB\n", (vma->vm_end - vma->vm_start) >> 10, mss.resident >> 10, (unsigned long)(mss.pss >> (10 + PSS_SHIFT)), @@ -407,7 +408,8 @@ static int show_smap(struct seq_file *m, void *v) mss.private_dirty >> 10, mss.referenced >> 10, mss.swap >> 10, - vma_kernel_pagesize(vma) >> 10); + vma_kernel_pagesize(vma) >> 10, + vma_mmu_pagesize(vma) >> 10); if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 648e1e25979e..f1d2fba19ea0 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -235,6 +235,8 @@ static inline unsigned long huge_page_size(struct hstate *h) extern unsigned long vma_kernel_pagesize(struct vm_area_struct *vma); +extern unsigned long vma_mmu_pagesize(struct vm_area_struct *vma); + static inline unsigned long huge_page_mask(struct hstate *h) { return h->mask; @@ -276,6 +278,7 @@ struct hstate {}; #define huge_page_size(h) PAGE_SIZE #define huge_page_mask(h) PAGE_MASK #define vma_kernel_pagesize(v) PAGE_SIZE +#define vma_mmu_pagesize(v) PAGE_SIZE #define huge_page_order(h) 0 #define huge_page_shift(h) PAGE_SHIFT static inline unsigned int pages_per_huge_page(struct hstate *h) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 5cb8bc7c80f7..9595278b5ab4 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -235,6 +235,19 @@ unsigned long vma_kernel_pagesize(struct vm_area_struct *vma) return 1UL << (hstate->order + PAGE_SHIFT); } +/* + * Return the page size being used by the MMU to back a VMA. In the majority + * of cases, the page size used by the kernel matches the MMU size. On + * architectures where it differs, an architecture-specific version of this + * function is required. + */ +#ifndef vma_mmu_pagesize +unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) +{ + return vma_kernel_pagesize(vma); +} +#endif + /* * Flags for MAP_PRIVATE reservations. These are stored in the bottom * bits of the reservation map pointer, which are always clear due to -- cgit v1.2.3-71-gd317 From 1c0fe6e3bda0464728c23c8d84aa47567e8b716c Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 6 Jan 2009 14:38:59 -0800 Subject: mm: invoke oom-killer from page fault Rather than have the pagefault handler kill a process directly if it gets a VM_FAULT_OOM, have it call into the OOM killer. With increasingly sophisticated oom behaviour (cpusets, memory cgroups, oom killing throttling, oom priority adjustment or selective disabling, panic on oom, etc), it's silly to unconditionally kill the faulting process at page fault time. Create a hook for pagefault oom path to call into instead. Only converted x86 and uml so far. [akpm@linux-foundation.org: make __out_of_memory() static] [akpm@linux-foundation.org: fix comment] Signed-off-by: Nick Piggin Cc: Jeff Dike Acked-by: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/kernel/trap.c | 24 +++++-------- arch/x86/mm/fault.c | 24 ++++--------- include/linux/mm.h | 5 +++ mm/oom_kill.c | 94 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 84 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 44e490419495..7384d8accfe7 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -64,11 +64,10 @@ good_area: do { int fault; -survive: + fault = handle_mm_fault(mm, vma, address, is_write); if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) { - err = -ENOMEM; goto out_of_memory; } else if (fault & VM_FAULT_SIGBUS) { err = -EACCES; @@ -104,18 +103,14 @@ out: out_nosemaphore: return err; -/* - * We ran out of memory, or some other thing happened to us that made - * us unable to handle the page fault gracefully. - */ out_of_memory: - if (is_global_init(current)) { - up_read(&mm->mmap_sem); - yield(); - down_read(&mm->mmap_sem); - goto survive; - } - goto out; + /* + * We ran out of memory, call the OOM killer, and return the userspace + * (which will retry the fault, or kill us if we got oom-killed). + */ + up_read(&mm->mmap_sem); + pagefault_out_of_memory(); + return 0; } static void bad_segv(struct faultinfo fi, unsigned long ip) @@ -214,9 +209,6 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, si.si_addr = (void __user *)address; current->thread.arch.faultinfo = fi; force_sig_info(SIGBUS, &si, current); - } else if (err == -ENOMEM) { - printk(KERN_INFO "VM: killing process %s\n", current->comm); - do_exit(SIGKILL); } else { BUG_ON(err != -EFAULT); si.si_signo = SIGSEGV; diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 57ec8c86a877..9e268b6b204e 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -667,7 +667,6 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) if (unlikely(in_atomic() || !mm)) goto bad_area_nosemaphore; -again: /* * When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the @@ -859,25 +858,14 @@ no_context: oops_end(flags, regs, sig); #endif -/* - * We ran out of memory, or some other thing happened to us that made - * us unable to handle the page fault gracefully. - */ out_of_memory: + /* + * We ran out of memory, call the OOM killer, and return the userspace + * (which will retry the fault, or kill us if we got oom-killed). + */ up_read(&mm->mmap_sem); - if (is_global_init(tsk)) { - yield(); - /* - * Re-lookup the vma - in theory the vma tree might - * have changed: - */ - goto again; - } - - printk("VM: killing process %s\n", tsk->comm); - if (error_code & PF_USER) - do_group_exit(SIGKILL); - goto no_context; + pagefault_out_of_memory(); + return; do_sigbus: up_read(&mm->mmap_sem); diff --git a/include/linux/mm.h b/include/linux/mm.h index aaa8b843be28..4a3d28c86443 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -717,6 +717,11 @@ static inline int page_mapped(struct page *page) #define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS) +/* + * Can be called by the pagefault handler when it gets a VM_FAULT_OOM. + */ +extern void pagefault_out_of_memory(void); + #define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) extern void show_free_areas(void); diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 558f9afe6e4e..c592965dab2f 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -509,6 +509,69 @@ void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_mask) spin_unlock(&zone_scan_mutex); } +/* + * Must be called with tasklist_lock held for read. + */ +static void __out_of_memory(gfp_t gfp_mask, int order) +{ + if (sysctl_oom_kill_allocating_task) { + oom_kill_process(current, gfp_mask, order, 0, NULL, + "Out of memory (oom_kill_allocating_task)"); + + } else { + unsigned long points; + struct task_struct *p; + +retry: + /* + * Rambo mode: Shoot down a process and hope it solves whatever + * issues we may have. + */ + p = select_bad_process(&points, NULL); + + if (PTR_ERR(p) == -1UL) + return; + + /* Found nothing?!?! Either we hang forever, or we panic. */ + if (!p) { + read_unlock(&tasklist_lock); + panic("Out of memory and no killable processes...\n"); + } + + if (oom_kill_process(p, gfp_mask, order, points, NULL, + "Out of memory")) + goto retry; + } +} + +/* + * pagefault handler calls into here because it is out of memory but + * doesn't know exactly how or why. + */ +void pagefault_out_of_memory(void) +{ + unsigned long freed = 0; + + blocking_notifier_call_chain(&oom_notify_list, 0, &freed); + if (freed > 0) + /* Got some memory back in the last second. */ + return; + + if (sysctl_panic_on_oom) + panic("out of memory from page fault. panic_on_oom is selected.\n"); + + read_lock(&tasklist_lock); + __out_of_memory(0, 0); /* unknown gfp_mask and order */ + read_unlock(&tasklist_lock); + + /* + * Give "p" a good chance of killing itself before we + * retry to allocate memory. + */ + if (!test_thread_flag(TIF_MEMDIE)) + schedule_timeout_uninterruptible(1); +} + /** * out_of_memory - kill the "best" process when we run out of memory * @zonelist: zonelist pointer @@ -522,8 +585,6 @@ void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_mask) */ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) { - struct task_struct *p; - unsigned long points = 0; unsigned long freed = 0; enum oom_constraint constraint; @@ -544,7 +605,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) switch (constraint) { case CONSTRAINT_MEMORY_POLICY: - oom_kill_process(current, gfp_mask, order, points, NULL, + oom_kill_process(current, gfp_mask, order, 0, NULL, "No available memory (MPOL_BIND)"); break; @@ -553,35 +614,10 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) panic("out of memory. panic_on_oom is selected\n"); /* Fall-through */ case CONSTRAINT_CPUSET: - if (sysctl_oom_kill_allocating_task) { - oom_kill_process(current, gfp_mask, order, points, NULL, - "Out of memory (oom_kill_allocating_task)"); - break; - } -retry: - /* - * Rambo mode: Shoot down a process and hope it solves whatever - * issues we may have. - */ - p = select_bad_process(&points, NULL); - - if (PTR_ERR(p) == -1UL) - goto out; - - /* Found nothing?!?! Either we hang forever, or we panic. */ - if (!p) { - read_unlock(&tasklist_lock); - panic("Out of memory and no killable processes...\n"); - } - - if (oom_kill_process(p, gfp_mask, order, points, NULL, - "Out of memory")) - goto retry; - + __out_of_memory(gfp_mask, order); break; } -out: read_unlock(&tasklist_lock); /* -- cgit v1.2.3-71-gd317 From 75aa199410359dc5fbcf9025ff7af98a9d20f0d5 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 6 Jan 2009 14:39:01 -0800 Subject: oom: print triggering task's cpuset and mems allowed When cpusets are enabled, it's necessary to print the triggering task's set of allowable nodes so the subsequently printed meminfo can be interpreted correctly. We also print the task's cpuset name for informational purposes. [rientjes@google.com: task lock current before dereferencing cpuset] Cc: Paul Menage Cc: Li Zefan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpuset.h | 6 ++++++ kernel/cpuset.c | 34 ++++++++++++++++++++++++++++++++++ mm/oom_kill.c | 3 +++ 3 files changed, 43 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 8e540d32c9fe..51ea2bdea0f9 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -78,6 +78,8 @@ extern int current_cpuset_is_being_rebound(void); extern void rebuild_sched_domains(void); +extern void cpuset_print_task_mems_allowed(struct task_struct *p); + #else /* !CONFIG_CPUSETS */ static inline int cpuset_init_early(void) { return 0; } @@ -159,6 +161,10 @@ static inline void rebuild_sched_domains(void) partition_sched_domains(1, NULL, NULL); } +static inline void cpuset_print_task_mems_allowed(struct task_struct *p) +{ +} + #endif /* !CONFIG_CPUSETS */ #endif /* _LINUX_CPUSET_H */ diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 39c1a4c1c5a9..345ace5117de 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -239,6 +239,17 @@ static struct cpuset top_cpuset = { static DEFINE_MUTEX(callback_mutex); +/* + * cpuset_buffer_lock protects both the cpuset_name and cpuset_nodelist + * buffers. They are statically allocated to prevent using excess stack + * when calling cpuset_print_task_mems_allowed(). + */ +#define CPUSET_NAME_LEN (128) +#define CPUSET_NODELIST_LEN (256) +static char cpuset_name[CPUSET_NAME_LEN]; +static char cpuset_nodelist[CPUSET_NODELIST_LEN]; +static DEFINE_SPINLOCK(cpuset_buffer_lock); + /* * This is ugly, but preserves the userspace API for existing cpuset * users. If someone tries to mount the "cpuset" filesystem, we @@ -2356,6 +2367,29 @@ int cpuset_mems_allowed_intersects(const struct task_struct *tsk1, return nodes_intersects(tsk1->mems_allowed, tsk2->mems_allowed); } +/** + * cpuset_print_task_mems_allowed - prints task's cpuset and mems_allowed + * @task: pointer to task_struct of some task. + * + * Description: Prints @task's name, cpuset name, and cached copy of its + * mems_allowed to the kernel log. Must hold task_lock(task) to allow + * dereferencing task_cs(task). + */ +void cpuset_print_task_mems_allowed(struct task_struct *tsk) +{ + struct dentry *dentry; + + dentry = task_cs(tsk)->css.cgroup->dentry; + spin_lock(&cpuset_buffer_lock); + snprintf(cpuset_name, CPUSET_NAME_LEN, + dentry ? (const char *)dentry->d_name.name : "/"); + nodelist_scnprintf(cpuset_nodelist, CPUSET_NODELIST_LEN, + tsk->mems_allowed); + printk(KERN_INFO "%s cpuset=%s mems_allowed=%s\n", + tsk->comm, cpuset_name, cpuset_nodelist); + spin_unlock(&cpuset_buffer_lock); +} + /* * Collection of memory_pressure is suppressed unless * this flag is enabled by writing "1" to the special diff --git a/mm/oom_kill.c b/mm/oom_kill.c index e5f50cfdca4d..6b9e758c98a5 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -392,6 +392,9 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, printk(KERN_WARNING "%s invoked oom-killer: " "gfp_mask=0x%x, order=%d, oomkilladj=%d\n", current->comm, gfp_mask, order, current->oomkilladj); + task_lock(current); + cpuset_print_task_mems_allowed(current); + task_unlock(current); dump_stack(); show_mem(); if (sysctl_oom_dump_tasks) -- cgit v1.2.3-71-gd317 From c04fc586c1a480ba198f03ae7b6cbd7b57380b91 Mon Sep 17 00:00:00 2001 From: Gary Hade Date: Tue, 6 Jan 2009 14:39:14 -0800 Subject: mm: show node to memory section relationship with symlinks in sysfs Show node to memory section relationship with symlinks in sysfs Add /sys/devices/system/node/nodeX/memoryY symlinks for all the memory sections located on nodeX. For example: /sys/devices/system/node/node1/memory135 -> ../../memory/memory135 indicates that memory section 135 resides on node1. Also revises documentation to cover this change as well as updating Documentation/ABI/testing/sysfs-devices-memory to include descriptions of memory hotremove files 'phys_device', 'phys_index', and 'state' that were previously not described there. In addition to it always being a good policy to provide users with the maximum possible amount of physical location information for resources that can be hot-added and/or hot-removed, the following are some (but likely not all) of the user benefits provided by this change. Immediate: - Provides information needed to determine the specific node on which a defective DIMM is located. This will reduce system downtime when the node or defective DIMM is swapped out. - Prevents unintended onlining of a memory section that was previously offlined due to a defective DIMM. This could happen during node hot-add when the user or node hot-add assist script onlines _all_ offlined sections due to user or script inability to identify the specific memory sections located on the hot-added node. The consequences of reintroducing the defective memory could be ugly. - Provides information needed to vary the amount and distribution of memory on specific nodes for testing or debugging purposes. Future: - Will provide information needed to identify the memory sections that need to be offlined prior to physical removal of a specific node. Symlink creation during boot was tested on 2-node x86_64, 2-node ppc64, and 2-node ia64 systems. Symlink creation during physical memory hot-add tested on a 2-node x86_64 system. Signed-off-by: Gary Hade Signed-off-by: Badari Pulavarty Acked-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-devices-memory | 51 +++++++++++- Documentation/memory-hotplug.txt | 16 +++- arch/ia64/mm/init.c | 2 +- arch/powerpc/mm/mem.c | 2 +- arch/s390/mm/init.c | 2 +- arch/sh/mm/init.c | 3 +- arch/x86/mm/init_32.c | 2 +- arch/x86/mm/init_64.c | 2 +- drivers/base/memory.c | 19 +++-- drivers/base/node.c | 103 +++++++++++++++++++++++++ include/linux/memory.h | 6 +- include/linux/memory_hotplug.h | 2 +- include/linux/node.h | 13 ++++ mm/memory_hotplug.c | 11 +-- 14 files changed, 209 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-devices-memory b/Documentation/ABI/testing/sysfs-devices-memory index 7a16fe1e2270..9fe91c02ee40 100644 --- a/Documentation/ABI/testing/sysfs-devices-memory +++ b/Documentation/ABI/testing/sysfs-devices-memory @@ -6,7 +6,6 @@ Description: internal state of the kernel memory blocks. Files could be added or removed dynamically to represent hot-add/remove operations. - Users: hotplug memory add/remove tools https://w3.opensource.ibm.com/projects/powerpc-utils/ @@ -19,6 +18,56 @@ Description: This is useful for a user-level agent to determine identify removable sections of the memory before attempting potentially expensive hot-remove memory operation +Users: hotplug memory remove tools + https://w3.opensource.ibm.com/projects/powerpc-utils/ + +What: /sys/devices/system/memory/memoryX/phys_device +Date: September 2008 +Contact: Badari Pulavarty +Description: + The file /sys/devices/system/memory/memoryX/phys_device + is read-only and is designed to show the name of physical + memory device. Implementation is currently incomplete. +What: /sys/devices/system/memory/memoryX/phys_index +Date: September 2008 +Contact: Badari Pulavarty +Description: + The file /sys/devices/system/memory/memoryX/phys_index + is read-only and contains the section ID in hexadecimal + which is equivalent to decimal X contained in the + memory section directory name. + +What: /sys/devices/system/memory/memoryX/state +Date: September 2008 +Contact: Badari Pulavarty +Description: + The file /sys/devices/system/memory/memoryX/state + is read-write. When read, it's contents show the + online/offline state of the memory section. When written, + root can toggle the the online/offline state of a removable + memory section (see removable file description above) + using the following commands. + # echo online > /sys/devices/system/memory/memoryX/state + # echo offline > /sys/devices/system/memory/memoryX/state + + For example, if /sys/devices/system/memory/memory22/removable + contains a value of 1 and + /sys/devices/system/memory/memory22/state contains the + string "online" the following command can be executed by + by root to offline that section. + # echo offline > /sys/devices/system/memory/memory22/state Users: hotplug memory remove tools https://w3.opensource.ibm.com/projects/powerpc-utils/ + +What: /sys/devices/system/node/nodeX/memoryY +Date: September 2008 +Contact: Gary Hade +Description: + When CONFIG_NUMA is enabled + /sys/devices/system/node/nodeX/memoryY is a symbolic link that + points to the corresponding /sys/devices/system/memory/memoryY + memory section directory. For example, the following symbolic + link is created for memory section 9 on node0. + /sys/devices/system/node/node0/memory9 -> ../../memory/memory9 + diff --git a/Documentation/memory-hotplug.txt b/Documentation/memory-hotplug.txt index 168117bd6ee8..4c2ecf537a4a 100644 --- a/Documentation/memory-hotplug.txt +++ b/Documentation/memory-hotplug.txt @@ -124,7 +124,7 @@ config options. This option can be kernel module too. -------------------------------- -3 sysfs files for memory hotplug +4 sysfs files for memory hotplug -------------------------------- All sections have their device information under /sys/devices/system/memory as @@ -138,11 +138,12 @@ For example, assume 1GiB section size. A device for a memory starting at (0x100000000 / 1Gib = 4) This device covers address range [0x100000000 ... 0x140000000) -Under each section, you can see 3 files. +Under each section, you can see 4 files. /sys/devices/system/memory/memoryXXX/phys_index /sys/devices/system/memory/memoryXXX/phys_device /sys/devices/system/memory/memoryXXX/state +/sys/devices/system/memory/memoryXXX/removable 'phys_index' : read-only and contains section id, same as XXX. 'state' : read-write @@ -150,10 +151,20 @@ Under each section, you can see 3 files. at write: user can specify "online", "offline" command 'phys_device': read-only: designed to show the name of physical memory device. This is not well implemented now. +'removable' : read-only: contains an integer value indicating + whether the memory section is removable or not + removable. A value of 1 indicates that the memory + section is removable and a value of 0 indicates that + it is not removable. NOTE: These directories/files appear after physical memory hotplug phase. +If CONFIG_NUMA is enabled the +/sys/devices/system/memory/memoryXXX memory section +directories can also be accessed via symbolic links located in +the /sys/devices/system/node/node* directories. For example: +/sys/devices/system/node/node0/memory9 -> ../../memory/memory9 -------------------------------- 4. Physical memory hot-add phase @@ -365,7 +376,6 @@ node if necessary. - allowing memory hot-add to ZONE_MOVABLE. maybe we need some switch like sysctl or new control file. - showing memory section and physical device relationship. - - showing memory section and node relationship (maybe good for NUMA) - showing memory section is under ZONE_MOVABLE or not - test and make it better memory offlining. - support HugeTLB page migration and offlining. diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 054bcd9439aa..56e12903973c 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -692,7 +692,7 @@ int arch_add_memory(int nid, u64 start, u64 size) pgdat = NODE_DATA(nid); zone = pgdat->node_zones + ZONE_NORMAL; - ret = __add_pages(zone, start_pfn, nr_pages); + ret = __add_pages(nid, zone, start_pfn, nr_pages); if (ret) printk("%s: Problem encountered in __add_pages() as ret=%d\n", diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 53b06ebb3f2f..f00f09a77f12 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -132,7 +132,7 @@ int arch_add_memory(int nid, u64 start, u64 size) /* this should work for most non-highmem platforms */ zone = pgdata->node_zones; - return __add_pages(zone, start_pfn, nr_pages); + return __add_pages(nid, zone, start_pfn, nr_pages); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 158b0d6d7046..f0258ca3b17e 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -183,7 +183,7 @@ int arch_add_memory(int nid, u64 start, u64 size) rc = vmem_add_mapping(start, size); if (rc) return rc; - rc = __add_pages(zone, PFN_DOWN(start), PFN_DOWN(size)); + rc = __add_pages(nid, zone, PFN_DOWN(start), PFN_DOWN(size)); if (rc) vmem_remove_mapping(start, size); return rc; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 6cbef8caeb56..3edf297c829b 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -311,7 +311,8 @@ int arch_add_memory(int nid, u64 start, u64 size) pgdat = NODE_DATA(nid); /* We only have ZONE_NORMAL, so this is easy.. */ - ret = __add_pages(pgdat->node_zones + ZONE_NORMAL, start_pfn, nr_pages); + ret = __add_pages(nid, pgdat->node_zones + ZONE_NORMAL, + start_pfn, nr_pages); if (unlikely(ret)) printk("%s: Failed, __add_pages() == %d\n", __func__, ret); diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index f99a6c6c432e..544d724caeee 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -1079,7 +1079,7 @@ int arch_add_memory(int nid, u64 start, u64 size) unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - return __add_pages(zone, start_pfn, nr_pages); + return __add_pages(nid, zone, start_pfn, nr_pages); } #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 9f7a0d24d42a..54c437e96541 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -857,7 +857,7 @@ int arch_add_memory(int nid, u64 start, u64 size) if (last_mapped_pfn > max_pfn_mapped) max_pfn_mapped = last_mapped_pfn; - ret = __add_pages(zone, start_pfn, nr_pages); + ret = __add_pages(nid, zone, start_pfn, nr_pages); WARN_ON_ONCE(ret); return ret; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 5260e9e0df48..989429cfed88 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -347,8 +347,9 @@ static inline int memory_probe_init(void) * section belongs to... */ -static int add_memory_block(unsigned long node_id, struct mem_section *section, - unsigned long state, int phys_device) +static int add_memory_block(int nid, struct mem_section *section, + unsigned long state, int phys_device, + enum mem_add_context context) { struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); int ret = 0; @@ -370,6 +371,10 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, ret = mem_create_simple_file(mem, phys_device); if (!ret) ret = mem_create_simple_file(mem, removable); + if (!ret) { + if (context == HOTPLUG) + ret = register_mem_sect_under_node(mem, nid); + } return ret; } @@ -382,7 +387,7 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, * * This could be made generic for all sysdev classes. */ -static struct memory_block *find_memory_block(struct mem_section *section) +struct memory_block *find_memory_block(struct mem_section *section) { struct kobject *kobj; struct sys_device *sysdev; @@ -411,6 +416,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, struct memory_block *mem; mem = find_memory_block(section); + unregister_mem_sect_under_nodes(mem); mem_remove_simple_file(mem, phys_index); mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); @@ -424,9 +430,9 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, * need an interface for the VM to add new memory regions, * but without onlining it. */ -int register_new_memory(struct mem_section *section) +int register_new_memory(int nid, struct mem_section *section) { - return add_memory_block(0, section, MEM_OFFLINE, 0); + return add_memory_block(nid, section, MEM_OFFLINE, 0, HOTPLUG); } int unregister_memory_section(struct mem_section *section) @@ -458,7 +464,8 @@ int __init memory_dev_init(void) for (i = 0; i < NR_MEM_SECTIONS; i++) { if (!present_section_nr(i)) continue; - err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, 0); + err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, + 0, BOOT); if (!ret) ret = err; } diff --git a/drivers/base/node.c b/drivers/base/node.c index 91636cd8b6c9..43fa90b837ee 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,105 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) return 0; } +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE +#define page_initialized(page) (page->lru.next) + +static int get_nid_for_pfn(unsigned long pfn) +{ + struct page *page; + + if (!pfn_valid_within(pfn)) + return -1; + page = pfn_to_page(pfn); + if (!page_initialized(page)) + return -1; + return pfn_to_nid(pfn); +} + +/* register memory section under specified node if it spans that node */ +int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) +{ + unsigned long pfn, sect_start_pfn, sect_end_pfn; + + if (!mem_blk) + return -EFAULT; + if (!node_online(nid)) + return 0; + sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); + sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; + for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { + int page_nid; + + page_nid = get_nid_for_pfn(pfn); + if (page_nid < 0) + continue; + if (page_nid != nid) + continue; + return sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj, + &mem_blk->sysdev.kobj, + kobject_name(&mem_blk->sysdev.kobj)); + } + /* mem section does not span the specified node */ + return 0; +} + +/* unregister memory section under all nodes that it spans */ +int unregister_mem_sect_under_nodes(struct memory_block *mem_blk) +{ + nodemask_t unlinked_nodes; + unsigned long pfn, sect_start_pfn, sect_end_pfn; + + if (!mem_blk) + return -EFAULT; + nodes_clear(unlinked_nodes); + sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); + sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; + for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { + unsigned int nid; + + nid = get_nid_for_pfn(pfn); + if (nid < 0) + continue; + if (!node_online(nid)) + continue; + if (node_test_and_set(nid, unlinked_nodes)) + continue; + sysfs_remove_link(&node_devices[nid].sysdev.kobj, + kobject_name(&mem_blk->sysdev.kobj)); + } + return 0; +} + +static int link_mem_sections(int nid) +{ + unsigned long start_pfn = NODE_DATA(nid)->node_start_pfn; + unsigned long end_pfn = start_pfn + NODE_DATA(nid)->node_spanned_pages; + unsigned long pfn; + int err = 0; + + for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { + unsigned long section_nr = pfn_to_section_nr(pfn); + struct mem_section *mem_sect; + struct memory_block *mem_blk; + int ret; + + if (!present_section_nr(section_nr)) + continue; + mem_sect = __nr_to_section(section_nr); + mem_blk = find_memory_block(mem_sect); + ret = register_mem_sect_under_node(mem_blk, nid); + if (!err) + err = ret; + + /* discard ref obtained in find_memory_block() */ + kobject_put(&mem_blk->sysdev.kobj); + } + return err; +} +#else +static int link_mem_sections(int nid) { return 0; } +#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ + int register_one_node(int nid) { int error = 0; @@ -267,6 +367,9 @@ int register_one_node(int nid) if (cpu_to_node(cpu) == nid) register_cpu_under_node(cpu, nid); } + + /* link memory sections under this node */ + error = link_mem_sections(nid); } return error; diff --git a/include/linux/memory.h b/include/linux/memory.h index 36c82c9e6ea7..3fdc10806d31 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -79,14 +79,14 @@ static inline int memory_notify(unsigned long val, void *v) #else extern int register_memory_notifier(struct notifier_block *nb); extern void unregister_memory_notifier(struct notifier_block *nb); -extern int register_new_memory(struct mem_section *); +extern int register_new_memory(int, struct mem_section *); extern int unregister_memory_section(struct mem_section *); extern int memory_dev_init(void); extern int remove_memory_block(unsigned long, struct mem_section *, int); extern int memory_notify(unsigned long val, void *v); +extern struct memory_block *find_memory_block(struct mem_section *); #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION< Date: Tue, 6 Jan 2009 14:39:15 -0800 Subject: mm: get rid of pagevec_release_nonlru() speculative page references patch (commit: e286781d5f2e9c846e012a39653a166e9d31777d) removed last pagevec_release_nonlru() caller. So this function can be removed now. This patch doesn't have any functional change. Signed-off-by: KOSAKI Motohiro Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pagevec.h | 7 ------- mm/swap.c | 22 ---------------------- 2 files changed, 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h index e90a2cb02915..7b2886fa7fdc 100644 --- a/include/linux/pagevec.h +++ b/include/linux/pagevec.h @@ -21,7 +21,6 @@ struct pagevec { }; void __pagevec_release(struct pagevec *pvec); -void __pagevec_release_nonlru(struct pagevec *pvec); void __pagevec_free(struct pagevec *pvec); void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru); void pagevec_strip(struct pagevec *pvec); @@ -69,12 +68,6 @@ static inline void pagevec_release(struct pagevec *pvec) __pagevec_release(pvec); } -static inline void pagevec_release_nonlru(struct pagevec *pvec) -{ - if (pagevec_count(pvec)) - __pagevec_release_nonlru(pvec); -} - static inline void pagevec_free(struct pagevec *pvec) { if (pagevec_count(pvec)) diff --git a/mm/swap.c b/mm/swap.c index b135ec90cdeb..21a566fc4570 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -397,28 +397,6 @@ void __pagevec_release(struct pagevec *pvec) EXPORT_SYMBOL(__pagevec_release); -/* - * pagevec_release() for pages which are known to not be on the LRU - * - * This function reinitialises the caller's pagevec. - */ -void __pagevec_release_nonlru(struct pagevec *pvec) -{ - int i; - struct pagevec pages_to_free; - - pagevec_init(&pages_to_free, pvec->cold); - for (i = 0; i < pagevec_count(pvec); i++) { - struct page *page = pvec->pages[i]; - - VM_BUG_ON(PageLRU(page)); - if (put_page_testzero(page)) - pagevec_add(&pages_to_free, page); - } - pagevec_free(&pages_to_free); - pagevec_reinit(pvec); -} - /* * Add the passed pages to the LRU, then drop the caller's refcount * on them. Reinitialises the caller's pagevec. -- cgit v1.2.3-71-gd317 From 64cdd548ffe26849d4cd113ac640f60606063b14 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Tue, 6 Jan 2009 14:39:16 -0800 Subject: mm: cleanup: remove #ifdef CONFIG_MIGRATION #ifdef in *.c file decrease source readability a bit. removing is better. This patch doesn't have any functional change. Signed-off-by: KOSAKI Motohiro Cc: Christoph Lameter Cc: Mel Gorman Cc: Lee Schermerhorn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/migrate.h | 4 ++++ mm/mprotect.c | 6 ++---- mm/rmap.c | 10 +++------- 3 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/migrate.h b/include/linux/migrate.h index 3f34005068d4..527602cdea1c 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -7,6 +7,8 @@ typedef struct page *new_page_t(struct page *, unsigned long private, int **); #ifdef CONFIG_MIGRATION +#define PAGE_MIGRATION 1 + extern int putback_lru_pages(struct list_head *l); extern int migrate_page(struct address_space *, struct page *, struct page *); @@ -20,6 +22,8 @@ extern int migrate_vmas(struct mm_struct *mm, const nodemask_t *from, const nodemask_t *to, unsigned long flags); #else +#define PAGE_MIGRATION 0 + static inline int putback_lru_pages(struct list_head *l) { return 0; } static inline int migrate_pages(struct list_head *l, new_page_t x, unsigned long private) { return -ENOSYS; } diff --git a/mm/mprotect.c b/mm/mprotect.c index cfb4c4852062..d0f6e7ce09f1 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -59,8 +60,7 @@ static void change_pte_range(struct mm_struct *mm, pmd_t *pmd, ptent = pte_mkwrite(ptent); ptep_modify_prot_commit(mm, addr, pte, ptent); -#ifdef CONFIG_MIGRATION - } else if (!pte_file(oldpte)) { + } else if (PAGE_MIGRATION && !pte_file(oldpte)) { swp_entry_t entry = pte_to_swp_entry(oldpte); if (is_write_migration_entry(entry)) { @@ -72,9 +72,7 @@ static void change_pte_range(struct mm_struct *mm, pmd_t *pmd, set_pte_at(mm, addr, pte, swp_entry_to_pte(entry)); } -#endif } - } while (pte++, addr += PAGE_SIZE, addr != end); arch_leave_lazy_mmu_mode(); pte_unmap_unlock(pte - 1, ptl); diff --git a/mm/rmap.c b/mm/rmap.c index 10993942d6c9..53c56dacd725 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -818,8 +819,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, spin_unlock(&mmlist_lock); } dec_mm_counter(mm, anon_rss); -#ifdef CONFIG_MIGRATION - } else { + } else if (PAGE_MIGRATION) { /* * Store the pfn of the page in a special migration * pte. do_swap_page() will wait until the migration @@ -827,19 +827,15 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, */ BUG_ON(!migration); entry = make_migration_entry(page, pte_write(pteval)); -#endif } set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); BUG_ON(pte_file(*pte)); - } else -#ifdef CONFIG_MIGRATION - if (migration) { + } else if (PAGE_MIGRATION && migration) { /* Establish migration entry for a file page */ swp_entry_t entry; entry = make_migration_entry(page, pte_write(pteval)); set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); } else -#endif dec_mm_counter(mm, file_rss); -- cgit v1.2.3-71-gd317 From e5991371ee0d1c0ce19e133c6f9075b49c5b4ae8 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:22 -0800 Subject: mm: remove cgroup_mm_owner_callbacks cgroup_mm_owner_callbacks() was brought in to support the memrlimit controller, but sneaked into mainline ahead of it. That controller has now been shelved, and the mm_owner_changed() args were inadequate for it anyway (they needed an mm pointer instead of a task pointer). Remove the dead code, and restore mm_update_next_owner() locking to how it was before: taking mmap_sem there does nothing for memcontrol.c, now the only user of mm->owner. Signed-off-by: Hugh Dickins Cc: Paul Menage Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 14 +------------- kernel/cgroup.c | 33 --------------------------------- kernel/exit.c | 16 ++++++---------- 3 files changed, 7 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 1164963c3a85..08b78c09b09a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -329,13 +329,7 @@ struct cgroup_subsys { struct cgroup *cgrp); void (*post_clone)(struct cgroup_subsys *ss, struct cgroup *cgrp); void (*bind)(struct cgroup_subsys *ss, struct cgroup *root); - /* - * This routine is called with the task_lock of mm->owner held - */ - void (*mm_owner_changed)(struct cgroup_subsys *ss, - struct cgroup *old, - struct cgroup *new, - struct task_struct *p); + int subsys_id; int active; int disabled; @@ -400,9 +394,6 @@ void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it); int cgroup_scan_tasks(struct cgroup_scanner *scan); int cgroup_attach_task(struct cgroup *, struct task_struct *); -void cgroup_mm_owner_callbacks(struct task_struct *old, - struct task_struct *new); - #else /* !CONFIG_CGROUPS */ static inline int cgroup_init_early(void) { return 0; } @@ -420,9 +411,6 @@ static inline int cgroupstats_build(struct cgroupstats *stats, return -EINVAL; } -static inline void cgroup_mm_owner_callbacks(struct task_struct *old, - struct task_struct *new) {} - #endif /* !CONFIG_CGROUPS */ #endif /* _LINUX_CGROUP_H */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 87bb0258fd27..f221446aa02d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -116,7 +116,6 @@ static int root_count; * be called. */ static int need_forkexit_callback __read_mostly; -static int need_mm_owner_callback __read_mostly; /* convenient tests for these bits */ inline int cgroup_is_removed(const struct cgroup *cgrp) @@ -2539,7 +2538,6 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) init_css_set.subsys[ss->subsys_id] = dummytop->subsys[ss->subsys_id]; need_forkexit_callback |= ss->fork || ss->exit; - need_mm_owner_callback |= !!ss->mm_owner_changed; /* At system boot, before all subsystems have been * registered, no tasks have been forked, so we don't @@ -2789,37 +2787,6 @@ void cgroup_fork_callbacks(struct task_struct *child) } } -#ifdef CONFIG_MM_OWNER -/** - * cgroup_mm_owner_callbacks - run callbacks when the mm->owner changes - * @p: the new owner - * - * Called on every change to mm->owner. mm_init_owner() does not - * invoke this routine, since it assigns the mm->owner the first time - * and does not change it. - * - * The callbacks are invoked with mmap_sem held in read mode. - */ -void cgroup_mm_owner_callbacks(struct task_struct *old, struct task_struct *new) -{ - struct cgroup *oldcgrp, *newcgrp = NULL; - - if (need_mm_owner_callback) { - int i; - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - oldcgrp = task_cgroup(old, ss->subsys_id); - if (new) - newcgrp = task_cgroup(new, ss->subsys_id); - if (oldcgrp == newcgrp) - continue; - if (ss->mm_owner_changed) - ss->mm_owner_changed(ss, oldcgrp, newcgrp, new); - } - } -} -#endif /* CONFIG_MM_OWNER */ - /** * cgroup_post_fork - called on a new task after adding it to the task list * @child: the task in question diff --git a/kernel/exit.c b/kernel/exit.c index c9e5a1c14e08..f923724ab3c9 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -642,35 +642,31 @@ retry: /* * We found no owner yet mm_users > 1: this implies that we are * most likely racing with swapoff (try_to_unuse()) or /proc or - * ptrace or page migration (get_task_mm()). Mark owner as NULL, - * so that subsystems can understand the callback and take action. + * ptrace or page migration (get_task_mm()). Mark owner as NULL. */ - down_write(&mm->mmap_sem); - cgroup_mm_owner_callbacks(mm->owner, NULL); mm->owner = NULL; - up_write(&mm->mmap_sem); return; assign_new_owner: BUG_ON(c == p); get_task_struct(c); - read_unlock(&tasklist_lock); - down_write(&mm->mmap_sem); /* * The task_lock protects c->mm from changing. * We always want mm->owner->mm == mm */ task_lock(c); + /* + * Delay read_unlock() till we have the task_lock() + * to ensure that c does not slip away underneath us + */ + read_unlock(&tasklist_lock); if (c->mm != mm) { task_unlock(c); - up_write(&mm->mmap_sem); put_task_struct(c); goto retry; } - cgroup_mm_owner_callbacks(mm->owner, c); mm->owner = c; task_unlock(c); - up_write(&mm->mmap_sem); put_task_struct(c); } #endif /* CONFIG_MM_OWNER */ -- cgit v1.2.3-71-gd317 From 3c1d43787b48c798f44dc32a6e6deb5ca2da3e68 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:23 -0800 Subject: mm: remove GFP_HIGHUSER_PAGECACHE GFP_HIGHUSER_PAGECACHE is just an alias for GFP_HIGHUSER_MOVABLE, making that harder to track down: remove it, and its out-of-work brothers GFP_NOFS_PAGECACHE and GFP_USER_PAGECACHE. Since we're making that improvement to hotremove_migrate_alloc(), I think we can now also remove one of the "o"s from its comment. Signed-off-by: Hugh Dickins Acked-by: Mel Gorman Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inode.c | 4 ++-- include/linux/gfp.h | 6 ------ mm/memory_hotplug.c | 9 +++------ 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/inode.c b/fs/inode.c index bd48e5e6d3e8..a903860bc5ac 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -166,7 +166,7 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode) mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags = 0; - mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE); + mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->assoc_mapping = NULL; mapping->backing_dev_info = &default_backing_dev_info; mapping->writeback_index = 0; @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(inode_add_to_lists); * @sb: superblock * * Allocates a new inode for given superblock. The default gfp_mask - * for allocations related to inode->i_mapping is GFP_HIGHUSER_PAGECACHE. + * for allocations related to inode->i_mapping is GFP_HIGHUSER_MOVABLE. * If HIGHMEM pages are unsuitable or it is known that pages allocated * for the page cache are not reclaimable or migratable, * mapping_set_gfp_mask() must be called with suitable flags on the diff --git a/include/linux/gfp.h b/include/linux/gfp.h index e8003afeffba..dd20cd78faa8 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -69,12 +69,6 @@ struct vm_area_struct; #define GFP_HIGHUSER_MOVABLE (__GFP_WAIT | __GFP_IO | __GFP_FS | \ __GFP_HARDWALL | __GFP_HIGHMEM | \ __GFP_MOVABLE) -#define GFP_NOFS_PAGECACHE (__GFP_WAIT | __GFP_IO | __GFP_MOVABLE) -#define GFP_USER_PAGECACHE (__GFP_WAIT | __GFP_IO | __GFP_FS | \ - __GFP_HARDWALL | __GFP_MOVABLE) -#define GFP_HIGHUSER_PAGECACHE (__GFP_WAIT | __GFP_IO | __GFP_FS | \ - __GFP_HARDWALL | __GFP_HIGHMEM | \ - __GFP_MOVABLE) #ifdef CONFIG_NUMA #define GFP_THISNODE (__GFP_THISNODE | __GFP_NOWARN | __GFP_NORETRY) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 2ba38bc07b47..c083cf5fd6df 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -627,15 +627,12 @@ int scan_lru_pages(unsigned long start, unsigned long end) } static struct page * -hotremove_migrate_alloc(struct page *page, - unsigned long private, - int **x) +hotremove_migrate_alloc(struct page *page, unsigned long private, int **x) { - /* This should be improoooooved!! */ - return alloc_page(GFP_HIGHUSER_PAGECACHE); + /* This should be improooooved!! */ + return alloc_page(GFP_HIGHUSER_MOVABLE); } - #define NR_OFFLINE_AT_ONCE_PAGES (256) static int do_migrate_range(unsigned long start_pfn, unsigned long end_pfn) -- cgit v1.2.3-71-gd317 From 6d91add09f4bad5f4d4233b13faa392f0c4b16be Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:24 -0800 Subject: mm: add Set,ClearPageSwapCache stubs If we add NOOP stubs for SetPageSwapCache() and ClearPageSwapCache(), then we can remove the #ifdef CONFIG_SWAPs from mm/migrate.c. Signed-off-by: Hugh Dickins Acked-by: Christoph Lameter Cc: Nick Piggin Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 1 + mm/migrate.c | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index b12f93a3c345..628ec0802492 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -228,6 +228,7 @@ PAGEFLAG_FALSE(HighMem) PAGEFLAG(SwapCache, swapcache) #else PAGEFLAG_FALSE(SwapCache) + SETPAGEFLAG_NOOP(SwapCache) CLEARPAGEFLAG_NOOP(SwapCache) #endif #ifdef CONFIG_UNEVICTABLE_LRU diff --git a/mm/migrate.c b/mm/migrate.c index 60510306c44a..55373983c9c6 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -300,12 +300,10 @@ static int migrate_page_move_mapping(struct address_space *mapping, * Now we know that no one else is looking at the page. */ get_page(newpage); /* add cache reference */ -#ifdef CONFIG_SWAP if (PageSwapCache(page)) { SetPageSwapCache(newpage); set_page_private(newpage, page_private(page)); } -#endif radix_tree_replace_slot(pslot, newpage); @@ -373,9 +371,7 @@ static void migrate_page_copy(struct page *newpage, struct page *page) mlock_migrate_page(newpage, page); -#ifdef CONFIG_SWAP ClearPageSwapCache(page); -#endif ClearPagePrivate(page); set_page_private(page, 0); /* page->mapping contains a flag for PageAnon() */ -- cgit v1.2.3-71-gd317 From b5934c531849ff4a51ce0f290141efe564290e40 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:25 -0800 Subject: mm: add_active_or_unevictable into rmap lru_cache_add_active_or_unevictable() and page_add_new_anon_rmap() always appear together. Save some symbol table space and some jumping around by removing lru_cache_add_active_or_unevictable(), folding its code into page_add_new_anon_rmap(): like how we add file pages to lru just after adding them to page cache. Remove the nearby "TODO: is this safe?" comments (yes, it is safe), and change page_add_new_anon_rmap()'s address BUG_ON to VM_BUG_ON as originally intended. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Cc: Lee Schermerhorn Cc: Nick Piggin Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 2 -- mm/memory.c | 6 ------ mm/rmap.c | 7 ++++++- mm/swap.c | 19 ------------------- 4 files changed, 6 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index a3af95b2cb6d..48f309dc5a0c 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -174,8 +174,6 @@ extern unsigned int nr_free_pagecache_pages(void); /* linux/mm/swap.c */ extern void __lru_cache_add(struct page *, enum lru_list lru); extern void lru_cache_add_lru(struct page *, enum lru_list lru); -extern void lru_cache_add_active_or_unevictable(struct page *, - struct vm_area_struct *); extern void activate_page(struct page *); extern void mark_page_accessed(struct page *); extern void lru_add_drain(void); diff --git a/mm/memory.c b/mm/memory.c index b5af358b8b22..a138c50dc39a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1949,10 +1949,7 @@ gotten: */ ptep_clear_flush_notify(vma, address, page_table); SetPageSwapBacked(new_page); - lru_cache_add_active_or_unevictable(new_page, vma); page_add_new_anon_rmap(new_page, vma, address); - -//TODO: is this safe? do_anonymous_page() does it this way. set_pte_at(mm, address, page_table, entry); update_mmu_cache(vma, address, entry); if (old_page) { @@ -2448,7 +2445,6 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, goto release; inc_mm_counter(mm, anon_rss); SetPageSwapBacked(page); - lru_cache_add_active_or_unevictable(page, vma); page_add_new_anon_rmap(page, vma, address); set_pte_at(mm, address, page_table, entry); @@ -2597,7 +2593,6 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (anon) { inc_mm_counter(mm, anon_rss); SetPageSwapBacked(page); - lru_cache_add_active_or_unevictable(page, vma); page_add_new_anon_rmap(page, vma, address); } else { inc_mm_counter(mm, file_rss); @@ -2607,7 +2602,6 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, get_page(dirty_page); } } -//TODO: is this safe? do_anonymous_page() does it this way. set_pte_at(mm, address, page_table, entry); /* no need to invalidate: a not-present page won't be cached */ diff --git a/mm/rmap.c b/mm/rmap.c index f01e92244c53..10da68202b10 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -671,9 +672,13 @@ void page_add_anon_rmap(struct page *page, void page_add_new_anon_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address) { - BUG_ON(address < vma->vm_start || address >= vma->vm_end); + VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end); atomic_set(&page->_mapcount, 0); /* elevate count by 1 (starts at -1) */ __page_set_anon_rmap(page, vma, address); + if (page_evictable(page, vma)) + lru_cache_add_lru(page, LRU_ACTIVE + page_is_file_cache(page)); + else + add_page_to_unevictable_list(page); } /** diff --git a/mm/swap.c b/mm/swap.c index 21a566fc4570..ff0b290475fd 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -246,25 +246,6 @@ void add_page_to_unevictable_list(struct page *page) spin_unlock_irq(&zone->lru_lock); } -/** - * lru_cache_add_active_or_unevictable - * @page: the page to be added to LRU - * @vma: vma in which page is mapped for determining reclaimability - * - * place @page on active or unevictable LRU list, depending on - * page_evictable(). Note that if the page is not evictable, - * it goes directly back onto it's zone's unevictable list. It does - * NOT use a per cpu pagevec. - */ -void lru_cache_add_active_or_unevictable(struct page *page, - struct vm_area_struct *vma) -{ - if (page_evictable(page, vma)) - lru_cache_add_lru(page, LRU_ACTIVE + page_is_file_cache(page)); - else - add_page_to_unevictable_list(page); -} - /* * Drain pages out of the cpu's pagevecs. * Either "cpu" is the current CPU, and preemption has already been -- cgit v1.2.3-71-gd317 From 2afd1c928f1132b8d0099866e75ce8ad713a1180 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:26 -0800 Subject: mm: make page_lock_anon_vma() static page_lock_anon_vma() and page_unlock_anon_vma() were made available to show_page_path() in vmscan.c; but now that has been removed, make them static in rmap.c again, they're better kept private if possible. Signed-off-by: Hugh Dickins Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rmap.h | 3 --- mm/rmap.c | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 89f0564b10c8..3593b18a07dd 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -63,9 +63,6 @@ void anon_vma_unlink(struct vm_area_struct *); void anon_vma_link(struct vm_area_struct *); void __anon_vma_link(struct vm_area_struct *); -extern struct anon_vma *page_lock_anon_vma(struct page *page); -extern void page_unlock_anon_vma(struct anon_vma *anon_vma); - /* * rmap interfaces called when adding or removing pte of page */ diff --git a/mm/rmap.c b/mm/rmap.c index 10da68202b10..892e1877366b 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -193,7 +193,7 @@ void __init anon_vma_init(void) * Getting a lock on a stable anon_vma from a page off the LRU is * tricky: page_lock_anon_vma rely on RCU to guard against the races. */ -struct anon_vma *page_lock_anon_vma(struct page *page) +static struct anon_vma *page_lock_anon_vma(struct page *page) { struct anon_vma *anon_vma; unsigned long anon_mapping; @@ -213,7 +213,7 @@ out: return NULL; } -void page_unlock_anon_vma(struct anon_vma *anon_vma) +static void page_unlock_anon_vma(struct anon_vma *anon_vma) { spin_unlock(&anon_vma->lock); rcu_read_unlock(); -- cgit v1.2.3-71-gd317 From 364aeb2849789b51bf4b9af2ddd02fee7285c54e Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 6 Jan 2009 14:39:29 -0800 Subject: mm: change dirty limit type specifiers to unsigned long The background dirty and dirty limits are better defined with type specifiers of unsigned long since negative writeback thresholds are not possible. These values, as returned by get_dirty_limits(), are normally compared with ZVC values to determine whether writeback shall commence or be throttled. Such page counts cannot be negative, so declaring the page limits as signed is unnecessary. Acked-by: Peter Zijlstra Cc: Dave Chinner Cc: Christoph Lameter Signed-off-by: David Rientjes Cc: Andrea Righi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/writeback.h | 4 ++-- mm/backing-dev.c | 6 +++--- mm/page-writeback.c | 22 +++++++++++----------- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/writeback.h b/include/linux/writeback.h index e585657e9831..259e9ea58cab 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -125,8 +125,8 @@ struct file; int dirty_writeback_centisecs_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); -void get_dirty_limits(long *pbackground, long *pdirty, long *pbdi_dirty, - struct backing_dev_info *bdi); +void get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, + unsigned long *pbdi_dirty, struct backing_dev_info *bdi); void page_writeback_init(void); void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 801c08b046e6..6f80beddd8a4 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -24,9 +24,9 @@ static void bdi_debug_init(void) static int bdi_debug_stats_show(struct seq_file *m, void *v) { struct backing_dev_info *bdi = m->private; - long background_thresh; - long dirty_thresh; - long bdi_thresh; + unsigned long background_thresh; + unsigned long dirty_thresh; + unsigned long bdi_thresh; get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 08d2b960b294..4d4074cff300 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -362,13 +362,13 @@ unsigned long determine_dirtyable_memory(void) } void -get_dirty_limits(long *pbackground, long *pdirty, long *pbdi_dirty, - struct backing_dev_info *bdi) +get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, + unsigned long *pbdi_dirty, struct backing_dev_info *bdi) { int background_ratio; /* Percentages */ int dirty_ratio; - long background; - long dirty; + unsigned long background; + unsigned long dirty; unsigned long available_memory = determine_dirtyable_memory(); struct task_struct *tsk; @@ -423,9 +423,9 @@ static void balance_dirty_pages(struct address_space *mapping) { long nr_reclaimable, bdi_nr_reclaimable; long nr_writeback, bdi_nr_writeback; - long background_thresh; - long dirty_thresh; - long bdi_thresh; + unsigned long background_thresh; + unsigned long dirty_thresh; + unsigned long bdi_thresh; unsigned long pages_written = 0; unsigned long write_chunk = sync_writeback_pages(); @@ -580,8 +580,8 @@ EXPORT_SYMBOL(balance_dirty_pages_ratelimited_nr); void throttle_vm_writeout(gfp_t gfp_mask) { - long background_thresh; - long dirty_thresh; + unsigned long background_thresh; + unsigned long dirty_thresh; for ( ; ; ) { get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL); @@ -624,8 +624,8 @@ static void background_writeout(unsigned long _min_pages) }; for ( ; ; ) { - long background_thresh; - long dirty_thresh; + unsigned long background_thresh; + unsigned long dirty_thresh; get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL); if (global_page_state(NR_FILE_DIRTY) + -- cgit v1.2.3-71-gd317 From 2da02997e08d3efe8174c7a47696e6f7cbe69ba9 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 6 Jan 2009 14:39:31 -0800 Subject: mm: add dirty_background_bytes and dirty_bytes sysctls This change introduces two new sysctls to /proc/sys/vm: dirty_background_bytes and dirty_bytes. dirty_background_bytes is the counterpart to dirty_background_ratio and dirty_bytes is the counterpart to dirty_ratio. With growing memory capacities of individual machines, it's no longer sufficient to specify dirty thresholds as a percentage of the amount of dirtyable memory over the entire system. dirty_background_bytes and dirty_bytes specify quantities of memory, in bytes, that represent the dirty limits for the entire system. If either of these values is set, its value represents the amount of dirty memory that is needed to commence either background or direct writeback. When a `bytes' or `ratio' file is written, its counterpart becomes a function of the written value. For example, if dirty_bytes is written to be 8096, 8K of memory is required to commence direct writeback. dirty_ratio is then functionally equivalent to 8K / the amount of dirtyable memory: dirtyable_memory = free pages + mapped pages + file cache dirty_background_bytes = dirty_background_ratio * dirtyable_memory -or- dirty_background_ratio = dirty_background_bytes / dirtyable_memory AND dirty_bytes = dirty_ratio * dirtyable_memory -or- dirty_ratio = dirty_bytes / dirtyable_memory Only one of dirty_background_bytes and dirty_background_ratio may be specified at a time, and only one of dirty_bytes and dirty_ratio may be specified. When one sysctl is written, the other appears as 0 when read. The `bytes' files operate on a page size granularity since dirty limits are compared with ZVC values, which are in page units. Prior to this change, the minimum dirty_ratio was 5 as implemented by get_dirty_limits() although /proc/sys/vm/dirty_ratio would show any user written value between 0 and 100. This restriction is maintained, but dirty_bytes has a lower limit of only one page. Also prior to this change, the dirty_background_ratio could not equal or exceed dirty_ratio. This restriction is maintained in addition to restricting dirty_background_bytes. If either background threshold equals or exceeds that of the dirty threshold, it is implicitly set to half the dirty threshold. Acked-by: Peter Zijlstra Cc: Dave Chinner Cc: Christoph Lameter Signed-off-by: David Rientjes Cc: Andrea Righi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 26 +++++++++- Documentation/sysctl/vm.txt | 3 +- include/linux/writeback.h | 11 ++++ kernel/sysctl.c | 27 ++++++++-- mm/page-writeback.c | 102 +++++++++++++++++++++++++++++++------ 5 files changed, 146 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 71df353e367c..32e94635484f 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1385,6 +1385,15 @@ swapcache reclaim. Decreasing vfs_cache_pressure causes the kernel to prefer to retain dentry and inode caches. Increasing vfs_cache_pressure beyond 100 causes the kernel to prefer to reclaim dentries and inodes. +dirty_background_bytes +---------------------- + +Contains the amount of dirty memory at which the pdflush background writeback +daemon will start writeback. + +If dirty_background_bytes is written, dirty_background_ratio becomes a function +of its value (dirty_background_bytes / the amount of dirtyable system memory). + dirty_background_ratio ---------------------- @@ -1393,14 +1402,29 @@ pages + file cache, not including locked pages and HugePages), the number of pages at which the pdflush background writeback daemon will start writing out dirty data. +If dirty_background_ratio is written, dirty_background_bytes becomes a function +of its value (dirty_background_ratio * the amount of dirtyable system memory). + +dirty_bytes +----------- + +Contains the amount of dirty memory at which a process generating disk writes +will itself start writeback. + +If dirty_bytes is written, dirty_ratio becomes a function of its value +(dirty_bytes / the amount of dirtyable system memory). + dirty_ratio ------------------ +----------- Contains, as a percentage of the dirtyable system memory (free pages + mapped pages + file cache, not including locked pages and HugePages), the number of pages at which a process which is generating disk writes will itself start writing out dirty data. +If dirty_ratio is written, dirty_bytes becomes a function of its value +(dirty_ratio * the amount of dirtyable system memory). + dirty_writeback_centisecs ------------------------- diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index d79eeda7a699..cd05994a49e6 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -41,7 +41,8 @@ Currently, these files are in /proc/sys/vm: ============================================================== -dirty_ratio, dirty_background_ratio, dirty_expire_centisecs, +dirty_bytes, dirty_ratio, dirty_background_bytes, +dirty_background_ratio, dirty_expire_centisecs, dirty_writeback_centisecs, highmem_is_dirtyable, vfs_cache_pressure, laptop_mode, block_dump, swap_token_timeout, drop-caches, hugepages_treat_as_movable: diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 259e9ea58cab..bb28c975c1d7 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -107,7 +107,9 @@ void throttle_vm_writeout(gfp_t gfp_mask); /* These are exported to sysctl. */ extern int dirty_background_ratio; +extern unsigned long dirty_background_bytes; extern int vm_dirty_ratio; +extern unsigned long vm_dirty_bytes; extern int dirty_writeback_interval; extern int dirty_expire_interval; extern int vm_highmem_is_dirtyable; @@ -116,9 +118,18 @@ extern int laptop_mode; extern unsigned long determine_dirtyable_memory(void); +extern int dirty_background_ratio_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos); +extern int dirty_background_bytes_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos); extern int dirty_ratio_handler(struct ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos); +extern int dirty_bytes_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos); struct ctl_table; struct file; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ff6d45c7626f..92f6e5bc3c24 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -87,10 +87,6 @@ extern int rcutorture_runnable; #endif /* #ifdef CONFIG_RCU_TORTURE_TEST */ /* Constants used for minimum and maximum */ -#if defined(CONFIG_HIGHMEM) || defined(CONFIG_DETECT_SOFTLOCKUP) -static int one = 1; -#endif - #ifdef CONFIG_DETECT_SOFTLOCKUP static int sixty = 60; static int neg_one = -1; @@ -101,6 +97,7 @@ static int two = 2; #endif static int zero; +static int one = 1; static int one_hundred = 100; /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ @@ -952,11 +949,21 @@ static struct ctl_table vm_table[] = { .data = &dirty_background_ratio, .maxlen = sizeof(dirty_background_ratio), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, + .proc_handler = &dirty_background_ratio_handler, .strategy = &sysctl_intvec, .extra1 = &zero, .extra2 = &one_hundred, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "dirty_background_bytes", + .data = &dirty_background_bytes, + .maxlen = sizeof(dirty_background_bytes), + .mode = 0644, + .proc_handler = &dirty_background_bytes_handler, + .strategy = &sysctl_intvec, + .extra1 = &one, + }, { .ctl_name = VM_DIRTY_RATIO, .procname = "dirty_ratio", @@ -968,6 +975,16 @@ static struct ctl_table vm_table[] = { .extra1 = &zero, .extra2 = &one_hundred, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "dirty_bytes", + .data = &vm_dirty_bytes, + .maxlen = sizeof(vm_dirty_bytes), + .mode = 0644, + .proc_handler = &dirty_bytes_handler, + .strategy = &sysctl_intvec, + .extra1 = &one, + }, { .procname = "dirty_writeback_centisecs", .data = &dirty_writeback_interval, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 4d4074cff300..b493db7841dc 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -68,6 +68,12 @@ static inline long sync_writeback_pages(void) */ int dirty_background_ratio = 5; +/* + * dirty_background_bytes starts at 0 (disabled) so that it is a function of + * dirty_background_ratio * the amount of dirtyable memory + */ +unsigned long dirty_background_bytes; + /* * free highmem will not be subtracted from the total free memory * for calculating free ratios if vm_highmem_is_dirtyable is true @@ -79,6 +85,12 @@ int vm_highmem_is_dirtyable; */ int vm_dirty_ratio = 10; +/* + * vm_dirty_bytes starts at 0 (disabled) so that it is a function of + * vm_dirty_ratio * the amount of dirtyable memory + */ +unsigned long vm_dirty_bytes; + /* * The interval between `kupdate'-style writebacks, in jiffies */ @@ -135,23 +147,75 @@ static int calc_period_shift(void) { unsigned long dirty_total; - dirty_total = (vm_dirty_ratio * determine_dirtyable_memory()) / 100; + if (vm_dirty_bytes) + dirty_total = vm_dirty_bytes / PAGE_SIZE; + else + dirty_total = (vm_dirty_ratio * determine_dirtyable_memory()) / + 100; return 2 + ilog2(dirty_total - 1); } /* - * update the period when the dirty ratio changes. + * update the period when the dirty threshold changes. */ +static void update_completion_period(void) +{ + int shift = calc_period_shift(); + prop_change_shift(&vm_completions, shift); + prop_change_shift(&vm_dirties, shift); +} + +int dirty_background_ratio_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + if (ret == 0 && write) + dirty_background_bytes = 0; + return ret; +} + +int dirty_background_bytes_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + + ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + if (ret == 0 && write) + dirty_background_ratio = 0; + return ret; +} + int dirty_ratio_handler(struct ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { int old_ratio = vm_dirty_ratio; - int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + int ret; + + ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_ratio != old_ratio) { - int shift = calc_period_shift(); - prop_change_shift(&vm_completions, shift); - prop_change_shift(&vm_dirties, shift); + update_completion_period(); + vm_dirty_bytes = 0; + } + return ret; +} + + +int dirty_bytes_handler(struct ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int old_bytes = vm_dirty_bytes; + int ret; + + ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + if (ret == 0 && write && vm_dirty_bytes != old_bytes) { + update_completion_period(); + vm_dirty_ratio = 0; } return ret; } @@ -365,23 +429,29 @@ void get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, unsigned long *pbdi_dirty, struct backing_dev_info *bdi) { - int background_ratio; /* Percentages */ - int dirty_ratio; unsigned long background; unsigned long dirty; unsigned long available_memory = determine_dirtyable_memory(); struct task_struct *tsk; - dirty_ratio = vm_dirty_ratio; - if (dirty_ratio < 5) - dirty_ratio = 5; + if (vm_dirty_bytes) + dirty = DIV_ROUND_UP(vm_dirty_bytes, PAGE_SIZE); + else { + int dirty_ratio; - background_ratio = dirty_background_ratio; - if (background_ratio >= dirty_ratio) - background_ratio = dirty_ratio / 2; + dirty_ratio = vm_dirty_ratio; + if (dirty_ratio < 5) + dirty_ratio = 5; + dirty = (dirty_ratio * available_memory) / 100; + } + + if (dirty_background_bytes) + background = DIV_ROUND_UP(dirty_background_bytes, PAGE_SIZE); + else + background = (dirty_background_ratio * available_memory) / 100; - background = (background_ratio * available_memory) / 100; - dirty = (dirty_ratio * available_memory) / 100; + if (background >= dirty) + background = dirty / 2; tsk = current; if (tsk->flags & PF_LESS_THROTTLE || rt_task(tsk)) { background += background / 4; -- cgit v1.2.3-71-gd317 From 7b1fe59793e61f826bef053107b57b23954833bb Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:34 -0800 Subject: mm: reuse_swap_page replaces can_share_swap_page A good place to free up old swap is where do_wp_page(), or do_swap_page(), is about to redirty the page: the data on disk is then stale and won't be read again; and if we do decide to write the page out later, using the previous swap location makes an unnecessary disk seek very likely. So give can_share_swap_page() the side-effect of delete_from_swap_cache() when it safely can. And can_share_swap_page() was always a misleading name, the more so if it has a side-effect: rename it reuse_swap_page(). Irrelevant cleanup nearby: remove swap_token_default_timeout definition from swap.h: it's used nowhere. Signed-off-by: Hugh Dickins Cc: Lee Schermerhorn Acked-by: Rik van Riel Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 6 ++---- mm/memory.c | 4 ++-- mm/swapfile.c | 15 +++++++++++---- 3 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 48f309dc5a0c..366556c5b148 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -304,7 +304,7 @@ extern unsigned int count_swap_pages(int, int); extern sector_t map_swap_page(struct swap_info_struct *, pgoff_t); extern sector_t swapdev_block(int, pgoff_t); extern struct swap_info_struct *get_swap_info_struct(unsigned); -extern int can_share_swap_page(struct page *); +extern int reuse_swap_page(struct page *); extern int remove_exclusive_swap_page(struct page *); extern int remove_exclusive_swap_page_ref(struct page *); struct backing_dev_info; @@ -372,8 +372,6 @@ static inline struct page *lookup_swap_cache(swp_entry_t swp) return NULL; } -#define can_share_swap_page(p) (page_mapcount(p) == 1) - static inline int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) { @@ -388,7 +386,7 @@ static inline void delete_from_swap_cache(struct page *page) { } -#define swap_token_default_timeout 0 +#define reuse_swap_page(page) (page_mapcount(page) == 1) static inline int remove_exclusive_swap_page(struct page *p) { diff --git a/mm/memory.c b/mm/memory.c index 3922ffcf3dff..8f471edcb985 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1861,7 +1861,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, } page_cache_release(old_page); } - reuse = can_share_swap_page(old_page); + reuse = reuse_swap_page(old_page); unlock_page(old_page); } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))) { @@ -2392,7 +2392,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, inc_mm_counter(mm, anon_rss); pte = mk_pte(page, vma->vm_page_prot); - if (write_access && can_share_swap_page(page)) { + if (write_access && reuse_swap_page(page)) { pte = maybe_mkwrite(pte_mkdirty(pte), vma); write_access = 0; } diff --git a/mm/swapfile.c b/mm/swapfile.c index 214e90b94946..bfd4ee59cb88 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -326,17 +326,24 @@ static inline int page_swapcount(struct page *page) } /* - * We can use this swap cache entry directly - * if there are no other references to it. + * We can write to an anon page without COW if there are no other references + * to it. And as a side-effect, free up its swap: because the old content + * on disk will never be read, and seeking back there to write new content + * later would only waste time away from clustering. */ -int can_share_swap_page(struct page *page) +int reuse_swap_page(struct page *page) { int count; VM_BUG_ON(!PageLocked(page)); count = page_mapcount(page); - if (count <= 1 && PageSwapCache(page)) + if (count <= 1 && PageSwapCache(page)) { count += page_swapcount(page); + if (count == 1 && !PageWriteback(page)) { + delete_from_swap_cache(page); + SetPageDirty(page); + } + } return count == 1; } -- cgit v1.2.3-71-gd317 From a2c43eed8334e878702fca713b212ae2a11d84b9 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:36 -0800 Subject: mm: try_to_free_swap replaces remove_exclusive_swap_page remove_exclusive_swap_page(): its problem is in living up to its name. It doesn't matter if someone else has a reference to the page (raised page_count); it doesn't matter if the page is mapped into userspace (raised page_mapcount - though that hints it may be worth keeping the swap): all that matters is that there be no more references to the swap (and no writeback in progress). swapoff (try_to_unuse) has been removing pages from swapcache for years, with no concern for page count or page mapcount, and we used to have a comment in lookup_swap_cache() recognizing that: if you go for a page of swapcache, you'll get the right page, but it could have been removed from swapcache by the time you get page lock. So, give up asking for exclusivity: get rid of remove_exclusive_swap_page(), and remove_exclusive_swap_page_ref() and remove_exclusive_swap_page_count() which were spawned for the recent LRU work: replace them by the simpler try_to_free_swap() which just checks page_swapcount(). Similarly, remove the page_count limitation from free_swap_and_count(), but assume that it's worth holding on to the swap if page is mapped and swap nowhere near full. Add a vm_swap_full() test in free_swap_cache()? It would be consistent, but I think we probably have enough for now. Signed-off-by: Hugh Dickins Cc: Lee Schermerhorn Cc: Rik van Riel Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 10 ++------ mm/memory.c | 2 +- mm/page_io.c | 2 +- mm/swap.c | 3 +-- mm/swap_state.c | 8 +++--- mm/swapfile.c | 70 +++++++++------------------------------------------- mm/vmscan.c | 2 +- 7 files changed, 22 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 366556c5b148..c3ecd478840e 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -305,8 +305,7 @@ extern sector_t map_swap_page(struct swap_info_struct *, pgoff_t); extern sector_t swapdev_block(int, pgoff_t); extern struct swap_info_struct *get_swap_info_struct(unsigned); extern int reuse_swap_page(struct page *); -extern int remove_exclusive_swap_page(struct page *); -extern int remove_exclusive_swap_page_ref(struct page *); +extern int try_to_free_swap(struct page *); struct backing_dev_info; /* linux/mm/thrash.c */ @@ -388,12 +387,7 @@ static inline void delete_from_swap_cache(struct page *page) #define reuse_swap_page(page) (page_mapcount(page) == 1) -static inline int remove_exclusive_swap_page(struct page *p) -{ - return 0; -} - -static inline int remove_exclusive_swap_page_ref(struct page *page) +static inline int try_to_free_swap(struct page *page) { return 0; } diff --git a/mm/memory.c b/mm/memory.c index 8f471edcb985..1a83fe5339a9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2403,7 +2403,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, swap_free(entry); if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page)) - remove_exclusive_swap_page(page); + try_to_free_swap(page); unlock_page(page); if (write_access) { diff --git a/mm/page_io.c b/mm/page_io.c index d277a80efa71..dc6ce0afbded 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -98,7 +98,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) struct bio *bio; int ret = 0, rw = WRITE; - if (remove_exclusive_swap_page(page)) { + if (try_to_free_swap(page)) { unlock_page(page); goto out; } diff --git a/mm/swap.c b/mm/swap.c index ff0b290475fd..ba2c0e8b8b54 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -454,8 +454,7 @@ void pagevec_swap_free(struct pagevec *pvec) struct page *page = pvec->pages[i]; if (PageSwapCache(page) && trylock_page(page)) { - if (PageSwapCache(page)) - remove_exclusive_swap_page_ref(page); + try_to_free_swap(page); unlock_page(page); } } diff --git a/mm/swap_state.c b/mm/swap_state.c index e793fdea275d..bcb472769299 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -195,14 +195,14 @@ void delete_from_swap_cache(struct page *page) * If we are the only user, then try to free up the swap cache. * * Its ok to check for PageSwapCache without the page lock - * here because we are going to recheck again inside - * exclusive_swap_page() _with_ the lock. + * here because we are going to recheck again inside + * try_to_free_swap() _with_ the lock. * - Marcelo */ static inline void free_swap_cache(struct page *page) { - if (PageSwapCache(page) && trylock_page(page)) { - remove_exclusive_swap_page(page); + if (PageSwapCache(page) && !page_mapped(page) && trylock_page(page)) { + try_to_free_swap(page); unlock_page(page); } } diff --git a/mm/swapfile.c b/mm/swapfile.c index bfd4ee59cb88..f43601827607 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -348,68 +348,23 @@ int reuse_swap_page(struct page *page) } /* - * Work out if there are any other processes sharing this - * swap cache page. Free it if you can. Return success. + * If swap is getting full, or if there are no more mappings of this page, + * then try_to_free_swap is called to free its swap space. */ -static int remove_exclusive_swap_page_count(struct page *page, int count) +int try_to_free_swap(struct page *page) { - int retval; - struct swap_info_struct * p; - swp_entry_t entry; - VM_BUG_ON(!PageLocked(page)); if (!PageSwapCache(page)) return 0; if (PageWriteback(page)) return 0; - if (page_count(page) != count) /* us + cache + ptes */ - return 0; - - entry.val = page_private(page); - p = swap_info_get(entry); - if (!p) + if (page_swapcount(page)) return 0; - /* Is the only swap cache user the cache itself? */ - retval = 0; - if (p->swap_map[swp_offset(entry)] == 1) { - /* Recheck the page count with the swapcache lock held.. */ - spin_lock_irq(&swapper_space.tree_lock); - if ((page_count(page) == count) && !PageWriteback(page)) { - __delete_from_swap_cache(page); - SetPageDirty(page); - retval = 1; - } - spin_unlock_irq(&swapper_space.tree_lock); - } - spin_unlock(&swap_lock); - - if (retval) { - swap_free(entry); - page_cache_release(page); - } - - return retval; -} - -/* - * Most of the time the page should have two references: one for the - * process and one for the swap cache. - */ -int remove_exclusive_swap_page(struct page *page) -{ - return remove_exclusive_swap_page_count(page, 2); -} - -/* - * The pageout code holds an extra reference to the page. That raises - * the reference count to test for to 2 for a page that is only in the - * swap cache plus 1 for each process that maps the page. - */ -int remove_exclusive_swap_page_ref(struct page *page) -{ - return remove_exclusive_swap_page_count(page, 2 + page_mapcount(page)); + delete_from_swap_cache(page); + SetPageDirty(page); + return 1; } /* @@ -436,13 +391,12 @@ void free_swap_and_cache(swp_entry_t entry) spin_unlock(&swap_lock); } if (page) { - int one_user; - - one_user = (page_count(page) == 2); - /* Only cache user (+us), or swap space full? Free it! */ - /* Also recheck PageSwapCache after page is locked (above) */ + /* + * Not mapped elsewhere, or swap space full? Free it! + * Also recheck PageSwapCache now page is locked (above). + */ if (PageSwapCache(page) && !PageWriteback(page) && - (one_user || vm_swap_full())) { + (!page_mapped(page) || vm_swap_full())) { delete_from_swap_cache(page); SetPageDirty(page); } diff --git a/mm/vmscan.c b/mm/vmscan.c index d196f46c8808..c8601dd36603 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -759,7 +759,7 @@ cull_mlocked: activate_locked: /* Not a candidate for swapping, so reclaim swap space. */ if (PageSwapCache(page) && vm_swap_full()) - remove_exclusive_swap_page_ref(page); + try_to_free_swap(page); VM_BUG_ON(PageActive(page)); SetPageActive(page); pgactivate++; -- cgit v1.2.3-71-gd317 From ac47b003d03c2a4f28aef1d505b66d24ad191c4f Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:39 -0800 Subject: mm: remove gfp_mask from add_to_swap Remove gfp_mask argument from add_to_swap(): it's misleading because its only caller, shrink_page_list(), is not atomic at that point; and in due course (implementing discard) we'll sometimes want to allocate some memory with GFP_NOIO (as is used in swap_writepage) when allocating swap. No change to the gfp_mask passed down to add_to_swap_cache(): still use __GFP_HIGH without __GFP_WAIT (with nomemalloc and nowarn as before): though it's not obvious if that's the best combination to ask for here. Signed-off-by: Hugh Dickins Cc: Lee Schermerhorn Cc: Rik van Riel Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 2 +- mm/swap_state.c | 4 ++-- mm/vmscan.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index c3ecd478840e..c38bd157695b 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -278,7 +278,7 @@ extern void end_swap_bio_read(struct bio *bio, int err); extern struct address_space swapper_space; #define total_swapcache_pages swapper_space.nrpages extern void show_swap_cache_info(void); -extern int add_to_swap(struct page *, gfp_t); +extern int add_to_swap(struct page *); extern int add_to_swap_cache(struct page *, swp_entry_t, gfp_t); extern void __delete_from_swap_cache(struct page *); extern void delete_from_swap_cache(struct page *); diff --git a/mm/swap_state.c b/mm/swap_state.c index bcb472769299..81c825f67a7f 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -128,7 +128,7 @@ void __delete_from_swap_cache(struct page *page) * Allocate swap space for the page and add the page to the * swap cache. Caller needs to hold the page lock. */ -int add_to_swap(struct page * page, gfp_t gfp_mask) +int add_to_swap(struct page *page) { swp_entry_t entry; int err; @@ -153,7 +153,7 @@ int add_to_swap(struct page * page, gfp_t gfp_mask) * Add it to the swap cache and mark it dirty */ err = add_to_swap_cache(page, entry, - gfp_mask|__GFP_NOMEMALLOC|__GFP_NOWARN); + __GFP_HIGH|__GFP_NOMEMALLOC|__GFP_NOWARN); switch (err) { case 0: /* Success */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 74f875733e2b..cc7401571cba 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -625,7 +625,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (PageAnon(page) && !PageSwapCache(page)) { if (!(sc->gfp_mask & __GFP_IO)) goto keep_locked; - if (!add_to_swap(page, GFP_ATOMIC)) + if (!add_to_swap(page)) goto activate_locked; may_enter_fs = 1; } -- cgit v1.2.3-71-gd317 From 60371d971a3d01afd102f0bbf2681f32ecc31d78 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:40 -0800 Subject: mm: add add_to_swap stub If we add a failing stub for add_to_swap(), then we can remove the #ifdef CONFIG_SWAP from mm/vmscan.c. This was intended as a source cleanup, but looking more closely, it turns out that the !CONFIG_SWAP case was going to keep_locked for an anonymous page, whereas now it goes to the more suitable activate_locked, like the CONFIG_SWAP nr_swap_pages 0 case. Signed-off-by: Hugh Dickins Cc: Lee Schermerhorn Acked-by: Rik van Riel Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 5 +++++ mm/vmscan.c | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index c38bd157695b..c0d23ac710d5 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -371,6 +371,11 @@ static inline struct page *lookup_swap_cache(swp_entry_t swp) return NULL; } +static inline int add_to_swap(struct page *page) +{ + return 0; +} + static inline int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) { diff --git a/mm/vmscan.c b/mm/vmscan.c index cc7401571cba..f350523a8eee 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -617,7 +617,6 @@ static unsigned long shrink_page_list(struct list_head *page_list, referenced && page_mapping_inuse(page)) goto activate_locked; -#ifdef CONFIG_SWAP /* * Anonymous process memory has backing store? * Try to allocate it some swap space here. @@ -629,7 +628,6 @@ static unsigned long shrink_page_list(struct list_head *page_list, goto activate_locked; may_enter_fs = 1; } -#endif /* CONFIG_SWAP */ mapping = page_mapping(page); -- cgit v1.2.3-71-gd317 From b962716b459505a8d83aea313fea0abe76749f42 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:41 -0800 Subject: mm: optimize get_scan_ratio for no swap Rik suggests a simplified get_scan_ratio() for !CONFIG_SWAP. Yes, the gcc optimizer gives us that, when nr_swap_pages is #defined as 0L. Move usual declaration to swapfile.c: it never belonged in page_alloc.c. Signed-off-by: Hugh Dickins Cc: Lee Schermerhorn Acked-by: Rik van Riel Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 5 +++-- mm/page_alloc.c | 1 - mm/swapfile.c | 1 + mm/vmscan.c | 12 ++++++------ 4 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index c0d23ac710d5..3a31cc25bd2c 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -163,7 +163,6 @@ struct swap_list_t { /* linux/mm/page_alloc.c */ extern unsigned long totalram_pages; extern unsigned long totalreserve_pages; -extern long nr_swap_pages; extern unsigned int nr_free_buffer_pages(void); extern unsigned int nr_free_pagecache_pages(void); @@ -291,6 +290,7 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t, struct vm_area_struct *vma, unsigned long addr); /* linux/mm/swapfile.c */ +extern long nr_swap_pages; extern long total_swap_pages; extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); @@ -331,7 +331,8 @@ static inline void disable_swap_token(void) #else /* CONFIG_SWAP */ -#define total_swap_pages 0 +#define nr_swap_pages 0L +#define total_swap_pages 0L #define total_swapcache_pages 0UL #define si_swapinfo(val) \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ce2a026219bc..65133436fe3d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -69,7 +69,6 @@ EXPORT_SYMBOL(node_states); unsigned long totalram_pages __read_mostly; unsigned long totalreserve_pages __read_mostly; -long nr_swap_pages; int percpu_pagelist_fraction; #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE diff --git a/mm/swapfile.c b/mm/swapfile.c index 9ce7f81c8abc..725e56c362de 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -35,6 +35,7 @@ static DEFINE_SPINLOCK(swap_lock); static unsigned int nr_swapfiles; +long nr_swap_pages; long total_swap_pages; static int swap_overflow; static int least_priority; diff --git a/mm/vmscan.c b/mm/vmscan.c index f350523a8eee..d500b906746c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1327,12 +1327,6 @@ static void get_scan_ratio(struct zone *zone, struct scan_control *sc, unsigned long anon_prio, file_prio; unsigned long ap, fp; - anon = zone_page_state(zone, NR_ACTIVE_ANON) + - zone_page_state(zone, NR_INACTIVE_ANON); - file = zone_page_state(zone, NR_ACTIVE_FILE) + - zone_page_state(zone, NR_INACTIVE_FILE); - free = zone_page_state(zone, NR_FREE_PAGES); - /* If we have no swap space, do not bother scanning anon pages. */ if (nr_swap_pages <= 0) { percent[0] = 0; @@ -1340,6 +1334,12 @@ static void get_scan_ratio(struct zone *zone, struct scan_control *sc, return; } + anon = zone_page_state(zone, NR_ACTIVE_ANON) + + zone_page_state(zone, NR_INACTIVE_ANON); + file = zone_page_state(zone, NR_ACTIVE_FILE) + + zone_page_state(zone, NR_INACTIVE_FILE); + free = zone_page_state(zone, NR_FREE_PAGES); + /* If we have very few page cache pages, force-scan anon pages. */ if (unlikely(file + free <= zone->pages_high)) { percent[0] = 100; -- cgit v1.2.3-71-gd317 From 69beeb1d3428424fbc7546f85e5cd7ac4119c09d Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Tue, 6 Jan 2009 14:39:46 -0800 Subject: mm: make vread() and vwrite() declaration Sparse output following warnings. mm/vmalloc.c:1436:6: warning: symbol 'vread' was not declared. Should it be static? mm/vmalloc.c:1474:6: warning: symbol 'vwrite' was not declared. Should it be static? However, it is used by /dev/kmem. fixed here. Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mem.c | 3 --- include/linux/vmalloc.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6431f6921a67..3586b3b3df3f 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -425,9 +425,6 @@ static ssize_t read_oldmem(struct file *file, char __user *buf, } #endif -extern long vread(char *buf, char *addr, unsigned long count); -extern long vwrite(char *buf, char *addr, unsigned long count); - #ifdef CONFIG_DEVKMEM /* * This function reads the *virtual* memory as seen by the kernel. diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 307b88577eaa..506e7620a986 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -97,6 +97,10 @@ extern void unmap_kernel_range(unsigned long addr, unsigned long size); extern struct vm_struct *alloc_vm_area(size_t size); extern void free_vm_area(struct vm_struct *area); +/* for /dev/kmem */ +extern long vread(char *buf, char *addr, unsigned long count); +extern long vwrite(char *buf, char *addr, unsigned long count); + /* * Internals. Dont't use.. */ -- cgit v1.2.3-71-gd317 From 22c6f8fdb31993cf49bdd4a47b64a7002391e1c7 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:48 -0800 Subject: swapfile: remove SWP_ACTIVE mask Remove the SWP_ACTIVE mask: it just obscures the SWP_WRITEOK flag. Signed-off-by: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 1 - mm/swapfile.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 3a31cc25bd2c..410c8e473727 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -120,7 +120,6 @@ struct swap_extent { enum { SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ - SWP_ACTIVE = (SWP_USED | SWP_WRITEOK), /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; diff --git a/mm/swapfile.c b/mm/swapfile.c index e2adc8eb9317..915cb3fc43d7 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1222,7 +1222,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile) spin_lock(&swap_lock); for (type = swap_list.head; type >= 0; type = swap_info[type].next) { p = swap_info + type; - if ((p->flags & SWP_ACTIVE) == SWP_ACTIVE) { + if (p->flags & SWP_WRITEOK) { if (p->swap_file->f_mapping == mapping) break; } @@ -1674,7 +1674,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) else p->prio = --least_priority; p->swap_map = swap_map; - p->flags = SWP_ACTIVE; + p->flags |= SWP_WRITEOK; nr_swap_pages += nr_good_pages; total_swap_pages += nr_good_pages; -- cgit v1.2.3-71-gd317 From ebebbbe904634b0ca1c674457b399f68db5e05b1 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:50 -0800 Subject: swapfile: rearrange scan and swap_info Before making functional changes, rearrange scan_swap_map() to simplify subsequent diffs. Actually, there is one functional change in there: leave cluster_nr negative while scanning for a new cluster - resetting it early increased the likelihood that when we have difficulty finding a free cluster, another task may come in and try doing exactly the same - just a waste of cpu. Before making functional changes, rearrange struct swap_info_struct slightly: flags will be needed as an unsigned long (for wait_on_bit), next is a good int to pair with prio, old_block_size is uninteresting so shift it to the end. Signed-off-by: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 8 +++---- mm/swapfile.c | 66 +++++++++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 410c8e473727..9cabb8b21aba 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -133,14 +133,14 @@ enum { * The in-memory structure used to track swap areas. */ struct swap_info_struct { - unsigned int flags; + unsigned long flags; int prio; /* swap priority */ + int next; /* next entry on swap list */ struct file *swap_file; struct block_device *bdev; struct list_head extent_list; struct swap_extent *curr_swap_extent; - unsigned old_block_size; - unsigned short * swap_map; + unsigned short *swap_map; unsigned int lowest_bit; unsigned int highest_bit; unsigned int cluster_next; @@ -148,7 +148,7 @@ struct swap_info_struct { unsigned int pages; unsigned int max; unsigned int inuse_pages; - int next; /* next entry on swap list */ + unsigned int old_block_size; }; struct swap_list_t { diff --git a/mm/swapfile.c b/mm/swapfile.c index 85ff603385c3..4d9855f86e7d 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -89,7 +89,8 @@ void swap_unplug_io_fn(struct backing_dev_info *unused_bdi, struct page *page) static inline unsigned long scan_swap_map(struct swap_info_struct *si) { - unsigned long offset, last_in_cluster; + unsigned long offset; + unsigned long last_in_cluster; int latency_ration = LATENCY_LIMIT; /* @@ -103,10 +104,13 @@ static inline unsigned long scan_swap_map(struct swap_info_struct *si) */ si->flags += SWP_SCANNING; - if (unlikely(!si->cluster_nr)) { - si->cluster_nr = SWAPFILE_CLUSTER - 1; - if (si->pages - si->inuse_pages < SWAPFILE_CLUSTER) - goto lowest; + offset = si->cluster_next; + + if (unlikely(!si->cluster_nr--)) { + if (si->pages - si->inuse_pages < SWAPFILE_CLUSTER) { + si->cluster_nr = SWAPFILE_CLUSTER - 1; + goto checks; + } spin_unlock(&swap_lock); offset = si->lowest_bit; @@ -118,43 +122,47 @@ static inline unsigned long scan_swap_map(struct swap_info_struct *si) last_in_cluster = offset + SWAPFILE_CLUSTER; else if (offset == last_in_cluster) { spin_lock(&swap_lock); - si->cluster_next = offset-SWAPFILE_CLUSTER+1; - goto cluster; + offset -= SWAPFILE_CLUSTER - 1; + si->cluster_next = offset; + si->cluster_nr = SWAPFILE_CLUSTER - 1; + goto checks; } if (unlikely(--latency_ration < 0)) { cond_resched(); latency_ration = LATENCY_LIMIT; } } + + offset = si->lowest_bit; spin_lock(&swap_lock); - goto lowest; + si->cluster_nr = SWAPFILE_CLUSTER - 1; } - si->cluster_nr--; -cluster: - offset = si->cluster_next; - if (offset > si->highest_bit) -lowest: offset = si->lowest_bit; -checks: if (!(si->flags & SWP_WRITEOK)) +checks: + if (!(si->flags & SWP_WRITEOK)) goto no_page; if (!si->highest_bit) goto no_page; - if (!si->swap_map[offset]) { - if (offset == si->lowest_bit) - si->lowest_bit++; - if (offset == si->highest_bit) - si->highest_bit--; - si->inuse_pages++; - if (si->inuse_pages == si->pages) { - si->lowest_bit = si->max; - si->highest_bit = 0; - } - si->swap_map[offset] = 1; - si->cluster_next = offset + 1; - si->flags -= SWP_SCANNING; - return offset; + if (offset > si->highest_bit) + offset = si->lowest_bit; + if (si->swap_map[offset]) + goto scan; + + if (offset == si->lowest_bit) + si->lowest_bit++; + if (offset == si->highest_bit) + si->highest_bit--; + si->inuse_pages++; + if (si->inuse_pages == si->pages) { + si->lowest_bit = si->max; + si->highest_bit = 0; } + si->swap_map[offset] = 1; + si->cluster_next = offset + 1; + si->flags -= SWP_SCANNING; + return offset; +scan: spin_unlock(&swap_lock); while (++offset <= si->highest_bit) { if (!si->swap_map[offset]) { @@ -167,7 +175,7 @@ checks: if (!(si->flags & SWP_WRITEOK)) } } spin_lock(&swap_lock); - goto lowest; + goto checks; no_page: si->flags -= SWP_SCANNING; -- cgit v1.2.3-71-gd317 From 6a6ba83175c029c7820765bae44692266b29e67a Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:51 -0800 Subject: swapfile: swapon use discard (trim) When adding swap, all the old data on swap can be forgotten: sys_swapon() discard all but the header page of the swap partition (or every extent but the header of the swap file), to give a solidstate swap device the opportunity to optimize its wear-levelling. If that succeeds, note SWP_DISCARDABLE for later use, and report it with a "D" at the right end of the kernel's "Adding ... swap" message. Perhaps something should be shown in /proc/swaps (swapon -s), but we have to be more cautious before making any addition to that format. Signed-off-by: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Nick Piggin Cc: David Woodhouse Cc: Jens Axboe Cc: Matthew Wilcox Cc: Joern Engel Cc: James Bottomley Cc: Donjun Shin Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 1 + mm/swapfile.c | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 9cabb8b21aba..0b9210ea96c7 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -120,6 +120,7 @@ struct swap_extent { enum { SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ + SWP_DISCARDABLE = (1 << 2), /* blkdev supports discard */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; diff --git a/mm/swapfile.c b/mm/swapfile.c index 4d9855f86e7d..fbeb4bb8eb50 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -84,6 +84,37 @@ void swap_unplug_io_fn(struct backing_dev_info *unused_bdi, struct page *page) up_read(&swap_unplug_sem); } +/* + * swapon tell device that all the old swap contents can be discarded, + * to allow the swap device to optimize its wear-levelling. + */ +static int discard_swap(struct swap_info_struct *si) +{ + struct swap_extent *se; + int err = 0; + + list_for_each_entry(se, &si->extent_list, list) { + sector_t start_block = se->start_block << (PAGE_SHIFT - 9); + pgoff_t nr_blocks = se->nr_pages << (PAGE_SHIFT - 9); + + if (se->start_page == 0) { + /* Do not discard the swap header page! */ + start_block += 1 << (PAGE_SHIFT - 9); + nr_blocks -= 1 << (PAGE_SHIFT - 9); + if (!nr_blocks) + continue; + } + + err = blkdev_issue_discard(si->bdev, start_block, + nr_blocks, GFP_KERNEL); + if (err) + break; + + cond_resched(); + } + return err; /* That will often be -EOPNOTSUPP */ +} + #define SWAPFILE_CLUSTER 256 #define LATENCY_LIMIT 256 @@ -1658,6 +1689,9 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) goto bad_swap; } + if (discard_swap(p) == 0) + p->flags |= SWP_DISCARDABLE; + mutex_lock(&swapon_mutex); spin_lock(&swap_lock); if (swap_flags & SWAP_FLAG_PREFER) @@ -1671,9 +1705,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) total_swap_pages += nr_good_pages; printk(KERN_INFO "Adding %uk swap on %s. " - "Priority:%d extents:%d across:%lluk\n", + "Priority:%d extents:%d across:%lluk%s\n", nr_good_pages<<(PAGE_SHIFT-10), name, p->prio, - nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10)); + nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), + (p->flags & SWP_DISCARDABLE) ? " D" : ""); /* insert swap space into swap_list: */ prev = -1; -- cgit v1.2.3-71-gd317 From 7992fde72ce06c73280a1939b7a1e903bc95ef85 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:53 -0800 Subject: swapfile: swap allocation use discard When scan_swap_map() finds a free cluster of swap pages to allocate, discard the old contents of the cluster if the device supports discard. But don't bother when swap is so fragmented that we allocate single pages. Be careful about racing allocations made while we're scanning for a cluster; and hold up allocations made while we're discarding. Signed-off-by: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Nick Piggin Cc: David Woodhouse Cc: Jens Axboe Cc: Matthew Wilcox Cc: Joern Engel Cc: James Bottomley Cc: Donjun Shin Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 3 ++ mm/swapfile.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 0b9210ea96c7..fe79f44c858e 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -121,6 +121,7 @@ enum { SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ SWP_DISCARDABLE = (1 << 2), /* blkdev supports discard */ + SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; @@ -144,6 +145,8 @@ struct swap_info_struct { unsigned short *swap_map; unsigned int lowest_bit; unsigned int highest_bit; + unsigned int lowest_alloc; /* while preparing discard cluster */ + unsigned int highest_alloc; /* while preparing discard cluster */ unsigned int cluster_next; unsigned int cluster_nr; unsigned int pages; diff --git a/mm/swapfile.c b/mm/swapfile.c index fbeb4bb8eb50..ca75b9e7c09f 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -115,14 +115,62 @@ static int discard_swap(struct swap_info_struct *si) return err; /* That will often be -EOPNOTSUPP */ } +/* + * swap allocation tell device that a cluster of swap can now be discarded, + * to allow the swap device to optimize its wear-levelling. + */ +static void discard_swap_cluster(struct swap_info_struct *si, + pgoff_t start_page, pgoff_t nr_pages) +{ + struct swap_extent *se = si->curr_swap_extent; + int found_extent = 0; + + while (nr_pages) { + struct list_head *lh; + + if (se->start_page <= start_page && + start_page < se->start_page + se->nr_pages) { + pgoff_t offset = start_page - se->start_page; + sector_t start_block = se->start_block + offset; + pgoff_t nr_blocks = se->nr_pages - offset; + + if (nr_blocks > nr_pages) + nr_blocks = nr_pages; + start_page += nr_blocks; + nr_pages -= nr_blocks; + + if (!found_extent++) + si->curr_swap_extent = se; + + start_block <<= PAGE_SHIFT - 9; + nr_blocks <<= PAGE_SHIFT - 9; + if (blkdev_issue_discard(si->bdev, start_block, + nr_blocks, GFP_NOIO)) + break; + } + + lh = se->list.next; + if (lh == &si->extent_list) + lh = lh->next; + se = list_entry(lh, struct swap_extent, list); + } +} + +static int wait_for_discard(void *word) +{ + schedule(); + return 0; +} + #define SWAPFILE_CLUSTER 256 #define LATENCY_LIMIT 256 static inline unsigned long scan_swap_map(struct swap_info_struct *si) { unsigned long offset; - unsigned long last_in_cluster; + unsigned long last_in_cluster = 0; int latency_ration = LATENCY_LIMIT; + int found_free_cluster = 0; /* * We try to cluster swap pages by allocating them sequentially @@ -142,6 +190,19 @@ static inline unsigned long scan_swap_map(struct swap_info_struct *si) si->cluster_nr = SWAPFILE_CLUSTER - 1; goto checks; } + if (si->flags & SWP_DISCARDABLE) { + /* + * Start range check on racing allocations, in case + * they overlap the cluster we eventually decide on + * (we scan without swap_lock to allow preemption). + * It's hardly conceivable that cluster_nr could be + * wrapped during our scan, but don't depend on it. + */ + if (si->lowest_alloc) + goto checks; + si->lowest_alloc = si->max; + si->highest_alloc = 0; + } spin_unlock(&swap_lock); offset = si->lowest_bit; @@ -156,6 +217,7 @@ static inline unsigned long scan_swap_map(struct swap_info_struct *si) offset -= SWAPFILE_CLUSTER - 1; si->cluster_next = offset; si->cluster_nr = SWAPFILE_CLUSTER - 1; + found_free_cluster = 1; goto checks; } if (unlikely(--latency_ration < 0)) { @@ -167,6 +229,7 @@ static inline unsigned long scan_swap_map(struct swap_info_struct *si) offset = si->lowest_bit; spin_lock(&swap_lock); si->cluster_nr = SWAPFILE_CLUSTER - 1; + si->lowest_alloc = 0; } checks: @@ -191,6 +254,60 @@ checks: si->swap_map[offset] = 1; si->cluster_next = offset + 1; si->flags -= SWP_SCANNING; + + if (si->lowest_alloc) { + /* + * Only set when SWP_DISCARDABLE, and there's a scan + * for a free cluster in progress or just completed. + */ + if (found_free_cluster) { + /* + * To optimize wear-levelling, discard the + * old data of the cluster, taking care not to + * discard any of its pages that have already + * been allocated by racing tasks (offset has + * already stepped over any at the beginning). + */ + if (offset < si->highest_alloc && + si->lowest_alloc <= last_in_cluster) + last_in_cluster = si->lowest_alloc - 1; + si->flags |= SWP_DISCARDING; + spin_unlock(&swap_lock); + + if (offset < last_in_cluster) + discard_swap_cluster(si, offset, + last_in_cluster - offset + 1); + + spin_lock(&swap_lock); + si->lowest_alloc = 0; + si->flags &= ~SWP_DISCARDING; + + smp_mb(); /* wake_up_bit advises this */ + wake_up_bit(&si->flags, ilog2(SWP_DISCARDING)); + + } else if (si->flags & SWP_DISCARDING) { + /* + * Delay using pages allocated by racing tasks + * until the whole discard has been issued. We + * could defer that delay until swap_writepage, + * but it's easier to keep this self-contained. + */ + spin_unlock(&swap_lock); + wait_on_bit(&si->flags, ilog2(SWP_DISCARDING), + wait_for_discard, TASK_UNINTERRUPTIBLE); + spin_lock(&swap_lock); + } else { + /* + * Note pages allocated by racing tasks while + * scan for a free cluster is in progress, so + * that its final discard can exclude them. + */ + if (offset < si->lowest_alloc) + si->lowest_alloc = offset; + if (offset > si->highest_alloc) + si->highest_alloc = offset; + } + } return offset; scan: -- cgit v1.2.3-71-gd317 From 20137a490f397d9c01fc9fadd83a8d198bda4477 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:39:54 -0800 Subject: swapfile: swapon randomize if nonrot Swap allocation has always started from the beginning of the swap area; but if we're dealing with a solidstate swap device which can only remap blocks within limited zones, that would sooner wear out the first zone. Therefore sys_swapon() test whether blk_queue is non-rotational, and if so randomize the cluster_next starting position for allocation. If blk_queue is nonrot, note SWP_SOLIDSTATE for later use, and report it with an "SS" at the right end of the kernel's "Adding ... swap" message (so that if it's both nonrot and discardable, "SSD" will be shown there). Perhaps something should be shown in /proc/swaps (swapon -s), but we have to be more cautious before making any addition to that format. Signed-off-by: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Nick Piggin Cc: David Woodhouse Cc: Jens Axboe Cc: Matthew Wilcox Cc: Joern Engel Cc: James Bottomley Cc: Donjun Shin Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 1 + mm/swapfile.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index fe79f44c858e..cbf7fbed3dfd 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -122,6 +122,7 @@ enum { SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ SWP_DISCARDABLE = (1 << 2), /* blkdev supports discard */ SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ + SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; diff --git a/mm/swapfile.c b/mm/swapfile.c index ca75b9e7c09f..b0f56603b9be 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1806,6 +1807,11 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) goto bad_swap; } + if (blk_queue_nonrot(bdev_get_queue(p->bdev))) { + p->flags |= SWP_SOLIDSTATE; + srandom32((u32)get_seconds()); + p->cluster_next = 1 + (random32() % p->highest_bit); + } if (discard_swap(p) == 0) p->flags |= SWP_DISCARDABLE; @@ -1822,10 +1828,11 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) total_swap_pages += nr_good_pages; printk(KERN_INFO "Adding %uk swap on %s. " - "Priority:%d extents:%d across:%lluk%s\n", + "Priority:%d extents:%d across:%lluk %s%s\n", nr_good_pages<<(PAGE_SHIFT-10), name, p->prio, nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), - (p->flags & SWP_DISCARDABLE) ? " D" : ""); + (p->flags & SWP_SOLIDSTATE) ? "SS" : "", + (p->flags & SWP_DISCARDABLE) ? "D" : ""); /* insert swap space into swap_list: */ prev = -1; -- cgit v1.2.3-71-gd317 From 79f4b7bf393e67bbffec807cc68caaefc72b82ee Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:40:05 -0800 Subject: badpage: simplify page_alloc flag check+clear Simplify the PAGE_FLAGS checking and clearing when freeing and allocating a page: check the same flags as before when freeing, clear ALL the flags (unless PageReserved) when freeing, check ALL flags off when allocating. Signed-off-by: Hugh Dickins Cc: Nick Piggin Cc: Christoph Lameter Cc: Mel Gorman Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 25 ++++++++----------------- mm/page_alloc.c | 19 ++++++------------- 2 files changed, 14 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 628ec0802492..219a523ecdb0 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -373,31 +373,22 @@ static inline void __ClearPageTail(struct page *page) #define __PG_MLOCKED 0 #endif -#define PAGE_FLAGS (1 << PG_lru | 1 << PG_private | 1 << PG_locked | \ - 1 << PG_buddy | 1 << PG_writeback | \ - 1 << PG_slab | 1 << PG_swapcache | 1 << PG_active | \ - __PG_UNEVICTABLE | __PG_MLOCKED) - -/* - * Flags checked in bad_page(). Pages on the free list should not have - * these flags set. It they are, there is a problem. - */ -#define PAGE_FLAGS_CLEAR_WHEN_BAD (PAGE_FLAGS | \ - 1 << PG_reclaim | 1 << PG_dirty | 1 << PG_swapbacked) - /* * Flags checked when a page is freed. Pages being freed should not have * these flags set. It they are, there is a problem. */ -#define PAGE_FLAGS_CHECK_AT_FREE (PAGE_FLAGS | 1 << PG_reserved) +#define PAGE_FLAGS_CHECK_AT_FREE \ + (1 << PG_lru | 1 << PG_private | 1 << PG_locked | \ + 1 << PG_buddy | 1 << PG_writeback | 1 << PG_reserved | \ + 1 << PG_slab | 1 << PG_swapcache | 1 << PG_active | \ + __PG_UNEVICTABLE | __PG_MLOCKED) /* * Flags checked when a page is prepped for return by the page allocator. - * Pages being prepped should not have these flags set. It they are, there - * is a problem. + * Pages being prepped should not have any flags set. It they are set, + * there has been a kernel bug or struct page corruption. */ -#define PAGE_FLAGS_CHECK_AT_PREP (PAGE_FLAGS | \ - 1 << PG_reserved | 1 << PG_dirty | 1 << PG_swapbacked) +#define PAGE_FLAGS_CHECK_AT_PREP ((1 << NR_PAGEFLAGS) - 1) #endif /* !__GENERATING_BOUNDS_H */ #endif /* PAGE_FLAGS_H */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 31c512410a99..b90a74d28485 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -231,7 +231,6 @@ static void bad_page(struct page *page) printk(KERN_EMERG "Trying to fix it up, but a reboot is needed\n" KERN_EMERG "Backtrace:\n"); dump_stack(); - page->flags &= ~PAGE_FLAGS_CLEAR_WHEN_BAD; set_page_count(page, 0); reset_page_mapcount(page); page->mapping = NULL; @@ -468,16 +467,16 @@ static inline int free_pages_check(struct page *page) (page_count(page) != 0) | (page->flags & PAGE_FLAGS_CHECK_AT_FREE))) bad_page(page); - if (PageDirty(page)) - __ClearPageDirty(page); - if (PageSwapBacked(page)) - __ClearPageSwapBacked(page); /* * For now, we report if PG_reserved was found set, but do not * clear it, and do not free the page. But we shall soon need * to do more, for when the ZERO_PAGE count wraps negative. */ - return PageReserved(page); + if (PageReserved(page)) + return 1; + if (page->flags & PAGE_FLAGS_CHECK_AT_PREP) + page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; + return 0; } /* @@ -621,13 +620,7 @@ static int prep_new_page(struct page *page, int order, gfp_t gfp_flags) if (PageReserved(page)) return 1; - page->flags &= ~(1 << PG_uptodate | 1 << PG_error | 1 << PG_reclaim | - 1 << PG_referenced | 1 << PG_arch_1 | - 1 << PG_owner_priv_1 | 1 << PG_mappedtodisk -#ifdef CONFIG_UNEVICTABLE_LRU - | 1 << PG_mlocked -#endif - ); + page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; set_page_private(page, 0); set_page_refcounted(page); -- cgit v1.2.3-71-gd317 From 2509ef26db4699a5d9fa876e90ddfc107afcab84 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:40:10 -0800 Subject: badpage: zap print_bad_pte on swap and file Complete zap_pte_range()'s coverage of bad pagetable entries by calling print_bad_pte() on a pte_file in a linear vma and on a bad swap entry. That needs free_swap_and_cache() to tell it, which will also have shown one of those "swap_free" errors (but with much less information). Similar checks in fork's copy_one_pte()? No, that would be more noisy than helpful: we'll see them when parent and child exec or exit. Where do_nonlinear_fault() calls print_bad_pte(): omit !VM_CAN_NONLINEAR case, that could only be a bug in sys_remap_file_pages(), not a bad pte. VM_FAULT_OOM rather than VM_FAULT_SIGBUS? Well, okay, that is consistent with what happens if do_swap_page() operates a bad swap entry; but don't we have patches to be more careful about killing when VM_FAULT_OOM? Signed-off-by: Hugh Dickins Cc: Nick Piggin Cc: Christoph Lameter Cc: Mel Gorman Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 12 +++--------- mm/memory.c | 11 +++++++---- mm/swapfile.c | 7 ++++--- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index cbf7fbed3dfd..91dee50fe260 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -302,7 +302,7 @@ extern swp_entry_t get_swap_page_of_type(int); extern int swap_duplicate(swp_entry_t); extern int valid_swaphandles(swp_entry_t, unsigned long *); extern void swap_free(swp_entry_t); -extern void free_swap_and_cache(swp_entry_t); +extern int free_swap_and_cache(swp_entry_t); extern int swap_type_of(dev_t, sector_t, struct block_device **); extern unsigned int count_swap_pages(int, int); extern sector_t map_swap_page(struct swap_info_struct *, pgoff_t); @@ -352,14 +352,8 @@ static inline void show_swap_cache_info(void) { } -static inline void free_swap_and_cache(swp_entry_t swp) -{ -} - -static inline int swap_duplicate(swp_entry_t swp) -{ - return 0; -} +#define free_swap_and_cache(swp) is_migration_entry(swp) +#define swap_duplicate(swp) is_migration_entry(swp) static inline void swap_free(swp_entry_t swp) { diff --git a/mm/memory.c b/mm/memory.c index 890095f5f36d..b273cc12b15d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -810,8 +810,12 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, */ if (unlikely(details)) continue; - if (!pte_file(ptent)) - free_swap_and_cache(pte_to_swp_entry(ptent)); + if (pte_file(ptent)) { + if (unlikely(!(vma->vm_flags & VM_NONLINEAR))) + print_bad_pte(vma, addr, ptent, NULL); + } else if + (unlikely(!free_swap_and_cache(pte_to_swp_entry(ptent)))) + print_bad_pte(vma, addr, ptent, NULL); pte_clear_not_present_full(mm, addr, pte, tlb->fullmm); } while (pte++, addr += PAGE_SIZE, (addr != end && *zap_work > 0)); @@ -2707,8 +2711,7 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (!pte_unmap_same(mm, pmd, page_table, orig_pte)) return 0; - if (unlikely(!(vma->vm_flags & VM_NONLINEAR) || - !(vma->vm_flags & VM_CAN_NONLINEAR))) { + if (unlikely(!(vma->vm_flags & VM_NONLINEAR))) { /* * Page table corrupted: show pte and kill process. */ diff --git a/mm/swapfile.c b/mm/swapfile.c index d00523601913..f28745855772 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -571,13 +571,13 @@ int try_to_free_swap(struct page *page) * Free the swap entry like above, but also try to * free the page cache entry if it is the last user. */ -void free_swap_and_cache(swp_entry_t entry) +int free_swap_and_cache(swp_entry_t entry) { - struct swap_info_struct * p; + struct swap_info_struct *p; struct page *page = NULL; if (is_migration_entry(entry)) - return; + return 1; p = swap_info_get(entry); if (p) { @@ -603,6 +603,7 @@ void free_swap_and_cache(swp_entry_t entry) unlock_page(page); page_cache_release(page); } + return p != NULL; } #ifdef CONFIG_HIBERNATION -- cgit v1.2.3-71-gd317 From edc315fd222497ae4f4b959a9e31ada1e68a4755 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 6 Jan 2009 14:40:11 -0800 Subject: badpage: remove vma from page_remove_rmap Remove page_remove_rmap()'s vma arg, which was only for the Eeek message. And remove the BUG_ON(page_mapcount(page) == 0) from CONFIG_DEBUG_VM's page_dup_rmap(): we're trying to be more resilient about that than BUGs. Signed-off-by: Hugh Dickins Cc: Nick Piggin Cc: Christoph Lameter Cc: Mel Gorman Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rmap.h | 2 +- mm/filemap_xip.c | 2 +- mm/fremap.c | 2 +- mm/memory.c | 4 ++-- mm/rmap.c | 8 +++----- 5 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 3593b18a07dd..b35bc0e19cd9 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -69,7 +69,7 @@ void __anon_vma_link(struct vm_area_struct *); void page_add_anon_rmap(struct page *, struct vm_area_struct *, unsigned long); void page_add_new_anon_rmap(struct page *, struct vm_area_struct *, unsigned long); void page_add_file_rmap(struct page *); -void page_remove_rmap(struct page *, struct vm_area_struct *); +void page_remove_rmap(struct page *); #ifdef CONFIG_DEBUG_VM void page_dup_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address); diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index b5167dfb2f2d..0c04615651b7 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -193,7 +193,7 @@ retry: /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); pteval = ptep_clear_flush_notify(vma, address, pte); - page_remove_rmap(page, vma); + page_remove_rmap(page); dec_mm_counter(mm, file_rss); BUG_ON(pte_dirty(pteval)); pte_unmap_unlock(pte, ptl); diff --git a/mm/fremap.c b/mm/fremap.c index 7d12ca70ef7b..62d5bbda921a 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -37,7 +37,7 @@ static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma, if (page) { if (pte_dirty(pte)) set_page_dirty(page); - page_remove_rmap(page, vma); + page_remove_rmap(page); page_cache_release(page); update_hiwater_rss(mm); dec_mm_counter(mm, file_rss); diff --git a/mm/memory.c b/mm/memory.c index b273cc12b15d..0f9abbaf618c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -798,7 +798,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, mark_page_accessed(page); file_rss--; } - page_remove_rmap(page, vma); + page_remove_rmap(page); if (unlikely(page_mapcount(page) < 0)) print_bad_pte(vma, addr, ptent, page); tlb_remove_page(tlb, page); @@ -2023,7 +2023,7 @@ gotten: * mapcount is visible. So transitively, TLBs to * old page will be flushed before it can be reused. */ - page_remove_rmap(old_page, vma); + page_remove_rmap(old_page); } /* Free the old page.. */ diff --git a/mm/rmap.c b/mm/rmap.c index 32098255082e..ac4af8cffbf9 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -707,7 +707,6 @@ void page_add_file_rmap(struct page *page) */ void page_dup_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address) { - BUG_ON(page_mapcount(page) == 0); if (PageAnon(page)) __page_check_anon_rmap(page, vma, address); atomic_inc(&page->_mapcount); @@ -717,11 +716,10 @@ void page_dup_rmap(struct page *page, struct vm_area_struct *vma, unsigned long /** * page_remove_rmap - take down pte mapping from a page * @page: page to remove mapping from - * @vma: the vm area in which the mapping is removed * * The caller needs to hold the pte lock. */ -void page_remove_rmap(struct page *page, struct vm_area_struct *vma) +void page_remove_rmap(struct page *page) { if (atomic_add_negative(-1, &page->_mapcount)) { /* @@ -837,7 +835,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, dec_mm_counter(mm, file_rss); - page_remove_rmap(page, vma); + page_remove_rmap(page); page_cache_release(page); out_unmap: @@ -952,7 +950,7 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount, if (pte_dirty(pteval)) set_page_dirty(page); - page_remove_rmap(page, vma); + page_remove_rmap(page); page_cache_release(page); dec_mm_counter(mm, file_rss); (*mapcount)--; -- cgit v1.2.3-71-gd317 From 4f5a99d64c17470a784a6c68064207d82e3e74a5 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 6 Jan 2009 14:40:25 -0800 Subject: fs: remove WB_SYNC_HOLD Remove WB_SYNC_HOLD. The primary motiviation is the design of my anti-starvation code for fsync. It requires taking an inode lock over the sync operation, so we could run into lock ordering problems with multiple inodes. It is possible to take a single global lock to solve the ordering problem, but then that would prevent a future nice implementation of "sync multiple inodes" based on lock order via inode address. Seems like a backward step to remove this, but actually it is busted anyway: we can't use the inode lists for data integrity wait: an inode can be taken off the dirty lists but still be under writeback. In order to satisfy data integrity semantics, we should wait for it to finish writeback, but if we only search the dirty lists, we'll miss it. It would be possible to have a "writeback" list, for sys_sync, I suppose. But why complicate things by prematurely optimise? For unmounting, we could avoid the "livelock avoidance" code, which would be easier, but again premature IMO. Fixing the existing data integrity problem will come next. Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 12 ++---------- include/linux/writeback.h | 1 - 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d0ff0b8cf309..d99601af9e48 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -421,9 +421,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * If we're a pdlfush thread, then implement pdflush collision avoidance * against the entire list. * - * WB_SYNC_HOLD is a hack for sys_sync(): reattach the inode to sb->s_dirty so - * that it can be located for waiting on in __writeback_single_inode(). - * * If `bdi' is non-zero then we're being asked to writeback a specific queue. * This function assumes that the blockdev superblock's inodes are backed by * a variety of queues, so all inodes are searched. For other superblocks, @@ -499,10 +496,6 @@ void generic_sync_sb_inodes(struct super_block *sb, __iget(inode); pages_skipped = wbc->pages_skipped; __writeback_single_inode(inode, wbc); - if (wbc->sync_mode == WB_SYNC_HOLD) { - inode->dirtied_when = jiffies; - list_move(&inode->i_list, &sb->s_dirty); - } if (current_is_pdflush()) writeback_release(bdi); if (wbc->pages_skipped != pages_skipped) { @@ -588,8 +581,7 @@ restart: /* * writeback and wait upon the filesystem's dirty inodes. The caller will - * do this in two passes - one to write, and one to wait. WB_SYNC_HOLD is - * used to park the written inodes on sb->s_dirty for the wait pass. + * do this in two passes - one to write, and one to wait. * * A finite limit is set on the number of pages which will be written. * To prevent infinite livelock of sys_sync(). @@ -600,7 +592,7 @@ restart: void sync_inodes_sb(struct super_block *sb, int wait) { struct writeback_control wbc = { - .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_HOLD, + .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_NONE, .range_start = 0, .range_end = LLONG_MAX, }; diff --git a/include/linux/writeback.h b/include/linux/writeback.h index bb28c975c1d7..7300ecdc480c 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -30,7 +30,6 @@ static inline int task_is_pdflush(struct task_struct *task) enum writeback_sync_modes { WB_SYNC_NONE, /* Don't wait on anything */ WB_SYNC_ALL, /* Wait on every mapping */ - WB_SYNC_HOLD, /* Hold the inode on sb_dirty for sys_sync() */ }; /* -- cgit v1.2.3-71-gd317 From 856bf4d717feb8c55d4e2f817b71ebb70cfbc67b Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 6 Jan 2009 14:40:26 -0800 Subject: fs: sys_sync fix s_syncing livelock avoidance was breaking data integrity guarantee of sys_sync, by allowing sys_sync to skip writing or waiting for superblocks if there is a concurrent sys_sync happening. This livelock avoidance is much less important now that we don't have the get_super_to_sync() call after every sb that we sync. This was replaced by __put_super_and_need_restart. Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 20 +------------------- include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) (limited to 'include/linux') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index a9ee474f9691..e5eaa62fd17f 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -652,18 +652,6 @@ void sync_inodes_sb(struct super_block *sb, int wait) sync_sb_inodes(sb, &wbc); } -/* - * Rather lame livelock avoidance. - */ -static void set_sb_syncing(int val) -{ - struct super_block *sb; - spin_lock(&sb_lock); - list_for_each_entry_reverse(sb, &super_blocks, s_list) - sb->s_syncing = val; - spin_unlock(&sb_lock); -} - /** * sync_inodes - writes all inodes to disk * @wait: wait for completion @@ -690,9 +678,6 @@ static void __sync_inodes(int wait) spin_lock(&sb_lock); restart: list_for_each_entry(sb, &super_blocks, s_list) { - if (sb->s_syncing) - continue; - sb->s_syncing = 1; sb->s_count++; spin_unlock(&sb_lock); down_read(&sb->s_umount); @@ -710,13 +695,10 @@ restart: void sync_inodes(int wait) { - set_sb_syncing(0); __sync_inodes(0); - if (wait) { - set_sb_syncing(0); + if (wait) __sync_inodes(1); - } } /** diff --git a/include/linux/fs.h b/include/linux/fs.h index fb59673c60b1..d7eba77f666e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1133,7 +1133,6 @@ struct super_block { struct rw_semaphore s_umount; struct mutex s_lock; int s_count; - int s_syncing; int s_need_sync_fs; atomic_t s_active; #ifdef CONFIG_SECURITY -- cgit v1.2.3-71-gd317 From 901608d9045146aec6f14a7777ea4b1501c379f0 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 6 Jan 2009 14:40:29 -0800 Subject: mm: introduce get_mm_hiwater_xxx(), fix taskstats->hiwater_xxx accounting xacct_add_tsk() relies on do_exit()->update_hiwater_xxx() and uses mm->hiwater_xxx directly, this leads to 2 problems: - taskstats_user_cmd() can call fill_pid()->xacct_add_tsk() at any moment before the task exits, so we should check the current values of rss/vm anyway. - do_exit()->update_hiwater_xxx() calls are racy. An exiting thread can be preempted right before mm->hiwater_xxx = new_val, and another thread can use A_LOT of memory and exit in between. When the first thread resumes it can be the last thread in the thread group, in that case we report the wrong hiwater_xxx values which do not take A_LOT into account. Introduce get_mm_hiwater_rss() and get_mm_hiwater_vm() helpers and change xacct_add_tsk() to use them. The first helper will also be used by rusage->ru_maxrss accounting. Kill do_exit()->update_hiwater_xxx() calls. Unless we are going to decrease rss/vm there is no point to update mm->hiwater_xxx, and nobody can look at this mm_struct when exit_mmap() actually unmaps the memory. Signed-off-by: Oleg Nesterov Acked-by: Hugh Dickins Reviewed-by: KOSAKI Motohiro Acked-by: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 3 +++ kernel/exit.c | 5 +---- kernel/tsacct.c | 4 ++-- mm/mmap.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 38a3f4b15394..ea415136ac9e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -386,6 +386,9 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); (mm)->hiwater_vm = (mm)->total_vm; \ } while (0) +#define get_mm_hiwater_rss(mm) max((mm)->hiwater_rss, get_mm_rss(mm)) +#define get_mm_hiwater_vm(mm) max((mm)->hiwater_vm, (mm)->total_vm) + extern void set_dumpable(struct mm_struct *mm, int value); extern int get_dumpable(struct mm_struct *mm); diff --git a/kernel/exit.c b/kernel/exit.c index f923724ab3c9..c7740fa3252c 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1051,10 +1051,7 @@ NORET_TYPE void do_exit(long code) preempt_count()); acct_update_integrals(tsk); - if (tsk->mm) { - update_hiwater_rss(tsk->mm); - update_hiwater_vm(tsk->mm); - } + group_dead = atomic_dec_and_test(&tsk->signal->live); if (group_dead) { hrtimer_cancel(&tsk->signal->real_timer); diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 2dc06ab35716..43f891b05a4b 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -92,8 +92,8 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) mm = get_task_mm(p); if (mm) { /* adjust to KB unit */ - stats->hiwater_rss = mm->hiwater_rss * PAGE_SIZE / KB; - stats->hiwater_vm = mm->hiwater_vm * PAGE_SIZE / KB; + stats->hiwater_rss = get_mm_hiwater_rss(mm) * PAGE_SIZE / KB; + stats->hiwater_vm = get_mm_hiwater_vm(mm) * PAGE_SIZE / KB; mmput(mm); } stats->read_char = p->ioac.rchar; diff --git a/mm/mmap.c b/mm/mmap.c index e4507b23e620..1f97d8aa9b05 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2102,7 +2102,7 @@ void exit_mmap(struct mm_struct *mm) lru_add_drain(); flush_cache_mm(mm); tlb = tlb_gather_mmu(mm, 1); - /* Don't update_hiwater_rss(mm) here, do_exit already did */ + /* update_hiwater_rss(mm) here? but nobody should be looking */ /* Use -1 here to ensure all VMAs in the mm are unmapped */ end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); -- cgit v1.2.3-71-gd317 From ea435467500612636f8f4fb639ff6e76b2496e4b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 6 Jan 2009 14:40:39 -0800 Subject: atomic_t: unify all arch definitions The atomic_t type cannot currently be used in some header files because it would create an include loop with asm/atomic.h. Move the type definition to linux/types.h to break the loop. Signed-off-by: Matthew Wilcox Cc: Huang Ying Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/atomic.h | 9 +-------- arch/arm/include/asm/atomic.h | 3 +-- arch/avr32/include/asm/atomic.h | 2 +- arch/blackfin/include/asm/atomic.h | 4 +--- arch/cris/include/asm/atomic.h | 4 +--- arch/h8300/include/asm/atomic.h | 3 ++- arch/ia64/include/asm/atomic.h | 6 ------ arch/m68knommu/include/asm/atomic.h | 2 +- arch/mips/include/asm/atomic.h | 5 +---- arch/parisc/include/asm/atomic.h | 11 +++-------- arch/powerpc/include/asm/atomic.h | 4 +--- arch/s390/include/asm/atomic.h | 7 +------ arch/sh/include/asm/atomic.h | 7 +++---- arch/sparc/include/asm/atomic_32.h | 2 -- arch/sparc/include/asm/atomic_64.h | 3 --- arch/x86/include/asm/atomic_32.h | 10 +--------- arch/x86/include/asm/atomic_64.h | 18 ++---------------- include/asm-frv/atomic.h | 4 ---- include/asm-m32r/atomic.h | 8 +------- include/asm-m68k/atomic.h | 3 +-- include/asm-mn10300/atomic.h | 9 --------- include/asm-xtensa/atomic.h | 3 +-- include/linux/types.h | 10 ++++++++++ 23 files changed, 33 insertions(+), 104 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h index ca88e54dec93..62b363584b2b 100644 --- a/arch/alpha/include/asm/atomic.h +++ b/arch/alpha/include/asm/atomic.h @@ -1,6 +1,7 @@ #ifndef _ALPHA_ATOMIC_H #define _ALPHA_ATOMIC_H +#include #include #include @@ -13,14 +14,6 @@ */ -/* - * Counter is volatile to make sure gcc doesn't try to be clever - * and move things around on us. We need to use _exactly_ the address - * the user gave us, not some alias that contains the same information. - */ -typedef struct { volatile int counter; } atomic_t; -typedef struct { volatile long counter; } atomic64_t; - #define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) #define ATOMIC64_INIT(i) ( (atomic64_t) { (i) } ) diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h index 325f881ccb50..ee99723b3a6c 100644 --- a/arch/arm/include/asm/atomic.h +++ b/arch/arm/include/asm/atomic.h @@ -12,10 +12,9 @@ #define __ASM_ARM_ATOMIC_H #include +#include #include -typedef struct { volatile int counter; } atomic_t; - #define ATOMIC_INIT(i) { (i) } #ifdef __KERNEL__ diff --git a/arch/avr32/include/asm/atomic.h b/arch/avr32/include/asm/atomic.h index 7ef3862a73d0..318815107748 100644 --- a/arch/avr32/include/asm/atomic.h +++ b/arch/avr32/include/asm/atomic.h @@ -14,9 +14,9 @@ #ifndef __ASM_AVR32_ATOMIC_H #define __ASM_AVR32_ATOMIC_H +#include #include -typedef struct { volatile int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/arch/blackfin/include/asm/atomic.h b/arch/blackfin/include/asm/atomic.h index 7cf508718605..25776c19064b 100644 --- a/arch/blackfin/include/asm/atomic.h +++ b/arch/blackfin/include/asm/atomic.h @@ -1,6 +1,7 @@ #ifndef __ARCH_BLACKFIN_ATOMIC__ #define __ARCH_BLACKFIN_ATOMIC__ +#include #include /* local_irq_XXX() */ /* @@ -13,9 +14,6 @@ * Tony Kou (tonyko@lineo.ca) Lineo Inc. 2001 */ -typedef struct { - int counter; -} atomic_t; #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/arch/cris/include/asm/atomic.h b/arch/cris/include/asm/atomic.h index f71ea686a2ea..5718dd8902a1 100644 --- a/arch/cris/include/asm/atomic.h +++ b/arch/cris/include/asm/atomic.h @@ -4,7 +4,7 @@ #define __ASM_CRIS_ATOMIC__ #include - +#include #include #include @@ -13,8 +13,6 @@ * resource counting etc.. */ -typedef struct { volatile int counter; } atomic_t; - #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/arch/h8300/include/asm/atomic.h b/arch/h8300/include/asm/atomic.h index b4cf0ea97ede..833186c8dc3b 100644 --- a/arch/h8300/include/asm/atomic.h +++ b/arch/h8300/include/asm/atomic.h @@ -1,12 +1,13 @@ #ifndef __ARCH_H8300_ATOMIC__ #define __ARCH_H8300_ATOMIC__ +#include + /* * Atomic operations that C can't guarantee us. Useful for * resource counting etc.. */ -typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/arch/ia64/include/asm/atomic.h b/arch/ia64/include/asm/atomic.h index 50c2b83fd5a0..d37292bd9875 100644 --- a/arch/ia64/include/asm/atomic.h +++ b/arch/ia64/include/asm/atomic.h @@ -17,12 +17,6 @@ #include #include -/* - * On IA-64, counter must always be volatile to ensure that that the - * memory accesses are ordered. - */ -typedef struct { volatile __s32 counter; } atomic_t; -typedef struct { volatile __s64 counter; } atomic64_t; #define ATOMIC_INIT(i) ((atomic_t) { (i) }) #define ATOMIC64_INIT(i) ((atomic64_t) { (i) }) diff --git a/arch/m68knommu/include/asm/atomic.h b/arch/m68knommu/include/asm/atomic.h index d5632a305dae..6bb674855a3f 100644 --- a/arch/m68knommu/include/asm/atomic.h +++ b/arch/m68knommu/include/asm/atomic.h @@ -1,6 +1,7 @@ #ifndef __ARCH_M68KNOMMU_ATOMIC__ #define __ARCH_M68KNOMMU_ATOMIC__ +#include #include /* @@ -12,7 +13,6 @@ * We do not have SMP m68k systems, so we don't have to deal with that. */ -typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h index 1232be3885b0..c996c3b4d074 100644 --- a/arch/mips/include/asm/atomic.h +++ b/arch/mips/include/asm/atomic.h @@ -15,13 +15,12 @@ #define _ASM_ATOMIC_H #include +#include #include #include #include #include -typedef struct { volatile int counter; } atomic_t; - #define ATOMIC_INIT(i) { (i) } /* @@ -404,8 +403,6 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) #ifdef CONFIG_64BIT -typedef struct { volatile long counter; } atomic64_t; - #define ATOMIC64_INIT(i) { (i) } /* diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h index 57fcc4a5ebb4..edbfe25c5fc1 100644 --- a/arch/parisc/include/asm/atomic.h +++ b/arch/parisc/include/asm/atomic.h @@ -155,14 +155,11 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr, #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) #endif -/* Note that we need not lock read accesses - aligned word writes/reads - * are atomic, so a reader never sees unconsistent values. - * - * Cache-line alignment would conflict with, for example, linux/module.h +/* + * Note that we need not lock read accesses - aligned word writes/reads + * are atomic, so a reader never sees inconsistent values. */ -typedef struct { volatile int counter; } atomic_t; - /* It's possible to reduce all atomic operations to either * __atomic_add_return, atomic_set and atomic_read (the latter * is there only for consistency). @@ -260,8 +257,6 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) #ifdef CONFIG_64BIT -typedef struct { volatile s64 counter; } atomic64_t; - #define ATOMIC64_INIT(i) ((atomic64_t) { (i) }) static __inline__ int diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index 499be5bdd6fa..b401950f5259 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h @@ -5,7 +5,7 @@ * PowerPC atomic operations */ -typedef struct { int counter; } atomic_t; +#include #ifdef __KERNEL__ #include @@ -251,8 +251,6 @@ static __inline__ int atomic_dec_if_positive(atomic_t *v) #ifdef __powerpc64__ -typedef struct { long counter; } atomic64_t; - #define ATOMIC64_INIT(i) { (i) } static __inline__ long atomic64_read(const atomic64_t *v) diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h index 2d184655bc5d..de432f2de2d2 100644 --- a/arch/s390/include/asm/atomic.h +++ b/arch/s390/include/asm/atomic.h @@ -2,6 +2,7 @@ #define __ARCH_S390_ATOMIC__ #include +#include /* * include/asm-s390/atomic.h @@ -23,9 +24,6 @@ * S390 uses 'Compare And Swap' for atomicity in SMP enviroment */ -typedef struct { - int counter; -} __attribute__ ((aligned (4))) atomic_t; #define ATOMIC_INIT(i) { (i) } #ifdef __KERNEL__ @@ -149,9 +147,6 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) #undef __CS_LOOP #ifdef __s390x__ -typedef struct { - long long counter; -} __attribute__ ((aligned (8))) atomic64_t; #define ATOMIC64_INIT(i) { (i) } #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2) diff --git a/arch/sh/include/asm/atomic.h b/arch/sh/include/asm/atomic.h index c043ef003028..6327ffbb1992 100644 --- a/arch/sh/include/asm/atomic.h +++ b/arch/sh/include/asm/atomic.h @@ -7,16 +7,15 @@ * */ -typedef struct { volatile int counter; } atomic_t; +#include +#include +#include #define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) #define atomic_read(v) ((v)->counter) #define atomic_set(v,i) ((v)->counter = (i)) -#include -#include - #if defined(CONFIG_GUSA_RB) #include #elif defined(CONFIG_CPU_SH4A) diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h index 5c944b5a8040..ce465975a6a5 100644 --- a/arch/sparc/include/asm/atomic_32.h +++ b/arch/sparc/include/asm/atomic_32.h @@ -13,8 +13,6 @@ #include -typedef struct { volatile int counter; } atomic_t; - #ifdef __KERNEL__ #define ATOMIC_INIT(i) { (i) } diff --git a/arch/sparc/include/asm/atomic_64.h b/arch/sparc/include/asm/atomic_64.h index 5982c5ae7f07..a0a706492696 100644 --- a/arch/sparc/include/asm/atomic_64.h +++ b/arch/sparc/include/asm/atomic_64.h @@ -10,9 +10,6 @@ #include #include -typedef struct { volatile int counter; } atomic_t; -typedef struct { volatile __s64 counter; } atomic64_t; - #define ATOMIC_INIT(i) { (i) } #define ATOMIC64_INIT(i) { (i) } diff --git a/arch/x86/include/asm/atomic_32.h b/arch/x86/include/asm/atomic_32.h index ad5b9f6ecddf..85b46fba4229 100644 --- a/arch/x86/include/asm/atomic_32.h +++ b/arch/x86/include/asm/atomic_32.h @@ -2,6 +2,7 @@ #define _ASM_X86_ATOMIC_32_H #include +#include #include #include @@ -10,15 +11,6 @@ * resource counting etc.. */ -/* - * Make sure gcc doesn't try to be clever and move things around - * on us. We need to use _exactly_ the address the user gave us, - * not some alias that contains the same information. - */ -typedef struct { - int counter; -} atomic_t; - #define ATOMIC_INIT(i) { (i) } /** diff --git a/arch/x86/include/asm/atomic_64.h b/arch/x86/include/asm/atomic_64.h index 279d2a731f3f..8c21731984da 100644 --- a/arch/x86/include/asm/atomic_64.h +++ b/arch/x86/include/asm/atomic_64.h @@ -1,25 +1,15 @@ #ifndef _ASM_X86_ATOMIC_64_H #define _ASM_X86_ATOMIC_64_H +#include #include #include -/* atomic_t should be 32 bit signed type */ - /* * Atomic operations that C can't guarantee us. Useful for * resource counting etc.. */ -/* - * Make sure gcc doesn't try to be clever and move things around - * on us. We need to use _exactly_ the address the user gave us, - * not some alias that contains the same information. - */ -typedef struct { - int counter; -} atomic_t; - #define ATOMIC_INIT(i) { (i) } /** @@ -191,11 +181,7 @@ static inline int atomic_sub_return(int i, atomic_t *v) #define atomic_inc_return(v) (atomic_add_return(1, v)) #define atomic_dec_return(v) (atomic_sub_return(1, v)) -/* An 64bit atomic type */ - -typedef struct { - long counter; -} atomic64_t; +/* The 64-bit atomic type */ #define ATOMIC64_INIT(i) { (i) } diff --git a/include/asm-frv/atomic.h b/include/asm-frv/atomic.h index 46d696b331e7..296c35cfb207 100644 --- a/include/asm-frv/atomic.h +++ b/include/asm-frv/atomic.h @@ -35,10 +35,6 @@ #define smp_mb__before_atomic_inc() barrier() #define smp_mb__after_atomic_inc() barrier() -typedef struct { - int counter; -} atomic_t; - #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) #define atomic_set(v, i) (((v)->counter) = (i)) diff --git a/include/asm-m32r/atomic.h b/include/asm-m32r/atomic.h index 3a38ffe4a4f4..2eed30f84080 100644 --- a/include/asm-m32r/atomic.h +++ b/include/asm-m32r/atomic.h @@ -9,6 +9,7 @@ * Copyright (C) 2004 Hirokazu Takata */ +#include #include #include @@ -17,13 +18,6 @@ * resource counting etc.. */ -/* - * Make sure gcc doesn't try to be clever and move things around - * on us. We need to use _exactly_ the address the user gave us, - * not some alias that contains the same information. - */ -typedef struct { volatile int counter; } atomic_t; - #define ATOMIC_INIT(i) { (i) } /** diff --git a/include/asm-m68k/atomic.h b/include/asm-m68k/atomic.h index 4915294fea63..eb0ab9d4ee77 100644 --- a/include/asm-m68k/atomic.h +++ b/include/asm-m68k/atomic.h @@ -1,7 +1,7 @@ #ifndef __ARCH_M68K_ATOMIC__ #define __ARCH_M68K_ATOMIC__ - +#include #include /* @@ -13,7 +13,6 @@ * We do not have SMP m68k systems, so we don't have to deal with that. */ -typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) diff --git a/include/asm-mn10300/atomic.h b/include/asm-mn10300/atomic.h index 27c9690b9574..bc064825f9b1 100644 --- a/include/asm-mn10300/atomic.h +++ b/include/asm-mn10300/atomic.h @@ -20,15 +20,6 @@ * resource counting etc.. */ -/* - * Make sure gcc doesn't try to be clever and move things around - * on us. We need to use _exactly_ the address the user gave us, - * not some alias that contains the same information. - */ -typedef struct { - int counter; -} atomic_t; - #define ATOMIC_INIT(i) { (i) } #ifdef __KERNEL__ diff --git a/include/asm-xtensa/atomic.h b/include/asm-xtensa/atomic.h index b3b23540f14d..67ad67bed8c1 100644 --- a/include/asm-xtensa/atomic.h +++ b/include/asm-xtensa/atomic.h @@ -14,8 +14,7 @@ #define _XTENSA_ATOMIC_H #include - -typedef struct { volatile int counter; } atomic_t; +#include #ifdef __KERNEL__ #include diff --git a/include/linux/types.h b/include/linux/types.h index 121f349cb7ec..3b864f2d9560 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -195,6 +195,16 @@ typedef u32 phys_addr_t; typedef phys_addr_t resource_size_t; +typedef struct { + volatile int counter; +} atomic_t; + +#ifdef CONFIG_64BIT +typedef struct { + volatile long counter; +} atomic64_t; +#endif + struct ustat { __kernel_daddr_t f_tfree; __kernel_ino_t f_tinode; -- cgit v1.2.3-71-gd317 From f1883f86dea84fe47a71a39fc1afccc005915ed8 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 6 Jan 2009 14:40:45 -0800 Subject: Remove remaining unwinder code Signed-off-by: Alexey Dobriyan Cc: Gabor Gombas Cc: Jan Beulich Cc: Andi Kleen Cc: Ingo Molnar , Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/include/asm/unwind.h | 13 --------- arch/x86/kernel/traps.c | 2 -- include/linux/module.h | 3 -- include/linux/unwind.h | 68 ------------------------------------------- init/main.c | 3 -- kernel/module.c | 15 ---------- lib/fault-inject.c | 1 - 7 files changed, 105 deletions(-) delete mode 100644 arch/x86/include/asm/unwind.h delete mode 100644 include/linux/unwind.h (limited to 'include/linux') diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h deleted file mode 100644 index 8b064bd9c553..000000000000 --- a/arch/x86/include/asm/unwind.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _ASM_X86_UNWIND_H -#define _ASM_X86_UNWIND_H - -#define UNW_PC(frame) ((void)(frame), 0UL) -#define UNW_SP(frame) ((void)(frame), 0UL) -#define UNW_FP(frame) ((void)(frame), 0UL) - -static inline int arch_unw_user_mode(const void *info) -{ - return 0; -} - -#endif /* _ASM_X86_UNWIND_H */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index ce6650eb64e9..c9a666cdd3db 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -51,7 +50,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/module.h b/include/linux/module.h index 3bfed013350b..03cb93d1865a 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -294,9 +294,6 @@ struct module /* The size of the executable code in each section. */ unsigned int init_text_size, core_text_size; - /* The handle returned from unwind_add_table. */ - void *unwind_info; - /* Arch-specific module values */ struct mod_arch_specific arch; diff --git a/include/linux/unwind.h b/include/linux/unwind.h deleted file mode 100644 index 7760860fa170..000000000000 --- a/include/linux/unwind.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _LINUX_UNWIND_H -#define _LINUX_UNWIND_H - -/* - * Copyright (C) 2002-2006 Novell, Inc. - * Jan Beulich - * This code is released under version 2 of the GNU GPL. - * - * A simple API for unwinding kernel stacks. This is used for - * debugging and error reporting purposes. The kernel doesn't need - * full-blown stack unwinding with all the bells and whistles, so there - * is not much point in implementing the full Dwarf2 unwind API. - */ - -struct module; - -struct unwind_frame_info {}; - -static inline void unwind_init(void) {} -static inline void unwind_setup(void) {} - -#ifdef CONFIG_MODULES - -static inline void *unwind_add_table(struct module *mod, - const void *table_start, - unsigned long table_size) -{ - return NULL; -} - -static inline void unwind_remove_table(void *handle, int init_only) -{ -} - -#endif - -static inline int unwind_init_frame_info(struct unwind_frame_info *info, - struct task_struct *tsk, - const struct pt_regs *regs) -{ - return -ENOSYS; -} - -static inline int unwind_init_blocked(struct unwind_frame_info *info, - struct task_struct *tsk) -{ - return -ENOSYS; -} - -static inline int unwind_init_running(struct unwind_frame_info *info, - asmlinkage int (*cb)(struct unwind_frame_info *, - void *arg), - void *arg) -{ - return -ENOSYS; -} - -static inline int unwind(struct unwind_frame_info *info) -{ - return -ENOSYS; -} - -static inline int unwind_to_user(struct unwind_frame_info *info) -{ - return -ENOSYS; -} - -#endif /* _LINUX_UNWIND_H */ diff --git a/init/main.c b/init/main.c index 90926dadc20d..e119dd28dd7d 100644 --- a/init/main.c +++ b/init/main.c @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -537,7 +536,6 @@ asmlinkage void __init start_kernel(void) * Need to run as early as possible, to initialize the * lockdep hash: */ - unwind_init(); lockdep_init(); debug_objects_early_init(); cgroup_init_early(); @@ -559,7 +557,6 @@ asmlinkage void __init start_kernel(void) setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); setup_command_line(command_line); - unwind_setup(); setup_per_cpu_areas(); setup_nr_cpu_ids(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ diff --git a/kernel/module.c b/kernel/module.c index f47cce910f25..34b56cf06615 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -1449,8 +1448,6 @@ static void free_module(struct module *mod) remove_sect_attrs(mod); mod_kobject_remove(mod); - unwind_remove_table(mod->unwind_info, 0); - /* Arch-specific cleanup. */ module_arch_cleanup(mod); @@ -1867,7 +1864,6 @@ static noinline struct module *load_module(void __user *umod, unsigned int symindex = 0; unsigned int strindex = 0; unsigned int modindex, versindex, infoindex, pcpuindex; - unsigned int unwindex = 0; unsigned int num_kp, num_mcount; struct kernel_param *kp; struct module *mod; @@ -1957,9 +1953,6 @@ static noinline struct module *load_module(void __user *umod, versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); pcpuindex = find_pcpusec(hdr, sechdrs, secstrings); -#ifdef ARCH_UNWIND_SECTION_NAME - unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME); -#endif /* Don't keep modinfo and version sections. */ sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; @@ -1969,8 +1962,6 @@ static noinline struct module *load_module(void __user *umod, sechdrs[symindex].sh_flags |= SHF_ALLOC; sechdrs[strindex].sh_flags |= SHF_ALLOC; #endif - if (unwindex) - sechdrs[unwindex].sh_flags |= SHF_ALLOC; /* Check module struct version now, before we try to use module. */ if (!check_modstruct_version(sechdrs, versindex, mod)) { @@ -2267,11 +2258,6 @@ static noinline struct module *load_module(void __user *umod, add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); - /* Size of section 0 is 0, so this works well if no unwind info. */ - mod->unwind_info = unwind_add_table(mod, - (void *)sechdrs[unwindex].sh_addr, - sechdrs[unwindex].sh_size); - /* Get rid of temporary copy */ vfree(hdr); @@ -2370,7 +2356,6 @@ sys_init_module(void __user *umod, mutex_lock(&module_mutex); /* Drop initial reference. */ module_put(mod); - unwind_remove_table(mod->unwind_info, 1); module_free(mod, mod->module_init); mod->module_init = NULL; mod->init_size = 0; diff --git a/lib/fault-inject.c b/lib/fault-inject.c index a50a311554cc..f97af55bdd96 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-71-gd317 From 9fe06081ef145d6582c39e18203b5fffe6f3abc2 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 6 Jan 2009 14:40:51 -0800 Subject: Create a DIV_ROUND_CLOSEST macro to do division with rounding Create a helper macro to divide two numbers and round the result to the nearest whole number. This is a helper macro for hwmon drivers that want to convert incoming sysfs values per standard hwmon practice, though the macro itself can be used by anyone. Signed-off-by: Darrick J. Wong Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index ca9ff6411dfa..721984844c94 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -48,6 +48,12 @@ extern const char linux_proc_banner[]; #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + typeof(divisor) __divisor = divisor; \ + (((x) + ((__divisor) / 2)) / (__divisor)); \ +} \ +) #define _RET_IP_ (unsigned long)__builtin_return_address(0) #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) -- cgit v1.2.3-71-gd317 From 5f820f648c92a5ecc771a96b3c29aa6e90013bba Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2009 14:40:59 -0800 Subject: poll: allow f_op->poll to sleep f_op->poll is the only vfs operation which is not allowed to sleep. It's because poll and select implementation used task state to synchronize against wake ups, which doesn't have to be the case anymore as wait/wake interface can now use custom wake up functions. The non-sleep restriction can be a bit tricky because ->poll is not called from an atomic context and the result of accidentally sleeping in ->poll only shows up as temporary busy looping when the timing is right or rather wrong. This patch converts poll/select to use custom wake up function and use separate triggered variable to synchronize against wake up events. The only added overhead is an extra function call during wake up and negligible. This patch removes the one non-sleep exception from vfs locking rules and is beneficial to userland filesystem implementations like FUSE, 9p or peculiar fs like spufs as it's very difficult for those to implement non-sleeping poll method. While at it, make the following cosmetic changes to make poll.h and select.c checkpatch friendly. * s/type * symbol/type *symbol/ : three places in poll.h * remove blank line before EXPORT_SYMBOL() : two places in select.c Oleg: spotted missing barrier in poll_schedule_timeout() Davide: spotted missing write barrier in pollwake() Signed-off-by: Tejun Heo Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Ingo Molnar Cc: Christoph Hellwig Signed-off-by: Miklos Szeredi Cc: Davide Libenzi Cc: Brad Boyer Cc: Al Viro Cc: Roland McGrath Cc: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Cc: Davide Libenzi Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 +- drivers/media/video/v4l1-compat.c | 4 +-- fs/select.c | 76 +++++++++++++++++++++++++++++++-------- include/linux/poll.h | 15 ++++++-- 4 files changed, 76 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index ccec55394380..cfbfa15a46ba 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -397,7 +397,7 @@ prototypes: }; locking rules: - All except ->poll() may block. + All may block. BKL llseek: no (see below) read: no diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index d450cab20be4..b617bf05e2d7 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -203,7 +203,6 @@ static int poll_one(struct file *file, struct poll_wqueues *pwq) table = &pwq->pt; for (;;) { int mask; - set_current_state(TASK_INTERRUPTIBLE); mask = file->f_op->poll(file, table); if (mask & POLLIN) break; @@ -212,9 +211,8 @@ static int poll_one(struct file *file, struct poll_wqueues *pwq) retval = -ERESTARTSYS; break; } - schedule(); + poll_schedule(pwq, TASK_INTERRUPTIBLE); } - set_current_state(TASK_RUNNING); poll_freewait(pwq); return retval; } diff --git a/fs/select.c b/fs/select.c index 87df51eadcf2..08b91beed806 100644 --- a/fs/select.c +++ b/fs/select.c @@ -109,11 +109,11 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, void poll_initwait(struct poll_wqueues *pwq) { init_poll_funcptr(&pwq->pt, __pollwait); + pwq->polling_task = current; pwq->error = 0; pwq->table = NULL; pwq->inline_index = 0; } - EXPORT_SYMBOL(poll_initwait); static void free_poll_entry(struct poll_table_entry *entry) @@ -142,12 +142,10 @@ void poll_freewait(struct poll_wqueues *pwq) free_page((unsigned long) old); } } - EXPORT_SYMBOL(poll_freewait); -static struct poll_table_entry *poll_get_entry(poll_table *_p) +static struct poll_table_entry *poll_get_entry(struct poll_wqueues *p) { - struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt); struct poll_table_page *table = p->table; if (p->inline_index < N_INLINE_POLL_ENTRIES) @@ -159,7 +157,6 @@ static struct poll_table_entry *poll_get_entry(poll_table *_p) new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL); if (!new_table) { p->error = -ENOMEM; - __set_current_state(TASK_RUNNING); return NULL; } new_table->entry = new_table->entries; @@ -171,20 +168,75 @@ static struct poll_table_entry *poll_get_entry(poll_table *_p) return table->entry++; } +static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct poll_wqueues *pwq = wait->private; + DECLARE_WAITQUEUE(dummy_wait, pwq->polling_task); + + /* + * Although this function is called under waitqueue lock, LOCK + * doesn't imply write barrier and the users expect write + * barrier semantics on wakeup functions. The following + * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() + * and is paired with set_mb() in poll_schedule_timeout. + */ + smp_wmb(); + pwq->triggered = 1; + + /* + * Perform the default wake up operation using a dummy + * waitqueue. + * + * TODO: This is hacky but there currently is no interface to + * pass in @sync. @sync is scheduled to be removed and once + * that happens, wake_up_process() can be used directly. + */ + return default_wake_function(&dummy_wait, mode, sync, key); +} + /* Add a new entry */ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) { - struct poll_table_entry *entry = poll_get_entry(p); + struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt); + struct poll_table_entry *entry = poll_get_entry(pwq); if (!entry) return; get_file(filp); entry->filp = filp; entry->wait_address = wait_address; - init_waitqueue_entry(&entry->wait, current); + init_waitqueue_func_entry(&entry->wait, pollwake); + entry->wait.private = pwq; add_wait_queue(wait_address, &entry->wait); } +int poll_schedule_timeout(struct poll_wqueues *pwq, int state, + ktime_t *expires, unsigned long slack) +{ + int rc = -EINTR; + + set_current_state(state); + if (!pwq->triggered) + rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS); + __set_current_state(TASK_RUNNING); + + /* + * Prepare for the next iteration. + * + * The following set_mb() serves two purposes. First, it's + * the counterpart rmb of the wmb in pollwake() such that data + * written before wake up is always visible after wake up. + * Second, the full barrier guarantees that triggered clearing + * doesn't pass event check of the next iteration. Note that + * this problem doesn't exist for the first iteration as + * add_wait_queue() has full barrier semantics. + */ + set_mb(pwq->triggered, 0); + + return rc; +} +EXPORT_SYMBOL(poll_schedule_timeout); + /** * poll_select_set_timeout - helper function to setup the timeout value * @to: pointer to timespec variable for the final timeout @@ -340,8 +392,6 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) for (;;) { unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; - set_current_state(TASK_INTERRUPTIBLE); - inp = fds->in; outp = fds->out; exp = fds->ex; rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex; @@ -411,10 +461,10 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) to = &expire; } - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE, + to, slack)) timed_out = 1; } - __set_current_state(TASK_RUNNING); poll_freewait(&table); @@ -666,7 +716,6 @@ static int do_poll(unsigned int nfds, struct poll_list *list, for (;;) { struct poll_list *walk; - set_current_state(TASK_INTERRUPTIBLE); for (walk = list; walk != NULL; walk = walk->next) { struct pollfd * pfd, * pfd_end; @@ -709,10 +758,9 @@ static int do_poll(unsigned int nfds, struct poll_list *list, to = &expire; } - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)) timed_out = 1; } - __set_current_state(TASK_RUNNING); return count; } diff --git a/include/linux/poll.h b/include/linux/poll.h index badd98ab06f6..8c24ef8d9976 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -46,9 +46,9 @@ static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) } struct poll_table_entry { - struct file * filp; + struct file *filp; wait_queue_t wait; - wait_queue_head_t * wait_address; + wait_queue_head_t *wait_address; }; /* @@ -56,7 +56,9 @@ struct poll_table_entry { */ struct poll_wqueues { poll_table pt; - struct poll_table_page * table; + struct poll_table_page *table; + struct task_struct *polling_task; + int triggered; int error; int inline_index; struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES]; @@ -64,6 +66,13 @@ struct poll_wqueues { extern void poll_initwait(struct poll_wqueues *pwq); extern void poll_freewait(struct poll_wqueues *pwq); +extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state, + ktime_t *expires, unsigned long slack); + +static inline int poll_schedule(struct poll_wqueues *pwq, int state) +{ + return poll_schedule_timeout(pwq, state, NULL, 0); +} /* * Scaleable version of the fd_set. -- cgit v1.2.3-71-gd317 From 179f7ebff6be45738c6e2fa68c8d2cc5c2c6308e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jan 2009 14:41:04 -0800 Subject: percpu_counter: FBC_BATCH should be a variable For NR_CPUS >= 16 values, FBC_BATCH is 2*NR_CPUS Considering more and more distros are using high NR_CPUS values, it makes sense to use a more sensible value for FBC_BATCH, and get rid of NR_CPUS. A sensible value is 2*num_online_cpus(), with a minimum value of 32 (This minimum value helps branch prediction in __percpu_counter_add()) We already have a hotcpu notifier, so we can adjust FBC_BATCH dynamically. We rename FBC_BATCH to percpu_counter_batch since its not a constant anymore. Signed-off-by: Eric Dumazet Acked-by: David S. Miller Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext4/ext4.h | 6 +++--- fs/ext4/inode.c | 2 +- include/linux/percpu_counter.h | 8 ++------ lib/percpu_counter.c | 18 ++++++++++++++---- 4 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b0537c827024..6c46c648430d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1225,11 +1225,11 @@ do { \ } while (0) #ifdef CONFIG_SMP -/* Each CPU can accumulate FBC_BATCH blocks in their local +/* Each CPU can accumulate percpu_counter_batch blocks in their local * counters. So we need to make sure we have free blocks more - * than FBC_BATCH * nr_cpu_ids. Also add a window of 4 times. + * than percpu_counter_batch * nr_cpu_ids. Also add a window of 4 times. */ -#define EXT4_FREEBLOCKS_WATERMARK (4 * (FBC_BATCH * nr_cpu_ids)) +#define EXT4_FREEBLOCKS_WATERMARK (4 * (percpu_counter_batch * nr_cpu_ids)) #else #define EXT4_FREEBLOCKS_WATERMARK 0 #endif diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 6702a49992a6..98d3fe7057ef 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2498,7 +2498,7 @@ static int ext4_nonda_switch(struct super_block *sb) /* * switch to non delalloc mode if we are running low * on free block. The free block accounting via percpu - * counters can get slightly wrong with FBC_BATCH getting + * counters can get slightly wrong with percpu_counter_batch getting * accumulated on each CPU without updating global counters * Delalloc need an accurate free block accounting. So switch * to non delalloc when we are near to error range. diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 9007ccdfc112..99de7a31bab8 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -24,11 +24,7 @@ struct percpu_counter { s32 *counters; }; -#if NR_CPUS >= 16 -#define FBC_BATCH (NR_CPUS*2) -#else -#define FBC_BATCH (NR_CPUS*4) -#endif +extern int percpu_counter_batch; int percpu_counter_init(struct percpu_counter *fbc, s64 amount); int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount); @@ -39,7 +35,7 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc); static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount) { - __percpu_counter_add(fbc, amount, FBC_BATCH); + __percpu_counter_add(fbc, amount, percpu_counter_batch); } static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc) diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index b255b939bc1b..a60bd8046095 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -9,10 +9,8 @@ #include #include -#ifdef CONFIG_HOTPLUG_CPU static LIST_HEAD(percpu_counters); static DEFINE_MUTEX(percpu_counters_lock); -#endif void percpu_counter_set(struct percpu_counter *fbc, s64 amount) { @@ -111,13 +109,24 @@ void percpu_counter_destroy(struct percpu_counter *fbc) } EXPORT_SYMBOL(percpu_counter_destroy); -#ifdef CONFIG_HOTPLUG_CPU +int percpu_counter_batch __read_mostly = 32; +EXPORT_SYMBOL(percpu_counter_batch); + +static void compute_batch_value(void) +{ + int nr = num_online_cpus(); + + percpu_counter_batch = max(32, nr*2); +} + static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) { +#ifdef CONFIG_HOTPLUG_CPU unsigned int cpu; struct percpu_counter *fbc; + compute_batch_value(); if (action != CPU_DEAD) return NOTIFY_OK; @@ -134,13 +143,14 @@ static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb, spin_unlock_irqrestore(&fbc->lock, flags); } mutex_unlock(&percpu_counters_lock); +#endif return NOTIFY_OK; } static int __init percpu_counter_startup(void) { + compute_batch_value(); hotcpu_notifier(percpu_counter_hotcpu_callback, 0); return 0; } module_init(percpu_counter_startup); -#endif -- cgit v1.2.3-71-gd317 From 8c3659347efb43857b2c2d7bc63a9c7d68d1a608 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 6 Jan 2009 14:41:14 -0800 Subject: include/linux/interrupt.h: do not include linux/irqnr.h twice Signed-off-by: Jesper Juhl Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/interrupt.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 0702c4d7bdf0..af886b26c9d1 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-71-gd317 From 5cf0cc4e670b8da2231a3375db87ec3b6cb84432 Mon Sep 17 00:00:00 2001 From: Hiroshi Shimamoto Date: Tue, 6 Jan 2009 14:41:38 -0800 Subject: binfmts.h: include list.h linux_binfmt uses list_head, so list.h is needed. [akpm@linux-foundation.org: fix `make headerscheck'] Signed-off-by: Hiroshi Shimamoto Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/binfmts.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 6cbfbe297180..0d0150b4901e 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -18,6 +18,7 @@ struct pt_regs; #define BINPRM_BUF_SIZE 128 #ifdef __KERNEL__ +#include #define CORENAME_MAX_SIZE 128 -- cgit v1.2.3-71-gd317 From d29389de0b0ee1715333bafc6ac3f22a75aa4313 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 6 Jan 2009 14:41:41 -0800 Subject: spi_gpio driver Generalize the old at91rm9200 "bootstrap" bitbanging SPI master driver as "spi_gpio", so it works with arbitrary GPIOs and can be configured through platform_data. Such SPI masters support: - any number of bus instances (bus_num is the platform_device.id) - any number of chipselects (one GPIO per spi_device) - all four SPI_MODE values, and SPI_CS_HIGH - i/o word sizes from 1 to 32 bits; - devices configured as with any other spi_master controller When configured using platform_data, this provides relatively low clock rates. On platforms that support inlined GPIO calls, significantly improved transfer speeds are also possible with a semi-custom driver. (It's still painful when accessing flash memory, but less so.) Sanity checked by using this version to replace both native controllers on a board with six different SPI slaves, relying on three different SPI_MODE_* values and both SPI_CS_HIGH settings for correct operation. [akpm@linux-foundation.org: cleanups] Signed-off-by: David Brownell Acked-by: Magnus Damm Tested-by: Magnus Damm Cc: Torgil Svensson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 18 ++- drivers/spi/Makefile | 1 + drivers/spi/spi_gpio.c | 360 +++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi_gpio.h | 60 ++++++++ 4 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi_gpio.c create mode 100644 include/linux/spi/spi_gpio.h (limited to 'include/linux') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9d0efb6803f..4a6fe01831a8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -78,7 +78,7 @@ config SPI_AU1550 will be called au1550_spi. config SPI_BITBANG - tristate "Bitbanging SPI master" + tristate "Utilities for Bitbanging SPI masters" help With a few GPIO pins, your system can bitbang the SPI protocol. Select this to get SPI support through I/O pins (GPIO, parallel @@ -100,6 +100,22 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_GPIO + tristate "GPIO-based bitbanging SPI Master" + depends on GENERIC_GPIO + select SPI_BITBANG + help + This simple GPIO bitbanging SPI master uses the arch-neutral GPIO + interface to manage MOSI, MISO, SCK, and chipselect signals. SPI + slaves connected to a bus using this driver are configured as usual, + except that the spi_board_info.controller_data holds the GPIO number + for the chipselect used by this controller driver. + + Note that this driver often won't achieve even 1 Mbit/sec speeds, + making it unusually slow for SPI. If your platform can inline + GPIO operations, you should be able to leverage that for better + speed with a custom version of this driver; see the source code. + config SPI_IMX tristate "Freescale iMX SPI controller" depends on ARCH_IMX && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ccf18de34e1e..5e9f521b8844 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o +obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c new file mode 100644 index 000000000000..49698cabc30d --- /dev/null +++ b/drivers/spi/spi_gpio.c @@ -0,0 +1,360 @@ +/* + * spi_gpio.c - SPI master driver using generic bitbanged GPIO + * + * Copyright (C) 2006,2008 David Brownell + * + * 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 +#include +#include +#include + +#include +#include +#include + + +/* + * This bitbanging SPI master driver should help make systems usable + * when a native hardware SPI engine is not available, perhaps because + * its driver isn't yet working or because the I/O pins it requires + * are used for other purposes. + * + * platform_device->driver_data ... points to spi_gpio + * + * spi->controller_state ... reserved for bitbang framework code + * spi->controller_data ... holds chipselect GPIO + * + * spi->master->dev.driver_data ... points to spi_gpio->bitbang + */ + +struct spi_gpio { + struct spi_bitbang bitbang; + struct spi_gpio_platform_data pdata; + struct platform_device *pdev; +}; + +/*----------------------------------------------------------------------*/ + +/* + * Because the overhead of going through four GPIO procedure calls + * per transferred bit can make performance a problem, this code + * is set up so that you can use it in either of two ways: + * + * - The slow generic way: set up platform_data to hold the GPIO + * numbers used for MISO/MOSI/SCK, and issue procedure calls for + * each of them. This driver can handle several such busses. + * + * - The quicker inlined way: only helps with platform GPIO code + * that inlines operations for constant GPIOs. This can give + * you tight (fast!) inner loops, but each such bus needs a + * new driver. You'll define a new C file, with Makefile and + * Kconfig support; the C code can be a total of six lines: + * + * #define DRIVER_NAME "myboard_spi2" + * #define SPI_MISO_GPIO 119 + * #define SPI_MOSI_GPIO 120 + * #define SPI_SCK_GPIO 121 + * #define SPI_N_CHIPSEL 4 + * #include "spi_gpio.c" + */ + +#ifndef DRIVER_NAME +#define DRIVER_NAME "spi_gpio" + +#define GENERIC_BITBANG /* vs tight inlines */ + +/* all functions referencing these symbols must define pdata */ +#define SPI_MISO_GPIO ((pdata)->miso) +#define SPI_MOSI_GPIO ((pdata)->mosi) +#define SPI_SCK_GPIO ((pdata)->sck) + +#define SPI_N_CHIPSEL ((pdata)->num_chipselect) + +#endif + +/*----------------------------------------------------------------------*/ + +static inline const struct spi_gpio_platform_data * __pure +spi_to_pdata(const struct spi_device *spi) +{ + const struct spi_bitbang *bang; + const struct spi_gpio *spi_gpio; + + bang = spi_master_get_devdata(spi->master); + spi_gpio = container_of(bang, struct spi_gpio, bitbang); + return &spi_gpio->pdata; +} + +/* this is #defined to avoid unused-variable warnings when inlining */ +#define pdata spi_to_pdata(spi) + +static inline void setsck(const struct spi_device *spi, int is_on) +{ + gpio_set_value(SPI_SCK_GPIO, is_on); +} + +static inline void setmosi(const struct spi_device *spi, int is_on) +{ + gpio_set_value(SPI_MOSI_GPIO, is_on); +} + +static inline int getmiso(const struct spi_device *spi) +{ + return gpio_get_value(SPI_MISO_GPIO); +} + +#undef pdata + +/* + * NOTE: this clocks "as fast as we can". It "should" be a function of the + * requested device clock. Software overhead means we usually have trouble + * reaching even one Mbit/sec (except when we can inline bitops), so for now + * we'll just assume we never need additional per-bit slowdowns. + */ +#define spidelay(nsecs) do {} while (0) + +#define EXPAND_BITBANG_TXRX +#include + +/* + * These functions can leverage inline expansion of GPIO calls to shrink + * costs for a txrx bit, often by factors of around ten (by instruction + * count). That is particularly visible for larger word sizes, but helps + * even with default 8-bit words. + * + * REVISIT overheads calling these functions for each word also have + * significant performance costs. Having txrx_bufs() calls that inline + * the txrx_word() logic would help performance, e.g. on larger blocks + * used with flash storage or MMC/SD. There should also be ways to make + * GCC be less stupid about reloading registers inside the I/O loops, + * even without inlined GPIO calls; __attribute__((hot)) on GCC 4.3? + */ + +static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); +} + +static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); +} + +static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); +} + +static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); +} + +/*----------------------------------------------------------------------*/ + +static void spi_gpio_chipselect(struct spi_device *spi, int is_active) +{ + unsigned long cs = (unsigned long) spi->controller_data; + + /* set initial clock polarity */ + if (is_active) + setsck(spi, spi->mode & SPI_CPOL); + + /* SPI is normally active-low */ + gpio_set_value(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); +} + +static int spi_gpio_setup(struct spi_device *spi) +{ + unsigned long cs = (unsigned long) spi->controller_data; + int status = 0; + + if (spi->bits_per_word > 32) + return -EINVAL; + + if (!spi->controller_state) { + status = gpio_request(cs, spi->dev.bus_id); + if (status) + return status; + status = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); + } + if (!status) + status = spi_bitbang_setup(spi); + if (status) { + if (!spi->controller_state) + gpio_free(cs); + } + return status; +} + +static void spi_gpio_cleanup(struct spi_device *spi) +{ + unsigned long cs = (unsigned long) spi->controller_data; + + gpio_free(cs); + spi_bitbang_cleanup(spi); +} + +static int __init spi_gpio_alloc(unsigned pin, const char *label, bool is_in) +{ + int value; + + value = gpio_request(pin, label); + if (value == 0) { + if (is_in) + value = gpio_direction_input(pin); + else + value = gpio_direction_output(pin, 0); + } + return value; +} + +static int __init +spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label) +{ + int value; + + /* NOTE: SPI_*_GPIO symbols may reference "pdata" */ + + value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false); + if (value) + goto done; + + value = spi_gpio_alloc(SPI_MISO_GPIO, label, true); + if (value) + goto free_mosi; + + value = spi_gpio_alloc(SPI_SCK_GPIO, label, false); + if (value) + goto free_miso; + + goto done; + +free_miso: + gpio_free(SPI_MISO_GPIO); +free_mosi: + gpio_free(SPI_MOSI_GPIO); +done: + return value; +} + +static int __init spi_gpio_probe(struct platform_device *pdev) +{ + int status; + struct spi_master *master; + struct spi_gpio *spi_gpio; + struct spi_gpio_platform_data *pdata; + + pdata = pdev->dev.platform_data; +#ifdef GENERIC_BITBANG + if (!pdata || !pdata->num_chipselect) + return -ENODEV; +#endif + + status = spi_gpio_request(pdata, dev_name(&pdev->dev)); + if (status < 0) + return status; + + master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio); + if (!master) { + status = -ENOMEM; + goto gpio_free; + } + spi_gpio = spi_master_get_devdata(master); + platform_set_drvdata(pdev, spi_gpio); + + spi_gpio->pdev = pdev; + if (pdata) + spi_gpio->pdata = *pdata; + + master->bus_num = pdev->id; + master->num_chipselect = SPI_N_CHIPSEL; + master->setup = spi_gpio_setup; + master->cleanup = spi_gpio_cleanup; + + spi_gpio->bitbang.master = spi_master_get(master); + spi_gpio->bitbang.chipselect = spi_gpio_chipselect; + spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; + spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1; + spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2; + spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3; + spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer; + spi_gpio->bitbang.flags = SPI_CS_HIGH; + + status = spi_bitbang_start(&spi_gpio->bitbang); + if (status < 0) { + spi_master_put(spi_gpio->bitbang.master); +gpio_free: + gpio_free(SPI_MISO_GPIO); + gpio_free(SPI_MOSI_GPIO); + gpio_free(SPI_SCK_GPIO); + spi_master_put(master); + } + + return status; +} + +static int __exit spi_gpio_remove(struct platform_device *pdev) +{ + struct spi_gpio *spi_gpio; + struct spi_gpio_platform_data *pdata; + int status; + + spi_gpio = platform_get_drvdata(pdev); + pdata = pdev->dev.platform_data; + + /* stop() unregisters child devices too */ + status = spi_bitbang_stop(&spi_gpio->bitbang); + spi_master_put(spi_gpio->bitbang.master); + + platform_set_drvdata(pdev, NULL); + + gpio_free(SPI_MISO_GPIO); + gpio_free(SPI_MOSI_GPIO); + gpio_free(SPI_SCK_GPIO); + + return status; +} + +MODULE_ALIAS("platform:" DRIVER_NAME); + +static struct platform_driver spi_gpio_driver = { + .driver.name = DRIVER_NAME, + .driver.owner = THIS_MODULE, + .remove = __exit_p(spi_gpio_remove), +}; + +static int __init spi_gpio_init(void) +{ + return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe); +} +module_init(spi_gpio_init); + +static void __exit spi_gpio_exit(void) +{ + platform_driver_unregister(&spi_gpio_driver); +} +module_exit(spi_gpio_exit); + + +MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO "); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/spi_gpio.h b/include/linux/spi/spi_gpio.h new file mode 100644 index 000000000000..0f01a0f1f40c --- /dev/null +++ b/include/linux/spi/spi_gpio.h @@ -0,0 +1,60 @@ +#ifndef __LINUX_SPI_GPIO_H +#define __LINUX_SPI_GPIO_H + +/* + * For each bitbanged SPI bus, set up a platform_device node with: + * - name "spi_gpio" + * - id the same as the SPI bus number it implements + * - dev.platform data pointing to a struct spi_gpio_platform_data + * + * Or, see the driver code for information about speedups that are + * possible on platforms that support inlined access for GPIOs (no + * spi_gpio_platform_data is used). + * + * Use spi_board_info with these busses in the usual way, being sure + * that the controller_data being the GPIO used for each device's + * chipselect: + * + * static struct spi_board_info ... [] = { + * ... + * // this slave uses GPIO 42 for its chipselect + * .controller_data = (void *) 42, + * ... + * // this one uses GPIO 86 for its chipselect + * .controller_data = (void *) 86, + * ... + * }; + * + * If the bitbanged bus is later switched to a "native" controller, + * that platform_device and controller_data should be removed. + */ + +/** + * struct spi_gpio_platform_data - parameter for bitbanged SPI master + * @sck: number of the GPIO used for clock output + * @mosi: number of the GPIO used for Master Output, Slave In (MOSI) data + * @miso: number of the GPIO used for Master Input, Slave Output (MISO) data + * @num_chipselect: how many slaves to allow + * + * All GPIO signals used with the SPI bus managed through this driver + * (chipselects, MOSI, MISO, SCK) must be configured as GPIOs, instead + * of some alternate function. + * + * It can be convenient to use this driver with pins that have alternate + * functions associated with a "native" SPI controller if a driver for that + * controller is not available, or is missing important functionality. + * + * On platforms which can do so, configure MISO with a weak pullup unless + * there's an external pullup on that signal. That saves power by avoiding + * floating signals. (A weak pulldown would save power too, but many + * drivers expect to see all-ones data as the no slave "response".) + */ +struct spi_gpio_platform_data { + unsigned sck; + unsigned mosi; + unsigned miso; + + u16 num_chipselect; +}; + +#endif /* __LINUX_SPI_GPIO_H */ -- cgit v1.2.3-71-gd317 From a06f6211ef9b1785922f9d0e8766d63ac4e66de1 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 6 Jan 2009 14:41:49 -0800 Subject: module: add within_module_core() and within_module_init() This series of patches allows kprobes to probe module's __init and __exit functions. This means, you can probe driver initialization and terminating. Currently, kprobes can't probe __init function because these functions are freed after module initialization. And it also can't probe module __exit functions because kprobe increments reference count of target module and user can't unload it. this means __exit functions never be called unless removing probes from the module. To solve both cases, this series of patches introduces GONE flag and sets it when the target code is freed(for this purpose, kprobes hooks MODULE_STATE_* events). This also removes refcount incrementing for allowing user to unload target module. Users can check which probes are GONE by debugfs interface. For taking timing of freeing module's .init text, these also include a patch which adds module's notifier of MODULE_STATE_LIVE event. This patch: Add within_module_core() and within_module_init() for checking whether an address is in the module .init.text section or .text section, and replace within() local inline functions in kernel/module.c with them. kprobes uses these functions to check where the kprobe is inserted. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Acked-by: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/module.h | 12 ++++++++++++ kernel/module.c | 16 ++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 03cb93d1865a..4f7ea12463d3 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -365,6 +365,18 @@ struct module *module_text_address(unsigned long addr); struct module *__module_text_address(unsigned long addr); int is_module_address(unsigned long addr); +static inline int within_module_core(unsigned long addr, struct module *mod) +{ + return (unsigned long)mod->module_core <= addr && + addr < (unsigned long)mod->module_core + mod->core_size; +} + +static inline int within_module_init(unsigned long addr, struct module *mod) +{ + return (unsigned long)mod->module_init <= addr && + addr < (unsigned long)mod->module_init + mod->init_size; +} + /* Returns 0 and fills in value, defined and namebuf, or -ERANGE if symnum out of range. */ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, diff --git a/kernel/module.c b/kernel/module.c index 34b56cf06615..cc79c942c572 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2390,7 +2390,7 @@ static const char *get_ksymbol(struct module *mod, unsigned long nextval; /* At worse, next value is at end of module */ - if (within(addr, mod->module_init, mod->init_size)) + if (within_module_init(addr, mod)) nextval = (unsigned long)mod->module_init+mod->init_text_size; else nextval = (unsigned long)mod->module_core+mod->core_text_size; @@ -2438,8 +2438,8 @@ const char *module_address_lookup(unsigned long addr, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { - if (within(addr, mod->module_init, mod->init_size) - || within(addr, mod->module_core, mod->core_size)) { + if (within_module_init(addr, mod) || + within_module_core(addr, mod)) { if (modname) *modname = mod->name; ret = get_ksymbol(mod, addr, size, offset); @@ -2461,8 +2461,8 @@ int lookup_module_symbol_name(unsigned long addr, char *symname) preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { - if (within(addr, mod->module_init, mod->init_size) || - within(addr, mod->module_core, mod->core_size)) { + if (within_module_init(addr, mod) || + within_module_core(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, NULL, NULL); @@ -2485,8 +2485,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { - if (within(addr, mod->module_init, mod->init_size) || - within(addr, mod->module_core, mod->core_size)) { + if (within_module_init(addr, mod) || + within_module_core(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, size, offset); @@ -2705,7 +2705,7 @@ int is_module_address(unsigned long addr) preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { - if (within(addr, mod->module_core, mod->core_size)) { + if (within_module_core(addr, mod)) { preempt_enable(); return 1; } -- cgit v1.2.3-71-gd317 From 129415607845d4daea11ddcba706005c69dcb942 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 6 Jan 2009 14:41:50 -0800 Subject: kprobes: add kprobe_insn_mutex and cleanup arch_remove_kprobe() Add kprobe_insn_mutex for protecting kprobe_insn_pages hlist, and remove kprobe_mutex from architecture dependent code. This allows us to call arch_remove_kprobe() (and free_insn_slot) while holding kprobe_mutex. Signed-off-by: Masami Hiramatsu Acked-by: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: Russell King Cc: "Luck, Tony" Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/kprobes.c | 2 -- arch/ia64/kernel/kprobes.c | 8 +++++--- arch/powerpc/kernel/kprobes.c | 7 ++++--- arch/s390/kernel/kprobes.c | 7 ++++--- arch/x86/kernel/kprobes.c | 7 ++++--- include/linux/kprobes.h | 1 - kernel/kprobes.c | 25 +++++++++++++++++++++---- 7 files changed, 38 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 3f9abe0e9aff..f692efddd449 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -92,9 +92,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { if (p->ainsn.insn) { - mutex_lock(&kprobe_mutex); free_insn_slot(p->ainsn.insn, 0); - mutex_unlock(&kprobe_mutex); p->ainsn.insn = NULL; } } diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index f07688da947c..097b84d54e73 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -670,9 +670,11 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { - mutex_lock(&kprobe_mutex); - free_insn_slot(p->ainsn.insn, p->ainsn.inst_flag & INST_FLAG_BOOSTABLE); - mutex_unlock(&kprobe_mutex); + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, + p->ainsn.inst_flag & INST_FLAG_BOOSTABLE); + p->ainsn.insn = NULL; + } } /* * We are resuming execution after a single step fault, so the pt_regs diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index de79915452c8..989edcdf0297 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -96,9 +96,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { - mutex_lock(&kprobe_mutex); - free_insn_slot(p->ainsn.insn, 0); - mutex_unlock(&kprobe_mutex); + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; + } } static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 569079ec4ff0..9b92856632cf 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -218,9 +218,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { - mutex_lock(&kprobe_mutex); - free_insn_slot(p->ainsn.insn, 0); - mutex_unlock(&kprobe_mutex); + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; + } } static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 6c27679ec6aa..eead6f8f9218 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -376,9 +376,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { - mutex_lock(&kprobe_mutex); - free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1)); - mutex_unlock(&kprobe_mutex); + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1)); + p->ainsn.insn = NULL; + } } static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 497b1d1f7a05..b93e44ce2284 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -201,7 +201,6 @@ static inline int init_test_probes(void) } #endif /* CONFIG_KPROBES_SANITY_TEST */ -extern struct mutex kprobe_mutex; extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p); extern void arch_disarm_kprobe(struct kprobe *p); diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 3afd354c46f1..29e87921437d 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; /* NOTE: change this value only with kprobe_mutex held */ static bool kprobe_enabled; -DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ +static DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; static struct { spinlock_t lock ____cacheline_aligned_in_smp; @@ -115,6 +115,7 @@ enum kprobe_slot_state { SLOT_USED = 2, }; +static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */ static struct hlist_head kprobe_insn_pages; static int kprobe_garbage_slots; static int collect_garbage_slots(void); @@ -144,10 +145,10 @@ loop_end: } /** - * get_insn_slot() - Find a slot on an executable page for an instruction. + * __get_insn_slot() - Find a slot on an executable page for an instruction. * We allocate an executable page if there's no room on existing ones. */ -kprobe_opcode_t __kprobes *get_insn_slot(void) +static kprobe_opcode_t __kprobes *__get_insn_slot(void) { struct kprobe_insn_page *kip; struct hlist_node *pos; @@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void) return kip->insns; } +kprobe_opcode_t __kprobes *get_insn_slot(void) +{ + kprobe_opcode_t *ret; + mutex_lock(&kprobe_insn_mutex); + ret = __get_insn_slot(); + mutex_unlock(&kprobe_insn_mutex); + return ret; +} + /* Return 1 if all garbages are collected, otherwise 0. */ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx) { @@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void) { struct kprobe_insn_page *kip; struct hlist_node *pos, *next; + int safety; /* Ensure no-one is preepmted on the garbages */ - if (check_safety() != 0) + mutex_unlock(&kprobe_insn_mutex); + safety = check_safety(); + mutex_lock(&kprobe_insn_mutex); + if (safety != 0) return -EAGAIN; hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) { @@ -251,6 +265,7 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) struct kprobe_insn_page *kip; struct hlist_node *pos; + mutex_lock(&kprobe_insn_mutex); hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) { if (kip->insns <= slot && slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) { @@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE) collect_garbage_slots(); + + mutex_unlock(&kprobe_insn_mutex); } #endif -- cgit v1.2.3-71-gd317 From e8386a0cb22f4a2d439384212c494ad0bda848fe Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 6 Jan 2009 14:41:52 -0800 Subject: kprobes: support probing module __exit function Allows kprobes to probe __exit routine. This adds flags member to struct kprobe. When module is freed(kprobes hooks module_notifier to get this event), kprobes which probe the functions in that module are set to "Gone" flag to the flags member. These "Gone" probes are never be enabled. Users can check the GONE flag through debugfs. This also removes mod_refcounted, because we couldn't free a module if kprobe incremented the refcount of that module. [akpm@linux-foundation.org: document some locking] [mhiramat@redhat.com: bugfix: pass aggr_kprobe to arch_remove_kprobe] [mhiramat@redhat.com: bugfix: release old_p's insn_slot before error return] Signed-off-by: Masami Hiramatsu Acked-by: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Signed-off-by: Masami Hiramatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kprobes.txt | 5 +- include/linux/kprobes.h | 14 +++- kernel/kprobes.c | 159 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 134 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index a79633d702bf..48b3de90eb1e 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -497,7 +497,10 @@ The first column provides the kernel address where the probe is inserted. The second column identifies the type of probe (k - kprobe, r - kretprobe and j - jprobe), while the third column specifies the symbol+offset of the probe. If the probed function belongs to a module, the module name -is also specified. +is also specified. Following columns show probe status. If the probe is on +a virtual address that is no longer valid (module init sections, module +virtual addresses that correspond to modules that've been unloaded), +such probes are marked with [GONE]. /debug/kprobes/enabled: Turn kprobes ON/OFF diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index b93e44ce2284..d6ea19e314bb 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -69,9 +69,6 @@ struct kprobe { /* list of kprobes for multi-handler support */ struct list_head list; - /* Indicates that the corresponding module has been ref counted */ - unsigned int mod_refcounted; - /*count the number of times this probe was temporarily disarmed */ unsigned long nmissed; @@ -103,8 +100,19 @@ struct kprobe { /* copy of the original instruction */ struct arch_specific_insn ainsn; + + /* Indicates various status flags. Protected by kprobe_mutex. */ + u32 flags; }; +/* Kprobe status flags */ +#define KPROBE_FLAG_GONE 1 /* breakpoint has already gone */ + +static inline int kprobe_gone(struct kprobe *p) +{ + return p->flags & KPROBE_FLAG_GONE; +} + /* * Special probe type that uses setjmp-longjmp type tricks to resume * execution at a specified entry with a matching prototype corresponding diff --git a/kernel/kprobes.c b/kernel/kprobes.c index a1e233a19586..cb732a9aa55f 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -327,7 +327,7 @@ static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs) struct kprobe *kp; list_for_each_entry_rcu(kp, &p->list, list) { - if (kp->pre_handler) { + if (kp->pre_handler && !kprobe_gone(kp)) { set_kprobe_instance(kp); if (kp->pre_handler(kp, regs)) return 1; @@ -343,7 +343,7 @@ static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs, struct kprobe *kp; list_for_each_entry_rcu(kp, &p->list, list) { - if (kp->post_handler) { + if (kp->post_handler && !kprobe_gone(kp)) { set_kprobe_instance(kp); kp->post_handler(kp, regs, flags); reset_kprobe_instance(); @@ -545,9 +545,10 @@ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) ap->addr = p->addr; ap->pre_handler = aggr_pre_handler; ap->fault_handler = aggr_fault_handler; - if (p->post_handler) + /* We don't care the kprobe which has gone. */ + if (p->post_handler && !kprobe_gone(p)) ap->post_handler = aggr_post_handler; - if (p->break_handler) + if (p->break_handler && !kprobe_gone(p)) ap->break_handler = aggr_break_handler; INIT_LIST_HEAD(&ap->list); @@ -566,17 +567,41 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p, int ret = 0; struct kprobe *ap; + if (kprobe_gone(old_p)) { + /* + * Attempting to insert new probe at the same location that + * had a probe in the module vaddr area which already + * freed. So, the instruction slot has already been + * released. We need a new slot for the new probe. + */ + ret = arch_prepare_kprobe(old_p); + if (ret) + return ret; + } if (old_p->pre_handler == aggr_pre_handler) { copy_kprobe(old_p, p); ret = add_new_kprobe(old_p, p); + ap = old_p; } else { ap = kzalloc(sizeof(struct kprobe), GFP_KERNEL); - if (!ap) + if (!ap) { + if (kprobe_gone(old_p)) + arch_remove_kprobe(old_p); return -ENOMEM; + } add_aggr_kprobe(ap, old_p); copy_kprobe(ap, p); ret = add_new_kprobe(ap, p); } + if (kprobe_gone(old_p)) { + /* + * If the old_p has gone, its breakpoint has been disarmed. + * We have to arm it again after preparing real kprobes. + */ + ap->flags &= ~KPROBE_FLAG_GONE; + if (kprobe_enabled) + arch_arm_kprobe(ap); + } return ret; } @@ -639,8 +664,7 @@ static int __kprobes __register_kprobe(struct kprobe *p, return -EINVAL; } - p->mod_refcounted = 0; - + p->flags = 0; /* * Check if are we probing a module. */ @@ -649,16 +673,14 @@ static int __kprobes __register_kprobe(struct kprobe *p, struct module *calling_mod; calling_mod = __module_text_address(called_from); /* - * We must allow modules to probe themself and in this case - * avoid incrementing the module refcount, so as to allow - * unloading of self probing modules. + * We must hold a refcount of the probed module while updating + * its code to prohibit unexpected unloading. */ if (calling_mod != probed_mod) { if (unlikely(!try_module_get(probed_mod))) { preempt_enable(); return -EINVAL; } - p->mod_refcounted = 1; } else probed_mod = NULL; } @@ -687,8 +709,9 @@ static int __kprobes __register_kprobe(struct kprobe *p, out: mutex_unlock(&kprobe_mutex); - if (ret && probed_mod) + if (probed_mod) module_put(probed_mod); + return ret; } @@ -716,16 +739,16 @@ valid_p: list_is_singular(&old_p->list))) { /* * Only probe on the hash list. Disarm only if kprobes are - * enabled - otherwise, the breakpoint would already have - * been removed. We save on flushing icache. + * enabled and not gone - otherwise, the breakpoint would + * already have been removed. We save on flushing icache. */ - if (kprobe_enabled) + if (kprobe_enabled && !kprobe_gone(old_p)) arch_disarm_kprobe(p); hlist_del_rcu(&old_p->hlist); } else { - if (p->break_handler) + if (p->break_handler && !kprobe_gone(p)) old_p->break_handler = NULL; - if (p->post_handler) { + if (p->post_handler && !kprobe_gone(p)) { list_for_each_entry_rcu(list_p, &old_p->list, list) { if ((list_p != p) && (list_p->post_handler)) goto noclean; @@ -740,27 +763,16 @@ noclean: static void __kprobes __unregister_kprobe_bottom(struct kprobe *p) { - struct module *mod; struct kprobe *old_p; - if (p->mod_refcounted) { - /* - * Since we've already incremented refcount, - * we don't need to disable preemption. - */ - mod = module_text_address((unsigned long)p->addr); - if (mod) - module_put(mod); - } - - if (list_empty(&p->list) || list_is_singular(&p->list)) { - if (!list_empty(&p->list)) { - /* "p" is the last child of an aggr_kprobe */ - old_p = list_entry(p->list.next, struct kprobe, list); - list_del(&p->list); - kfree(old_p); - } + if (list_empty(&p->list)) arch_remove_kprobe(p); + else if (list_is_singular(&p->list)) { + /* "p" is the last child of an aggr_kprobe */ + old_p = list_entry(p->list.next, struct kprobe, list); + list_del(&p->list); + arch_remove_kprobe(old_p); + kfree(old_p); } } @@ -1074,6 +1086,67 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, #endif /* CONFIG_KRETPROBES */ +/* Set the kprobe gone and remove its instruction buffer. */ +static void __kprobes kill_kprobe(struct kprobe *p) +{ + struct kprobe *kp; + p->flags |= KPROBE_FLAG_GONE; + if (p->pre_handler == aggr_pre_handler) { + /* + * If this is an aggr_kprobe, we have to list all the + * chained probes and mark them GONE. + */ + list_for_each_entry_rcu(kp, &p->list, list) + kp->flags |= KPROBE_FLAG_GONE; + p->post_handler = NULL; + p->break_handler = NULL; + } + /* + * Here, we can remove insn_slot safely, because no thread calls + * the original probed function (which will be freed soon) any more. + */ + arch_remove_kprobe(p); +} + +/* Module notifier call back, checking kprobes on the module */ +static int __kprobes kprobes_module_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct module *mod = data; + struct hlist_head *head; + struct hlist_node *node; + struct kprobe *p; + unsigned int i; + + if (val != MODULE_STATE_GOING) + return NOTIFY_DONE; + + /* + * module .text section will be freed. We need to + * disable kprobes which have been inserted in the section. + */ + mutex_lock(&kprobe_mutex); + for (i = 0; i < KPROBE_TABLE_SIZE; i++) { + head = &kprobe_table[i]; + hlist_for_each_entry_rcu(p, node, head, hlist) + if (within_module_core((unsigned long)p->addr, mod)) { + /* + * The vaddr this probe is installed will soon + * be vfreed buy not synced to disk. Hence, + * disarming the breakpoint isn't needed. + */ + kill_kprobe(p); + } + } + mutex_unlock(&kprobe_mutex); + return NOTIFY_DONE; +} + +static struct notifier_block kprobe_module_nb = { + .notifier_call = kprobes_module_callback, + .priority = 0 +}; + static int __init init_kprobes(void) { int i, err = 0; @@ -1130,6 +1203,9 @@ static int __init init_kprobes(void) err = arch_init_kprobes(); if (!err) err = register_die_notifier(&kprobe_exceptions_nb); + if (!err) + err = register_module_notifier(&kprobe_module_nb); + kprobes_initialized = (err == 0); if (!err) @@ -1150,10 +1226,12 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p, else kprobe_type = "k"; if (sym) - seq_printf(pi, "%p %s %s+0x%x %s\n", p->addr, kprobe_type, - sym, offset, (modname ? modname : " ")); + seq_printf(pi, "%p %s %s+0x%x %s %s\n", p->addr, kprobe_type, + sym, offset, (modname ? modname : " "), + (kprobe_gone(p) ? "[GONE]" : "")); else - seq_printf(pi, "%p %s %p\n", p->addr, kprobe_type, p->addr); + seq_printf(pi, "%p %s %p %s\n", p->addr, kprobe_type, p->addr, + (kprobe_gone(p) ? "[GONE]" : "")); } static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos) @@ -1234,7 +1312,8 @@ static void __kprobes enable_all_kprobes(void) for (i = 0; i < KPROBE_TABLE_SIZE; i++) { head = &kprobe_table[i]; hlist_for_each_entry_rcu(p, node, head, hlist) - arch_arm_kprobe(p); + if (!kprobe_gone(p)) + arch_arm_kprobe(p); } kprobe_enabled = true; @@ -1263,7 +1342,7 @@ static void __kprobes disable_all_kprobes(void) for (i = 0; i < KPROBE_TABLE_SIZE; i++) { head = &kprobe_table[i]; hlist_for_each_entry_rcu(p, node, head, hlist) { - if (!arch_trampoline_kprobe(p)) + if (!arch_trampoline_kprobe(p) && !kprobe_gone(p)) arch_disarm_kprobe(p); } } -- cgit v1.2.3-71-gd317 From 730c9eeca9808fc2cfb506cc68c90aa330da17b0 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Tue, 6 Jan 2009 14:42:06 -0800 Subject: autofs4: improve parameter usage The parameter usage in the device node ioctl code uses arg1 and arg2 as parameter names. This patch redefines the parameter names to reflect what they actually are in an effort to make the code more readable. Signed-off-by: Ian Kent Signed-off-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/dev-ioctl.c | 54 ++++++++++++++++-------------- include/linux/auto_dev-ioctl.h | 75 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index 63b7c7afe8df..054d6d9ad9ba 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -180,7 +180,7 @@ static int autofs_dev_ioctl_protover(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { - param->arg1 = sbi->version; + param->protover.version = sbi->version; return 0; } @@ -189,7 +189,7 @@ static int autofs_dev_ioctl_protosubver(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { - param->arg1 = sbi->sub_version; + param->protosubver.sub_version = sbi->sub_version; return 0; } @@ -335,13 +335,13 @@ static int autofs_dev_ioctl_openmount(struct file *fp, int err, fd; /* param->path has already been checked */ - if (!param->arg1) + if (!param->openmount.devid) return -EINVAL; param->ioctlfd = -1; path = param->path; - devid = param->arg1; + devid = param->openmount.devid; err = 0; fd = autofs_dev_ioctl_open_mountpoint(path, devid); @@ -373,7 +373,7 @@ static int autofs_dev_ioctl_ready(struct file *fp, { autofs_wqt_t token; - token = (autofs_wqt_t) param->arg1; + token = (autofs_wqt_t) param->ready.token; return autofs4_wait_release(sbi, token, 0); } @@ -388,8 +388,8 @@ static int autofs_dev_ioctl_fail(struct file *fp, autofs_wqt_t token; int status; - token = (autofs_wqt_t) param->arg1; - status = param->arg2 ? param->arg2 : -ENOENT; + token = (autofs_wqt_t) param->fail.token; + status = param->fail.status ? param->fail.status : -ENOENT; return autofs4_wait_release(sbi, token, status); } @@ -412,10 +412,10 @@ static int autofs_dev_ioctl_setpipefd(struct file *fp, int pipefd; int err = 0; - if (param->arg1 == -1) + if (param->setpipefd.pipefd == -1) return -EINVAL; - pipefd = param->arg1; + pipefd = param->setpipefd.pipefd; mutex_lock(&sbi->wq_mutex); if (!sbi->catatonic) { @@ -457,8 +457,8 @@ static int autofs_dev_ioctl_timeout(struct file *fp, { unsigned long timeout; - timeout = param->arg1; - param->arg1 = sbi->exp_timeout / HZ; + timeout = param->timeout.timeout; + param->timeout.timeout = sbi->exp_timeout / HZ; sbi->exp_timeout = timeout * HZ; return 0; } @@ -489,7 +489,7 @@ static int autofs_dev_ioctl_requester(struct file *fp, path = param->path; devid = sbi->sb->s_dev; - param->arg1 = param->arg2 = -1; + param->requester.uid = param->requester.gid = -1; /* Get nameidata of the parent directory */ err = path_lookup(path, LOOKUP_PARENT, &nd); @@ -505,8 +505,8 @@ static int autofs_dev_ioctl_requester(struct file *fp, err = 0; autofs4_expire_wait(nd.path.dentry); spin_lock(&sbi->fs_lock); - param->arg1 = ino->uid; - param->arg2 = ino->gid; + param->requester.uid = ino->uid; + param->requester.gid = ino->gid; spin_unlock(&sbi->fs_lock); } @@ -529,7 +529,7 @@ static int autofs_dev_ioctl_expire(struct file *fp, int err = -EAGAIN; int how; - how = param->arg1; + how = param->expire.how; mnt = fp->f_path.mnt; if (sbi->type & AUTOFS_TYPE_TRIGGER) @@ -565,9 +565,9 @@ static int autofs_dev_ioctl_askumount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { - param->arg1 = 0; + param->askumount.may_umount = 0; if (may_umount(fp->f_path.mnt)) - param->arg1 = 1; + param->askumount.may_umount = 1; return 0; } @@ -600,6 +600,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct nameidata nd; const char *path; unsigned int type; + unsigned int devid, magic; int err = -ENOENT; if (param->size <= sizeof(*param)) { @@ -608,10 +609,10 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, } path = param->path; - type = param->arg1; + type = param->ismountpoint.in.type; - param->arg1 = 0; - param->arg2 = 0; + param->ismountpoint.out.devid = devid = 0; + param->ismountpoint.out.magic = magic = 0; if (!fp || param->ioctlfd == -1) { if (type == AUTOFS_TYPE_ANY) { @@ -622,7 +623,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, goto out; sb = nd.path.dentry->d_sb; - param->arg1 = new_encode_dev(sb->s_dev); + devid = new_encode_dev(sb->s_dev); } else { struct autofs_info *ino; @@ -635,14 +636,14 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, goto out_release; ino = autofs4_dentry_ino(nd.path.dentry); - param->arg1 = autofs4_get_dev(ino->sbi); + devid = autofs4_get_dev(ino->sbi); } err = 0; if (nd.path.dentry->d_inode && nd.path.mnt->mnt_root == nd.path.dentry) { err = 1; - param->arg2 = nd.path.dentry->d_inode->i_sb->s_magic; + magic = nd.path.dentry->d_inode->i_sb->s_magic; } } else { dev_t devid = new_encode_dev(sbi->sb->s_dev); @@ -655,18 +656,21 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, if (err) goto out_release; - param->arg1 = autofs4_get_dev(sbi); + devid = autofs4_get_dev(sbi); err = have_submounts(nd.path.dentry); if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { if (follow_down(&nd.path.mnt, &nd.path.dentry)) { struct inode *inode = nd.path.dentry->d_inode; - param->arg2 = inode->i_sb->s_magic; + magic = inode->i_sb->s_magic; } } } + param->ismountpoint.out.devid = devid; + param->ismountpoint.out.magic = magic; + out_release: path_put(&nd.path); out: diff --git a/include/linux/auto_dev-ioctl.h b/include/linux/auto_dev-ioctl.h index f4d05ccd731f..91a773993a5c 100644 --- a/include/linux/auto_dev-ioctl.h +++ b/include/linux/auto_dev-ioctl.h @@ -10,6 +10,7 @@ #ifndef _LINUX_AUTO_DEV_IOCTL_H #define _LINUX_AUTO_DEV_IOCTL_H +#include #include #define AUTOFS_DEVICE_NAME "autofs" @@ -25,6 +26,60 @@ * An ioctl interface for autofs mount point control. */ +struct args_protover { + __u32 version; +}; + +struct args_protosubver { + __u32 sub_version; +}; + +struct args_openmount { + __u32 devid; +}; + +struct args_ready { + __u32 token; +}; + +struct args_fail { + __u32 token; + __s32 status; +}; + +struct args_setpipefd { + __s32 pipefd; +}; + +struct args_timeout { + __u64 timeout; +}; + +struct args_requester { + __u32 uid; + __u32 gid; +}; + +struct args_expire { + __u32 how; +}; + +struct args_askumount { + __u32 may_umount; +}; + +struct args_ismountpoint { + union { + struct args_in { + __u32 type; + } in; + struct args_out { + __u32 devid; + __u32 magic; + } out; + }; +}; + /* * All the ioctls use this structure. * When sending a path size must account for the total length @@ -39,20 +94,32 @@ struct autofs_dev_ioctl { * including this struct */ __s32 ioctlfd; /* automount command fd */ - __u32 arg1; /* Command parameters */ - __u32 arg2; + /* Command parameters */ + + union { + struct args_protover protover; + struct args_protosubver protosubver; + struct args_openmount openmount; + struct args_ready ready; + struct args_fail fail; + struct args_setpipefd setpipefd; + struct args_timeout timeout; + struct args_requester requester; + struct args_expire expire; + struct args_askumount askumount; + struct args_ismountpoint ismountpoint; + }; char path[0]; }; static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) { + memset(in, 0, sizeof(struct autofs_dev_ioctl)); in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; in->size = sizeof(struct autofs_dev_ioctl); in->ioctlfd = -1; - in->arg1 = 0; - in->arg2 = 0; return; } -- cgit v1.2.3-71-gd317 From a92daf6ba1f9ace8584edc8eb557a77aa7c2c71d Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Tue, 6 Jan 2009 14:42:08 -0800 Subject: autofs4: make autofs type usage explicit - the type assigned at mount when no type is given is changed from 0 to AUTOFS_TYPE_INDIRECT. This was done because 0 and AUTOFS_TYPE_INDIRECT were being treated implicitly as the same type. - previously, an offset mount had it's type set to AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET but the mount control re-implementation needs to be able distinguish all three types. So this was changed to make the type setting explicit. - a type AUTOFS_TYPE_ANY was added for use by the re-implementation when checking if a given path is a mountpoint. It's not really a type as we use this to ask if a given path is a mountpoint in the autofs_dev_ioctl_ismountpoint() function. - functions to set and test the autofs mount types have been added to improve readability and make the type usage explicit. - the mount type is used from user space for the mount control re-implementtion so, for consistency, all the definitions have been moved to the user space include file include/linux/auto_fs4.h. Signed-off-by: Ian Kent Signed-off-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/autofs_i.h | 2 -- fs/autofs4/dev-ioctl.c | 4 ++-- fs/autofs4/expire.c | 4 ++-- fs/autofs4/inode.c | 14 +++++------ fs/autofs4/waitq.c | 8 +++---- include/linux/auto_fs4.h | 62 ++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 73 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index e0f16da00e54..a76803108d06 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -25,8 +25,6 @@ #define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) #define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) -#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) - #include #include #include diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index 0566ff8db4cd..aa3f47293bb8 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -532,7 +532,7 @@ static int autofs_dev_ioctl_expire(struct file *fp, how = param->expire.how; mnt = fp->f_path.mnt; - if (sbi->type & AUTOFS_TYPE_TRIGGER) + if (autofs_type_trigger(sbi->type)) dentry = autofs4_expire_direct(sbi->sb, mnt, sbi, how); else dentry = autofs4_expire_indirect(sbi->sb, mnt, sbi, how); @@ -615,7 +615,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, param->ismountpoint.out.magic = magic = 0; if (!fp || param->ioctlfd == -1) { - if (type == AUTOFS_TYPE_ANY) { + if (autofs_type_any(type)) { struct super_block *sb; err = path_lookup(path, LOOKUP_FOLLOW, &nd); diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 4b6fb3f628c0..e3bd50776f9e 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); /* This is an autofs submount, we can't expire it */ - if (sbi->type == AUTOFS_TYPE_INDIRECT) + if (autofs_type_indirect(sbi->type)) goto done; /* @@ -490,7 +490,7 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, if (arg && get_user(do_now, arg)) return -EFAULT; - if (sbi->type & AUTOFS_TYPE_TRIGGER) + if (autofs_type_trigger(sbi->type)) dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); else dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index cfc23e53b6f4..716e12b627b2 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -197,9 +197,9 @@ static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) seq_printf(m, ",minproto=%d", sbi->min_proto); seq_printf(m, ",maxproto=%d", sbi->max_proto); - if (sbi->type & AUTOFS_TYPE_OFFSET) + if (autofs_type_offset(sbi->type)) seq_printf(m, ",offset"); - else if (sbi->type & AUTOFS_TYPE_DIRECT) + else if (autofs_type_direct(sbi->type)) seq_printf(m, ",direct"); else seq_printf(m, ",indirect"); @@ -284,13 +284,13 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, *maxproto = option; break; case Opt_indirect: - *type = AUTOFS_TYPE_INDIRECT; + set_autofs_type_indirect(type); break; case Opt_direct: - *type = AUTOFS_TYPE_DIRECT; + set_autofs_type_direct(type); break; case Opt_offset: - *type = AUTOFS_TYPE_OFFSET; + set_autofs_type_offset(type); break; default: return 1; @@ -338,7 +338,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) sbi->sb = s; sbi->version = 0; sbi->sub_version = 0; - sbi->type = AUTOFS_TYPE_INDIRECT; + set_autofs_type_indirect(&sbi->type); sbi->min_proto = 0; sbi->max_proto = 0; mutex_init(&sbi->wq_mutex); @@ -380,7 +380,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) } root_inode->i_fop = &autofs4_root_operations; - root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? + root_inode->i_op = autofs_type_trigger(sbi->type) ? &autofs4_direct_root_inode_operations : &autofs4_indirect_root_inode_operations; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index e02cc8ae5eb3..eeb246845909 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, * is very similar for indirect mounts except only dentrys * in the root of the autofs file system may be negative. */ - if (sbi->type & AUTOFS_TYPE_TRIGGER) + if (autofs_type_trigger(sbi->type)) return -ENOENT; else if (!IS_ROOT(dentry->d_parent)) return -ENOENT; @@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, return -ENOMEM; /* If this is a direct mount request create a dummy name */ - if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) + if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) qstr.len = sprintf(name, "%p", dentry); else { qstr.len = autofs4_getpath(sbi, dentry, &name); @@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, type = autofs_ptype_expire_multi; } else { if (notify == NFY_MOUNT) - type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? + type = autofs_type_trigger(sbi->type) ? autofs_ptype_missing_direct : autofs_ptype_missing_indirect; else - type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? + type = autofs_type_trigger(sbi->type) ? autofs_ptype_expire_direct : autofs_ptype_expire_indirect; } diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 2253716d4b92..55fa478bd639 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -29,10 +29,64 @@ #define AUTOFS_EXP_IMMEDIATE 1 #define AUTOFS_EXP_LEAVES 2 -#define AUTOFS_TYPE_ANY 0x0000 -#define AUTOFS_TYPE_INDIRECT 0x0001 -#define AUTOFS_TYPE_DIRECT 0x0002 -#define AUTOFS_TYPE_OFFSET 0x0004 +#define AUTOFS_TYPE_ANY 0U +#define AUTOFS_TYPE_INDIRECT 1U +#define AUTOFS_TYPE_DIRECT 2U +#define AUTOFS_TYPE_OFFSET 4U + +static inline void set_autofs_type_indirect(unsigned int *type) +{ + *type = AUTOFS_TYPE_INDIRECT; + return; +} + +static inline unsigned int autofs_type_indirect(unsigned int type) +{ + return (type == AUTOFS_TYPE_INDIRECT); +} + +static inline void set_autofs_type_direct(unsigned int *type) +{ + *type = AUTOFS_TYPE_DIRECT; + return; +} + +static inline unsigned int autofs_type_direct(unsigned int type) +{ + return (type == AUTOFS_TYPE_DIRECT); +} + +static inline void set_autofs_type_offset(unsigned int *type) +{ + *type = AUTOFS_TYPE_OFFSET; + return; +} + +static inline unsigned int autofs_type_offset(unsigned int type) +{ + return (type == AUTOFS_TYPE_OFFSET); +} + +static inline unsigned int autofs_type_trigger(unsigned int type) +{ + return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); +} + +/* + * This isn't really a type as we use it to say "no type set" to + * indicate we want to search for "any" mount in the + * autofs_dev_ioctl_ismountpoint() device ioctl function. + */ +static inline void set_autofs_type_any(unsigned int *type) +{ + *type = AUTOFS_TYPE_ANY; + return; +} + +static inline unsigned int autofs_type_any(unsigned int type) +{ + return (type == AUTOFS_TYPE_ANY); +} /* Daemon notification packet types */ enum autofs_notify { -- cgit v1.2.3-71-gd317 From cabb3fc4bd1628c37c37e054960eb3e4bf30dc26 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 6 Jan 2009 14:42:26 -0800 Subject: twl4030-gpio: cleanup debounce Provide a static debounce configuration mechanism for twl4030 GPIOs, replacing the previous dynamic one. The single user of that mechanism was for MMC card detect debouncing. Boards can provide a bitmask saying which GPIOs to debounce (30 msec). It's always enabled for pins with the MMC card-detect/VMMCx link active, so most boards won't need to set the debounce mask. This is a net code shrink, including runtime footprint. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/twl4030-gpio.c | 54 +++++++++++++++++++-------------------------- include/linux/i2c/twl4030.h | 9 +++----- 2 files changed, 26 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c index 37d3eec8730a..afad14792141 100644 --- a/drivers/gpio/twl4030-gpio.c +++ b/drivers/gpio/twl4030-gpio.c @@ -202,37 +202,6 @@ static int twl4030_get_gpio_datain(int gpio) return ret; } -/* - * Configure debounce timing value for a GPIO pin on TWL4030 - */ -int twl4030_set_gpio_debounce(int gpio, int enable) -{ - u8 d_bnk = gpio >> 3; - u8 d_msk = BIT(gpio & 0x7); - u8 reg = 0; - u8 base = 0; - int ret = 0; - - if (unlikely((gpio >= TWL4030_GPIO_MAX) - || !(gpio_usage_count & BIT(gpio)))) - return -EPERM; - - base = REG_GPIO_DEBEN1 + d_bnk; - mutex_lock(&gpio_lock); - ret = gpio_twl4030_read(base); - if (ret >= 0) { - if (enable) - reg = ret | d_msk; - else - reg = ret & ~d_msk; - - ret = gpio_twl4030_write(base, reg); - } - mutex_unlock(&gpio_lock); - return ret; -} -EXPORT_SYMBOL(twl4030_set_gpio_debounce); - /*----------------------------------------------------------------------*/ static int twl_request(struct gpio_chip *chip, unsigned offset) @@ -405,6 +374,23 @@ static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) REG_GPIOPUPDCTR1, 5); } +static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) +{ + u8 message[4]; + + /* 30 msec of debouncing is always used for MMC card detect, + * and is optional for everything else. + */ + message[1] = (debounce & 0xff) | (mmc_cd & 0x03); + debounce >>= 8; + message[2] = (debounce & 0xff); + debounce >>= 8; + message[3] = (debounce & 0x03); + + return twl4030_i2c_write(TWL4030_MODULE_GPIO, message, + REG_GPIO_DEBEN1, 3); +} + static int gpio_twl4030_remove(struct platform_device *pdev); static int __devinit gpio_twl4030_probe(struct platform_device *pdev) @@ -439,6 +425,12 @@ no_irqs: pdata->pullups, pdata->pulldowns, ret); + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, + ret); + twl_gpiochip.base = pdata->gpio_base; twl_gpiochip.ngpio = TWL4030_GPIO_MAX; twl_gpiochip.dev = &pdev->dev; diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index a8f84c01f82e..8137f660a5cc 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -234,6 +234,9 @@ struct twl4030_gpio_platform_data { /* gpio-n should control VMMC(n+1) if BIT(n) in mmc_cd is set */ u8 mmc_cd; + /* if BIT(N) is set, or VMMC(n+1) is linked, debounce GPIO-N */ + u32 debounce; + /* For gpio-N, bit (1 << N) in "pullups" is set if that pullup * should be enabled. Else, if that bit is set in "pulldowns", * that pulldown is enabled. Don't waste power by letting any @@ -307,12 +310,6 @@ int twl4030_sih_setup(int module); #define TWL4030_VAUX3_DEV_GRP 0x1F #define TWL4030_VAUX3_DEDICATED 0x22 -/* - * Exported TWL4030 GPIO APIs - * - * WARNING -- use standard GPIO and IRQ calls instead; these will vanish. - */ -int twl4030_set_gpio_debounce(int gpio, int enable); #if defined(CONFIG_TWL4030_BCI_BATTERY) || \ defined(CONFIG_TWL4030_BCI_BATTERY_MODULE) -- cgit v1.2.3-71-gd317 From d3635abfee0c55ad9dcd6b6172a0c6a5455900c9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 6 Jan 2009 14:42:41 -0800 Subject: rapidio: remove excess kernel-doc notation Remove excess kernel-doc notation from rio header and driver: Warning(include/linux/rio_drv.h:399): Excess function parameter or struct member 'buffer' description in 'rio_get_inb_message' Signed-off-by: Randy Dunlap Cc: Matt Porter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-driver.c | 1 - include/linux/rio_drv.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c index 956d3e79f6aa..addb87cf44d9 100644 --- a/drivers/rapidio/rio-driver.c +++ b/drivers/rapidio/rio-driver.c @@ -79,7 +79,6 @@ void rio_dev_put(struct rio_dev *rdev) /** * rio_device_probe - Tell if a RIO device structure has a matching RIO device id structure - * @id: the RIO device id structure to match against * @dev: the RIO device structure to match against * * return 0 and set rio_dev->driver when drv claims rio_dev, else error diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h index 32c0547ffafc..c93a58a40033 100644 --- a/include/linux/rio_drv.h +++ b/include/linux/rio_drv.h @@ -391,7 +391,6 @@ static inline int rio_add_inb_buffer(struct rio_mport *mport, int mbox, * rio_get_inb_message - Get A RIO message from an inbound mailbox queue * @mport: Master port containing the inbound mailbox * @mbox: The inbound mailbox number - * @buffer: Pointer to the message buffer * * Get a RIO message from an inbound mailbox queue. Returns 0 on success. */ -- cgit v1.2.3-71-gd317 From 8cd3ac3aca3f2afe8570708066d64d893da468e8 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 6 Jan 2009 14:42:48 -0800 Subject: fs/exec.c: make do_coredump() void No one cares do_coredump()'s return value, and also it seems that it is also not necessary. So make it void. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: WANG Cong Cc: Alexander Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 4 ++-- include/linux/binfmts.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index 6b09d6fa4f7e..71a6efe5d8bd 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1686,7 +1686,7 @@ int get_dumpable(struct mm_struct *mm) return (ret >= 2) ? 2 : ret; } -int do_coredump(long signr, int exit_code, struct pt_regs * regs) +void do_coredump(long signr, int exit_code, struct pt_regs *regs) { struct core_state core_state; char corename[CORENAME_MAX_SIZE + 1]; @@ -1842,5 +1842,5 @@ fail_unlock: put_cred(cred); coredump_finish(mm); fail: - return retval; + return; } diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 0d0150b4901e..77b4a9e46004 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -107,7 +107,7 @@ extern int setup_arg_pages(struct linux_binprm * bprm, extern int bprm_mm_init(struct linux_binprm *bprm); extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm); extern void install_exec_creds(struct linux_binprm *bprm); -extern int do_coredump(long signr, int exit_code, struct pt_regs * regs); +extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); extern int set_binfmt(struct linux_binfmt *new); extern void free_bprm(struct linux_binprm *); -- cgit v1.2.3-71-gd317 From 991c0e6d1ae3df59f0ddfe05edecec8319e35a1b Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 6 Jan 2009 14:56:21 -0800 Subject: byteorder: only use linux/swab.h The first step to make swab.h a regular header that will include an asm/swab.h with arch overrides. Avoid the gratuitous differences introduced in the new linux/swab.h by naming the ___constant_swabXX bits and __fswabXX bits exactly as found in the old implementation in byteorder/swab[b].h Use this new swab.h in byteorder/[big|little]_endian.h and remove the two old swab headers. Although the inclusion of asm/byteorder.h looks strange in linux/swab.h, this will allow each arch to move the actual arch overrides for the swab bits in an asm file and then the includes can be cleaned up without requiring a flag day for all arches at once. Keep providing __fswabXX in case some userspace was using them directly, but the revised __swabXX should be used instead in any new code and will always do constant folding not dependent on the optimization level, which means the __constant versions can be phased out in-kernel. Arches that use the old-style arch macros will lose their optimized versions until they move to the new style, but at least they will still compile. Many arches have already moved and the patches to move the remaining arches are trivial. Signed-off-by: Harvey Harrison Signed-off-by: Linus Torvalds --- include/linux/byteorder/Kbuild | 2 - include/linux/byteorder/big_endian.h | 3 +- include/linux/byteorder/little_endian.h | 3 +- include/linux/byteorder/swab.h | 222 -------------------------------- include/linux/byteorder/swabb.h | 135 ------------------- include/linux/swab.h | 50 +++---- 6 files changed, 27 insertions(+), 388 deletions(-) delete mode 100644 include/linux/byteorder/swab.h delete mode 100644 include/linux/byteorder/swabb.h (limited to 'include/linux') diff --git a/include/linux/byteorder/Kbuild b/include/linux/byteorder/Kbuild index fbaa7f9cee32..38437225b092 100644 --- a/include/linux/byteorder/Kbuild +++ b/include/linux/byteorder/Kbuild @@ -1,4 +1,2 @@ unifdef-y += big_endian.h unifdef-y += little_endian.h -unifdef-y += swab.h -unifdef-y += swabb.h diff --git a/include/linux/byteorder/big_endian.h b/include/linux/byteorder/big_endian.h index 1cba3f3efe5f..3c80fd7e8b56 100644 --- a/include/linux/byteorder/big_endian.h +++ b/include/linux/byteorder/big_endian.h @@ -9,8 +9,7 @@ #endif #include -#include -#include +#include #define __constant_htonl(x) ((__force __be32)(__u32)(x)) #define __constant_ntohl(x) ((__force __u32)(__be32)(x)) diff --git a/include/linux/byteorder/little_endian.h b/include/linux/byteorder/little_endian.h index cedc1b5a289c..83195fb82962 100644 --- a/include/linux/byteorder/little_endian.h +++ b/include/linux/byteorder/little_endian.h @@ -9,8 +9,7 @@ #endif #include -#include -#include +#include #define __constant_htonl(x) ((__force __be32)___constant_swab32((x))) #define __constant_ntohl(x) ___constant_swab32((__force __be32)(x)) diff --git a/include/linux/byteorder/swab.h b/include/linux/byteorder/swab.h deleted file mode 100644 index 142134ff1645..000000000000 --- a/include/linux/byteorder/swab.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef _LINUX_BYTEORDER_SWAB_H -#define _LINUX_BYTEORDER_SWAB_H - -/* - * linux/byteorder/swab.h - * Byte-swapping, independently from CPU endianness - * swabXX[ps]?(foo) - * - * Francois-Rene Rideau 19971205 - * separated swab functions from cpu_to_XX, - * to clean up support for bizarre-endian architectures. - * - * Trent Piepho 2007114 - * make constant-folding work, provide C versions that - * gcc can optimize better, explain different versions - * - * See asm-i386/byteorder.h and suches for examples of how to provide - * architecture-dependent optimized versions - * - */ - -#include - -/* Functions/macros defined, there are a lot: - * - * ___swabXX - * Generic C versions of the swab functions. - * - * ___constant_swabXX - * C versions that gcc can fold into a compile-time constant when - * the argument is a compile-time constant. - * - * __arch__swabXX[sp]? - * Architecture optimized versions of all the swab functions - * (including the s and p versions). These can be defined in - * asm-arch/byteorder.h. Any which are not, are defined here. - * __arch__swabXXs() is defined in terms of __arch__swabXXp(), which - * is defined in terms of __arch__swabXX(), which is in turn defined - * in terms of ___swabXX(x). - * These must be macros. They may be unsafe for arguments with - * side-effects. - * - * __fswabXX - * Inline function versions of the __arch__ macros. These _are_ safe - * if the arguments have side-effects. Note there are no s and p - * versions of these. - * - * __swabXX[sb] - * There are the ones you should actually use. The __swabXX versions - * will be a constant given a constant argument and use the arch - * specific code (if any) for non-constant arguments. The s and p - * versions always use the arch specific code (constant folding - * doesn't apply). They are safe to use with arguments with - * side-effects. - * - * swabXX[sb] - * Nicknames for __swabXX[sb] to use in the kernel. - */ - -/* casts are necessary for constants, because we never know how for sure - * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. - */ - -static __inline__ __attribute_const__ __u16 ___swab16(__u16 x) -{ - return x<<8 | x>>8; -} -static __inline__ __attribute_const__ __u32 ___swab32(__u32 x) -{ - return x<<24 | x>>24 | - (x & (__u32)0x0000ff00UL)<<8 | - (x & (__u32)0x00ff0000UL)>>8; -} -static __inline__ __attribute_const__ __u64 ___swab64(__u64 x) -{ - return x<<56 | x>>56 | - (x & (__u64)0x000000000000ff00ULL)<<40 | - (x & (__u64)0x0000000000ff0000ULL)<<24 | - (x & (__u64)0x00000000ff000000ULL)<< 8 | - (x & (__u64)0x000000ff00000000ULL)>> 8 | - (x & (__u64)0x0000ff0000000000ULL)>>24 | - (x & (__u64)0x00ff000000000000ULL)>>40; -} - -#define ___constant_swab16(x) \ - ((__u16)( \ - (((__u16)(x) & (__u16)0x00ffU) << 8) | \ - (((__u16)(x) & (__u16)0xff00U) >> 8) )) -#define ___constant_swab32(x) \ - ((__u32)( \ - (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ - (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ - (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ - (((__u32)(x) & (__u32)0xff000000UL) >> 24) )) -#define ___constant_swab64(x) \ - ((__u64)( \ - (__u64)(((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ - (__u64)(((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ - (__u64)(((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ - (__u64)(((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \ - (__u64)(((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \ - (__u64)(((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \ - (__u64)(((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ - (__u64)(((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56) )) - -/* - * provide defaults when no architecture-specific optimization is detected - */ -#ifndef __arch__swab16 -# define __arch__swab16(x) ___swab16(x) -#endif -#ifndef __arch__swab32 -# define __arch__swab32(x) ___swab32(x) -#endif -#ifndef __arch__swab64 -# define __arch__swab64(x) ___swab64(x) -#endif - -#ifndef __arch__swab16p -# define __arch__swab16p(x) __arch__swab16(*(x)) -#endif -#ifndef __arch__swab32p -# define __arch__swab32p(x) __arch__swab32(*(x)) -#endif -#ifndef __arch__swab64p -# define __arch__swab64p(x) __arch__swab64(*(x)) -#endif - -#ifndef __arch__swab16s -# define __arch__swab16s(x) ((void)(*(x) = __arch__swab16p(x))) -#endif -#ifndef __arch__swab32s -# define __arch__swab32s(x) ((void)(*(x) = __arch__swab32p(x))) -#endif -#ifndef __arch__swab64s -# define __arch__swab64s(x) ((void)(*(x) = __arch__swab64p(x))) -#endif - - -/* - * Allow constant folding - */ -#if defined(__GNUC__) && defined(__OPTIMIZE__) -# define __swab16(x) \ -(__builtin_constant_p((__u16)(x)) ? \ - ___constant_swab16((x)) : \ - __fswab16((x))) -# define __swab32(x) \ -(__builtin_constant_p((__u32)(x)) ? \ - ___constant_swab32((x)) : \ - __fswab32((x))) -# define __swab64(x) \ -(__builtin_constant_p((__u64)(x)) ? \ - ___constant_swab64((x)) : \ - __fswab64((x))) -#else -# define __swab16(x) __fswab16(x) -# define __swab32(x) __fswab32(x) -# define __swab64(x) __fswab64(x) -#endif /* OPTIMIZE */ - - -static __inline__ __attribute_const__ __u16 __fswab16(__u16 x) -{ - return __arch__swab16(x); -} -static __inline__ __u16 __swab16p(const __u16 *x) -{ - return __arch__swab16p(x); -} -static __inline__ void __swab16s(__u16 *addr) -{ - __arch__swab16s(addr); -} - -static __inline__ __attribute_const__ __u32 __fswab32(__u32 x) -{ - return __arch__swab32(x); -} -static __inline__ __u32 __swab32p(const __u32 *x) -{ - return __arch__swab32p(x); -} -static __inline__ void __swab32s(__u32 *addr) -{ - __arch__swab32s(addr); -} - -#ifdef __BYTEORDER_HAS_U64__ -static __inline__ __attribute_const__ __u64 __fswab64(__u64 x) -{ -# ifdef __SWAB_64_THRU_32__ - __u32 h = x >> 32; - __u32 l = x & ((1ULL<<32)-1); - return (((__u64)__swab32(l)) << 32) | ((__u64)(__swab32(h))); -# else - return __arch__swab64(x); -# endif -} -static __inline__ __u64 __swab64p(const __u64 *x) -{ - return __arch__swab64p(x); -} -static __inline__ void __swab64s(__u64 *addr) -{ - __arch__swab64s(addr); -} -#endif /* __BYTEORDER_HAS_U64__ */ - -#if defined(__KERNEL__) -#define swab16 __swab16 -#define swab32 __swab32 -#define swab64 __swab64 -#define swab16p __swab16p -#define swab32p __swab32p -#define swab64p __swab64p -#define swab16s __swab16s -#define swab32s __swab32s -#define swab64s __swab64s -#endif - -#endif /* _LINUX_BYTEORDER_SWAB_H */ diff --git a/include/linux/byteorder/swabb.h b/include/linux/byteorder/swabb.h deleted file mode 100644 index 8c780c7d779e..000000000000 --- a/include/linux/byteorder/swabb.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef _LINUX_BYTEORDER_SWABB_H -#define _LINUX_BYTEORDER_SWABB_H - -/* - * linux/byteorder/swabb.h - * SWAp Bytes Bizarrely - * swaHHXX[ps]?(foo) - * - * Support for obNUXIous pdp-endian and other bizarre architectures. - * Will Linux ever run on such ancient beasts? if not, this file - * will be but a programming pearl. Still, it's a reminder that we - * shouldn't be making too many assumptions when trying to be portable. - * - */ - -/* - * Meaning of the names I chose (vaxlinux people feel free to correct them): - * swahw32 swap 16-bit half-words in a 32-bit word - * swahb32 swap 8-bit halves of each 16-bit half-word in a 32-bit word - * - * No 64-bit support yet. I don't know NUXI conventions for long longs. - * I guarantee it will be a mess when it's there, though :-> - * It will be even worse if there are conflicting 64-bit conventions. - * Hopefully, no one ever used 64-bit objects on NUXI machines. - * - */ - -#include - -#define ___swahw32(x) \ -({ \ - __u32 __x = (x); \ - ((__u32)( \ - (((__u32)(__x) & (__u32)0x0000ffffUL) << 16) | \ - (((__u32)(__x) & (__u32)0xffff0000UL) >> 16) )); \ -}) -#define ___swahb32(x) \ -({ \ - __u32 __x = (x); \ - ((__u32)( \ - (((__u32)(__x) & (__u32)0x00ff00ffUL) << 8) | \ - (((__u32)(__x) & (__u32)0xff00ff00UL) >> 8) )); \ -}) - -#define ___constant_swahw32(x) \ - ((__u32)( \ - (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \ - (((__u32)(x) & (__u32)0xffff0000UL) >> 16) )) -#define ___constant_swahb32(x) \ - ((__u32)( \ - (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \ - (((__u32)(x) & (__u32)0xff00ff00UL) >> 8) )) - -/* - * provide defaults when no architecture-specific optimization is detected - */ -#ifndef __arch__swahw32 -# define __arch__swahw32(x) ___swahw32(x) -#endif -#ifndef __arch__swahb32 -# define __arch__swahb32(x) ___swahb32(x) -#endif - -#ifndef __arch__swahw32p -# define __arch__swahw32p(x) __swahw32(*(x)) -#endif -#ifndef __arch__swahb32p -# define __arch__swahb32p(x) __swahb32(*(x)) -#endif - -#ifndef __arch__swahw32s -# define __arch__swahw32s(x) do { *(x) = __swahw32p((x)); } while (0) -#endif -#ifndef __arch__swahb32s -# define __arch__swahb32s(x) do { *(x) = __swahb32p((x)); } while (0) -#endif - - -/* - * Allow constant folding - */ -#define __swahw32(x) \ -(__builtin_constant_p((__u32)(x)) ? \ - ___swahw32((x)) : \ - __fswahw32((x))) -#define __swahb32(x) \ -(__builtin_constant_p((__u32)(x)) ? \ - ___swahb32((x)) : \ - __fswahb32((x))) - - -static inline __u32 __fswahw32(__u32 x) -{ - return __arch__swahw32(x); -} - -static inline __u32 __swahw32p(__u32 *x) -{ - return __arch__swahw32p(x); -} - -static inline void __swahw32s(__u32 *addr) -{ - __arch__swahw32s(addr); -} - -static inline __u32 __fswahb32(__u32 x) -{ - return __arch__swahb32(x); -} - -static inline __u32 __swahb32p(__u32 *x) -{ - return __arch__swahb32p(x); -} - -static inline void __swahb32s(__u32 *addr) -{ - __arch__swahb32s(addr); -} - -#ifdef __BYTEORDER_HAS_U64__ -/* - * Not supported yet - */ -#endif /* __BYTEORDER_HAS_U64__ */ - -#define swahw32 __swahw32 -#define swahb32 __swahb32 -#define swahw32p __swahw32p -#define swahb32p __swahb32p -#define swahw32s __swahw32s -#define swahb32s __swahb32s - -#endif /* _LINUX_BYTEORDER_SWABB_H */ diff --git a/include/linux/swab.h b/include/linux/swab.h index bbed279f3b32..9a2d33e0a98a 100644 --- a/include/linux/swab.h +++ b/include/linux/swab.h @@ -9,17 +9,17 @@ * casts are necessary for constants, because we never know how for sure * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. */ -#define __const_swab16(x) ((__u16)( \ +#define ___constant_swab16(x) ((__u16)( \ (((__u16)(x) & (__u16)0x00ffU) << 8) | \ (((__u16)(x) & (__u16)0xff00U) >> 8))) -#define __const_swab32(x) ((__u32)( \ +#define ___constant_swab32(x) ((__u32)( \ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ (((__u32)(x) & (__u32)0xff000000UL) >> 24))) -#define __const_swab64(x) ((__u64)( \ +#define ___constant_swab64(x) ((__u64)( \ (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ @@ -29,11 +29,11 @@ (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) -#define __const_swahw32(x) ((__u32)( \ +#define ___constant_swahw32(x) ((__u32)( \ (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \ (((__u32)(x) & (__u32)0xffff0000UL) >> 16))) -#define __const_swahb32(x) ((__u32)( \ +#define ___constant_swahb32(x) ((__u32)( \ (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \ (((__u32)(x) & (__u32)0xff00ff00UL) >> 8))) @@ -43,25 +43,25 @@ * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 */ -static inline __attribute_const__ __u16 ___swab16(__u16 val) +static inline __attribute_const__ __u16 __fswab16(__u16 val) { #ifdef __arch_swab16 return __arch_swab16(val); #else - return __const_swab16(val); + return ___constant_swab16(val); #endif } -static inline __attribute_const__ __u32 ___swab32(__u32 val) +static inline __attribute_const__ __u32 __fswab32(__u32 val) { #ifdef __arch_swab32 return __arch_swab32(val); #else - return __const_swab32(val); + return ___constant_swab32(val); #endif } -static inline __attribute_const__ __u64 ___swab64(__u64 val) +static inline __attribute_const__ __u64 __fswab64(__u64 val) { #ifdef __arch_swab64 return __arch_swab64(val); @@ -70,25 +70,25 @@ static inline __attribute_const__ __u64 ___swab64(__u64 val) __u32 l = val & ((1ULL << 32) - 1); return (((__u64)___swab32(l)) << 32) | ((__u64)(___swab32(h))); #else - return __const_swab64(val); + return ___constant_swab64(val); #endif } -static inline __attribute_const__ __u32 ___swahw32(__u32 val) +static inline __attribute_const__ __u32 __fswahw32(__u32 val) { #ifdef __arch_swahw32 return __arch_swahw32(val); #else - return __const_swahw32(val); + return ___constant_swahw32(val); #endif } -static inline __attribute_const__ __u32 ___swahb32(__u32 val) +static inline __attribute_const__ __u32 __fswahb32(__u32 val) { #ifdef __arch_swahb32 return __arch_swahb32(val); #else - return __const_swahb32(val); + return ___constant_swahb32(val); #endif } @@ -98,8 +98,8 @@ static inline __attribute_const__ __u32 ___swahb32(__u32 val) */ #define __swab16(x) \ (__builtin_constant_p((__u16)(x)) ? \ - __const_swab16((x)) : \ - ___swab16((x))) + ___constant_swab16(x) : \ + __fswab16(x)) /** * __swab32 - return a byteswapped 32-bit value @@ -107,8 +107,8 @@ static inline __attribute_const__ __u32 ___swahb32(__u32 val) */ #define __swab32(x) \ (__builtin_constant_p((__u32)(x)) ? \ - __const_swab32((x)) : \ - ___swab32((x))) + ___constant_swab32(x) : \ + __fswab32(x)) /** * __swab64 - return a byteswapped 64-bit value @@ -116,8 +116,8 @@ static inline __attribute_const__ __u32 ___swahb32(__u32 val) */ #define __swab64(x) \ (__builtin_constant_p((__u64)(x)) ? \ - __const_swab64((x)) : \ - ___swab64((x))) + ___constant_swab64(x) : \ + __fswab64(x)) /** * __swahw32 - return a word-swapped 32-bit value @@ -127,8 +127,8 @@ static inline __attribute_const__ __u32 ___swahb32(__u32 val) */ #define __swahw32(x) \ (__builtin_constant_p((__u32)(x)) ? \ - __const_swahw32((x)) : \ - ___swahw32((x))) + ___constant_swahw32(x) : \ + __fswahw32(x)) /** * __swahb32 - return a high and low byte-swapped 32-bit value @@ -138,8 +138,8 @@ static inline __attribute_const__ __u32 ___swahb32(__u32 val) */ #define __swahb32(x) \ (__builtin_constant_p((__u32)(x)) ? \ - __const_swahb32((x)) : \ - ___swahb32((x))) + ___constant_swahb32(x) : \ + __fswahb32(x)) /** * __swab16p - return a byteswapped 16-bit value from a pointer -- cgit v1.2.3-71-gd317 From 637b180c23313f2964e0ef20f1ee375203866968 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 6 Jan 2009 13:30:58 -0800 Subject: byteorder: remove the now unused byteorder.h This implementation caused problems in userspace which can, and does define _both_ __LITTLE_ENDIAN and __BIG_ENDIAN. Signed-off-by: Harvey Harrison Signed-off-by: Linus Torvalds --- include/linux/Kbuild | 1 - include/linux/byteorder.h | 372 ---------------------------------------------- 2 files changed, 373 deletions(-) delete mode 100644 include/linux/byteorder.h (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 39da666067b9..a3323f337e4d 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -179,7 +179,6 @@ unifdef-y += auto_fs.h unifdef-y += auxvec.h unifdef-y += binfmts.h unifdef-y += blktrace_api.h -unifdef-y += byteorder.h unifdef-y += capability.h unifdef-y += capi.h unifdef-y += cciss_ioctl.h diff --git a/include/linux/byteorder.h b/include/linux/byteorder.h deleted file mode 100644 index 29f002d73d98..000000000000 --- a/include/linux/byteorder.h +++ /dev/null @@ -1,372 +0,0 @@ -#ifndef _LINUX_BYTEORDER_H -#define _LINUX_BYTEORDER_H - -#include -#include - -#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) -# error Fix asm/byteorder.h to define one endianness -#endif - -#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) -# error Fix asm/byteorder.h to define arch endianness -#endif - -#ifdef __LITTLE_ENDIAN -# undef __LITTLE_ENDIAN -# define __LITTLE_ENDIAN 1234 -#endif - -#ifdef __BIG_ENDIAN -# undef __BIG_ENDIAN -# define __BIG_ENDIAN 4321 -#endif - -#if defined(__LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN_BITFIELD) -# define __LITTLE_ENDIAN_BITFIELD -#endif - -#if defined(__BIG_ENDIAN) && !defined(__BIG_ENDIAN_BITFIELD) -# define __BIG_ENDIAN_BITFIELD -#endif - -#ifdef __LITTLE_ENDIAN -# define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) -# define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) -# define __le64_to_cpu(x) ((__force __u64)(__le64)(x)) -# define __cpu_to_le16(x) ((__force __le16)(__u16)(x)) -# define __cpu_to_le32(x) ((__force __le32)(__u32)(x)) -# define __cpu_to_le64(x) ((__force __le64)(__u64)(x)) - -# define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x)) -# define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x)) -# define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x)) -# define __cpu_to_be16(x) ((__force __be16)__swab16(x)) -# define __cpu_to_be32(x) ((__force __be32)__swab32(x)) -# define __cpu_to_be64(x) ((__force __be64)__swab64(x)) -#endif - -#ifdef __BIG_ENDIAN -# define __be16_to_cpu(x) ((__force __u16)(__be16)(x)) -# define __be32_to_cpu(x) ((__force __u32)(__be32)(x)) -# define __be64_to_cpu(x) ((__force __u64)(__be64)(x)) -# define __cpu_to_be16(x) ((__force __be16)(__u16)(x)) -# define __cpu_to_be32(x) ((__force __be32)(__u32)(x)) -# define __cpu_to_be64(x) ((__force __be64)(__u64)(x)) - -# define __le16_to_cpu(x) __swab16((__force __u16)(__le16)(x)) -# define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) -# define __le64_to_cpu(x) __swab64((__force __u64)(__le64)(x)) -# define __cpu_to_le16(x) ((__force __le16)__swab16(x)) -# define __cpu_to_le32(x) ((__force __le32)__swab32(x)) -# define __cpu_to_le64(x) ((__force __le64)__swab64(x)) -#endif - -/* - * These helpers could be phased out over time as the base version - * handles constant folding. - */ -#define __constant_htonl(x) __cpu_to_be32(x) -#define __constant_ntohl(x) __be32_to_cpu(x) -#define __constant_htons(x) __cpu_to_be16(x) -#define __constant_ntohs(x) __be16_to_cpu(x) - -#define __constant_le16_to_cpu(x) __le16_to_cpu(x) -#define __constant_le32_to_cpu(x) __le32_to_cpu(x) -#define __constant_le64_to_cpu(x) __le64_to_cpu(x) -#define __constant_be16_to_cpu(x) __be16_to_cpu(x) -#define __constant_be32_to_cpu(x) __be32_to_cpu(x) -#define __constant_be64_to_cpu(x) __be64_to_cpu(x) - -#define __constant_cpu_to_le16(x) __cpu_to_le16(x) -#define __constant_cpu_to_le32(x) __cpu_to_le32(x) -#define __constant_cpu_to_le64(x) __cpu_to_le64(x) -#define __constant_cpu_to_be16(x) __cpu_to_be16(x) -#define __constant_cpu_to_be32(x) __cpu_to_be32(x) -#define __constant_cpu_to_be64(x) __cpu_to_be64(x) - -static inline void __le16_to_cpus(__u16 *p) -{ -#ifdef __BIG_ENDIAN - __swab16s(p); -#endif -} - -static inline void __cpu_to_le16s(__u16 *p) -{ -#ifdef __BIG_ENDIAN - __swab16s(p); -#endif -} - -static inline void __le32_to_cpus(__u32 *p) -{ -#ifdef __BIG_ENDIAN - __swab32s(p); -#endif -} - -static inline void __cpu_to_le32s(__u32 *p) -{ -#ifdef __BIG_ENDIAN - __swab32s(p); -#endif -} - -static inline void __le64_to_cpus(__u64 *p) -{ -#ifdef __BIG_ENDIAN - __swab64s(p); -#endif -} - -static inline void __cpu_to_le64s(__u64 *p) -{ -#ifdef __BIG_ENDIAN - __swab64s(p); -#endif -} - -static inline void __be16_to_cpus(__u16 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab16s(p); -#endif -} - -static inline void __cpu_to_be16s(__u16 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab16s(p); -#endif -} - -static inline void __be32_to_cpus(__u32 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab32s(p); -#endif -} - -static inline void __cpu_to_be32s(__u32 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab32s(p); -#endif -} - -static inline void __be64_to_cpus(__u64 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab64s(p); -#endif -} - -static inline void __cpu_to_be64s(__u64 *p) -{ -#ifdef __LITTLE_ENDIAN - __swab64s(p); -#endif -} - -static inline __u16 __le16_to_cpup(const __le16 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __u16)*p; -#else - return __swab16p((__force __u16 *)p); -#endif -} - -static inline __u32 __le32_to_cpup(const __le32 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __u32)*p; -#else - return __swab32p((__force __u32 *)p); -#endif -} - -static inline __u64 __le64_to_cpup(const __le64 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __u64)*p; -#else - return __swab64p((__force __u64 *)p); -#endif -} - -static inline __le16 __cpu_to_le16p(const __u16 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __le16)*p; -#else - return (__force __le16)__swab16p(p); -#endif -} - -static inline __le32 __cpu_to_le32p(const __u32 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __le32)*p; -#else - return (__force __le32)__swab32p(p); -#endif -} - -static inline __le64 __cpu_to_le64p(const __u64 *p) -{ -#ifdef __LITTLE_ENDIAN - return (__force __le64)*p; -#else - return (__force __le64)__swab64p(p); -#endif -} - -static inline __u16 __be16_to_cpup(const __be16 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __u16)*p; -#else - return __swab16p((__force __u16 *)p); -#endif -} - -static inline __u32 __be32_to_cpup(const __be32 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __u32)*p; -#else - return __swab32p((__force __u32 *)p); -#endif -} - -static inline __u64 __be64_to_cpup(const __be64 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __u64)*p; -#else - return __swab64p((__force __u64 *)p); -#endif -} - -static inline __be16 __cpu_to_be16p(const __u16 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __be16)*p; -#else - return (__force __be16)__swab16p(p); -#endif -} - -static inline __be32 __cpu_to_be32p(const __u32 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __be32)*p; -#else - return (__force __be32)__swab32p(p); -#endif -} - -static inline __be64 __cpu_to_be64p(const __u64 *p) -{ -#ifdef __BIG_ENDIAN - return (__force __be64)*p; -#else - return (__force __be64)__swab64p(p); -#endif -} - -#ifdef __KERNEL__ - -# define le16_to_cpu __le16_to_cpu -# define le32_to_cpu __le32_to_cpu -# define le64_to_cpu __le64_to_cpu -# define be16_to_cpu __be16_to_cpu -# define be32_to_cpu __be32_to_cpu -# define be64_to_cpu __be64_to_cpu -# define cpu_to_le16 __cpu_to_le16 -# define cpu_to_le32 __cpu_to_le32 -# define cpu_to_le64 __cpu_to_le64 -# define cpu_to_be16 __cpu_to_be16 -# define cpu_to_be32 __cpu_to_be32 -# define cpu_to_be64 __cpu_to_be64 - -# define le16_to_cpup __le16_to_cpup -# define le32_to_cpup __le32_to_cpup -# define le64_to_cpup __le64_to_cpup -# define be16_to_cpup __be16_to_cpup -# define be32_to_cpup __be32_to_cpup -# define be64_to_cpup __be64_to_cpup -# define cpu_to_le16p __cpu_to_le16p -# define cpu_to_le32p __cpu_to_le32p -# define cpu_to_le64p __cpu_to_le64p -# define cpu_to_be16p __cpu_to_be16p -# define cpu_to_be32p __cpu_to_be32p -# define cpu_to_be64p __cpu_to_be64p - -# define le16_to_cpus __le16_to_cpus -# define le32_to_cpus __le32_to_cpus -# define le64_to_cpus __le64_to_cpus -# define be16_to_cpus __be16_to_cpus -# define be32_to_cpus __be32_to_cpus -# define be64_to_cpus __be64_to_cpus -# define cpu_to_le16s __cpu_to_le16s -# define cpu_to_le32s __cpu_to_le32s -# define cpu_to_le64s __cpu_to_le64s -# define cpu_to_be16s __cpu_to_be16s -# define cpu_to_be32s __cpu_to_be32s -# define cpu_to_be64s __cpu_to_be64s - -/* - * They have to be macros in order to do the constant folding - * correctly - if the argument passed into a inline function - * it is no longer constant according to gcc.. - */ -# undef ntohl -# undef ntohs -# undef htonl -# undef htons - -# define ___htonl(x) __cpu_to_be32(x) -# define ___htons(x) __cpu_to_be16(x) -# define ___ntohl(x) __be32_to_cpu(x) -# define ___ntohs(x) __be16_to_cpu(x) - -# define htonl(x) ___htonl(x) -# define ntohl(x) ___ntohl(x) -# define htons(x) ___htons(x) -# define ntohs(x) ___ntohs(x) - -static inline void le16_add_cpu(__le16 *var, u16 val) -{ - *var = cpu_to_le16(le16_to_cpup(var) + val); -} - -static inline void le32_add_cpu(__le32 *var, u32 val) -{ - *var = cpu_to_le32(le32_to_cpup(var) + val); -} - -static inline void le64_add_cpu(__le64 *var, u64 val) -{ - *var = cpu_to_le64(le64_to_cpup(var) + val); -} - -static inline void be16_add_cpu(__be16 *var, u16 val) -{ - *var = cpu_to_be16(be16_to_cpup(var) + val); -} - -static inline void be32_add_cpu(__be32 *var, u32 val) -{ - *var = cpu_to_be32(be32_to_cpup(var) + val); -} - -static inline void be64_add_cpu(__be64 *var, u64 val) -{ - *var = cpu_to_be64(be64_to_cpup(var) + val); -} - -#endif /* __KERNEL__ */ -#endif /* _LINUX_BYTEORDER_H */ -- cgit v1.2.3-71-gd317 From ede6f5aea054d3fb67c78857f7abdee602302043 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 6 Jan 2009 21:17:57 -0800 Subject: Fix up 64-bit byte swaps for most 32-bit architectures The __SWAB_64_THRU_32__ case of a 64-bit byte swap was depending on the no-longer-existant ___swab32() method (three underscores). We got rid of some of the worst indirection and complexity, and now it should just use the 32-bit swab function that was defined right above it. Reported-and-tested-by: Nicolas Pitre Reported-by: Benjamin Herrenschmidt Cc: Harvey Harrison Signed-off-by: Linus Torvalds --- include/linux/swab.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/swab.h b/include/linux/swab.h index 9a2d33e0a98a..be5284d4a053 100644 --- a/include/linux/swab.h +++ b/include/linux/swab.h @@ -68,7 +68,7 @@ static inline __attribute_const__ __u64 __fswab64(__u64 val) #elif defined(__SWAB_64_THRU_32__) __u32 h = val >> 32; __u32 l = val & ((1ULL << 32) - 1); - return (((__u64)___swab32(l)) << 32) | ((__u64)(___swab32(h))); + return (((__u64)__fswab32(l)) << 32) | ((__u64)(__fswab32(h))); #else return ___constant_swab64(val); #endif -- cgit v1.2.3-71-gd317