Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Tue, 9 Aug 2005 23:00:05 +0000 (16:00 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Tue, 9 Aug 2005 23:00:05 +0000 (16:00 -0700)
drivers/char/watchdog/i8xx_tco.c
drivers/i2c/busses/i2c-sibyte.c
drivers/message/i2o/Kconfig
drivers/message/i2o/config-osm.c
drivers/serial/cpm_uart/cpm_uart.h
drivers/serial/cpm_uart/cpm_uart_core.c
drivers/serial/cpm_uart/cpm_uart_cpm1.c
kernel/cpuset.c

index f975dab1ddf95417f4c6214a0057661a0cf71c7c..a13395e2c372a9b27355040c9c94eae21040992b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     i8xx_tco 0.07:  TCO timer driver for i8xx chipsets
+ *     i8xx_tco:       TCO timer driver for i8xx chipsets
  *
  *     (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
  *                             http://www.kernelconcepts.de
@@ -63,6 +63,9 @@
  *  20050128 Wim Van Sebroeck <wim@iguana.be>
  *     0.07 Added support for the ICH4-M, ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW
  *          chipsets. Also added support for the "undocumented" ICH7 chipset.
+ *  20050807 Wim Van Sebroeck <wim@iguana.be>
+ *     0.08 Make sure that the watchdog is only "armed" when started.
+ *          (Kernel Bug 4251)
  */
 
 /*
@@ -87,7 +90,7 @@
 #include "i8xx_tco.h"
 
 /* Module and version information */
-#define TCO_VERSION "0.07"
+#define TCO_VERSION "0.08"
 #define TCO_MODULE_NAME "i8xx TCO timer"
 #define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
 #define PFX TCO_MODULE_NAME ": "
@@ -125,10 +128,18 @@ static int tco_timer_start (void)
        unsigned char val;
 
        spin_lock(&tco_lock);
+
+       /* disable chipset's NO_REBOOT bit */
+       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val);
+       val &= 0xfd;
+       pci_write_config_byte (i8xx_tco_pci, 0xd4, val);
+
+       /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
        val = inb (TCO1_CNT + 1);
        val &= 0xf7;
        outb (val, TCO1_CNT + 1);
        val = inb (TCO1_CNT + 1);
+
        spin_unlock(&tco_lock);
 
        if (val & 0x08)
@@ -138,13 +149,20 @@ static int tco_timer_start (void)
 
 static int tco_timer_stop (void)
 {
-       unsigned char val;
+       unsigned char val, val1;
 
        spin_lock(&tco_lock);
+       /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
        val = inb (TCO1_CNT + 1);
        val |= 0x08;
        outb (val, TCO1_CNT + 1);
        val = inb (TCO1_CNT + 1);
+
+       /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
+       val1 |= 0x02;
+       pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
+
        spin_unlock(&tco_lock);
 
        if ((val & 0x08) == 0)
@@ -155,6 +173,7 @@ static int tco_timer_stop (void)
 static int tco_timer_keepalive (void)
 {
        spin_lock(&tco_lock);
+       /* Reload the timer by writing to the TCO Timer Reload register */
        outb (0x01, TCO1_RLD);
        spin_unlock(&tco_lock);
        return 0;
@@ -417,9 +436,8 @@ static unsigned char __init i8xx_tco_getdevice (void)
                        printk (KERN_ERR PFX "failed to get TCOBASE address\n");
                        return 0;
                }
-               /*
-                * Check chipset's NO_REBOOT bit
-                */
+
+               /* Check chipset's NO_REBOOT bit */
                pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
                if (val1 & 0x02) {
                        val1 &= 0xfd;
@@ -430,6 +448,10 @@ static unsigned char __init i8xx_tco_getdevice (void)
                                return 0;       /* Cannot reset NO_REBOOT bit */
                        }
                }
+               /* Disable reboots untill the watchdog starts */
+               val1 |= 0x02;
+               pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
+
                /* Set the TCO_EN bit in SMI_EN register */
                if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) {
                        printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
@@ -505,17 +527,10 @@ out:
 
 static void __exit watchdog_cleanup (void)
 {
-       u8 val;
-
        /* Stop the timer before we leave */
        if (!nowayout)
                tco_timer_stop ();
 
-       /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
-       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val);
-       val |= 0x02;
-       pci_write_config_byte (i8xx_tco_pci, 0xd4, val);
-
        /* Deregister */
        misc_deregister (&i8xx_tco_miscdev);
        unregister_reboot_notifier(&i8xx_tco_notifier);
index 1c99536b673b7775d97ff39091f112aa84cca17e..fa503ed9f86db6d8fb32899d817a9db78d6e60f7 100644 (file)
@@ -23,8 +23,8 @@
 #include <asm/sibyte/sb1250_smbus.h>
 
 static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
-       { NULL, 0, (void *) (KSEG1+A_SMB_BASE(0)) },
-       { NULL, 1, (void *) (KSEG1+A_SMB_BASE(1)) }
+       { NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
+       { NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
 };
 
 static struct i2c_adapter sibyte_board_adapter[2] = {
index 06e8eb19a05c5df2fab8386cd9a68a8ab242e394..43a942a29c2e0fec852c217d3e9867deb8d4d8e4 100644 (file)
@@ -53,6 +53,9 @@ config I2O_CONFIG
          To compile this support as a module, choose M here: the
          module will be called i2o_config.
 
+         Note: If you want to use the new API you have to download the
+         i2o_config patch from http://i2o.shadowconnect.com/
+
 config I2O_CONFIG_OLD_IOCTL
        bool "Enable ioctls (OBSOLETE)"
        depends on I2O_CONFIG
index fe2e7afc9eae25d0cc52ea46a5d96120e4771958..af32ab4e90cd28eff940016f57f9e2c06e50cf6d 100644 (file)
 
 static struct i2o_driver i2o_config_driver;
 
-/* Special file operations for sysfs */
-struct fops_attribute {
-       struct bin_attribute bin;
-       struct file_operations fops;
-};
-
-/**
- *     sysfs_read_dummy
- */
-static ssize_t sysfs_read_dummy(struct kobject *kobj, char *buf, loff_t offset,
-                               size_t count)
-{
-       return 0;
-};
-
-/**
- *     sysfs_write_dummy
- */
-static ssize_t sysfs_write_dummy(struct kobject *kobj, char *buf, loff_t offset,
-                                size_t count)
-{
-       return 0;
-};
-
-/**
- *     sysfs_create_fops_file - Creates attribute with special file operations
- *     @kobj: kobject which should contains the attribute
- *     @attr: attributes which should be used to create file
- *
- *     First creates attribute @attr in kobject @kobj. If it is the first time
- *     this function is called, merge old fops from sysfs with new one and
- *     write it back. Afterwords the new fops will be set for the created
- *     attribute.
- *
- *     Returns 0 on success or negative error code on failure.
- */
-static int sysfs_create_fops_file(struct kobject *kobj,
-                                 struct fops_attribute *attr)
-{
-       struct file_operations tmp, *fops;
-       struct dentry *d;
-       struct qstr qstr;
-       int rc;
-
-       fops = &attr->fops;
-
-       if (fops->read)
-               attr->bin.read = sysfs_read_dummy;
-
-       if (fops->write)
-               attr->bin.write = sysfs_write_dummy;
-
-       if ((rc = sysfs_create_bin_file(kobj, &attr->bin)))
-               return rc;
-
-       qstr.name = attr->bin.attr.name;
-       qstr.len = strlen(qstr.name);
-       qstr.hash = full_name_hash(qstr.name, qstr.len);
-
-       if ((d = lookup_hash(&qstr, kobj->dentry))) {
-               if (!fops->owner) {
-                       memcpy(&tmp, d->d_inode->i_fop, sizeof(tmp));
-                       if (fops->read)
-                               tmp.read = fops->read;
-                       if (fops->write)
-                               tmp.write = fops->write;
-                       memcpy(fops, &tmp, sizeof(tmp));
-               }
-
-               d->d_inode->i_fop = fops;
-       } else
-               sysfs_remove_bin_file(kobj, &attr->bin);
-
-       return -ENOENT;
-};
-
-/**
- *     sysfs_remove_fops_file - Remove attribute with special file operations
- *     @kobj: kobject which contains the attribute
- *     @attr: attributes which are used to create file
- *
- *     Only wrapper arround sysfs_remove_bin_file()
- *
- *     Returns 0 on success or negative error code on failure.
- */
-static inline int sysfs_remove_fops_file(struct kobject *kobj,
-                                        struct fops_attribute *attr)
-{
-       return sysfs_remove_bin_file(kobj, &attr->bin);
-};
-
-/**
- *     i2o_config_read_hrt - Returns the HRT of the controller
- *     @kob: kernel object handle
- *     @buf: buffer into which the HRT should be copied
- *     @off: file offset
- *     @count: number of bytes to read
- *
- *     Put @count bytes starting at @off into @buf from the HRT of the I2O
- *     controller corresponding to @kobj.
- *
- *     Returns number of bytes copied into buffer.
- */
-static ssize_t i2o_config_read_hrt(struct kobject *kobj, char *buf,
-                                  loff_t offset, size_t count)
-{
-       struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop;
-       i2o_hrt *hrt = c->hrt.virt;
-
-       u32 size = (hrt->num_entries * hrt->entry_len + 2) * 4;
-
-       if (offset > size)
-               return 0;
-
-       if (offset + count > size)
-               count = size - offset;
-
-       memcpy(buf, (u8 *) hrt + offset, count);
-
-       return count;
-};
-
-/**
- *     i2o_config_read_lct - Returns the LCT of the controller
- *     @kob: kernel object handle
- *     @buf: buffer into which the LCT should be copied
- *     @off: file offset
- *     @count: number of bytes to read
- *
- *     Put @count bytes starting at @off into @buf from the LCT of the I2O
- *     controller corresponding to @kobj.
- *
- *     Returns number of bytes copied into buffer.
- */
-static ssize_t i2o_config_read_lct(struct kobject *kobj, char *buf,
-                                  loff_t offset, size_t count)
-{
-       struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop;
-       u32 size = c->lct->table_size * 4;
-
-       if (offset > size)
-               return 0;
-
-       if (offset + count > size)
-               count = size - offset;
-
-       memcpy(buf, (u8 *) c->lct + offset, count);
-
-       return count;
-};
-
-#define I2O_CONFIG_SW_ATTR(_name,_mode,_type,_swid) \
-static ssize_t i2o_config_##_name##_read(struct file *file, char __user *buf, size_t count, loff_t * offset) { \
-       return i2o_config_sw_read(file, buf, count, offset, _type, _swid); \
-};\
-\
-static ssize_t i2o_config_##_name##_write(struct file *file, const char __user *buf, size_t count, loff_t * offset) { \
-       return i2o_config_sw_write(file, buf, count, offset, _type, _swid); \
-}; \
-\
-static struct fops_attribute i2o_config_attr_##_name = { \
-       .bin = { .attr = { .name = __stringify(_name), .mode = _mode, \
-                          .owner = THIS_MODULE }, \
-                .size = 0, }, \
-       .fops = { .write = i2o_config_##_name##_write, \
-                 .read = i2o_config_##_name##_read} \
-};
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-
-/**
- *     i2o_config_dpt_reagion - Converts type and id to flash region
- *     @swtype: type of software module reading
- *     @swid: id of software which should be read
- *
- *     Converts type and id from I2O spec to the matching region for DPT /
- *     Adaptec controllers.
- *
- *     Returns region which match type and id or -1 on error.
- */
-static u32 i2o_config_dpt_region(u8 swtype, u8 swid)
-{
-       switch (swtype) {
-       case I2O_SOFTWARE_MODULE_IRTOS:
-               /*
-                * content: operation firmware
-                * region size:
-                *      0xbc000 for 2554, 3754, 2564, 3757
-                *      0x170000 for 2865
-                *      0x17c000 for 3966
-                */
-               if (!swid)
-                       return 0;
-
-               break;
-
-       case I2O_SOFTWARE_MODULE_IOP_PRIVATE:
-               /*
-                * content: BIOS and SMOR
-                * BIOS size: first 0x8000 bytes
-                * region size:
-                *      0x40000 for 2554, 3754, 2564, 3757
-                *      0x80000 for 2865, 3966
-                */
-               if (!swid)
-                       return 1;
-
-               break;
-
-       case I2O_SOFTWARE_MODULE_IOP_CONFIG:
-               switch (swid) {
-               case 0:
-                       /*
-                        * content: NVRAM defaults
-                        * region size: 0x2000 bytes
-                        */
-                       return 2;
-               case 1:
-                       /*
-                        * content: serial number
-                        * region size: 0x2000 bytes
-                        */
-                       return 3;
-               }
-               break;
-       }
-
-       return -1;
-};
-
-#endif
-
-/**
- *     i2o_config_sw_read - Read a software module from controller
- *     @file: file pointer
- *     @buf: buffer into which the data should be copied
- *     @count: number of bytes to read
- *     @off: file offset
- *     @swtype: type of software module reading
- *     @swid: id of software which should be read
- *
- *     Transfers @count bytes at offset @offset from IOP into buffer using
- *     type @swtype and id @swid as described in I2O spec.
- *
- *     Returns number of bytes copied into buffer or error code on failure.
- */
-static ssize_t i2o_config_sw_read(struct file *file, char __user * buf,
-                                 size_t count, loff_t * offset, u8 swtype,
-                                 u32 swid)
-{
-       struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata;
-       struct kobject *kobj = sd->s_element;
-       struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop;
-       u32 m, function = I2O_CMD_SW_UPLOAD;
-       struct i2o_dma buffer;
-       struct i2o_message __iomem *msg;
-       u32 __iomem *mptr;
-       int rc, status;
-
-       m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
-       if (m == I2O_QUEUE_EMPTY)
-               return -EBUSY;
-
-       mptr = &msg->body[3];
-
-       if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL))) {
-               i2o_msg_nop(c, m);
-               return rc;
-       }
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (c->adaptec) {
-               mptr = &msg->body[4];
-               function = I2O_CMD_PRIVATE;
-
-               writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]);
-
-               writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_READ,
-                      &msg->body[0]);
-               writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]);
-               writel(*offset, &msg->body[2]);
-               writel(count, &msg->body[3]);
-       } else
-#endif
-               writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);
-
-       writel(0xD0000000 | count, mptr++);
-       writel(buffer.phys, mptr);
-
-       writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]);
-       writel(i2o_config_driver.context, &msg->u.head[2]);
-       writel(0, &msg->u.head[3]);
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (!c->adaptec)
-#endif
-       {
-               writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]);
-               writel(0, &msg->body[1]);
-               writel(swid, &msg->body[2]);
-       }
-
-       status = i2o_msg_post_wait_mem(c, m, 60, &buffer);
-
-       if (status == I2O_POST_WAIT_OK) {
-               if (!(rc = copy_to_user(buf, buffer.virt, count))) {
-                       rc = count;
-                       *offset += count;
-               }
-       } else
-               rc = -EIO;
-
-       if (status != -ETIMEDOUT)
-               i2o_dma_free(&c->pdev->dev, &buffer);
-
-       return rc;
-};
-
-/**
- *     i2o_config_sw_write - Write a software module to controller
- *     @file: file pointer
- *     @buf: buffer into which the data should be copied
- *     @count: number of bytes to read
- *     @off: file offset
- *     @swtype: type of software module writing
- *     @swid: id of software which should be written
- *
- *     Transfers @count bytes at offset @offset from buffer to IOP using
- *     type @swtype and id @swid as described in I2O spec.
- *
- *     Returns number of bytes copied from buffer or error code on failure.
- */
-static ssize_t i2o_config_sw_write(struct file *file, const char __user * buf,
-                                  size_t count, loff_t * offset, u8 swtype,
-                                  u32 swid)
-{
-       struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata;
-       struct kobject *kobj = sd->s_element;
-       struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop;
-       u32 m, function = I2O_CMD_SW_DOWNLOAD;
-       struct i2o_dma buffer;
-       struct i2o_message __iomem *msg;
-       u32 __iomem *mptr;
-       int rc, status;
-
-       m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
-       if (m == I2O_QUEUE_EMPTY)
-               return -EBUSY;
-
-       mptr = &msg->body[3];
-
-       if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL)))
-               goto nop_msg;
-
-       if ((rc = copy_from_user(buffer.virt, buf, count)))
-               goto free_buffer;
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (c->adaptec) {
-               mptr = &msg->body[4];
-               function = I2O_CMD_PRIVATE;
-
-               writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]);
-
-               writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_WRITE,
-                      &msg->body[0]);
-               writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]);
-               writel(*offset, &msg->body[2]);
-               writel(count, &msg->body[3]);
-       } else
-#endif
-               writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);
-
-       writel(0xD4000000 | count, mptr++);
-       writel(buffer.phys, mptr);
-
-       writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]);
-       writel(i2o_config_driver.context, &msg->u.head[2]);
-       writel(0, &msg->u.head[3]);
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (!c->adaptec)
-#endif
-       {
-               writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]);
-               writel(0, &msg->body[1]);
-               writel(swid, &msg->body[2]);
-       }
-
-       status = i2o_msg_post_wait_mem(c, m, 60, &buffer);
-
-       if (status != -ETIMEDOUT)
-               i2o_dma_free(&c->pdev->dev, &buffer);
-
-       if (status != I2O_POST_WAIT_OK)
-               return -EIO;
-
-       *offset += count;
-
-       return count;
-
-      free_buffer:
-       i2o_dma_free(&c->pdev->dev, &buffer);
-
-      nop_msg:
-       i2o_msg_nop(c, m);
-
-       return rc;
-};
-
-/* attribute for HRT in sysfs */
-static struct bin_attribute i2o_config_hrt_attr = {
-       .attr = {
-                .name = "hrt",
-                .mode = S_IRUGO,
-                .owner = THIS_MODULE},
-       .size = 0,
-       .read = i2o_config_read_hrt
-};
-
-/* attribute for LCT in sysfs */
-static struct bin_attribute i2o_config_lct_attr = {
-       .attr = {
-                .name = "lct",
-                .mode = S_IRUGO,
-                .owner = THIS_MODULE},
-       .size = 0,
-       .read = i2o_config_read_lct
-};
-
-/* IRTOS firmware access */
-I2O_CONFIG_SW_ATTR(irtos, S_IWRSR, I2O_SOFTWARE_MODULE_IRTOS, 0);
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-
-/*
- * attribute for BIOS / SMOR, nvram and serial number access on DPT / Adaptec
- * controllers
- */
-I2O_CONFIG_SW_ATTR(bios, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_PRIVATE, 0);
-I2O_CONFIG_SW_ATTR(nvram, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 0);
-I2O_CONFIG_SW_ATTR(serial, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 1);
-
-#endif
-
-/**
- *     i2o_config_notify_controller_add - Notify of added controller
- *     @c: the controller which was added
- *
- *     If a I2O controller is added, we catch the notification to add sysfs
- *     entries.
- */
-static void i2o_config_notify_controller_add(struct i2o_controller *c)
-{
-       struct kobject *kobj = &c->exec->device.kobj;
-
-       sysfs_create_bin_file(kobj, &i2o_config_hrt_attr);
-       sysfs_create_bin_file(kobj, &i2o_config_lct_attr);
-
-       sysfs_create_fops_file(kobj, &i2o_config_attr_irtos);
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (c->adaptec) {
-               sysfs_create_fops_file(kobj, &i2o_config_attr_bios);
-               sysfs_create_fops_file(kobj, &i2o_config_attr_nvram);
-               sysfs_create_fops_file(kobj, &i2o_config_attr_serial);
-       }
-#endif
-};
-
-/**
- *     i2o_config_notify_controller_remove - Notify of removed controller
- *     @c: the controller which was removed
- *
- *     If a I2O controller is removed, we catch the notification to remove the
- *     sysfs entries.
- */
-static void i2o_config_notify_controller_remove(struct i2o_controller *c)
-{
-       struct kobject *kobj = &c->exec->device.kobj;
-
-#ifdef CONFIG_I2O_EXT_ADAPTEC
-       if (c->adaptec) {
-               sysfs_remove_fops_file(kobj, &i2o_config_attr_serial);
-               sysfs_remove_fops_file(kobj, &i2o_config_attr_nvram);
-               sysfs_remove_fops_file(kobj, &i2o_config_attr_bios);
-       }
-#endif
-       sysfs_remove_fops_file(kobj, &i2o_config_attr_irtos);
-
-       sysfs_remove_bin_file(kobj, &i2o_config_lct_attr);
-       sysfs_remove_bin_file(kobj, &i2o_config_hrt_attr);
-};
-
 /* Config OSM driver struct */
 static struct i2o_driver i2o_config_driver = {
        .name = OSM_NAME,
-       .notify_controller_add = i2o_config_notify_controller_add,
-       .notify_controller_remove = i2o_config_notify_controller_remove
 };
 
 #ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
index 5f6187baad86454f61a50053b824f97b62177e28..73c8a088c160fcda57b98bbc6574cb8f673dadb5 100644 (file)
 #define TX_NUM_FIFO    4
 #define TX_BUF_SIZE    32
 
+#define SCC_WAIT_CLOSING 100
+
 struct uart_cpm_port {
        struct uart_port        port;
-       u16                     rx_nrfifos;     
+       u16                     rx_nrfifos;
        u16                     rx_fifosize;
-       u16                     tx_nrfifos;     
+       u16                     tx_nrfifos;
        u16                     tx_fifosize;
-       smc_t                   *smcp;  
+       smc_t                   *smcp;
        smc_uart_t              *smcup;
        scc_t                   *sccp;
        scc_uart_t              *sccup;
@@ -67,6 +69,8 @@ struct uart_cpm_port {
        int                      bits;
        /* Keep track of 'odd' SMC2 wirings */
        int                     is_portb;
+       /* wait on close if needed */
+       int                     wait_closing;
 };
 
 extern int cpm_uart_port_map[UART_NR];
index 29db677d4284dd5455a720acfe3a9d077bab4d40..d639ac92a117c73eec982310534d7b2b4d908df8 100644 (file)
@@ -9,9 +9,10 @@
  *
  *  Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
  *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
- * 
+ *
  *  Copyright (C) 2004 Freescale Semiconductor, Inc.
  *            (C) 2004 Intracom, S.A.
+ *            (C) 2005 MontaVista Software, Inc. by Vitaly Bordug <vbordug@ru.mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -70,8 +71,22 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
 
 /**************************************************************/
 
+static inline unsigned long cpu2cpm_addr(void *addr)
+{
+       if ((unsigned long)addr >= CPM_ADDR)
+               return (unsigned long)addr;
+       return virt_to_bus(addr);
+}
+
+static inline void *cpm2cpu_addr(unsigned long addr)
+{
+       if (addr >= CPM_ADDR)
+               return (void *)addr;
+       return bus_to_virt(addr);
+}
+
 /*
- * Check, if transmit buffers are processed            
+ * Check, if transmit buffers are processed
 */
 static unsigned int cpm_uart_tx_empty(struct uart_port *port)
 {
@@ -143,15 +158,18 @@ static void cpm_uart_start_tx(struct uart_port *port, unsigned int tty_start)
        }
 
        if (cpm_uart_tx_pump(port) != 0) {
-               if (IS_SMC(pinfo))
+               if (IS_SMC(pinfo)) {
                        smcp->smc_smcm |= SMCM_TX;
-               else
+                       smcp->smc_smcmr |= SMCMR_TEN;
+               } else {
                        sccp->scc_sccm |= UART_SCCM_TX;
+                       pinfo->sccp->scc_gsmrl |= SCC_GSMRL_ENT;
+               }
        }
 }
 
 /*
- * Stop receiver 
+ * Stop receiver
  */
 static void cpm_uart_stop_rx(struct uart_port *port)
 {
@@ -176,7 +194,7 @@ static void cpm_uart_enable_ms(struct uart_port *port)
 }
 
 /*
- * Generate a break. 
+ * Generate a break.
  */
 static void cpm_uart_break_ctl(struct uart_port *port, int break_state)
 {
@@ -231,7 +249,7 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
                /* get number of characters, and check spce in flip-buffer */
                i = bdp->cbd_datlen;
 
-               /* If we have not enough room in tty flip buffer, then we try 
+               /* If we have not enough room in tty flip buffer, then we try
                 * later, which will be the next rx-interrupt or a timeout
                 */
                if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) {
@@ -243,7 +261,7 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
                }
 
                /* get pointer */
-               cp = (unsigned char *)bus_to_virt(bdp->cbd_bufaddr);
+               cp = cpm2cpu_addr(bdp->cbd_bufaddr);
 
                /* loop through the buffer */
                while (i-- > 0) {
@@ -265,13 +283,14 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
                }               /* End while (i--) */
 
                /* This BD is ready to be used again. Clear status. get next */
-               bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+               bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID);
                bdp->cbd_sc |= BD_SC_EMPTY;
 
                if (bdp->cbd_sc & BD_SC_WRAP)
                        bdp = pinfo->rx_bd_base;
                else
                        bdp++;
+
        } /* End for (;;) */
 
        /* Write back buffer pointer */
@@ -336,22 +355,22 @@ static irqreturn_t cpm_uart_int(int irq, void *data, struct pt_regs *regs)
 
        if (IS_SMC(pinfo)) {
                events = smcp->smc_smce;
+               smcp->smc_smce = events;
                if (events & SMCM_BRKE)
                        uart_handle_break(port);
                if (events & SMCM_RX)
                        cpm_uart_int_rx(port, regs);
                if (events & SMCM_TX)
                        cpm_uart_int_tx(port, regs);
-               smcp->smc_smce = events;
        } else {
                events = sccp->scc_scce;
+               sccp->scc_scce = events;
                if (events & UART_SCCM_BRKE)
                        uart_handle_break(port);
                if (events & UART_SCCM_RX)
                        cpm_uart_int_rx(port, regs);
                if (events & UART_SCCM_TX)
                        cpm_uart_int_tx(port, regs);
-               sccp->scc_scce = events;
        }
        return (events) ? IRQ_HANDLED : IRQ_NONE;
 }
@@ -360,6 +379,7 @@ static int cpm_uart_startup(struct uart_port *port)
 {
        int retval;
        struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+       int line = pinfo - cpm_uart_ports;
 
        pr_debug("CPM uart[%d]:startup\n", port->line);
 
@@ -376,9 +396,19 @@ static int cpm_uart_startup(struct uart_port *port)
                pinfo->sccp->scc_sccm |= UART_SCCM_RX;
        }
 
+       if (!(pinfo->flags & FLAG_CONSOLE))
+               cpm_line_cr_cmd(line,CPM_CR_INIT_TRX);
        return 0;
 }
 
+inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo)
+{
+       unsigned long target_jiffies = jiffies + pinfo->wait_closing;
+
+       while (!time_after(jiffies, target_jiffies))
+               schedule();
+}
+
 /*
  * Shutdown the uart
  */
@@ -394,6 +424,12 @@ static void cpm_uart_shutdown(struct uart_port *port)
 
        /* If the port is not the console, disable Rx and Tx. */
        if (!(pinfo->flags & FLAG_CONSOLE)) {
+               /* Wait for all the BDs marked sent */
+               while(!cpm_uart_tx_empty(port))
+                       schedule_timeout(2);
+               if(pinfo->wait_closing)
+                       cpm_uart_wait_until_send(pinfo);
+
                /* Stop uarts */
                if (IS_SMC(pinfo)) {
                        volatile smc_t *smcp = pinfo->smcp;
@@ -502,7 +538,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
         */
        if ((termios->c_cflag & CREAD) == 0)
                port->read_status_mask &= ~BD_SC_EMPTY;
-       
+
        spin_lock_irqsave(&port->lock, flags);
 
        /* Start bit has not been added (so don't, because we would just
@@ -569,7 +605,8 @@ static int cpm_uart_tx_pump(struct uart_port *port)
                /* Pick next descriptor and fill from buffer */
                bdp = pinfo->tx_cur;
 
-               p = bus_to_virt(bdp->cbd_bufaddr);
+               p = cpm2cpu_addr(bdp->cbd_bufaddr);
+
                *p++ = xmit->buf[xmit->tail];
                bdp->cbd_datlen = 1;
                bdp->cbd_sc |= BD_SC_READY;
@@ -595,7 +632,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
 
        while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) {
                count = 0;
-               p = bus_to_virt(bdp->cbd_bufaddr);
+               p = cpm2cpu_addr(bdp->cbd_bufaddr);
                while (count < pinfo->tx_fifosize) {
                        *p++ = xmit->buf[xmit->tail];
                        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -606,6 +643,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
                }
                bdp->cbd_datlen = count;
                bdp->cbd_sc |= BD_SC_READY;
+               __asm__("eieio");
                /* Get next BD. */
                if (bdp->cbd_sc & BD_SC_WRAP)
                        bdp = pinfo->tx_bd_base;
@@ -643,12 +681,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
        mem_addr = pinfo->mem_addr;
        bdp = pinfo->rx_cur = pinfo->rx_bd_base;
        for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+               bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
                bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
                mem_addr += pinfo->rx_fifosize;
        }
-       
-       bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+
+       bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
        bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
 
        /* Set the physical address of the host memory
@@ -658,12 +696,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
        mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
        bdp = pinfo->tx_cur = pinfo->tx_bd_base;
        for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+               bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
                bdp->cbd_sc = BD_SC_INTRPT;
                mem_addr += pinfo->tx_fifosize;
        }
-       
-       bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+
+       bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
        bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT;
 }
 
@@ -763,6 +801,8 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
        /* Using idle charater time requires some additional tuning.  */
        up->smc_mrblr = pinfo->rx_fifosize;
        up->smc_maxidl = pinfo->rx_fifosize;
+       up->smc_brklen = 0;
+       up->smc_brkec = 0;
        up->smc_brkcr = 1;
 
        cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
@@ -796,7 +836,7 @@ static int cpm_uart_request_port(struct uart_port *port)
        /*
         * Setup any port IO, connect any baud rate generators,
         * etc.  This is expected to be handled by board
-        * dependant code 
+        * dependant code
         */
        if (pinfo->set_lineif)
                pinfo->set_lineif(pinfo);
@@ -815,6 +855,10 @@ static int cpm_uart_request_port(struct uart_port *port)
                return ret;
 
        cpm_uart_initbd(pinfo);
+       if (IS_SMC(pinfo))
+               cpm_uart_init_smc(pinfo);
+       else
+               cpm_uart_init_scc(pinfo);
 
        return 0;
 }
@@ -869,7 +913,7 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                .flags = FLAG_SMC,
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = smc1_lineif,
        },
@@ -883,7 +927,7 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                .flags = FLAG_SMC,
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = smc2_lineif,
 #ifdef CONFIG_SERIAL_CPM_ALT_SMC2
@@ -899,9 +943,10 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc1_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC2] = {
                .port = {
@@ -912,9 +957,10 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc2_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC3] = {
                .port = {
@@ -925,9 +971,10 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc3_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC4] = {
                .port = {
@@ -938,9 +985,10 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc4_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
 };
 
@@ -983,11 +1031,8 @@ static void cpm_uart_console_write(struct console *co, const char *s,
                 * If the buffer address is in the CPM DPRAM, don't
                 * convert it.
                 */
-               if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
-                       cp = (unsigned char *) (bdp->cbd_bufaddr);
-               else
-                       cp = bus_to_virt(bdp->cbd_bufaddr);
-               
+               cp = cpm2cpu_addr(bdp->cbd_bufaddr);
+
                *cp = *s;
 
                bdp->cbd_datlen = 1;
@@ -1003,10 +1048,7 @@ static void cpm_uart_console_write(struct console *co, const char *s,
                        while ((bdp->cbd_sc & BD_SC_READY) != 0)
                                ;
 
-                       if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
-                               cp = (unsigned char *) (bdp->cbd_bufaddr);
-                       else
-                               cp = bus_to_virt(bdp->cbd_bufaddr);
+                       cp = cpm2cpu_addr(bdp->cbd_bufaddr);
 
                        *cp = 13;
                        bdp->cbd_datlen = 1;
@@ -1045,7 +1087,7 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
        port =
            (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]];
        pinfo = (struct uart_cpm_port *)port;
-       
+
        pinfo->flags |= FLAG_CONSOLE;
 
        if (options) {
@@ -1062,7 +1104,7 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
        /*
         * Setup any port IO, connect any baud rate generators,
         * etc.  This is expected to be handled by board
-        * dependant code 
+        * dependant code
         */
        if (pinfo->set_lineif)
                pinfo->set_lineif(pinfo);
@@ -1092,14 +1134,14 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
        return 0;
 }
 
-extern struct uart_driver cpm_reg;
+static struct uart_driver cpm_reg;
 static struct console cpm_scc_uart_console = {
-       .name           "ttyCPM",
-       .write          cpm_uart_console_write,
-       .device         uart_console_device,
-       .setup          cpm_uart_console_setup,
-       .flags          CON_PRINTBUFFER,
-       .index          -1,
+       .name           "ttyCPM",
+       .write          cpm_uart_console_write,
+       .device         uart_console_device,
+       .setup          cpm_uart_console_setup,
+       .flags          CON_PRINTBUFFER,
+       .index          -1,
        .data           = &cpm_reg,
 };
 
index 8efbd6d1d6a402b2144e7c9d42b80a535d38c1e4..4b0786e7eb7ff6ba2ca3b85b4887badef8d72938 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
  *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
- * 
+ *
  *  Copyright (C) 2004 Freescale Semiconductor, Inc.
  *            (C) 2004 Intracom, S.A.
  *
@@ -82,6 +82,17 @@ void cpm_line_cr_cmd(int line, int cmd)
 void smc1_lineif(struct uart_cpm_port *pinfo)
 {
        volatile cpm8xx_t *cp = cpmp;
+
+       (void)cp;       /* fix warning */
+#if defined (CONFIG_MPC885ADS)
+       /* Enable SMC1 transceivers */
+       {
+               cp->cp_pepar |= 0x000000c0;
+               cp->cp_pedir &= ~0x000000c0;
+               cp->cp_peso &= ~0x00000040;
+               cp->cp_peso |= 0x00000080;
+       }
+#elif defined (CONFIG_MPC86XADS)
        unsigned int iobits = 0x000000c0;
 
        if (!pinfo->is_portb) {
@@ -93,41 +104,33 @@ void smc1_lineif(struct uart_cpm_port *pinfo)
                ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits;
                ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits;
        }
-
-#ifdef CONFIG_MPC885ADS
-       /* Enable SMC1 transceivers */
-       {
-               volatile uint __iomem *bcsr1 = ioremap(BCSR1, 4);
-               uint tmp;
-
-               tmp = in_be32(bcsr1);
-               tmp &= ~BCSR1_RS232EN_1;
-               out_be32(bcsr1, tmp);
-               iounmap(bcsr1);
-       }
 #endif
-
        pinfo->brg = 1;
 }
 
 void smc2_lineif(struct uart_cpm_port *pinfo)
 {
-#ifdef CONFIG_MPC885ADS
        volatile cpm8xx_t *cp = cpmp;
-       volatile uint __iomem *bcsr1;
-       uint tmp;
 
+       (void)cp;       /* fix warning */
+#if defined (CONFIG_MPC885ADS)
        cp->cp_pepar |= 0x00000c00;
        cp->cp_pedir &= ~0x00000c00;
        cp->cp_peso &= ~0x00000400;
        cp->cp_peso |= 0x00000800;
+#elif defined (CONFIG_MPC86XADS)
+       unsigned int iobits = 0x00000c00;
+
+       if (!pinfo->is_portb) {
+               cp->cp_pbpar |= iobits;
+               cp->cp_pbdir &= ~iobits;
+               cp->cp_pbodr &= ~iobits;
+       } else {
+               ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits;
+               ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits;
+               ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits;
+       }
 
-       /* Enable SMC2 transceivers */
-       bcsr1 = ioremap(BCSR1, 4);
-       tmp = in_be32(bcsr1);
-       tmp &= ~BCSR1_RS232EN_2;
-       out_be32(bcsr1, tmp);
-       iounmap(bcsr1);
 #endif
 
        pinfo->brg = 2;
@@ -158,7 +161,7 @@ void scc4_lineif(struct uart_cpm_port *pinfo)
 }
 
 /*
- * Allocate DP-Ram and memory buffers. We need to allocate a transmit and 
+ * Allocate DP-Ram and memory buffers. We need to allocate a transmit and
  * receive buffer descriptors from dual port ram, and a character
  * buffer area from host mem. If we are allocating for the console we need
  * to do it from bootmem
@@ -185,6 +188,8 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
        memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
            L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
        if (is_con) {
+               /* was hostalloc but changed cause it blows away the */
+               /* large tlb mapping when pinning the kernel area    */
                mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8));
                dma_addr = 0;
        } else
index 805fb9097318b761b60b98e8ad8f11a7d3756fcf..21a4e3b2cbda63c3b6e80c19df832f8b0016544d 100644 (file)
@@ -398,21 +398,31 @@ static int cpuset_path(const struct cpuset *cs, char *buf, int buflen)
  * to continue to serve a useful existence.  Next time it's released,
  * we will get notified again, if it still has 'notify_on_release' set.
  *
- * Note final arg to call_usermodehelper() is 0 - that means
- * don't wait.  Since we are holding the global cpuset_sem here,
- * and we are asking another thread (started from keventd) to rmdir a
- * cpuset, we can't wait - or we'd deadlock with the removing thread
- * on cpuset_sem.
+ * The final arg to call_usermodehelper() is 0, which means don't
+ * wait.  The separate /sbin/cpuset_release_agent task is forked by
+ * call_usermodehelper(), then control in this thread returns here,
+ * without waiting for the release agent task.  We don't bother to
+ * wait because the caller of this routine has no use for the exit
+ * status of the /sbin/cpuset_release_agent task, so no sense holding
+ * our caller up for that.
+ *
+ * The simple act of forking that task might require more memory,
+ * which might need cpuset_sem.  So this routine must be called while
+ * cpuset_sem is not held, to avoid a possible deadlock.  See also
+ * comments for check_for_release(), below.
  */
 
-static int cpuset_release_agent(char *cpuset_str)
+static void cpuset_release_agent(const char *pathbuf)
 {
        char *argv[3], *envp[3];
        int i;
 
+       if (!pathbuf)
+               return;
+
        i = 0;
        argv[i++] = "/sbin/cpuset_release_agent";
-       argv[i++] = cpuset_str;
+       argv[i++] = (char *)pathbuf;
        argv[i] = NULL;
 
        i = 0;
@@ -421,17 +431,29 @@ static int cpuset_release_agent(char *cpuset_str)
        envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
        envp[i] = NULL;
 
-       return call_usermodehelper(argv[0], argv, envp, 0);
+       call_usermodehelper(argv[0], argv, envp, 0);
+       kfree(pathbuf);
 }
 
 /*
  * Either cs->count of using tasks transitioned to zero, or the
  * cs->children list of child cpusets just became empty.  If this
  * cs is notify_on_release() and now both the user count is zero and
- * the list of children is empty, send notice to user land.
+ * the list of children is empty, prepare cpuset path in a kmalloc'd
+ * buffer, to be returned via ppathbuf, so that the caller can invoke
+ * cpuset_release_agent() with it later on, once cpuset_sem is dropped.
+ * Call here with cpuset_sem held.
+ *
+ * This check_for_release() routine is responsible for kmalloc'ing
+ * pathbuf.  The above cpuset_release_agent() is responsible for
+ * kfree'ing pathbuf.  The caller of these routines is responsible
+ * for providing a pathbuf pointer, initialized to NULL, then
+ * calling check_for_release() with cpuset_sem held and the address
+ * of the pathbuf pointer, then dropping cpuset_sem, then calling
+ * cpuset_release_agent() with pathbuf, as set by check_for_release().
  */
 
-static void check_for_release(struct cpuset *cs)
+static void check_for_release(struct cpuset *cs, char **ppathbuf)
 {
        if (notify_on_release(cs) && atomic_read(&cs->count) == 0 &&
            list_empty(&cs->children)) {
@@ -441,10 +463,9 @@ static void check_for_release(struct cpuset *cs)
                if (!buf)
                        return;
                if (cpuset_path(cs, buf, PAGE_SIZE) < 0)
-                       goto out;
-               cpuset_release_agent(buf);
-out:
-               kfree(buf);
+                       kfree(buf);
+               else
+                       *ppathbuf = buf;
        }
 }
 
@@ -727,14 +748,14 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf)
        return 0;
 }
 
-static int attach_task(struct cpuset *cs, char *buf)
+static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
 {
        pid_t pid;
        struct task_struct *tsk;
        struct cpuset *oldcs;
        cpumask_t cpus;
 
-       if (sscanf(buf, "%d", &pid) != 1)
+       if (sscanf(pidbuf, "%d", &pid) != 1)
                return -EIO;
        if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
                return -ENOSPC;
@@ -777,7 +798,7 @@ static int attach_task(struct cpuset *cs, char *buf)
 
        put_task_struct(tsk);
        if (atomic_dec_and_test(&oldcs->count))
-               check_for_release(oldcs);
+               check_for_release(oldcs, ppathbuf);
        return 0;
 }
 
@@ -801,6 +822,7 @@ static ssize_t cpuset_common_file_write(struct file *file, const char __user *us
        struct cftype *cft = __d_cft(file->f_dentry);
        cpuset_filetype_t type = cft->private;
        char *buffer;
+       char *pathbuf = NULL;
        int retval = 0;
 
        /* Crude upper limit on largest legitimate cpulist user might write. */
@@ -841,7 +863,7 @@ static ssize_t cpuset_common_file_write(struct file *file, const char __user *us
                retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer);
                break;
        case FILE_TASKLIST:
-               retval = attach_task(cs, buffer);
+               retval = attach_task(cs, buffer, &pathbuf);
                break;
        default:
                retval = -EINVAL;
@@ -852,6 +874,7 @@ static ssize_t cpuset_common_file_write(struct file *file, const char __user *us
                retval = nbytes;
 out2:
        up(&cpuset_sem);
+       cpuset_release_agent(pathbuf);
 out1:
        kfree(buffer);
        return retval;
@@ -1357,6 +1380,7 @@ static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry)
        struct cpuset *cs = dentry->d_fsdata;
        struct dentry *d;
        struct cpuset *parent;
+       char *pathbuf = NULL;
 
        /* the vfs holds both inode->i_sem already */
 
@@ -1376,7 +1400,7 @@ static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry)
                update_cpu_domains(cs);
        list_del(&cs->sibling); /* delete my sibling from parent->children */
        if (list_empty(&parent->children))
-               check_for_release(parent);
+               check_for_release(parent, &pathbuf);
        spin_lock(&cs->dentry->d_lock);
        d = dget(cs->dentry);
        cs->dentry = NULL;
@@ -1384,6 +1408,7 @@ static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry)
        cpuset_d_remove_dir(d);
        dput(d);
        up(&cpuset_sem);
+       cpuset_release_agent(pathbuf);
        return 0;
 }
 
@@ -1483,10 +1508,13 @@ void cpuset_exit(struct task_struct *tsk)
        task_unlock(tsk);
 
        if (notify_on_release(cs)) {
+               char *pathbuf = NULL;
+
                down(&cpuset_sem);
                if (atomic_dec_and_test(&cs->count))
-                       check_for_release(cs);
+                       check_for_release(cs, &pathbuf);
                up(&cpuset_sem);
+               cpuset_release_agent(pathbuf);
        } else {
                atomic_dec(&cs->count);
        }