diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-05-03 11:17:01 +0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-05-03 11:17:01 +0400 |
commit | 53ba4f2fa73225113a488584df0d85d3cba52943 (patch) | |
tree | d85b984d9818abc3ccc0237eb53b710d9e96c39e /drivers/misc | |
parent | bd6d29c25bb1a24a4c160ec5de43e0004e01f72b (diff) | |
parent | 66f41d4c5c8a5deed66fdcc84509376c9a0bf9d8 (diff) | |
download | linux-53ba4f2fa73225113a488584df0d85d3cba52943.tar.xz |
Merge commit 'v2.6.34-rc6' into core/locking
Diffstat (limited to 'drivers/misc')
39 files changed, 1851 insertions, 181 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e3551d20464f..0d0d625fece2 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -212,6 +212,15 @@ config CS5535_MFGPT_DEFAULT_IRQ want to use a different IRQ by default. This is here for architectures to set as necessary. +config CS5535_CLOCK_EVENT_SRC + tristate "CS5535/CS5536 high-res timer (MFGPT) events" + depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT + help + This driver provides a clock event source based on the MFGPT + timer(s) in the CS5535 and CS5536 companion chips. + MFGPTs have a better resolution and max interval than the + generic PIT, and are suitable for use as high-res timers. + config HP_ILO tristate "Channel interface driver for HP iLO/iLO2 processor" depends on PCI @@ -259,6 +268,16 @@ config ISL29003 This driver can also be built as a module. If so, the module will be called isl29003. +config SENSORS_TSL2550 + tristate "Taos TSL2550 ambient light sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Taos TSL2550 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called tsl2550. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX @@ -292,6 +311,22 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be calles ti_dac7512. +config VMWARE_BALLOON + tristate "VMware Balloon Driver" + depends on X86 + help + This is VMware physical memory management driver which acts + like a "balloon" that can be inflated to reclaim physical pages + by reserving them in the guest and invalidating them in the + monitor, freeing up the underlying machine pages so they can + be allocated to other guests. The balloon can also be deflated + to allow the guest to use more physical memory. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called vmware_balloon. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 049ff2482f30..7b6f7eefdf8d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o +obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o @@ -28,3 +29,4 @@ obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-y += eeprom/ obj-y += cb710/ +obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 558bf3f2c276..4afffe610f99 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/spinlock.h> #include <linux/atmel-ssc.h> +#include <linux/slab.h> /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 6aa5294dfec4..0f3fb4f03bdf 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -1,6 +1,7 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/slab.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/platform_device.h> diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index 05dc8a31f280..3891124001f2 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -6,6 +6,7 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/platform_device.h> +#include <linux/slab.h> /* Number of bytes to reserve for the iomem resource */ #define ATMEL_TC_IOMEM_SIZE 256 diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index b5346b4db91a..ed090e77c9cd 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -20,6 +20,7 @@ #include <linux/delay.h> #include <linux/idr.h> #include <linux/sched.h> +#include <linux/slab.h> #include <linux/c2port.h> @@ -912,8 +913,8 @@ struct c2port_device *c2port_device_register(char *name, c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, "c2port%d", id); - if (unlikely(!c2dev->dev)) { - ret = -ENOMEM; + if (unlikely(IS_ERR(c2dev->dev))) { + ret = PTR_ERR(c2dev->dev); goto error_device_create; } dev_set_drvdata(c2dev->dev, c2dev); diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index b14eab0f2ba5..efec4139c3f6 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -9,11 +9,11 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> #include <linux/pci.h> #include <linux/spinlock.h> #include <linux/idr.h> #include <linux/cb710.h> +#include <linux/gfp.h> static DEFINE_IDA(cb710_ida); static DEFINE_SPINLOCK(cb710_ida_lock); diff --git a/drivers/misc/cb710/debug.c b/drivers/misc/cb710/debug.c index 02358d086e03..fcb3b8e30c52 100644 --- a/drivers/misc/cb710/debug.c +++ b/drivers/misc/cb710/debug.c @@ -10,7 +10,6 @@ #include <linux/cb710.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> #define CB710_REG_COUNT 0x80 diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index 8110460558ff..9bec24db4d41 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/cs5535.h> +#include <linux/slab.h> #define DRV_NAME "cs5535-mfgpt" #define MFGPT_BAR 2 diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index f3ee4a1abb77..9197cfc55015 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -33,7 +33,6 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/slab.h> #include <linux/i2c.h> #include <linux/string.h> #include <linux/list.h> diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2cb2736d65aa..db7d0f21b65d 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -505,6 +505,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) * Export the EEPROM bytes through sysfs, since that's convenient. * By default, only root should see the data (maybe passwords etc) */ + sysfs_bin_attr_init(&at24->bin); at24->bin.attr.name = "eeprom"; at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; at24->bin.read = at24_bin_read; diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index d902d81dde39..d194212a41f6 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -347,6 +347,7 @@ static int at25_probe(struct spi_device *spi) * that's sensitive for read and/or write, like ethernet addresses, * security codes, board-specific manufacturing calibrations, etc. */ + sysfs_bin_attr_init(&at25->bin); at25->bin.attr.name = "eeprom"; at25->bin.attr.mode = S_IRUSR; at25->bin.read = at25_bin_read; diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 1eac626e710a..48c84a58163e 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/slab.h> static LIST_HEAD(container_list); static DEFINE_MUTEX(container_list_lock); diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index ba4694169d79..46b3439673e9 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index a92a3a742b43..98ad0120aa9b 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -25,6 +25,7 @@ #include <linux/io.h> #include <linux/wait.h> #include <linux/poll.h> +#include <linux/slab.h> #include "hpilo.h" static struct class *ilo_class; diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c index e2031739aa29..5c766b4fb238 100644 --- a/drivers/misc/ibmasm/command.c +++ b/drivers/misc/ibmasm/command.c @@ -23,6 +23,7 @@ */ #include <linux/sched.h> +#include <linux/slab.h> #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c index 572d41ffc186..76bfda1ffaa9 100644 --- a/drivers/misc/ibmasm/event.c +++ b/drivers/misc/ibmasm/event.c @@ -23,6 +23,7 @@ */ #include <linux/sched.h> +#include <linux/slab.h> #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index aecf40ecb3a4..8844a3f45381 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -75,6 +75,7 @@ #include <linux/fs.h> #include <linux/pagemap.h> +#include <linux/slab.h> #include <asm/uaccess.h> #include <asm/io.h> #include "ibmasm.h" diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index dc14b0b9cbfa..a234d965243b 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -52,6 +52,7 @@ #include <linux/pci.h> #include <linux/init.h> +#include <linux/slab.h> #include "ibmasm.h" #include "lowlevel.h" #include "remote.h" diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 395a4ea64e9c..152e9d93eecb 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/log2.h> +#include <linux/slab.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c index 09dcb699e667..193206602d88 100644 --- a/drivers/misc/ioc4.c +++ b/drivers/misc/ioc4.c @@ -30,6 +30,7 @@ #include <linux/pci.h> #include <linux/ioc4.h> #include <linux/ktime.h> +#include <linux/slab.h> #include <linux/mutex.h> #include <linux/time.h> #include <asm/io.h> diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c index 0c8ea0a1c8a3..e9eda471f6e0 100644 --- a/drivers/misc/iwmc3200top/debugfs.c +++ b/drivers/misc/iwmc3200top/debugfs.c @@ -25,6 +25,7 @@ */ #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/mmc/sdio_func.h> diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c index 50d431e469f5..e27afde6e99f 100644 --- a/drivers/misc/iwmc3200top/fw-download.c +++ b/drivers/misc/iwmc3200top/fw-download.c @@ -26,6 +26,7 @@ #include <linux/firmware.h> #include <linux/mmc/sdio_func.h> +#include <linux/slab.h> #include <asm/unaligned.h> #include "iwmc3200top.h" @@ -43,15 +44,14 @@ static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file, struct iwmct_parser *parser = &priv->parser; struct iwmct_fw_hdr *fw_hdr = &parser->versions; - LOG_INFOEX(priv, INIT, "-->\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size); parser->file = file; parser->file_size = file_size; parser->cur_pos = 0; - parser->buf = NULL; - + parser->entry_point = 0; parser->buf = kzalloc(block_size, GFP_KERNEL); if (!parser->buf) { LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n"); @@ -70,7 +70,7 @@ static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file, parser->cur_pos += sizeof(struct iwmct_fw_hdr); - LOG_INFOEX(priv, INIT, "<--\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return 0; } @@ -113,7 +113,7 @@ static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec, struct iwmct_dbg *dbg = &priv->dbg; struct iwmct_fw_sec_hdr *sec_hdr; - LOG_INFOEX(priv, INIT, "-->\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr) <= parser->file_size) { @@ -152,7 +152,7 @@ static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec, "finished with section cur_pos=%zd\n", parser->cur_pos); } - LOG_INFOEX(priv, INIT, "<--\n"); + LOG_TRACE(priv, INIT, "<--\n"); return 0; } @@ -167,7 +167,7 @@ static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, int ret = 0; u32 cmd = 0; - LOG_INFOEX(priv, INIT, "-->\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n", addr, sec_size); @@ -229,7 +229,7 @@ static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, hdr->cmd = cpu_to_le32(cmd); /* send it down */ /* TODO: add more proper sending and error checking */ - ret = iwmct_tx(priv, 0, parser->buf, trans_size); + ret = iwmct_tx(priv, parser->buf, trans_size); if (ret != 0) { LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d\n", ret); @@ -251,7 +251,7 @@ static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, if (sent < sec_size) ret = -EINVAL; exit: - LOG_INFOEX(priv, INIT, "<--\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return ret; } @@ -262,7 +262,7 @@ static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) int ret; u32 cmd; - LOG_INFOEX(priv, INIT, "-->\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); memset(parser->buf, 0, parser->buf_size); cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; @@ -281,11 +281,11 @@ static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr)); /* send it down */ /* TODO: add more proper sending and error checking */ - ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE); + ret = iwmct_tx(priv, parser->buf, IWMC_SDIO_BLK_SIZE); if (ret) LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret); - LOG_INFOEX(priv, INIT, "<--\n"); + LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return 0; } @@ -298,8 +298,16 @@ int iwmct_fw_load(struct iwmct_priv *priv) __le32 addr; int ret; - /* clear parser struct */ - memset(&priv->parser, 0, sizeof(struct iwmct_parser)); + + LOG_INFO(priv, FW_DOWNLOAD, "barker download request 0x%x is:\n", + priv->barker); + LOG_INFO(priv, FW_DOWNLOAD, "******* Top FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not"); + LOG_INFO(priv, FW_DOWNLOAD, "******* GPS FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not"); + LOG_INFO(priv, FW_DOWNLOAD, "******* BT FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not"); + /* get the firmware */ ret = request_firmware(&raw, fw_name, &priv->func->dev); @@ -317,6 +325,7 @@ int iwmct_fw_load(struct iwmct_priv *priv) LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name); + /* clear parser struct */ ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len); if (ret < 0) { LOG_ERROR(priv, FW_DOWNLOAD, @@ -324,7 +333,6 @@ int iwmct_fw_load(struct iwmct_priv *priv) goto exit; } - /* checksum */ if (!iwmct_checksum(priv)) { LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n"); ret = -EINVAL; @@ -333,23 +341,18 @@ int iwmct_fw_load(struct iwmct_priv *priv) /* download firmware to device */ while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) { - if (iwmct_download_section(priv, pdata, len, addr)) { + ret = iwmct_download_section(priv, pdata, len, addr); + if (ret) { LOG_ERROR(priv, FW_DOWNLOAD, "%s download section failed\n", fw_name); - ret = -EIO; goto exit; } } - iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK)); + ret = iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK)); exit: kfree(priv->parser.buf); - - if (raw) - release_firmware(raw); - - raw = NULL; - + release_firmware(raw); return ret; } diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h index 43bd510e1872..740ff0738ea8 100644 --- a/drivers/misc/iwmc3200top/iwmc3200top.h +++ b/drivers/misc/iwmc3200top/iwmc3200top.h @@ -196,9 +196,7 @@ struct iwmct_priv { struct list_head read_req_list; }; -extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, - void *src, int count); - +extern int iwmct_tx(struct iwmct_priv *priv, void *src, int count); extern int iwmct_fw_load(struct iwmct_priv *priv); extern void iwmct_dbg_init_params(struct iwmct_priv *drv); diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c index d569279698f6..a36a55a49cac 100644 --- a/drivers/misc/iwmc3200top/log.c +++ b/drivers/misc/iwmc3200top/log.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/mmc/sdio_func.h> +#include <linux/slab.h> #include <linux/ctype.h> #include "fw-msg.h" #include "iwmc3200top.h" diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h index aba8121f978c..4434bb16cea7 100644 --- a/drivers/misc/iwmc3200top/log.h +++ b/drivers/misc/iwmc3200top/log.h @@ -37,13 +37,26 @@ #define LOG_SEV_INFO 3 #define LOG_SEV_INFOEX 4 -#define LOG_SEV_FILTER_ALL \ - (BIT(LOG_SEV_CRITICAL) | \ - BIT(LOG_SEV_ERROR) | \ - BIT(LOG_SEV_WARNING) | \ - BIT(LOG_SEV_INFO) | \ +/* Log levels not defined for FW */ +#define LOG_SEV_TRACE 5 +#define LOG_SEV_DUMP 6 + +#define LOG_SEV_FW_FILTER_ALL \ + (BIT(LOG_SEV_CRITICAL) | \ + BIT(LOG_SEV_ERROR) | \ + BIT(LOG_SEV_WARNING) | \ + BIT(LOG_SEV_INFO) | \ BIT(LOG_SEV_INFOEX)) +#define LOG_SEV_FILTER_ALL \ + (BIT(LOG_SEV_CRITICAL) | \ + BIT(LOG_SEV_ERROR) | \ + BIT(LOG_SEV_WARNING) | \ + BIT(LOG_SEV_INFO) | \ + BIT(LOG_SEV_INFOEX) | \ + BIT(LOG_SEV_TRACE) | \ + BIT(LOG_SEV_DUMP)) + /* log source */ #define LOG_SRC_INIT 0 #define LOG_SRC_DEBUGFS 1 @@ -104,16 +117,16 @@ do { \ __func__, __LINE__, ##args); \ } while (0) -#define LOG_INFOEX(priv, src, fmt, args...) \ +#define LOG_TRACE(priv, src, fmt, args...) \ do { \ - if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_TRACE)) \ dev_dbg(priv2dev(priv), "%s %d: " fmt, \ __func__, __LINE__, ##args); \ } while (0) #define LOG_HEXDUMP(src, ptr, len) \ do { \ - if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_DUMP)) \ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \ 16, 1, ptr, len, false); \ } while (0) @@ -142,7 +155,7 @@ ssize_t store_iwmct_log_level_fw(struct device *d, #define LOG_ERROR(priv, src, fmt, args...) #define LOG_WARNING(priv, src, fmt, args...) #define LOG_INFO(priv, src, fmt, args...) -#define LOG_INFOEX(priv, src, fmt, args...) +#define LOG_TRACE(priv, src, fmt, args...) #define LOG_HEXDUMP(src, ptr, len) static inline void iwmct_log_top_message(struct iwmct_priv *priv, diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c index fafcaa481d74..c73cef2c3c5e 100644 --- a/drivers/misc/iwmc3200top/main.c +++ b/drivers/misc/iwmc3200top/main.c @@ -25,6 +25,7 @@ */ #include <linux/module.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/debugfs.h> @@ -49,6 +50,20 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_COPYRIGHT); MODULE_FIRMWARE(FW_NAME(FW_API_VER)); + +static inline int __iwmct_tx(struct iwmct_priv *priv, void *src, int count) +{ + return sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, src, count); + +} +int iwmct_tx(struct iwmct_priv *priv, void *src, int count) +{ + int ret; + sdio_claim_host(priv->func); + ret = __iwmct_tx(priv, src, count); + sdio_release_host(priv->func); + return ret; +} /* * This workers main task is to wait for OP_OPR_ALIVE * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed. @@ -66,7 +81,7 @@ static void iwmct_rescan_worker(struct work_struct *ws) ret = bus_rescan_devices(priv->func->dev.bus); if (ret < 0) - LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n"); + LOG_INFO(priv, INIT, "bus_rescan_devices FAILED!!!\n"); } static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg) @@ -137,7 +152,7 @@ int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) int ret; u8 *buf; - LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n"); + LOG_TRACE(priv, FW_MSG, "Sending hcmd:\n"); /* add padding to 256 for IWMC */ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256; @@ -158,27 +173,12 @@ int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) } memcpy(buf, cmd, len); - - sdio_claim_host(priv->func); - ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf, - FW_HCMD_BLOCK_SIZE); - sdio_release_host(priv->func); + ret = iwmct_tx(priv, buf, FW_HCMD_BLOCK_SIZE); kfree(buf); return ret; } -int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, - void *src, int count) -{ - int ret; - - sdio_claim_host(priv->func); - ret = sdio_memcpy_toio(priv->func, addr, src, count); - sdio_release_host(priv->func); - - return ret; -} static void iwmct_irq_read_worker(struct work_struct *ws) { @@ -192,7 +192,7 @@ static void iwmct_irq_read_worker(struct work_struct *ws) priv = container_of(ws, struct iwmct_priv, isr_worker); - LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws); + LOG_TRACE(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws); /* --------------------- Handshake with device -------------------- */ sdio_claim_host(priv->func); @@ -273,8 +273,7 @@ static void iwmct_irq_read_worker(struct work_struct *ws) if (barker & BARKER_DNLOAD_SYNC_MSK) { /* Send the same barker back */ - ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, - buf, iosize); + ret = __iwmct_tx(priv, buf, iosize); if (ret) { LOG_ERROR(priv, IRQ, "error %d echoing barker\n", ret); @@ -292,15 +291,6 @@ static void iwmct_irq_read_worker(struct work_struct *ws) sdio_release_host(priv->func); - - LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker); - LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n", - (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not"); - LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n", - (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not"); - LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n", - (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not"); - if (priv->dbg.fw_download) iwmct_fw_load(priv); else @@ -312,7 +302,7 @@ exit_release: sdio_release_host(priv->func); exit: kfree(buf); - LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n"); + LOG_TRACE(priv, IRQ, "exit iwmct_irq_read_worker\n"); } static void iwmct_irq(struct sdio_func *func) @@ -325,12 +315,12 @@ static void iwmct_irq(struct sdio_func *func) priv = sdio_get_drvdata(func); - LOG_INFO(priv, IRQ, "enter iwmct_irq\n"); + LOG_TRACE(priv, IRQ, "enter iwmct_irq\n"); /* read the function's status register */ val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret); - LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret); + LOG_TRACE(priv, IRQ, "iir value = %d, ret=%d\n", val, ret); if (!val) { LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n"); @@ -372,7 +362,7 @@ static void iwmct_irq(struct sdio_func *func) queue_work(priv->wq, &priv->isr_worker); - LOG_INFO(priv, IRQ, "exit iwmct_irq\n"); + LOG_TRACE(priv, IRQ, "exit iwmct_irq\n"); return; @@ -608,8 +598,6 @@ static void iwmct_remove(struct sdio_func *func) struct iwmct_work_struct *read_req; struct iwmct_priv *priv = sdio_get_drvdata(func); - priv = sdio_get_drvdata(func); - LOG_INFO(priv, INIT, "enter\n"); sdio_claim_host(func); @@ -660,7 +648,7 @@ static int __init iwmct_init(void) /* Default log filter settings */ iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME); - iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL); + iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FW_FILTER_ALL); iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME); rc = sdio_register_driver(&iwmct_driver); diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index fcb6ec1af173..72450237a0f4 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -295,6 +295,10 @@ static int check_and_rewind_pc(char *put_str, char *arg) /* On x86 a breakpoint stop requires it to be decremented */ if (addr + 1 == kgdbts_regs.ip) offset = -1; +#elif defined(CONFIG_SUPERH) + /* On SUPERH a breakpoint stop requires it to be decremented */ + if (addr + 2 == kgdbts_regs.pc) + offset = -2; #endif if (strcmp(arg, "silent") && instruction_pointer(&kgdbts_regs) + offset != addr) { @@ -305,6 +309,8 @@ static int check_and_rewind_pc(char *put_str, char *arg) #ifdef CONFIG_X86 /* On x86 adjust the instruction pointer if needed */ kgdbts_regs.ip += offset; +#elif defined(CONFIG_SUPERH) + kgdbts_regs.pc += offset; #endif return 0; } diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 3648b23d5c92..31a991161f0a 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -26,21 +26,9 @@ * It is adapted from the Linux Kernel Dump Test Tool by * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> * - * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<> - * [cpoint_count={>0}] + * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> * - * recur_count : Recursion level for the stack overflow test. Default is 10. - * - * cpoint_name : Crash point where the kernel is to be crashed. It can be - * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY, - * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD, - * IDE_CORE_CP - * - * cpoint_type : Indicates the action to be taken on hitting the crash point. - * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW - * - * cpoint_count : Indicates the number of times the crash point is to be hit - * to trigger an action. The default is 10. + * See Documentation/fault-injection/provoke-crashes.txt for instructions */ #include <linux/kernel.h> @@ -52,14 +40,14 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/hrtimer.h> +#include <linux/slab.h> #include <scsi/scsi_cmnd.h> +#include <linux/debugfs.h> #ifdef CONFIG_IDE #include <linux/ide.h> #endif -#define NUM_CPOINTS 8 -#define NUM_CPOINT_TYPES 5 #define DEFAULT_COUNT 10 #define REC_NUM_DEFAULT 10 @@ -72,7 +60,8 @@ enum cname { MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD, - IDE_CORE_CP + IDE_CORE_CP, + DIRECT, }; enum ctype { @@ -81,7 +70,11 @@ enum ctype { BUG, EXCEPTION, LOOP, - OVERFLOW + OVERFLOW, + CORRUPT_STACK, + UNALIGNED_LOAD_STORE_WRITE, + OVERWRITE_ALLOCATION, + WRITE_AFTER_FREE, }; static char* cp_name[] = { @@ -92,7 +85,8 @@ static char* cp_name[] = { "MEM_SWAPOUT", "TIMERADD", "SCSI_DISPATCH_CMD", - "IDE_CORE_CP" + "IDE_CORE_CP", + "DIRECT", }; static char* cp_type[] = { @@ -100,7 +94,11 @@ static char* cp_type[] = { "BUG", "EXCEPTION", "LOOP", - "OVERFLOW" + "OVERFLOW", + "CORRUPT_STACK", + "UNALIGNED_LOAD_STORE_WRITE", + "OVERWRITE_ALLOCATION", + "WRITE_AFTER_FREE", }; static struct jprobe lkdtm; @@ -193,34 +191,66 @@ int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, } #endif +/* Return the crashpoint number or NONE if the name is invalid */ +static enum ctype parse_cp_type(const char *what, size_t count) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cp_type); i++) { + if (!strcmp(what, cp_type[i])) + return i + 1; + } + + return NONE; +} + +static const char *cp_type_to_str(enum ctype type) +{ + if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) + return "None"; + + return cp_type[type - 1]; +} + +static const char *cp_name_to_str(enum cname name) +{ + if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) + return "INVALID"; + + return cp_name[name - 1]; +} + + static int lkdtm_parse_commandline(void) { int i; - if (cpoint_name == NULL || cpoint_type == NULL || - cpoint_count < 1 || recur_count < 1) + if (cpoint_count < 1 || recur_count < 1) return -EINVAL; - for (i = 0; i < NUM_CPOINTS; ++i) { + count = cpoint_count; + + /* No special parameters */ + if (!cpoint_type && !cpoint_name) + return 0; + + /* Neither or both of these need to be set */ + if (!cpoint_type || !cpoint_name) + return -EINVAL; + + cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); + if (cptype == NONE) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(cp_name); i++) { if (!strcmp(cpoint_name, cp_name[i])) { cpoint = i + 1; - break; - } - } - - for (i = 0; i < NUM_CPOINT_TYPES; ++i) { - if (!strcmp(cpoint_type, cp_type[i])) { - cptype = i + 1; - break; + return 0; } } - if (cpoint == INVALID || cptype == NONE) - return -EINVAL; - - count = cpoint_count; - - return 0; + /* Could not find a valid crash point */ + return -EINVAL; } static int recursive_loop(int a) @@ -235,53 +265,92 @@ static int recursive_loop(int a) return recursive_loop(a); } -void lkdtm_handler(void) +static void lkdtm_do_action(enum ctype which) { - printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n", - cpoint_name, cpoint_type); - --count; + switch (which) { + case PANIC: + panic("dumptest"); + break; + case BUG: + BUG(); + break; + case EXCEPTION: + *((int *) 0) = 0; + break; + case LOOP: + for (;;) + ; + break; + case OVERFLOW: + (void) recursive_loop(0); + break; + case CORRUPT_STACK: { + volatile u32 data[8]; + volatile u32 *p = data; + + p[12] = 0x12345678; + break; + } + case UNALIGNED_LOAD_STORE_WRITE: { + static u8 data[5] __attribute__((aligned(4))) = {1, 2, + 3, 4, 5}; + u32 *p; + u32 val = 0x12345678; + + p = (u32 *)(data + 1); + if (*p == 0) + val = 0x87654321; + *p = val; + break; + } + case OVERWRITE_ALLOCATION: { + size_t len = 1020; + u32 *data = kmalloc(len, GFP_KERNEL); + + data[1024 / sizeof(u32)] = 0x12345678; + kfree(data); + break; + } + case WRITE_AFTER_FREE: { + size_t len = 1024; + u32 *data = kmalloc(len, GFP_KERNEL); + + kfree(data); + schedule(); + memset(data, 0x78, len); + break; + } + case NONE: + default: + break; + } + +} + +static void lkdtm_handler(void) +{ + count--; + printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", + cp_name_to_str(cpoint), cp_type_to_str(cptype), count); if (count == 0) { - switch (cptype) { - case NONE: - break; - case PANIC: - printk(KERN_INFO "lkdtm : PANIC\n"); - panic("dumptest"); - break; - case BUG: - printk(KERN_INFO "lkdtm : BUG\n"); - BUG(); - break; - case EXCEPTION: - printk(KERN_INFO "lkdtm : EXCEPTION\n"); - *((int *) 0) = 0; - break; - case LOOP: - printk(KERN_INFO "lkdtm : LOOP\n"); - for (;;); - break; - case OVERFLOW: - printk(KERN_INFO "lkdtm : OVERFLOW\n"); - (void) recursive_loop(0); - break; - default: - break; - } + lkdtm_do_action(cptype); count = cpoint_count; } } -static int __init lkdtm_module_init(void) +static int lkdtm_register_cpoint(enum cname which) { int ret; - if (lkdtm_parse_commandline() == -EINVAL) { - printk(KERN_INFO "lkdtm : Invalid command\n"); - return -EINVAL; - } + cpoint = INVALID; + if (lkdtm.entry != NULL) + unregister_jprobe(&lkdtm); - switch (cpoint) { + switch (which) { + case DIRECT: + lkdtm_do_action(cptype); + return 0; case INT_HARDWARE_ENTRY: lkdtm.kp.symbol_name = "do_IRQ"; lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; @@ -315,28 +384,268 @@ static int __init lkdtm_module_init(void) lkdtm.kp.symbol_name = "generic_ide_ioctl"; lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; #else - printk(KERN_INFO "lkdtm : Crash point not available\n"); + printk(KERN_INFO "lkdtm: Crash point not available\n"); + return -EINVAL; #endif break; default: - printk(KERN_INFO "lkdtm : Invalid Crash Point\n"); - break; + printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); + return -EINVAL; } + cpoint = which; if ((ret = register_jprobe(&lkdtm)) < 0) { - printk(KERN_INFO "lkdtm : Couldn't register jprobe\n"); - return ret; + printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); + cpoint = INVALID; + } + + return ret; +} + +static ssize_t do_register_entry(enum cname which, struct file *f, + const char __user *user_buf, size_t count, loff_t *off) +{ + char *buf; + int err; + + if (count >= PAGE_SIZE) + return -EINVAL; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (copy_from_user(buf, user_buf, count)) { + free_page((unsigned long) buf); + return -EFAULT; + } + /* NULL-terminate and remove enter */ + buf[count] = '\0'; + strim(buf); + + cptype = parse_cp_type(buf, count); + free_page((unsigned long) buf); + + if (cptype == NONE) + return -EINVAL; + + err = lkdtm_register_cpoint(which); + if (err < 0) + return err; + + *off += count; + + return count; +} + +/* Generic read callback that just prints out the available crash types */ +static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, + size_t count, loff_t *off) +{ + char *buf; + int i, n, out; + + buf = (char *)__get_free_page(GFP_KERNEL); + + n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); + for (i = 0; i < ARRAY_SIZE(cp_type); i++) + n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]); + buf[n] = '\0'; + + out = simple_read_from_buffer(user_buf, count, off, + buf, n); + free_page((unsigned long) buf); + + return out; +} + +static int lkdtm_debugfs_open(struct inode *inode, struct file *file) +{ + return 0; +} + + +static ssize_t int_hardware_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); +} + +static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); +} + +static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); +} + +static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(FS_DEVRW, f, buf, count, off); +} + +static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(MEM_SWAPOUT, f, buf, count, off); +} + +static ssize_t timeradd_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(TIMERADD, f, buf, count, off); +} + +static ssize_t scsi_dispatch_cmd_entry(struct file *f, + const char __user *buf, size_t count, loff_t *off) +{ + return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); +} + +static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + return do_register_entry(IDE_CORE_CP, f, buf, count, off); +} + +/* Special entry to just crash directly. Available without KPROBEs */ +static ssize_t direct_entry(struct file *f, const char __user *user_buf, + size_t count, loff_t *off) +{ + enum ctype type; + char *buf; + + if (count >= PAGE_SIZE) + return -EINVAL; + if (count < 1) + return -EINVAL; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (copy_from_user(buf, user_buf, count)) { + free_page((unsigned long) buf); + return -EFAULT; + } + /* NULL-terminate and remove enter */ + buf[count] = '\0'; + strim(buf); + + type = parse_cp_type(buf, count); + free_page((unsigned long) buf); + if (type == NONE) + return -EINVAL; + + printk(KERN_INFO "lkdtm: Performing direct entry %s\n", + cp_type_to_str(type)); + lkdtm_do_action(type); + *off += count; + + return count; +} + +struct crash_entry { + const char *name; + const struct file_operations fops; +}; + +static const struct crash_entry crash_entries[] = { + {"DIRECT", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = direct_entry} }, + {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = int_hardware_entry} }, + {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = int_hw_irq_en} }, + {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = int_tasklet_entry} }, + {"FS_DEVRW", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = fs_devrw_entry} }, + {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = mem_swapout_entry} }, + {"TIMERADD", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = timeradd_entry} }, + {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = scsi_dispatch_cmd_entry} }, + {"IDE_CORE_CP", {.read = lkdtm_debugfs_read, + .open = lkdtm_debugfs_open, + .write = ide_core_cp_entry} }, +}; + +static struct dentry *lkdtm_debugfs_root; + +static int __init lkdtm_module_init(void) +{ + int ret = -EINVAL; + int n_debugfs_entries = 1; /* Assume only the direct entry */ + int i; + + /* Register debugfs interface */ + lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); + if (!lkdtm_debugfs_root) { + printk(KERN_ERR "lkdtm: creating root dir failed\n"); + return -ENODEV; + } + +#ifdef CONFIG_KPROBES + n_debugfs_entries = ARRAY_SIZE(crash_entries); +#endif + + for (i = 0; i < n_debugfs_entries; i++) { + const struct crash_entry *cur = &crash_entries[i]; + struct dentry *de; + + de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, + NULL, &cur->fops); + if (de == NULL) { + printk(KERN_ERR "lkdtm: could not create %s\n", + cur->name); + goto out_err; + } + } + + if (lkdtm_parse_commandline() == -EINVAL) { + printk(KERN_INFO "lkdtm: Invalid command\n"); + goto out_err; + } + + if (cpoint != INVALID && cptype != NONE) { + ret = lkdtm_register_cpoint(cpoint); + if (ret < 0) { + printk(KERN_INFO "lkdtm: Invalid crash point %d\n", + cpoint); + goto out_err; + } + printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", + cpoint_name, cpoint_type); + } else { + printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); } - printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n", - cpoint_name, cpoint_type); return 0; + +out_err: + debugfs_remove_recursive(lkdtm_debugfs_root); + return ret; } static void __exit lkdtm_module_exit(void) { - unregister_jprobe(&lkdtm); - printk(KERN_INFO "lkdtm : Crash point unregistered\n"); + debugfs_remove_recursive(lkdtm_debugfs_root); + + unregister_jprobe(&lkdtm); + printk(KERN_INFO "lkdtm: Crash point unregistered\n"); } module_init(lkdtm_module_init); diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 04c27266f567..75ee0d3f6f45 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -21,6 +21,7 @@ #include <linux/poll.h> #include <linux/interrupt.h> #include <linux/cdev.h> +#include <linux/slab.h> #include <linux/phantom.h> #include <linux/sched.h> #include <linux/smp_lock.h> @@ -497,12 +498,7 @@ static struct pci_driver phantom_pci_driver = { .resume = phantom_resume }; -static ssize_t phantom_show_version(struct class *cls, char *buf) -{ - return sprintf(buf, PHANTOM_VERSION "\n"); -} - -static CLASS_ATTR(version, 0444, phantom_show_version, NULL); +static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION); static int __init phantom_init(void) { @@ -515,7 +511,7 @@ static int __init phantom_init(void) printk(KERN_ERR "phantom: can't register phantom class\n"); goto err; } - retval = class_create_file(phantom_class, &class_attr_version); + retval = class_create_file(phantom_class, &class_attr_version.attr); if (retval) { printk(KERN_ERR "phantom: can't create sysfs version file\n"); goto err_class; @@ -541,7 +537,7 @@ static int __init phantom_init(void) err_unchr: unregister_chrdev_region(dev, PHANTOM_MAX_MINORS); err_attr: - class_remove_file(phantom_class, &class_attr_version); + class_remove_file(phantom_class, &class_attr_version.attr); err_class: class_destroy(phantom_class); err: @@ -554,7 +550,7 @@ static void __exit phantom_exit(void) unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS); - class_remove_file(phantom_class, &class_attr_version); + class_remove_file(phantom_class, &class_attr_version.attr); class_destroy(phantom_class); pr_debug("phantom: module successfully removed\n"); diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 02a77b8b8eef..7a8b9068ea03 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -516,8 +516,7 @@ struct gru_blade_state { /* Scan all active GRUs in a GRU bitmap */ #define for_each_gru_in_bitmap(gid, map) \ - for ((gid) = find_first_bit((map), GRU_MAX_GRUS); (gid) < GRU_MAX_GRUS;\ - (gid)++, (gid) = find_next_bit((map), GRU_MAX_GRUS, (gid))) + for_each_set_bit((gid), (map), GRU_MAX_GRUS) /* Scan all active GRUs on a specific blade */ #define for_each_gru_on_blade(gru, nid, i) \ @@ -536,23 +535,17 @@ struct gru_blade_state { /* Scan each CBR whose bit is set in a TFM (or copy of) */ #define for_each_cbr_in_tfm(i, map) \ - for ((i) = find_first_bit(map, GRU_NUM_CBE); \ - (i) < GRU_NUM_CBE; \ - (i)++, (i) = find_next_bit(map, GRU_NUM_CBE, i)) + for_each_set_bit((i), (map), GRU_NUM_CBE) /* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ #define for_each_cbr_in_allocation_map(i, map, k) \ - for ((k) = find_first_bit(map, GRU_CBR_AU); (k) < GRU_CBR_AU; \ - (k) = find_next_bit(map, GRU_CBR_AU, (k) + 1)) \ + for_each_set_bit((k), (map), GRU_CBR_AU) \ for ((i) = (k)*GRU_CBR_AU_SIZE; \ (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) /* Scan each DSR in a DSR bitmap. Note: multiple DSRs in an allocation unit */ #define for_each_dsr_in_allocation_map(i, map, k) \ - for ((k) = find_first_bit((const unsigned long *)map, GRU_DSR_AU);\ - (k) < GRU_DSR_AU; \ - (k) = find_next_bit((const unsigned long *)map, \ - GRU_DSR_AU, (k) + 1)) \ + for_each_set_bit((k), (const unsigned long *)(map), GRU_DSR_AU) \ for ((i) = (k) * GRU_DSR_AU_CL; \ (i) < ((k) + 1) * GRU_DSR_AU_CL; (i)++) diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 832ed4c88cf7..8d082b46426b 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -44,6 +44,7 @@ */ #include <linux/module.h> +#include <linux/slab.h> #include <linux/sysctl.h> #include <linux/device.h> #include <linux/delay.h> diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 9a6268c89fdd..d551f09ccb79 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/hardirq.h> +#include <linux/slab.h> #include "xpc.h" #include <asm/uv/uv_hub.h> diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index 8b70e03f939f..7d71c04fc938 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -14,6 +14,7 @@ */ #include <linux/delay.h> +#include <linux/slab.h> #include <asm/uncached.h> #include <asm/sn/mspec.h> #include <asm/sn/sn_sal.h> diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 8725d5e8ab0c..1f59ee2226ca 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -19,6 +19,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/slab.h> #include <asm/uv/uv_hub.h> #if defined CONFIG_X86_64 #include <asm/uv/bios.h> diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 16f0abda1423..ee5109a3cd98 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -20,6 +20,7 @@ * */ +#include <linux/slab.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -475,7 +476,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->data[0] == 0xff) { /* we are being asked to broadcast to all partitions */ - for_each_bit(dest_partid, xpnet_broadcast_partitions, + for_each_set_bit(dest_partid, xpnet_broadcast_partitions, xp_max_npartitions) { xpnet_send(skb, queued_msg, start_addr, end_addr, diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 98bcba521da2..5f6852dff40b 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -10,6 +10,7 @@ */ #include <linux/tifm.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/idr.h> diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c new file mode 100644 index 000000000000..483ae5f7f68e --- /dev/null +++ b/drivers/misc/tsl2550.c @@ -0,0 +1,473 @@ +/* + * tsl2550.c - Linux kernel modules for ambient light sensor + * + * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#define TSL2550_DRV_NAME "tsl2550" +#define DRIVER_VERSION "1.2" + +/* + * Defines + */ + +#define TSL2550_POWER_DOWN 0x00 +#define TSL2550_POWER_UP 0x03 +#define TSL2550_STANDARD_RANGE 0x18 +#define TSL2550_EXTENDED_RANGE 0x1d +#define TSL2550_READ_ADC0 0x43 +#define TSL2550_READ_ADC1 0x83 + +/* + * Structs + */ + +struct tsl2550_data { + struct i2c_client *client; + struct mutex update_lock; + + unsigned int power_state:1; + unsigned int operating_mode:1; +}; + +/* + * Global data + */ + +static const u8 TSL2550_MODE_RANGE[2] = { + TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE, +}; + +/* + * Management functions + */ + +static int tsl2550_set_operating_mode(struct i2c_client *client, int mode) +{ + struct tsl2550_data *data = i2c_get_clientdata(client); + + int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]); + + data->operating_mode = mode; + + return ret; +} + +static int tsl2550_set_power_state(struct i2c_client *client, int state) +{ + struct tsl2550_data *data = i2c_get_clientdata(client); + int ret; + + if (state == 0) + ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN); + else { + ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP); + + /* On power up we should reset operating mode also... */ + tsl2550_set_operating_mode(client, data->operating_mode); + } + + data->power_state = state; + + return ret; +} + +static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret < 0) + return ret; + if (!(ret & 0x80)) + return -EAGAIN; + return ret & 0x7f; /* remove the "valid" bit */ +} + +/* + * LUX calculation + */ + +#define TSL2550_MAX_LUX 1846 + +static const u8 ratio_lut[] = { + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 98, 98, 98, 98, 98, + 98, 98, 97, 97, 97, 97, 97, 96, + 96, 96, 96, 95, 95, 95, 94, 94, + 93, 93, 93, 92, 92, 91, 91, 90, + 89, 89, 88, 87, 87, 86, 85, 84, + 83, 82, 81, 80, 79, 78, 77, 75, + 74, 73, 71, 69, 68, 66, 64, 62, + 60, 58, 56, 54, 52, 49, 47, 44, + 42, 41, 40, 40, 39, 39, 38, 38, + 37, 37, 37, 36, 36, 36, 35, 35, + 35, 35, 34, 34, 34, 34, 33, 33, + 33, 33, 32, 32, 32, 32, 32, 31, + 31, 31, 31, 31, 30, 30, 30, 30, + 30, +}; + +static const u16 count_lut[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 49, 53, 57, 61, 65, 69, 73, 77, + 81, 85, 89, 93, 97, 101, 105, 109, + 115, 123, 131, 139, 147, 155, 163, 171, + 179, 187, 195, 203, 211, 219, 227, 235, + 247, 263, 279, 295, 311, 327, 343, 359, + 375, 391, 407, 423, 439, 455, 471, 487, + 511, 543, 575, 607, 639, 671, 703, 735, + 767, 799, 831, 863, 895, 927, 959, 991, + 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487, + 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999, + 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991, + 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015, +}; + +/* + * This function is described into Taos TSL2550 Designer's Notebook + * pages 2, 3. + */ +static int tsl2550_calculate_lux(u8 ch0, u8 ch1) +{ + unsigned int lux; + + /* Look up count from channel values */ + u16 c0 = count_lut[ch0]; + u16 c1 = count_lut[ch1]; + + /* + * Calculate ratio. + * Note: the "128" is a scaling factor + */ + u8 r = 128; + + /* Avoid division by 0 and count 1 cannot be greater than count 0 */ + if (c1 <= c0) + if (c0) { + r = c1 * 128 / c0; + + /* Calculate LUX */ + lux = ((c0 - c1) * ratio_lut[r]) / 256; + } else + lux = 0; + else + return -EAGAIN; + + /* LUX range check */ + return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux; +} + +/* + * SysFS support + */ + +static ssize_t tsl2550_show_power_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + return sprintf(buf, "%u\n", data->power_state); +} + +static ssize_t tsl2550_store_power_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tsl2550_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = tsl2550_set_power_state(client, val); + mutex_unlock(&data->update_lock); + + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, + tsl2550_show_power_state, tsl2550_store_power_state); + +static ssize_t tsl2550_show_operating_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + return sprintf(buf, "%u\n", data->operating_mode); +} + +static ssize_t tsl2550_store_operating_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tsl2550_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (data->power_state == 0) + return -EBUSY; + + mutex_lock(&data->update_lock); + ret = tsl2550_set_operating_mode(client, val); + mutex_unlock(&data->update_lock); + + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO, + tsl2550_show_operating_mode, tsl2550_store_operating_mode); + +static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf) +{ + struct tsl2550_data *data = i2c_get_clientdata(client); + u8 ch0, ch1; + int ret; + + ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0); + if (ret < 0) + return ret; + ch0 = ret; + + ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1); + if (ret < 0) + return ret; + ch1 = ret; + + /* Do the job */ + ret = tsl2550_calculate_lux(ch0, ch1); + if (ret < 0) + return ret; + if (data->operating_mode == 1) + ret *= 5; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t tsl2550_show_lux1_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tsl2550_data *data = i2c_get_clientdata(client); + int ret; + + /* No LUX data if not operational */ + if (!data->power_state) + return -EBUSY; + + mutex_lock(&data->update_lock); + ret = __tsl2550_show_lux(client, buf); + mutex_unlock(&data->update_lock); + + return ret; +} + +static DEVICE_ATTR(lux1_input, S_IRUGO, + tsl2550_show_lux1_input, NULL); + +static struct attribute *tsl2550_attributes[] = { + &dev_attr_power_state.attr, + &dev_attr_operating_mode.attr, + &dev_attr_lux1_input.attr, + NULL +}; + +static const struct attribute_group tsl2550_attr_group = { + .attrs = tsl2550_attributes, +}; + +/* + * Initialization function + */ + +static int tsl2550_init_client(struct i2c_client *client) +{ + struct tsl2550_data *data = i2c_get_clientdata(client); + int err; + + /* + * Probe the chip. To do so we try to power up the device and then to + * read back the 0x03 code + */ + err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP); + if (err < 0) + return err; + if (err != TSL2550_POWER_UP) + return -ENODEV; + data->power_state = 1; + + /* Set the default operating mode */ + err = i2c_smbus_write_byte(client, + TSL2550_MODE_RANGE[data->operating_mode]); + if (err < 0) + return err; + + return 0; +} + +/* + * I2C init/probing/exit functions + */ + +static struct i2c_driver tsl2550_driver; +static int __devinit tsl2550_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct tsl2550_data *data; + int *opmode, err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + err = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + data->client = client; + i2c_set_clientdata(client, data); + + /* Check platform data */ + opmode = client->dev.platform_data; + if (opmode) { + if (*opmode < 0 || *opmode > 1) { + dev_err(&client->dev, "invalid operating_mode (%d)\n", + *opmode); + err = -EINVAL; + goto exit_kfree; + } + data->operating_mode = *opmode; + } else + data->operating_mode = 0; /* default mode is standard */ + dev_info(&client->dev, "%s operating mode\n", + data->operating_mode ? "extended" : "standard"); + + mutex_init(&data->update_lock); + + /* Initialize the TSL2550 chip */ + err = tsl2550_init_client(client); + if (err) + goto exit_kfree; + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group); + if (err) + goto exit_kfree; + + dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int __devexit tsl2550_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group); + + /* Power down the device */ + tsl2550_set_power_state(client, 0); + + kfree(i2c_get_clientdata(client)); + + return 0; +} + +#ifdef CONFIG_PM + +static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return tsl2550_set_power_state(client, 0); +} + +static int tsl2550_resume(struct i2c_client *client) +{ + return tsl2550_set_power_state(client, 1); +} + +#else + +#define tsl2550_suspend NULL +#define tsl2550_resume NULL + +#endif /* CONFIG_PM */ + +static const struct i2c_device_id tsl2550_id[] = { + { "tsl2550", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tsl2550_id); + +static struct i2c_driver tsl2550_driver = { + .driver = { + .name = TSL2550_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = tsl2550_suspend, + .resume = tsl2550_resume, + .probe = tsl2550_probe, + .remove = __devexit_p(tsl2550_remove), + .id_table = tsl2550_id, +}; + +static int __init tsl2550_init(void) +{ + return i2c_add_driver(&tsl2550_driver); +} + +static void __exit tsl2550_exit(void) +{ + i2c_del_driver(&tsl2550_driver); +} + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("TSL2550 ambient light sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(tsl2550_init); +module_exit(tsl2550_exit); diff --git a/drivers/misc/vmware_balloon.c b/drivers/misc/vmware_balloon.c new file mode 100644 index 000000000000..e7161c4e3798 --- /dev/null +++ b/drivers/misc/vmware_balloon.c @@ -0,0 +1,832 @@ +/* + * VMware Balloon driver. + * + * Copyright (C) 2000-2010, VMware, Inc. All Rights Reserved. + * + * 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; version 2 of the License and no 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, GOOD TITLE or + * NON INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained by: Dmitry Torokhov <dtor@vmware.com> + */ + +/* + * This is VMware physical memory management driver for Linux. The driver + * acts like a "balloon" that can be inflated to reclaim physical pages by + * reserving them in the guest and invalidating them in the monitor, + * freeing up the underlying machine pages so they can be allocated to + * other guests. The balloon can also be deflated to allow the guest to + * use more physical memory. Higher level policies can control the sizes + * of balloons in VMs in order to manage physical memory resources. + */ + +//#define DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <asm/vmware.h> + +MODULE_AUTHOR("VMware, Inc."); +MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver"); +MODULE_VERSION("1.2.1.0-K"); +MODULE_ALIAS("dmi:*:svnVMware*:*"); +MODULE_ALIAS("vmware_vmmemctl"); +MODULE_LICENSE("GPL"); + +/* + * Various constants controlling rate of inflaint/deflating balloon, + * measured in pages. + */ + +/* + * Rate of allocating memory when there is no memory pressure + * (driver performs non-sleeping allocations). + */ +#define VMW_BALLOON_NOSLEEP_ALLOC_MAX 16384U + +/* + * Rates of memory allocaton when guest experiences memory pressure + * (driver performs sleeping allocations). + */ +#define VMW_BALLOON_RATE_ALLOC_MIN 512U +#define VMW_BALLOON_RATE_ALLOC_MAX 2048U +#define VMW_BALLOON_RATE_ALLOC_INC 16U + +/* + * Rates for releasing pages while deflating balloon. + */ +#define VMW_BALLOON_RATE_FREE_MIN 512U +#define VMW_BALLOON_RATE_FREE_MAX 16384U +#define VMW_BALLOON_RATE_FREE_INC 16U + +/* + * When guest is under memory pressure, use a reduced page allocation + * rate for next several cycles. + */ +#define VMW_BALLOON_SLOW_CYCLES 4 + +/* + * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We don't + * allow wait (__GFP_WAIT) for NOSLEEP page allocations. Use + * __GFP_NOWARN, to suppress page allocation failure warnings. + */ +#define VMW_PAGE_ALLOC_NOSLEEP (__GFP_HIGHMEM|__GFP_NOWARN) + +/* + * Use GFP_HIGHUSER when executing in a separate kernel thread + * context and allocation can sleep. This is less stressful to + * the guest memory system, since it allows the thread to block + * while memory is reclaimed, and won't take pages from emergency + * low-memory pools. + */ +#define VMW_PAGE_ALLOC_CANSLEEP (GFP_HIGHUSER) + +/* Maximum number of page allocations without yielding processor */ +#define VMW_BALLOON_YIELD_THRESHOLD 1024 + + +/* + * Hypervisor communication port definitions. + */ +#define VMW_BALLOON_HV_PORT 0x5670 +#define VMW_BALLOON_HV_MAGIC 0x456c6d6f +#define VMW_BALLOON_PROTOCOL_VERSION 2 +#define VMW_BALLOON_GUEST_ID 1 /* Linux */ + +#define VMW_BALLOON_CMD_START 0 +#define VMW_BALLOON_CMD_GET_TARGET 1 +#define VMW_BALLOON_CMD_LOCK 2 +#define VMW_BALLOON_CMD_UNLOCK 3 +#define VMW_BALLOON_CMD_GUEST_ID 4 + +/* error codes */ +#define VMW_BALLOON_SUCCESS 0 +#define VMW_BALLOON_FAILURE -1 +#define VMW_BALLOON_ERROR_CMD_INVALID 1 +#define VMW_BALLOON_ERROR_PPN_INVALID 2 +#define VMW_BALLOON_ERROR_PPN_LOCKED 3 +#define VMW_BALLOON_ERROR_PPN_UNLOCKED 4 +#define VMW_BALLOON_ERROR_PPN_PINNED 5 +#define VMW_BALLOON_ERROR_PPN_NOTNEEDED 6 +#define VMW_BALLOON_ERROR_RESET 7 +#define VMW_BALLOON_ERROR_BUSY 8 + +#define VMWARE_BALLOON_CMD(cmd, data, result) \ +({ \ + unsigned long __stat, __dummy1, __dummy2; \ + __asm__ __volatile__ ("inl (%%dx)" : \ + "=a"(__stat), \ + "=c"(__dummy1), \ + "=d"(__dummy2), \ + "=b"(result) : \ + "0"(VMW_BALLOON_HV_MAGIC), \ + "1"(VMW_BALLOON_CMD_##cmd), \ + "2"(VMW_BALLOON_HV_PORT), \ + "3"(data) : \ + "memory"); \ + result &= -1UL; \ + __stat & -1UL; \ +}) + +#ifdef CONFIG_DEBUG_FS +struct vmballoon_stats { + unsigned int timer; + + /* allocation statustics */ + unsigned int alloc; + unsigned int alloc_fail; + unsigned int sleep_alloc; + unsigned int sleep_alloc_fail; + unsigned int refused_alloc; + unsigned int refused_free; + unsigned int free; + + /* monitor operations */ + unsigned int lock; + unsigned int lock_fail; + unsigned int unlock; + unsigned int unlock_fail; + unsigned int target; + unsigned int target_fail; + unsigned int start; + unsigned int start_fail; + unsigned int guest_type; + unsigned int guest_type_fail; +}; + +#define STATS_INC(stat) (stat)++ +#else +#define STATS_INC(stat) +#endif + +struct vmballoon { + + /* list of reserved physical pages */ + struct list_head pages; + + /* transient list of non-balloonable pages */ + struct list_head refused_pages; + + /* balloon size in pages */ + unsigned int size; + unsigned int target; + + /* reset flag */ + bool reset_required; + + /* adjustment rates (pages per second) */ + unsigned int rate_alloc; + unsigned int rate_free; + + /* slowdown page allocations for next few cycles */ + unsigned int slow_allocation_cycles; + +#ifdef CONFIG_DEBUG_FS + /* statistics */ + struct vmballoon_stats stats; + + /* debugfs file exporting statistics */ + struct dentry *dbg_entry; +#endif + + struct sysinfo sysinfo; + + struct delayed_work dwork; +}; + +static struct vmballoon balloon; +static struct workqueue_struct *vmballoon_wq; + +/* + * Send "start" command to the host, communicating supported version + * of the protocol. + */ +static bool vmballoon_send_start(struct vmballoon *b) +{ + unsigned long status, dummy; + + STATS_INC(b->stats.start); + + status = VMWARE_BALLOON_CMD(START, VMW_BALLOON_PROTOCOL_VERSION, dummy); + if (status == VMW_BALLOON_SUCCESS) + return true; + + pr_debug("%s - failed, hv returns %ld\n", __func__, status); + STATS_INC(b->stats.start_fail); + return false; +} + +static bool vmballoon_check_status(struct vmballoon *b, unsigned long status) +{ + switch (status) { + case VMW_BALLOON_SUCCESS: + return true; + + case VMW_BALLOON_ERROR_RESET: + b->reset_required = true; + /* fall through */ + + default: + return false; + } +} + +/* + * Communicate guest type to the host so that it can adjust ballooning + * algorithm to the one most appropriate for the guest. This command + * is normally issued after sending "start" command and is part of + * standard reset sequence. + */ +static bool vmballoon_send_guest_id(struct vmballoon *b) +{ + unsigned long status, dummy; + + status = VMWARE_BALLOON_CMD(GUEST_ID, VMW_BALLOON_GUEST_ID, dummy); + + STATS_INC(b->stats.guest_type); + + if (vmballoon_check_status(b, status)) + return true; + + pr_debug("%s - failed, hv returns %ld\n", __func__, status); + STATS_INC(b->stats.guest_type_fail); + return false; +} + +/* + * Retrieve desired balloon size from the host. + */ +static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) +{ + unsigned long status; + unsigned long target; + unsigned long limit; + u32 limit32; + + /* + * si_meminfo() is cheap. Moreover, we want to provide dynamic + * max balloon size later. So let us call si_meminfo() every + * iteration. + */ + si_meminfo(&b->sysinfo); + limit = b->sysinfo.totalram; + + /* Ensure limit fits in 32-bits */ + limit32 = (u32)limit; + if (limit != limit32) + return false; + + /* update stats */ + STATS_INC(b->stats.target); + + status = VMWARE_BALLOON_CMD(GET_TARGET, limit, target); + if (vmballoon_check_status(b, status)) { + *new_target = target; + return true; + } + + pr_debug("%s - failed, hv returns %ld\n", __func__, status); + STATS_INC(b->stats.target_fail); + return false; +} + +/* + * Notify the host about allocated page so that host can use it without + * fear that guest will need it. Host may reject some pages, we need to + * check the return value and maybe submit a different page. + */ +static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn) +{ + unsigned long status, dummy; + u32 pfn32; + + pfn32 = (u32)pfn; + if (pfn32 != pfn) + return false; + + STATS_INC(b->stats.lock); + + status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy); + if (vmballoon_check_status(b, status)) + return true; + + pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); + STATS_INC(b->stats.lock_fail); + return false; +} + +/* + * Notify the host that guest intends to release given page back into + * the pool of available (to the guest) pages. + */ +static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn) +{ + unsigned long status, dummy; + u32 pfn32; + + pfn32 = (u32)pfn; + if (pfn32 != pfn) + return false; + + STATS_INC(b->stats.unlock); + + status = VMWARE_BALLOON_CMD(UNLOCK, pfn, dummy); + if (vmballoon_check_status(b, status)) + return true; + + pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); + STATS_INC(b->stats.unlock_fail); + return false; +} + +/* + * Quickly release all pages allocated for the balloon. This function is + * called when host decides to "reset" balloon for one reason or another. + * Unlike normal "deflate" we do not (shall not) notify host of the pages + * being released. + */ +static void vmballoon_pop(struct vmballoon *b) +{ + struct page *page, *next; + unsigned int count = 0; + + list_for_each_entry_safe(page, next, &b->pages, lru) { + list_del(&page->lru); + __free_page(page); + STATS_INC(b->stats.free); + b->size--; + + if (++count >= b->rate_free) { + count = 0; + cond_resched(); + } + } +} + +/* + * Perform standard reset sequence by popping the balloon (in case it + * is not empty) and then restarting protocol. This operation normally + * happens when host responds with VMW_BALLOON_ERROR_RESET to a command. + */ +static void vmballoon_reset(struct vmballoon *b) +{ + /* free all pages, skipping monitor unlock */ + vmballoon_pop(b); + + if (vmballoon_send_start(b)) { + b->reset_required = false; + if (!vmballoon_send_guest_id(b)) + pr_err("failed to send guest ID to the host\n"); + } +} + +/* + * Allocate (or reserve) a page for the balloon and notify the host. If host + * refuses the page put it on "refuse" list and allocate another one until host + * is satisfied. "Refused" pages are released at the end of inflation cycle + * (when we allocate b->rate_alloc pages). + */ +static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) +{ + struct page *page; + gfp_t flags; + bool locked = false; + + do { + if (!can_sleep) + STATS_INC(b->stats.alloc); + else + STATS_INC(b->stats.sleep_alloc); + + flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP; + page = alloc_page(flags); + if (!page) { + if (!can_sleep) + STATS_INC(b->stats.alloc_fail); + else + STATS_INC(b->stats.sleep_alloc_fail); + return -ENOMEM; + } + + /* inform monitor */ + locked = vmballoon_send_lock_page(b, page_to_pfn(page)); + if (!locked) { + if (b->reset_required) { + __free_page(page); + return -EIO; + } + + /* place on list of non-balloonable pages, retry allocation */ + list_add(&page->lru, &b->refused_pages); + STATS_INC(b->stats.refused_alloc); + } + } while (!locked); + + /* track allocated page */ + list_add(&page->lru, &b->pages); + + /* update balloon size */ + b->size++; + + return 0; +} + +/* + * Release the page allocated for the balloon. Note that we first notify + * the host so it can make sure the page will be available for the guest + * to use, if needed. + */ +static int vmballoon_release_page(struct vmballoon *b, struct page *page) +{ + if (!vmballoon_send_unlock_page(b, page_to_pfn(page))) + return -EIO; + + list_del(&page->lru); + + /* deallocate page */ + __free_page(page); + STATS_INC(b->stats.free); + + /* update balloon size */ + b->size--; + + return 0; +} + +/* + * Release pages that were allocated while attempting to inflate the + * balloon but were refused by the host for one reason or another. + */ +static void vmballoon_release_refused_pages(struct vmballoon *b) +{ + struct page *page, *next; + + list_for_each_entry_safe(page, next, &b->refused_pages, lru) { + list_del(&page->lru); + __free_page(page); + STATS_INC(b->stats.refused_free); + } +} + +/* + * Inflate the balloon towards its target size. Note that we try to limit + * the rate of allocation to make sure we are not choking the rest of the + * system. + */ +static void vmballoon_inflate(struct vmballoon *b) +{ + unsigned int goal; + unsigned int rate; + unsigned int i; + unsigned int allocations = 0; + int error = 0; + bool alloc_can_sleep = false; + + pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); + + /* + * First try NOSLEEP page allocations to inflate balloon. + * + * If we do not throttle nosleep allocations, we can drain all + * free pages in the guest quickly (if the balloon target is high). + * As a side-effect, draining free pages helps to inform (force) + * the guest to start swapping if balloon target is not met yet, + * which is a desired behavior. However, balloon driver can consume + * all available CPU cycles if too many pages are allocated in a + * second. Therefore, we throttle nosleep allocations even when + * the guest is not under memory pressure. OTOH, if we have already + * predicted that the guest is under memory pressure, then we + * slowdown page allocations considerably. + */ + + goal = b->target - b->size; + /* + * Start with no sleep allocation rate which may be higher + * than sleeping allocation rate. + */ + rate = b->slow_allocation_cycles ? + b->rate_alloc : VMW_BALLOON_NOSLEEP_ALLOC_MAX; + + pr_debug("%s - goal: %d, no-sleep rate: %d, sleep rate: %d\n", + __func__, goal, rate, b->rate_alloc); + + for (i = 0; i < goal; i++) { + + error = vmballoon_reserve_page(b, alloc_can_sleep); + if (error) { + if (error != -ENOMEM) { + /* + * Not a page allocation failure, stop this + * cycle. Maybe we'll get new target from + * the host soon. + */ + break; + } + + if (alloc_can_sleep) { + /* + * CANSLEEP page allocation failed, so guest + * is under severe memory pressure. Quickly + * decrease allocation rate. + */ + b->rate_alloc = max(b->rate_alloc / 2, + VMW_BALLOON_RATE_ALLOC_MIN); + break; + } + + /* + * NOSLEEP page allocation failed, so the guest is + * under memory pressure. Let us slow down page + * allocations for next few cycles so that the guest + * gets out of memory pressure. Also, if we already + * allocated b->rate_alloc pages, let's pause, + * otherwise switch to sleeping allocations. + */ + b->slow_allocation_cycles = VMW_BALLOON_SLOW_CYCLES; + + if (i >= b->rate_alloc) + break; + + alloc_can_sleep = true; + /* Lower rate for sleeping allocations. */ + rate = b->rate_alloc; + } + + if (++allocations > VMW_BALLOON_YIELD_THRESHOLD) { + cond_resched(); + allocations = 0; + } + + if (i >= rate) { + /* We allocated enough pages, let's take a break. */ + break; + } + } + + /* + * We reached our goal without failures so try increasing + * allocation rate. + */ + if (error == 0 && i >= b->rate_alloc) { + unsigned int mult = i / b->rate_alloc; + + b->rate_alloc = + min(b->rate_alloc + mult * VMW_BALLOON_RATE_ALLOC_INC, + VMW_BALLOON_RATE_ALLOC_MAX); + } + + vmballoon_release_refused_pages(b); +} + +/* + * Decrease the size of the balloon allowing guest to use more memory. + */ +static void vmballoon_deflate(struct vmballoon *b) +{ + struct page *page, *next; + unsigned int i = 0; + unsigned int goal; + int error; + + pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); + + /* limit deallocation rate */ + goal = min(b->size - b->target, b->rate_free); + + pr_debug("%s - goal: %d, rate: %d\n", __func__, goal, b->rate_free); + + /* free pages to reach target */ + list_for_each_entry_safe(page, next, &b->pages, lru) { + error = vmballoon_release_page(b, page); + if (error) { + /* quickly decrease rate in case of error */ + b->rate_free = max(b->rate_free / 2, + VMW_BALLOON_RATE_FREE_MIN); + return; + } + + if (++i >= goal) + break; + } + + /* slowly increase rate if there were no errors */ + b->rate_free = min(b->rate_free + VMW_BALLOON_RATE_FREE_INC, + VMW_BALLOON_RATE_FREE_MAX); +} + +/* + * Balloon work function: reset protocol, if needed, get the new size and + * adjust balloon as needed. Repeat in 1 sec. + */ +static void vmballoon_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct vmballoon *b = container_of(dwork, struct vmballoon, dwork); + unsigned int target; + + STATS_INC(b->stats.timer); + + if (b->reset_required) + vmballoon_reset(b); + + if (b->slow_allocation_cycles > 0) + b->slow_allocation_cycles--; + + if (vmballoon_send_get_target(b, &target)) { + /* update target, adjust size */ + b->target = target; + + if (b->size < target) + vmballoon_inflate(b); + else if (b->size > target) + vmballoon_deflate(b); + } + + queue_delayed_work(vmballoon_wq, dwork, round_jiffies_relative(HZ)); +} + +/* + * DEBUGFS Interface + */ +#ifdef CONFIG_DEBUG_FS + +static int vmballoon_debug_show(struct seq_file *f, void *offset) +{ + struct vmballoon *b = f->private; + struct vmballoon_stats *stats = &b->stats; + + /* format size info */ + seq_printf(f, + "target: %8d pages\n" + "current: %8d pages\n", + b->target, b->size); + + /* format rate info */ + seq_printf(f, + "rateNoSleepAlloc: %8d pages/sec\n" + "rateSleepAlloc: %8d pages/sec\n" + "rateFree: %8d pages/sec\n", + VMW_BALLOON_NOSLEEP_ALLOC_MAX, + b->rate_alloc, b->rate_free); + + seq_printf(f, + "\n" + "timer: %8u\n" + "start: %8u (%4u failed)\n" + "guestType: %8u (%4u failed)\n" + "lock: %8u (%4u failed)\n" + "unlock: %8u (%4u failed)\n" + "target: %8u (%4u failed)\n" + "primNoSleepAlloc: %8u (%4u failed)\n" + "primCanSleepAlloc: %8u (%4u failed)\n" + "primFree: %8u\n" + "errAlloc: %8u\n" + "errFree: %8u\n", + stats->timer, + stats->start, stats->start_fail, + stats->guest_type, stats->guest_type_fail, + stats->lock, stats->lock_fail, + stats->unlock, stats->unlock_fail, + stats->target, stats->target_fail, + stats->alloc, stats->alloc_fail, + stats->sleep_alloc, stats->sleep_alloc_fail, + stats->free, + stats->refused_alloc, stats->refused_free); + + return 0; +} + +static int vmballoon_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vmballoon_debug_show, inode->i_private); +} + +static const struct file_operations vmballoon_debug_fops = { + .owner = THIS_MODULE, + .open = vmballoon_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init vmballoon_debugfs_init(struct vmballoon *b) +{ + int error; + + b->dbg_entry = debugfs_create_file("vmmemctl", S_IRUGO, NULL, b, + &vmballoon_debug_fops); + if (IS_ERR(b->dbg_entry)) { + error = PTR_ERR(b->dbg_entry); + pr_err("failed to create debugfs entry, error: %d\n", error); + return error; + } + + return 0; +} + +static void __exit vmballoon_debugfs_exit(struct vmballoon *b) +{ + debugfs_remove(b->dbg_entry); +} + +#else + +static inline int vmballoon_debugfs_init(struct vmballoon *b) +{ + return 0; +} + +static inline void vmballoon_debugfs_exit(struct vmballoon *b) +{ +} + +#endif /* CONFIG_DEBUG_FS */ + +static int __init vmballoon_init(void) +{ + int error; + + /* + * Check if we are running on VMware's hypervisor and bail out + * if we are not. + */ + if (!vmware_platform()) + return -ENODEV; + + vmballoon_wq = create_freezeable_workqueue("vmmemctl"); + if (!vmballoon_wq) { + pr_err("failed to create workqueue\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&balloon.pages); + INIT_LIST_HEAD(&balloon.refused_pages); + + /* initialize rates */ + balloon.rate_alloc = VMW_BALLOON_RATE_ALLOC_MAX; + balloon.rate_free = VMW_BALLOON_RATE_FREE_MAX; + + INIT_DELAYED_WORK(&balloon.dwork, vmballoon_work); + + /* + * Start balloon. + */ + if (!vmballoon_send_start(&balloon)) { + pr_err("failed to send start command to the host\n"); + error = -EIO; + goto fail; + } + + if (!vmballoon_send_guest_id(&balloon)) { + pr_err("failed to send guest ID to the host\n"); + error = -EIO; + goto fail; + } + + error = vmballoon_debugfs_init(&balloon); + if (error) + goto fail; + + queue_delayed_work(vmballoon_wq, &balloon.dwork, 0); + + return 0; + +fail: + destroy_workqueue(vmballoon_wq); + return error; +} +module_init(vmballoon_init); + +static void __exit vmballoon_exit(void) +{ + cancel_delayed_work_sync(&balloon.dwork); + destroy_workqueue(vmballoon_wq); + + vmballoon_debugfs_exit(&balloon); + + /* + * Deallocate all reserved memory, and reset connection with monitor. + * Reset connection before deallocating memory to avoid potential for + * additional spurious resets from guest touching deallocated pages. + */ + vmballoon_send_start(&balloon); + vmballoon_pop(&balloon); +} +module_exit(vmballoon_exit); |