From b0defcdbd2b7da7694e2645da92716cea0a3c0ff Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sun, 26 Mar 2006 01:37:20 -0800 Subject: [PATCH] ipmi: add generic PCI handling Modify the PCI hanling code for the IPMI driver to use the new method of tables and registering, and adds more generic PCI handling for IPMI. Unfortunately, this required a rather large rework of the way the driver did detection so it would be more event-driven. [bunk@stusta.de: make a struct static] Signed-off-by: Corey Minyard Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 947 ++++++++++++++++++++------------------- 1 file changed, 494 insertions(+), 453 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index e59b638766ef..f3b3b23c5330 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_HIGH_RES_TIMERS @@ -109,6 +110,7 @@ enum si_intf_state { enum si_type { SI_KCS, SI_SMIC, SI_BT }; +static char *si_to_str[] = { "KCS", "SMIC", "BT" }; struct ipmi_device_id { unsigned char device_id; @@ -147,6 +149,9 @@ struct smi_info int (*irq_setup)(struct smi_info *info); void (*irq_cleanup)(struct smi_info *info); unsigned int io_size; + char *addr_source; /* ACPI, PCI, SMBIOS, hardcode, default. */ + void (*addr_source_cleanup)(struct smi_info *info); + void *addr_source_data; /* Per-OEM handler, called from handle_flags(). Returns 1 when handle_flags() needs to be re-run @@ -224,8 +229,12 @@ struct smi_info unsigned long incoming_messages; struct task_struct *thread; + + struct list_head link; }; +static int try_smi_init(struct smi_info *smi); + static struct notifier_block *xaction_notifier_list; static int register_xaction_notifier(struct notifier_block * nb) { @@ -271,13 +280,13 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) spin_lock(&(smi_info->msg_lock)); /* Pick the high priority queue first. */ - if (! list_empty(&(smi_info->hp_xmit_msgs))) { + if (!list_empty(&(smi_info->hp_xmit_msgs))) { entry = smi_info->hp_xmit_msgs.next; - } else if (! list_empty(&(smi_info->xmit_msgs))) { + } else if (!list_empty(&(smi_info->xmit_msgs))) { entry = smi_info->xmit_msgs.next; } - if (! entry) { + if (!entry) { smi_info->curr_msg = NULL; rv = SI_SM_IDLE; } else { @@ -344,7 +353,7 @@ static void start_clear_flags(struct smi_info *smi_info) memory, we will re-enable the interrupt. */ static inline void disable_si_irq(struct smi_info *smi_info) { - if ((smi_info->irq) && (! smi_info->interrupt_disabled)) { + if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { disable_irq_nosync(smi_info->irq); smi_info->interrupt_disabled = 1; } @@ -375,7 +384,7 @@ static void handle_flags(struct smi_info *smi_info) } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); - if (! smi_info->curr_msg) { + if (!smi_info->curr_msg) { disable_si_irq(smi_info); smi_info->si_state = SI_NORMAL; return; @@ -394,7 +403,7 @@ static void handle_flags(struct smi_info *smi_info) } else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) { /* Events available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); - if (! smi_info->curr_msg) { + if (!smi_info->curr_msg) { disable_si_irq(smi_info); smi_info->si_state = SI_NORMAL; return; @@ -430,7 +439,7 @@ static void handle_transaction_done(struct smi_info *smi_info) #endif switch (smi_info->si_state) { case SI_NORMAL: - if (! smi_info->curr_msg) + if (!smi_info->curr_msg) break; smi_info->curr_msg->rsp_size @@ -880,7 +889,7 @@ static void smi_timeout(unsigned long data) smi_info->last_timeout_jiffies = jiffies_now; - if ((smi_info->irq) && (! smi_info->interrupt_disabled)) { + if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; spin_lock_irqsave(&smi_info->count_lock, flags); @@ -974,15 +983,12 @@ static struct ipmi_smi_handlers handlers = a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS */ #define SI_MAX_PARMS 4 -#define SI_MAX_DRIVERS ((SI_MAX_PARMS * 2) + 2) -static struct smi_info *smi_infos[SI_MAX_DRIVERS] = -{ NULL, NULL, NULL, NULL }; +static LIST_HEAD(smi_infos); +static DECLARE_MUTEX(smi_infos_lock); +static int smi_num; /* Used to sequence the SMIs */ #define DEVICE_NAME "ipmi_si" -#define DEFAULT_KCS_IO_PORT 0xca2 -#define DEFAULT_SMIC_IO_PORT 0xca9 -#define DEFAULT_BT_IO_PORT 0xe4 #define DEFAULT_REGSPACING 1 static int si_trydefaults = 1; @@ -1053,38 +1059,23 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" " by interface number."); +#define IPMI_IO_ADDR_SPACE 0 #define IPMI_MEM_ADDR_SPACE 1 -#define IPMI_IO_ADDR_SPACE 2 +static char *addr_space_to_str[] = { "I/O", "memory" }; -#if defined(CONFIG_ACPI) || defined(CONFIG_DMI) || defined(CONFIG_PCI) -static int is_new_interface(int intf, u8 addr_space, unsigned long base_addr) +static void std_irq_cleanup(struct smi_info *info) { - int i; - - for (i = 0; i < SI_MAX_PARMS; ++i) { - /* Don't check our address. */ - if (i == intf) - continue; - if (si_type[i] != NULL) { - if ((addr_space == IPMI_MEM_ADDR_SPACE && - base_addr == addrs[i]) || - (addr_space == IPMI_IO_ADDR_SPACE && - base_addr == ports[i])) - return 0; - } - else - break; - } - - return 1; + if (info->si_type == SI_BT) + /* Disable the interrupt in the BT interface. */ + info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, 0); + free_irq(info->irq, info); } -#endif static int std_irq_setup(struct smi_info *info) { int rv; - if (! info->irq) + if (!info->irq) return 0; if (info->si_type == SI_BT) { @@ -1093,7 +1084,7 @@ static int std_irq_setup(struct smi_info *info) SA_INTERRUPT, DEVICE_NAME, info); - if (! rv) + if (!rv) /* Enable the interrupt in the BT interface. */ info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, IPMI_BT_INTMASK_ENABLE_IRQ_BIT); @@ -1110,88 +1101,78 @@ static int std_irq_setup(struct smi_info *info) DEVICE_NAME, info->irq); info->irq = 0; } else { + info->irq_cleanup = std_irq_cleanup; printk(" Using irq %d\n", info->irq); } return rv; } -static void std_irq_cleanup(struct smi_info *info) -{ - if (! info->irq) - return; - - if (info->si_type == SI_BT) - /* Disable the interrupt in the BT interface. */ - info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, 0); - free_irq(info->irq, info); -} - static unsigned char port_inb(struct si_sm_io *io, unsigned int offset) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - return inb((*addr)+(offset*io->regspacing)); + return inb(addr + (offset * io->regspacing)); } static void port_outb(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - outb(b, (*addr)+(offset * io->regspacing)); + outb(b, addr + (offset * io->regspacing)); } static unsigned char port_inw(struct si_sm_io *io, unsigned int offset) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - return (inw((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; + return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; } static void port_outw(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - outw(b << io->regshift, (*addr)+(offset * io->regspacing)); + outw(b << io->regshift, addr + (offset * io->regspacing)); } static unsigned char port_inl(struct si_sm_io *io, unsigned int offset) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - return (inl((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; + return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; } static void port_outl(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int *addr = io->info; + unsigned int addr = io->addr_data; - outl(b << io->regshift, (*addr)+(offset * io->regspacing)); + outl(b << io->regshift, addr+(offset * io->regspacing)); } static void port_cleanup(struct smi_info *info) { - unsigned int *addr = info->io.info; - int mapsize; + unsigned int addr = info->io.addr_data; + int mapsize; - if (addr && (*addr)) { + if (addr) { mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - release_region (*addr, mapsize); + release_region (addr, mapsize); } kfree(info); } static int port_setup(struct smi_info *info) { - unsigned int *addr = info->io.info; - int mapsize; + unsigned int addr = info->io.addr_data; + int mapsize; - if (! addr || (! *addr)) + if (!addr) return -ENODEV; info->io_cleanup = port_cleanup; @@ -1225,51 +1206,11 @@ static int port_setup(struct smi_info *info) mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - if (request_region(*addr, mapsize, DEVICE_NAME) == NULL) + if (request_region(addr, mapsize, DEVICE_NAME) == NULL) return -EIO; return 0; } -static int try_init_port(int intf_num, struct smi_info **new_info) -{ - struct smi_info *info; - - if (! ports[intf_num]) - return -ENODEV; - - if (! is_new_interface(intf_num, IPMI_IO_ADDR_SPACE, - ports[intf_num])) - return -ENODEV; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (1)\n"); - return -ENOMEM; - } - memset(info, 0, sizeof(*info)); - - info->io_setup = port_setup; - info->io.info = &(ports[intf_num]); - info->io.addr = NULL; - info->io.regspacing = regspacings[intf_num]; - if (! info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = regsizes[intf_num]; - if (! info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = regshifts[intf_num]; - info->irq = 0; - info->irq_setup = NULL; - *new_info = info; - - if (si_type[intf_num] == NULL) - si_type[intf_num] = "kcs"; - - printk("ipmi_si: Trying \"%s\" at I/O port 0x%x\n", - si_type[intf_num], ports[intf_num]); - return 0; -} - static unsigned char intf_mem_inb(struct si_sm_io *io, unsigned int offset) { return readb((io->addr)+(offset * io->regspacing)); @@ -1321,7 +1262,7 @@ static void mem_outq(struct si_sm_io *io, unsigned int offset, static void mem_cleanup(struct smi_info *info) { - unsigned long *addr = info->io.info; + unsigned long addr = info->io.addr_data; int mapsize; if (info->io.addr) { @@ -1330,17 +1271,17 @@ static void mem_cleanup(struct smi_info *info) mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - release_mem_region(*addr, mapsize); + release_mem_region(addr, mapsize); } kfree(info); } static int mem_setup(struct smi_info *info) { - unsigned long *addr = info->io.info; + unsigned long addr = info->io.addr_data; int mapsize; - if (! addr || (! *addr)) + if (!addr) return -ENODEV; info->io_cleanup = mem_cleanup; @@ -1380,57 +1321,83 @@ static int mem_setup(struct smi_info *info) mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - if (request_mem_region(*addr, mapsize, DEVICE_NAME) == NULL) + if (request_mem_region(addr, mapsize, DEVICE_NAME) == NULL) return -EIO; - info->io.addr = ioremap(*addr, mapsize); + info->io.addr = ioremap(addr, mapsize); if (info->io.addr == NULL) { - release_mem_region(*addr, mapsize); + release_mem_region(addr, mapsize); return -EIO; } return 0; } -static int try_init_mem(int intf_num, struct smi_info **new_info) + +static __devinit void hardcode_find_bmc(void) { + int i; struct smi_info *info; - if (! addrs[intf_num]) - return -ENODEV; + for (i = 0; i < SI_MAX_PARMS; i++) { + if (!ports[i] && !addrs[i]) + continue; - if (! is_new_interface(intf_num, IPMI_MEM_ADDR_SPACE, - addrs[intf_num])) - return -ENODEV; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return; - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (2)\n"); - return -ENOMEM; - } - memset(info, 0, sizeof(*info)); + info->addr_source = "hardcoded"; - info->io_setup = mem_setup; - info->io.info = &addrs[intf_num]; - info->io.addr = NULL; - info->io.regspacing = regspacings[intf_num]; - if (! info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = regsizes[intf_num]; - if (! info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = regshifts[intf_num]; - info->irq = 0; - info->irq_setup = NULL; - *new_info = info; + if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { + info->si_type = SI_KCS; + } else if (strcmp(si_type[i], "smic") == 0) { + info->si_type = SI_SMIC; + } else if (strcmp(si_type[i], "bt") == 0) { + info->si_type = SI_BT; + } else { + printk(KERN_WARNING + "ipmi_si: Interface type specified " + "for interface %d, was invalid: %s\n", + i, si_type[i]); + kfree(info); + continue; + } - if (si_type[intf_num] == NULL) - si_type[intf_num] = "kcs"; + if (ports[i]) { + /* An I/O port */ + info->io_setup = port_setup; + info->io.addr_data = ports[i]; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + } else if (addrs[i]) { + /* A memory port */ + info->io_setup = mem_setup; + info->io.addr_data = addrs[i]; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } else { + printk(KERN_WARNING + "ipmi_si: Interface type specified " + "for interface %d, " + "but port and address were not set or " + "set to zero.\n", i); + kfree(info); + continue; + } - printk("ipmi_si: Trying \"%s\" at memory address 0x%lx\n", - si_type[intf_num], addrs[intf_num]); - return 0; -} + info->io.addr = NULL; + info->io.regspacing = regspacings[i]; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsizes[i]; + if (!info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[i]; + info->irq = irqs[i]; + if (info->irq) + info->irq_setup = std_irq_setup; + try_smi_init(info); + } +} #ifdef CONFIG_ACPI @@ -1470,11 +1437,19 @@ static u32 ipmi_acpi_gpe(void *context) return ACPI_INTERRUPT_HANDLED; } +static void acpi_gpe_irq_cleanup(struct smi_info *info) +{ + if (!info->irq) + return; + + acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe); +} + static int acpi_gpe_irq_setup(struct smi_info *info) { acpi_status status; - if (! info->irq) + if (!info->irq) return 0; /* FIXME - is level triggered right? */ @@ -1491,19 +1466,12 @@ static int acpi_gpe_irq_setup(struct smi_info *info) info->irq = 0; return -EINVAL; } else { + info->irq_cleanup = acpi_gpe_irq_cleanup; printk(" Using ACPI GPE %d\n", info->irq); return 0; } } -static void acpi_gpe_irq_cleanup(struct smi_info *info) -{ - if (! info->irq) - return; - - acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe); -} - /* * Defined at * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/Docs/TechPapers/IA64/hpspmi.pdf @@ -1546,28 +1514,12 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static int try_init_acpi(int intf_num, struct smi_info **new_info) +static __devinit int try_init_acpi(struct SPMITable *spmi) { struct smi_info *info; - acpi_status status; - struct SPMITable *spmi; char *io_type; u8 addr_space; - if (acpi_disabled) - return -ENODEV; - - if (acpi_failure) - return -ENODEV; - - status = acpi_get_firmware_table("SPMI", intf_num+1, - ACPI_LOGICAL_ADDRESSING, - (struct acpi_table_header **) &spmi); - if (status != AE_OK) { - acpi_failure = 1; - return -ENODEV; - } - if (spmi->IPMIlegacy != 1) { printk(KERN_INFO "IPMI: Bad SPMI legacy %d\n", spmi->IPMIlegacy); return -ENODEV; @@ -1577,47 +1529,42 @@ static int try_init_acpi(int intf_num, struct smi_info **new_info) addr_space = IPMI_MEM_ADDR_SPACE; else addr_space = IPMI_IO_ADDR_SPACE; - if (! is_new_interface(-1, addr_space, spmi->addr.address)) - return -ENODEV; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); + return -ENOMEM; + } + + info->addr_source = "ACPI"; /* Figure out the interface type. */ switch (spmi->InterfaceType) { case 1: /* KCS */ - si_type[intf_num] = "kcs"; + info->si_type = SI_KCS; break; - case 2: /* SMIC */ - si_type[intf_num] = "smic"; + info->si_type = SI_SMIC; break; - case 3: /* BT */ - si_type[intf_num] = "bt"; + info->si_type = SI_BT; break; - default: printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", spmi->InterfaceType); + kfree(info); return -EIO; } - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); - return -ENOMEM; - } - memset(info, 0, sizeof(*info)); - if (spmi->InterruptType & 1) { /* We've got a GPE interrupt. */ info->irq = spmi->GPE; info->irq_setup = acpi_gpe_irq_setup; - info->irq_cleanup = acpi_gpe_irq_cleanup; } else if (spmi->InterruptType & 2) { /* We've got an APIC/SAPIC interrupt. */ info->irq = spmi->GlobalSystemInterrupt; info->irq_setup = std_irq_setup; - info->irq_cleanup = std_irq_cleanup; } else { /* Use the default interrupt setting. */ info->irq = 0; @@ -1626,43 +1573,60 @@ static int try_init_acpi(int intf_num, struct smi_info **new_info) if (spmi->addr.register_bit_width) { /* A (hopefully) properly formed register bit width. */ - regspacings[intf_num] = spmi->addr.register_bit_width / 8; info->io.regspacing = spmi->addr.register_bit_width / 8; } else { - regspacings[intf_num] = DEFAULT_REGSPACING; info->io.regspacing = DEFAULT_REGSPACING; } - regsizes[intf_num] = regspacings[intf_num]; - info->io.regsize = regsizes[intf_num]; - regshifts[intf_num] = spmi->addr.register_bit_offset; - info->io.regshift = regshifts[intf_num]; + info->io.regsize = info->io.regspacing; + info->io.regshift = spmi->addr.register_bit_offset; if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { io_type = "memory"; info->io_setup = mem_setup; - addrs[intf_num] = spmi->addr.address; - info->io.info = &(addrs[intf_num]); + info->io.addr_type = IPMI_IO_ADDR_SPACE; } else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) { io_type = "I/O"; info->io_setup = port_setup; - ports[intf_num] = spmi->addr.address; - info->io.info = &(ports[intf_num]); + info->io.addr_type = IPMI_MEM_ADDR_SPACE; } else { kfree(info); printk("ipmi_si: Unknown ACPI I/O Address type\n"); return -EIO; } + info->io.addr_data = spmi->addr.address; - *new_info = info; + try_smi_init(info); - printk("ipmi_si: ACPI/SPMI specifies \"%s\" %s SI @ 0x%lx\n", - si_type[intf_num], io_type, (unsigned long) spmi->addr.address); return 0; } + +static __devinit void acpi_find_bmc(void) +{ + acpi_status status; + struct SPMITable *spmi; + int i; + + if (acpi_disabled) + return; + + if (acpi_failure) + return; + + for (i = 0; ; i++) { + status = acpi_get_firmware_table("SPMI", i+1, + ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) + &spmi); + if (status != AE_OK) + return; + + try_init_acpi(spmi); + } +} #endif #ifdef CONFIG_DMI -typedef struct dmi_ipmi_data +struct dmi_ipmi_data { u8 type; u8 addr_space; @@ -1670,49 +1634,46 @@ typedef struct dmi_ipmi_data u8 irq; u8 offset; u8 slave_addr; -} dmi_ipmi_data_t; - -static dmi_ipmi_data_t dmi_data[SI_MAX_DRIVERS]; -static int dmi_data_entries; +}; -static int __init decode_dmi(struct dmi_header *dm, int intf_num) +static int __devinit decode_dmi(struct dmi_header *dm, + struct dmi_ipmi_data *dmi) { u8 *data = (u8 *)dm; unsigned long base_addr; u8 reg_spacing; u8 len = dm->length; - dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; - ipmi_data->type = data[4]; + dmi->type = data[4]; memcpy(&base_addr, data+8, sizeof(unsigned long)); if (len >= 0x11) { if (base_addr & 1) { /* I/O */ base_addr &= 0xFFFE; - ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; + dmi->addr_space = IPMI_IO_ADDR_SPACE; } else { /* Memory */ - ipmi_data->addr_space = IPMI_MEM_ADDR_SPACE; + dmi->addr_space = IPMI_MEM_ADDR_SPACE; } /* If bit 4 of byte 0x10 is set, then the lsb for the address is odd. */ - ipmi_data->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); + dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); - ipmi_data->irq = data[0x11]; + dmi->irq = data[0x11]; /* The top two bits of byte 0x10 hold the register spacing. */ reg_spacing = (data[0x10] & 0xC0) >> 6; switch(reg_spacing){ case 0x00: /* Byte boundaries */ - ipmi_data->offset = 1; + dmi->offset = 1; break; case 0x01: /* 32-bit boundaries */ - ipmi_data->offset = 4; + dmi->offset = 4; break; case 0x02: /* 16-byte boundaries */ - ipmi_data->offset = 16; + dmi->offset = 16; break; default: /* Some other interface, just ignore it. */ @@ -1726,205 +1687,213 @@ static int __init decode_dmi(struct dmi_header *dm, int intf_num) * wrong (and all that I have seen are I/O) so we just * ignore that bit and assume I/O. Systems that use * memory should use the newer spec, anyway. */ - ipmi_data->base_addr = base_addr & 0xfffe; - ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; - ipmi_data->offset = 1; - } - - ipmi_data->slave_addr = data[6]; - - if (is_new_interface(-1, ipmi_data->addr_space,ipmi_data->base_addr)) { - dmi_data_entries++; - return 0; + dmi->base_addr = base_addr & 0xfffe; + dmi->addr_space = IPMI_IO_ADDR_SPACE; + dmi->offset = 1; } - memset(ipmi_data, 0, sizeof(dmi_ipmi_data_t)); + dmi->slave_addr = data[6]; - return -1; + return 0; } -static void __init dmi_find_bmc(void) +static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) { - struct dmi_device *dev = NULL; - int intf_num = 0; - - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { - if (intf_num >= SI_MAX_DRIVERS) - break; + struct smi_info *info; - decode_dmi((struct dmi_header *) dev->device_data, intf_num++); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR + "ipmi_si: Could not allocate SI data\n"); + return; } -} - -static int try_init_smbios(int intf_num, struct smi_info **new_info) -{ - struct smi_info *info; - dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; - char *io_type; - if (intf_num >= dmi_data_entries) - return -ENODEV; + info->addr_source = "SMBIOS"; switch (ipmi_data->type) { - case 0x01: /* KCS */ - si_type[intf_num] = "kcs"; - break; - case 0x02: /* SMIC */ - si_type[intf_num] = "smic"; - break; - case 0x03: /* BT */ - si_type[intf_num] = "bt"; - break; - default: - return -EIO; - } - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (4)\n"); - return -ENOMEM; + case 0x01: /* KCS */ + info->si_type = SI_KCS; + break; + case 0x02: /* SMIC */ + info->si_type = SI_SMIC; + break; + case 0x03: /* BT */ + info->si_type = SI_BT; + break; + default: + return; } - memset(info, 0, sizeof(*info)); - if (ipmi_data->addr_space == 1) { - io_type = "memory"; + switch (ipmi_data->addr_space) { + case IPMI_MEM_ADDR_SPACE: info->io_setup = mem_setup; - addrs[intf_num] = ipmi_data->base_addr; - info->io.info = &(addrs[intf_num]); - } else if (ipmi_data->addr_space == 2) { - io_type = "I/O"; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + break; + + case IPMI_IO_ADDR_SPACE: info->io_setup = port_setup; - ports[intf_num] = ipmi_data->base_addr; - info->io.info = &(ports[intf_num]); - } else { + info->io.addr_type = IPMI_IO_ADDR_SPACE; + break; + + default: kfree(info); - printk("ipmi_si: Unknown SMBIOS I/O Address type.\n"); - return -EIO; + printk(KERN_WARNING + "ipmi_si: Unknown SMBIOS I/O Address type: %d.\n", + ipmi_data->addr_space); + return; } + info->io.addr_data = ipmi_data->base_addr; - regspacings[intf_num] = ipmi_data->offset; - info->io.regspacing = regspacings[intf_num]; - if (! info->io.regspacing) + info->io.regspacing = ipmi_data->offset; + if (!info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = regshifts[intf_num]; + info->io.regshift = 0; info->slave_addr = ipmi_data->slave_addr; - irqs[intf_num] = ipmi_data->irq; + info->irq = ipmi_data->irq; + if (info->irq) + info->irq_setup = std_irq_setup; - *new_info = info; + try_smi_init(info); +} - printk("ipmi_si: Found SMBIOS-specified state machine at %s" - " address 0x%lx, slave address 0x%x\n", - io_type, (unsigned long)ipmi_data->base_addr, - ipmi_data->slave_addr); - return 0; +static void __devinit dmi_find_bmc(void) +{ + struct dmi_device *dev = NULL; + struct dmi_ipmi_data data; + int rv; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { + rv = decode_dmi((struct dmi_header *) dev->device_data, &data); + if (!rv) + try_init_dmi(&data); + } } #endif /* CONFIG_DMI */ #ifdef CONFIG_PCI -#define PCI_ERMC_CLASSCODE 0x0C0700 +#define PCI_ERMC_CLASSCODE 0x0C0700 +#define PCI_ERMC_CLASSCODE_MASK 0xffffff00 +#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff +#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00 +#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01 +#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02 + #define PCI_HP_VENDOR_ID 0x103C #define PCI_MMC_DEVICE_ID 0x121A #define PCI_MMC_ADDR_CW 0x10 -/* Avoid more than one attempt to probe pci smic. */ -static int pci_smic_checked = 0; +static void ipmi_pci_cleanup(struct smi_info *info) +{ + struct pci_dev *pdev = info->addr_source_data; + + pci_disable_device(pdev); +} -static int find_pci_smic(int intf_num, struct smi_info **new_info) +static int __devinit ipmi_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { - struct smi_info *info; - int error; - struct pci_dev *pci_dev = NULL; - u16 base_addr; - int fe_rmc = 0; + int rv; + int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; + struct smi_info *info; + int first_reg_offset = 0; - if (pci_smic_checked) - return -ENODEV; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ENOMEM; - pci_smic_checked = 1; + info->addr_source = "PCI"; - pci_dev = pci_get_device(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID, NULL); - if (! pci_dev) { - pci_dev = pci_get_class(PCI_ERMC_CLASSCODE, NULL); - if (pci_dev && (pci_dev->subsystem_vendor == PCI_HP_VENDOR_ID)) - fe_rmc = 1; - else - return -ENODEV; - } + switch (class_type) { + case PCI_ERMC_CLASSCODE_TYPE_SMIC: + info->si_type = SI_SMIC; + break; - error = pci_read_config_word(pci_dev, PCI_MMC_ADDR_CW, &base_addr); - if (error) - { - pci_dev_put(pci_dev); - printk(KERN_ERR - "ipmi_si: pci_read_config_word() failed (%d).\n", - error); - return -ENODEV; + case PCI_ERMC_CLASSCODE_TYPE_KCS: + info->si_type = SI_KCS; + break; + + case PCI_ERMC_CLASSCODE_TYPE_BT: + info->si_type = SI_BT; + break; + + default: + kfree(info); + printk(KERN_INFO "ipmi_si: %s: Unknown IPMI type: %d\n", + pci_name(pdev), class_type); + return ENOMEM; } - /* Bit 0: 1 specifies programmed I/O, 0 specifies memory mapped I/O */ - if (! (base_addr & 0x0001)) - { - pci_dev_put(pci_dev); - printk(KERN_ERR - "ipmi_si: memory mapped I/O not supported for PCI" - " smic.\n"); - return -ENODEV; + rv = pci_enable_device(pdev); + if (rv) { + printk(KERN_ERR "ipmi_si: %s: couldn't enable PCI device\n", + pci_name(pdev)); + kfree(info); + return rv; } - base_addr &= 0xFFFE; - if (! fe_rmc) - /* Data register starts at base address + 1 in eRMC */ - ++base_addr; + info->addr_source_cleanup = ipmi_pci_cleanup; + info->addr_source_data = pdev; - if (! is_new_interface(-1, IPMI_IO_ADDR_SPACE, base_addr)) { - pci_dev_put(pci_dev); - return -ENODEV; - } + if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID) + first_reg_offset = 1; - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - pci_dev_put(pci_dev); - printk(KERN_ERR "ipmi_si: Could not allocate SI data (5)\n"); - return -ENOMEM; + if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + } else { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; } - memset(info, 0, sizeof(*info)); + info->io.addr_data = pci_resource_start(pdev, 0); - info->io_setup = port_setup; - ports[intf_num] = base_addr; - info->io.info = &(ports[intf_num]); - info->io.regspacing = regspacings[intf_num]; - if (! info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; + info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = regshifts[intf_num]; + info->io.regshift = 0; - *new_info = info; + info->irq = pdev->irq; + if (info->irq) + info->irq_setup = std_irq_setup; - irqs[intf_num] = pci_dev->irq; - si_type[intf_num] = "smic"; + return try_smi_init(info); +} - printk("ipmi_si: Found PCI SMIC at I/O address 0x%lx\n", - (long unsigned int) base_addr); +static void __devexit ipmi_pci_remove(struct pci_dev *pdev) +{ +} - pci_dev_put(pci_dev); +#ifdef CONFIG_PM +static int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ return 0; } -#endif /* CONFIG_PCI */ -static int try_init_plug_and_play(int intf_num, struct smi_info **new_info) +static int ipmi_pci_resume(struct pci_dev *pdev) { -#ifdef CONFIG_PCI - if (find_pci_smic(intf_num, new_info) == 0) - return 0; + return 0; +} #endif - /* Include other methods here. */ - return -ENODEV; -} +static struct pci_device_id ipmi_pci_devices[] = { + { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, + { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE) } +}; +MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); + +static struct pci_driver ipmi_pci_driver = { + .name = DEVICE_NAME, + .id_table = ipmi_pci_devices, + .probe = ipmi_pci_probe, + .remove = __devexit_p(ipmi_pci_remove), +#ifdef CONFIG_PM + .suspend = ipmi_pci_suspend, + .resume = ipmi_pci_resume, +#endif +}; +#endif /* CONFIG_PCI */ static int try_get_dev_id(struct smi_info *smi_info) @@ -1936,7 +1905,7 @@ static int try_get_dev_id(struct smi_info *smi_info) int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (! resp) + if (!resp) return -ENOMEM; /* Do a Get Device ID command, since it comes back with some @@ -2018,7 +1987,7 @@ static int stat_file_read_proc(char *page, char **start, off_t off, struct smi_info *smi = data; out += sprintf(out, "interrupts_enabled: %d\n", - smi->irq && ! smi->interrupt_disabled); + smi->irq && !smi->interrupt_disabled); out += sprintf(out, "short_timeouts: %ld\n", smi->short_timeouts); out += sprintf(out, "long_timeouts: %ld\n", @@ -2094,7 +2063,7 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; const char mfr[3]=DELL_IANA_MFR_ID; - if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { + if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { @@ -2170,7 +2139,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; const char mfr[3]=DELL_IANA_MFR_ID; - if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && + if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && smi_info->si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -2200,62 +2169,110 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info) del_timer_sync(&smi_info->si_timer); } -/* Returns 0 if initialized, or negative on an error. */ -static int init_one_smi(int intf_num, struct smi_info **smi) +static struct ipmi_default_vals +{ + int type; + int port; +} __devinit ipmi_defaults[] = +{ + { .type = SI_KCS, .port = 0xca2 }, + { .type = SI_SMIC, .port = 0xca9 }, + { .type = SI_BT, .port = 0xe4 }, + { .port = 0 } +}; + +static __devinit void default_find_bmc(void) +{ + struct smi_info *info; + int i; + + for (i = 0; ; i++) { + if (!ipmi_defaults[i].port) + break; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return; + + info->addr_source = NULL; + + info->si_type = ipmi_defaults[i].type; + info->io_setup = port_setup; + info->io.addr_data = ipmi_defaults[i].port; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + + info->io.addr = NULL; + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = 0; + + if (try_smi_init(info) == 0) { + /* Found one... */ + printk(KERN_INFO "ipmi_si: Found default %s state" + " machine at %s address 0x%lx\n", + si_to_str[info->si_type], + addr_space_to_str[info->io.addr_type], + info->io.addr_data); + return; + } + } +} + +static int is_new_interface(struct smi_info *info) { - int rv; - struct smi_info *new_smi; + struct smi_info *e; + list_for_each_entry(e, &smi_infos, link) { + if (e->io.addr_type != info->io.addr_type) + continue; + if (e->io.addr_data == info->io.addr_data) + return 0; + } - rv = try_init_mem(intf_num, &new_smi); - if (rv) - rv = try_init_port(intf_num, &new_smi); -#ifdef CONFIG_ACPI - if (rv && si_trydefaults) - rv = try_init_acpi(intf_num, &new_smi); -#endif -#ifdef CONFIG_DMI - if (rv && si_trydefaults) - rv = try_init_smbios(intf_num, &new_smi); -#endif - if (rv && si_trydefaults) - rv = try_init_plug_and_play(intf_num, &new_smi); + return 1; +} - if (rv) - return rv; +static int try_smi_init(struct smi_info *new_smi) +{ + int rv; + + if (new_smi->addr_source) { + printk(KERN_INFO "ipmi_si: Trying %s-specified %s state" + " machine at %s address 0x%lx, slave address 0x%x," + " irq %d\n", + new_smi->addr_source, + si_to_str[new_smi->si_type], + addr_space_to_str[new_smi->io.addr_type], + new_smi->io.addr_data, + new_smi->slave_addr, new_smi->irq); + } + + down(&smi_infos_lock); + if (!is_new_interface(new_smi)) { + printk(KERN_WARNING "ipmi_si: duplicate interface\n"); + rv = -EBUSY; + goto out_err; + } /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; new_smi->si_sm = NULL; new_smi->handlers = NULL; - if (! new_smi->irq_setup) { - new_smi->irq = irqs[intf_num]; - new_smi->irq_setup = std_irq_setup; - new_smi->irq_cleanup = std_irq_cleanup; - } - - /* Default to KCS if no type is specified. */ - if (si_type[intf_num] == NULL) { - if (si_trydefaults) - si_type[intf_num] = "kcs"; - else { - rv = -EINVAL; - goto out_err; - } - } - - /* Set up the state machine to use. */ - if (strcmp(si_type[intf_num], "kcs") == 0) { + switch (new_smi->si_type) { + case SI_KCS: new_smi->handlers = &kcs_smi_handlers; - new_smi->si_type = SI_KCS; - } else if (strcmp(si_type[intf_num], "smic") == 0) { + break; + + case SI_SMIC: new_smi->handlers = &smic_smi_handlers; - new_smi->si_type = SI_SMIC; - } else if (strcmp(si_type[intf_num], "bt") == 0) { + break; + + case SI_BT: new_smi->handlers = &bt_smi_handlers; - new_smi->si_type = SI_BT; - } else { + break; + + default: /* No support for anything else yet. */ rv = -EIO; goto out_err; @@ -2263,7 +2280,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi) /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); - if (! new_smi->si_sm) { + if (!new_smi->si_sm) { printk(" Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; @@ -2284,21 +2301,29 @@ static int init_one_smi(int intf_num, struct smi_info **smi) /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { + if (new_smi->addr_source) + printk(KERN_INFO "ipmi_si: Interface detection" + " failed\n"); rv = -ENODEV; goto out_err; } /* Attempt a get device id command. If it fails, we probably - don't have a SMI here. */ + don't have a BMC here. */ rv = try_get_dev_id(new_smi); - if (rv) + if (rv) { + if (new_smi->addr_source) + printk(KERN_INFO "ipmi_si: There appears to be no BMC" + " at this location\n"); goto out_err; + } setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); /* Try to claim any interrupts. */ - new_smi->irq_setup(new_smi); + if (new_smi->irq_setup) + new_smi->irq_setup(new_smi); INIT_LIST_HEAD(&(new_smi->xmit_msgs)); INIT_LIST_HEAD(&(new_smi->hp_xmit_msgs)); @@ -2308,7 +2333,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) new_smi->interrupt_disabled = 0; atomic_set(&new_smi->stop_operation, 0); - new_smi->intf_num = intf_num; + new_smi->intf_num = smi_num; + smi_num++; /* Start clearing the flags before we enable interrupts or the timer to avoid racing with the timer. */ @@ -2365,9 +2391,11 @@ static int init_one_smi(int intf_num, struct smi_info **smi) goto out_err_stop_timer; } - *smi = new_smi; + list_add_tail(&new_smi->link, &smi_infos); + + up(&smi_infos_lock); - printk(" IPMI %s interface initialized\n", si_type[intf_num]); + printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]); return 0; @@ -2379,7 +2407,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) if (new_smi->intf) ipmi_unregister_smi(new_smi->intf); - new_smi->irq_cleanup(new_smi); + if (new_smi->irq_cleanup) + new_smi->irq_cleanup(new_smi); /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the @@ -2391,16 +2420,18 @@ static int init_one_smi(int intf_num, struct smi_info **smi) new_smi->handlers->cleanup(new_smi->si_sm); kfree(new_smi->si_sm); } + if (new_smi->addr_source_cleanup) + new_smi->addr_source_cleanup(new_smi); if (new_smi->io_cleanup) new_smi->io_cleanup(new_smi); + up(&smi_infos_lock); + return rv; } -static __init int init_ipmi_si(void) +static __devinit int init_ipmi_si(void) { - int rv = 0; - int pos = 0; int i; char *str; @@ -2425,63 +2456,66 @@ static __init int init_ipmi_si(void) printk(KERN_INFO "IPMI System Interface driver.\n"); + hardcode_find_bmc(); + #ifdef CONFIG_DMI dmi_find_bmc(); #endif - rv = init_one_smi(0, &(smi_infos[pos])); - if (rv && ! ports[0] && si_trydefaults) { - /* If we are trying defaults and the initial port is - not set, then set it. */ - si_type[0] = "kcs"; - ports[0] = DEFAULT_KCS_IO_PORT; - rv = init_one_smi(0, &(smi_infos[pos])); - if (rv) { - /* No KCS - try SMIC */ - si_type[0] = "smic"; - ports[0] = DEFAULT_SMIC_IO_PORT; - rv = init_one_smi(0, &(smi_infos[pos])); - } - if (rv) { - /* No SMIC - try BT */ - si_type[0] = "bt"; - ports[0] = DEFAULT_BT_IO_PORT; - rv = init_one_smi(0, &(smi_infos[pos])); - } - } - if (rv == 0) - pos++; +#ifdef CONFIG_ACPI + if (si_trydefaults) + acpi_find_bmc(); +#endif - for (i = 1; i < SI_MAX_PARMS; i++) { - rv = init_one_smi(i, &(smi_infos[pos])); - if (rv == 0) - pos++; +#ifdef CONFIG_PCI + pci_module_init(&ipmi_pci_driver); +#endif + + if (si_trydefaults) { + down(&smi_infos_lock); + if (list_empty(&smi_infos)) { + /* No BMC was found, try defaults. */ + up(&smi_infos_lock); + default_find_bmc(); + } else { + up(&smi_infos_lock); + } } - if (smi_infos[0] == NULL) { + down(&smi_infos_lock); + if (list_empty(&smi_infos)) { + up(&smi_infos_lock); +#ifdef CONFIG_PCI + pci_unregister_driver(&ipmi_pci_driver); +#endif printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; + } else { + up(&smi_infos_lock); + return 0; } - - return 0; } module_init(init_ipmi_si); -static void __exit cleanup_one_si(struct smi_info *to_clean) +static void __devexit cleanup_one_si(struct smi_info *to_clean) { int rv; unsigned long flags; - if (! to_clean) + if (!to_clean) return; + list_del(&to_clean->link); + /* Tell the timer and interrupt handlers that we are shutting down. */ spin_lock_irqsave(&(to_clean->si_lock), flags); spin_lock(&(to_clean->msg_lock)); atomic_inc(&to_clean->stop_operation); - to_clean->irq_cleanup(to_clean); + + if (to_clean->irq_cleanup) + to_clean->irq_cleanup(to_clean); spin_unlock(&(to_clean->msg_lock)); spin_unlock_irqrestore(&(to_clean->si_lock), flags); @@ -2511,20 +2545,27 @@ static void __exit cleanup_one_si(struct smi_info *to_clean) kfree(to_clean->si_sm); + if (to_clean->addr_source_cleanup) + to_clean->addr_source_cleanup(to_clean); if (to_clean->io_cleanup) to_clean->io_cleanup(to_clean); } static __exit void cleanup_ipmi_si(void) { - int i; + struct smi_info *e, *tmp_e; - if (! initialized) + if (!initialized) return; - for (i = 0; i < SI_MAX_DRIVERS; i++) { - cleanup_one_si(smi_infos[i]); - } +#ifdef CONFIG_PCI + pci_unregister_driver(&ipmi_pci_driver); +#endif + + down(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &smi_infos, link) + cleanup_one_si(e); + up(&smi_infos_lock); } module_exit(cleanup_ipmi_si); -- cgit v1.2.3-59-g8ed1b From 50c812b2b9513e3df34eae8c30cb2c221b79b2cb Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sun, 26 Mar 2006 01:37:21 -0800 Subject: [PATCH] ipmi: add full sysfs support Add full driver model support for the IPMI driver. It links in the proper bus and device support. It adds an "ipmi" driver interface that has each BMC discovered by the driver (as a device). These BMCs appear in the devices/platform directory. If there are multiple interfaces to the same BMC, the driver should discover this and will only have one BMC entry. The BMC entry will have pointers to each interface device that connects to it. The device information (statistics and config information) has not yet been ported over to the driver model from proc, that will come later. This work was based on work by Yani Ioannou. I basically rewrote it using that code as a guide, but he still deserves credit :). [bunk@stusta.de: make ipmi_find_bmc_guid() static] Signed-off-by: Corey Minyard Signed-off-by: Yani Ioannou Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_devintf.c | 48 +++- drivers/char/ipmi/ipmi_msghandler.c | 552 +++++++++++++++++++++++++++++++++++- drivers/char/ipmi/ipmi_poweroff.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 114 +++++--- drivers/char/ipmi/ipmi_watchdog.c | 2 +- include/linux/ipmi.h | 3 +- include/linux/ipmi_msgdefs.h | 1 + include/linux/ipmi_smi.h | 47 ++- 8 files changed, 712 insertions(+), 57 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 7c0684deea06..932feedda262 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -90,7 +90,7 @@ static unsigned int ipmi_poll(struct file *file, poll_table *wait) spin_lock_irqsave(&priv->recv_msg_lock, flags); - if (! list_empty(&(priv->recv_msgs))) + if (!list_empty(&(priv->recv_msgs))) mask |= (POLLIN | POLLRDNORM); spin_unlock_irqrestore(&priv->recv_msg_lock, flags); @@ -789,21 +789,53 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" " interface. Other values will set the major device number" " to that value."); +/* Keep track of the devices that are registered. */ +struct ipmi_reg_list { + dev_t dev; + struct list_head link; +}; +static LIST_HEAD(reg_list); +static DEFINE_MUTEX(reg_list_mutex); + static struct class *ipmi_class; -static void ipmi_new_smi(int if_num) +static void ipmi_new_smi(int if_num, struct device *device) { dev_t dev = MKDEV(ipmi_major, if_num); + struct ipmi_reg_list *entry; devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, "ipmidev/%d", if_num); - class_device_create(ipmi_class, NULL, dev, NULL, "ipmi%d", if_num); + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + printk(KERN_ERR "ipmi_devintf: Unable to create the" + " ipmi class device link\n"); + return; + } + entry->dev = dev; + + mutex_lock(®_list_mutex); + class_device_create(ipmi_class, NULL, dev, device, "ipmi%d", if_num); + list_add(&entry->link, ®_list); + mutex_unlock(®_list_mutex); } static void ipmi_smi_gone(int if_num) { - class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num)); + dev_t dev = MKDEV(ipmi_major, if_num); + struct ipmi_reg_list *entry; + + mutex_lock(®_list_mutex); + list_for_each_entry(entry, ®_list, link) { + if (entry->dev == dev) { + list_del(&entry->link); + kfree(entry); + break; + } + } + class_device_destroy(ipmi_class, dev); + mutex_unlock(®_list_mutex); devfs_remove("ipmidev/%d", if_num); } @@ -856,6 +888,14 @@ module_init(init_ipmi_devintf); static __exit void cleanup_ipmi(void) { + struct ipmi_reg_list *entry, *entry2; + mutex_lock(®_list_mutex); + list_for_each_entry_safe(entry, entry2, ®_list, link) { + list_del(&entry->link); + class_device_destroy(ipmi_class, entry->dev); + kfree(entry); + } + mutex_unlock(®_list_mutex); class_destroy(ipmi_class); ipmi_smi_watcher_unregister(&smi_watcher); devfs_remove(DEVICE_NAME); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index abd4c5118a1b..f553b7a86841 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -162,6 +162,28 @@ struct ipmi_proc_entry }; #endif +struct bmc_device +{ + struct platform_device *dev; + struct ipmi_device_id id; + unsigned char guid[16]; + int guid_set; + + struct kref refcount; + + /* bmc device attributes */ + struct device_attribute device_id_attr; + struct device_attribute provides_dev_sdrs_attr; + struct device_attribute revision_attr; + struct device_attribute firmware_rev_attr; + struct device_attribute version_attr; + struct device_attribute add_dev_support_attr; + struct device_attribute manufacturer_id_attr; + struct device_attribute product_id_attr; + struct device_attribute guid_attr; + struct device_attribute aux_firmware_rev_attr; +}; + #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 16 struct ipmi_smi @@ -178,9 +200,8 @@ struct ipmi_smi /* Used for wake ups at startup. */ wait_queue_head_t waitq; - /* The IPMI version of the BMC on the other end. */ - unsigned char version_major; - unsigned char version_minor; + struct bmc_device *bmc; + char *my_dev_name; /* This is the lower-layer's sender routine. */ struct ipmi_smi_handlers *handlers; @@ -194,6 +215,9 @@ struct ipmi_smi struct ipmi_proc_entry *proc_entries; #endif + /* Driver-model device for the system interface. */ + struct device *si_dev; + /* A table of sequence numbers for this interface. We use the sequence numbers for IPMB messages that go out of the interface to match them up with their responses. A routine @@ -312,6 +336,7 @@ struct ipmi_smi /* Events that were received with the proper format. */ unsigned int events; }; +#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) /* Used to mark an interface entry that cannot be used but is not a * free entry, either, primarily used at creation and deletion time so @@ -320,6 +345,15 @@ struct ipmi_smi #define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \ || (i == IPMI_INVALID_INTERFACE_ENTRY)) +/** + * The driver model view of the IPMI messaging driver. + */ +static struct device_driver ipmidriver = { + .name = "ipmi", + .bus = &platform_bus_type +}; +static DEFINE_MUTEX(ipmidriver_mutex); + #define MAX_IPMI_INTERFACES 4 static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES]; @@ -393,7 +427,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) if (IPMI_INVALID_INTERFACE(intf)) continue; spin_unlock_irqrestore(&interfaces_lock, flags); - watcher->new_smi(i); + watcher->new_smi(i, intf->si_dev); spin_lock_irqsave(&interfaces_lock, flags); } spin_unlock_irqrestore(&interfaces_lock, flags); @@ -409,14 +443,14 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) } static void -call_smi_watchers(int i) +call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; down_read(&smi_watchers_sem); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { - w->new_smi(i); + w->new_smi(i, dev); module_put(w->owner); } } @@ -844,8 +878,8 @@ void ipmi_get_version(ipmi_user_t user, unsigned char *major, unsigned char *minor) { - *major = user->intf->version_major; - *minor = user->intf->version_minor; + *major = ipmi_version_major(&user->intf->bmc->id); + *minor = ipmi_version_minor(&user->intf->bmc->id); } int ipmi_set_my_address(ipmi_user_t user, @@ -1553,7 +1587,8 @@ static int version_file_read_proc(char *page, char **start, off_t off, ipmi_smi_t intf = data; return sprintf(out, "%d.%d\n", - intf->version_major, intf->version_minor); + ipmi_version_major(&intf->bmc->id), + ipmi_version_minor(&intf->bmc->id)); } static int stat_file_read_proc(char *page, char **start, off_t off, @@ -1712,6 +1747,470 @@ static void remove_proc_entries(ipmi_smi_t smi) #endif /* CONFIG_PROC_FS */ } +static int __find_bmc_guid(struct device *dev, void *data) +{ + unsigned char *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + return memcmp(bmc->guid, id, 16) == 0; +} + +static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, + unsigned char *guid) +{ + struct device *dev; + + dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +struct prod_dev_id { + unsigned int product_id; + unsigned char device_id; +}; + +static int __find_bmc_prod_dev_id(struct device *dev, void *data) +{ + struct prod_dev_id *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + + return (bmc->id.product_id == id->product_id + && bmc->id.product_id == id->product_id + && bmc->id.device_id == id->device_id); +} + +static struct bmc_device *ipmi_find_bmc_prod_dev_id( + struct device_driver *drv, + unsigned char product_id, unsigned char device_id) +{ + struct prod_dev_id id = { + .product_id = product_id, + .device_id = device_id, + }; + struct device *dev; + + dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", bmc->id.device_id); +} + +static ssize_t provides_dev_sdrs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", + bmc->id.device_revision && 0x80 >> 7); +} + +static ssize_t revision_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u\n", + bmc->id.device_revision && 0x0F); +} + +static ssize_t firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, + bmc->id.firmware_revision_2); +} + +static ssize_t ipmi_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%u\n", + ipmi_version_major(&bmc->id), + ipmi_version_minor(&bmc->id)); +} + +static ssize_t add_dev_support_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%02x\n", + bmc->id.additional_device_support); +} + +static ssize_t manufacturer_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); +} + +static ssize_t product_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); +} + +static ssize_t aux_firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", + bmc->id.aux_firmware_revision[3], + bmc->id.aux_firmware_revision[2], + bmc->id.aux_firmware_revision[1], + bmc->id.aux_firmware_revision[0]); +} + +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 100, "%Lx%Lx\n", + (long long) bmc->guid[0], + (long long) bmc->guid[8]); +} + +static void +cleanup_bmc_device(struct kref *ref) +{ + struct bmc_device *bmc; + + bmc = container_of(ref, struct bmc_device, refcount); + + device_remove_file(&bmc->dev->dev, + &bmc->device_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + device_remove_file(&bmc->dev->dev, + &bmc->revision_attr); + device_remove_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + device_remove_file(&bmc->dev->dev, + &bmc->version_attr); + device_remove_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + device_remove_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (bmc->id.aux_firmware_revision_set) + device_remove_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (bmc->guid_set) + device_remove_file(&bmc->dev->dev, + &bmc->guid_attr); + platform_device_unregister(bmc->dev); + kfree(bmc); +} + +static void ipmi_bmc_unregister(ipmi_smi_t intf) +{ + struct bmc_device *bmc = intf->bmc; + + sysfs_remove_link(&intf->si_dev->kobj, "bmc"); + if (intf->my_dev_name) { + sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name); + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + } + + mutex_lock(&ipmidriver_mutex); + kref_put(&bmc->refcount, cleanup_bmc_device); + mutex_unlock(&ipmidriver_mutex); +} + +static int ipmi_bmc_register(ipmi_smi_t intf) +{ + int rv; + struct bmc_device *bmc = intf->bmc; + struct bmc_device *old_bmc; + int size; + char dummy[1]; + + mutex_lock(&ipmidriver_mutex); + + /* + * Try to find if there is an bmc_device struct + * representing the interfaced BMC already + */ + if (bmc->guid_set) + old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); + else + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, + bmc->id.product_id, + bmc->id.device_id); + + /* + * If there is already an bmc_device, free the new one, + * otherwise register the new BMC device + */ + if (old_bmc) { + kfree(bmc); + intf->bmc = old_bmc; + bmc = old_bmc; + + kref_get(&bmc->refcount); + mutex_unlock(&ipmidriver_mutex); + + printk(KERN_INFO + "ipmi: interfacing existing BMC (man_id: 0x%6.6x," + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } else { + bmc->dev = platform_device_alloc("ipmi_bmc", + bmc->id.device_id); + if (! bmc->dev) { + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to allocate platform device\n"); + return -ENOMEM; + } + bmc->dev->dev.driver = &ipmidriver; + dev_set_drvdata(&bmc->dev->dev, bmc); + kref_init(&bmc->refcount); + + rv = platform_device_register(bmc->dev); + mutex_unlock(&ipmidriver_mutex); + if (rv) { + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to register bmc device: %d\n", + rv); + /* Don't go to out_err, you can only do that if + the device is registered already. */ + return rv; + } + + bmc->device_id_attr.attr.name = "device_id"; + bmc->device_id_attr.attr.owner = THIS_MODULE; + bmc->device_id_attr.attr.mode = S_IRUGO; + bmc->device_id_attr.show = device_id_show; + + bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; + bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE; + bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; + bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; + + + bmc->revision_attr.attr.name = "revision"; + bmc->revision_attr.attr.owner = THIS_MODULE; + bmc->revision_attr.attr.mode = S_IRUGO; + bmc->revision_attr.show = revision_show; + + bmc->firmware_rev_attr.attr.name = "firmware_revision"; + bmc->firmware_rev_attr.attr.owner = THIS_MODULE; + bmc->firmware_rev_attr.attr.mode = S_IRUGO; + bmc->firmware_rev_attr.show = firmware_rev_show; + + bmc->version_attr.attr.name = "ipmi_version"; + bmc->version_attr.attr.owner = THIS_MODULE; + bmc->version_attr.attr.mode = S_IRUGO; + bmc->version_attr.show = ipmi_version_show; + + bmc->add_dev_support_attr.attr.name + = "additional_device_support"; + bmc->add_dev_support_attr.attr.owner = THIS_MODULE; + bmc->add_dev_support_attr.attr.mode = S_IRUGO; + bmc->add_dev_support_attr.show = add_dev_support_show; + + bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; + bmc->manufacturer_id_attr.attr.owner = THIS_MODULE; + bmc->manufacturer_id_attr.attr.mode = S_IRUGO; + bmc->manufacturer_id_attr.show = manufacturer_id_show; + + bmc->product_id_attr.attr.name = "product_id"; + bmc->product_id_attr.attr.owner = THIS_MODULE; + bmc->product_id_attr.attr.mode = S_IRUGO; + bmc->product_id_attr.show = product_id_show; + + bmc->guid_attr.attr.name = "guid"; + bmc->guid_attr.attr.owner = THIS_MODULE; + bmc->guid_attr.attr.mode = S_IRUGO; + bmc->guid_attr.show = guid_show; + + bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; + bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE; + bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; + bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; + + device_create_file(&bmc->dev->dev, + &bmc->device_id_attr); + device_create_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + device_create_file(&bmc->dev->dev, + &bmc->revision_attr); + device_create_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + device_create_file(&bmc->dev->dev, + &bmc->version_attr); + device_create_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + device_create_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + device_create_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (bmc->id.aux_firmware_revision_set) + device_create_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (bmc->guid_set) + device_create_file(&bmc->dev->dev, + &bmc->guid_attr); + + printk(KERN_INFO + "ipmi: Found new BMC (man_id: 0x%6.6x, " + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } + + /* + * create symlink from system interface device to bmc device + * and back. + */ + rv = sysfs_create_link(&intf->si_dev->kobj, + &bmc->dev->dev.kobj, "bmc"); + if (rv) { + printk(KERN_ERR + "ipmi_msghandler: Unable to create bmc symlink: %d\n", + rv); + goto out_err; + } + + size = snprintf(dummy, 0, "ipmi%d", intf->intf_num); + intf->my_dev_name = kmalloc(size+1, GFP_KERNEL); + if (!intf->my_dev_name) { + rv = -ENOMEM; + printk(KERN_ERR + "ipmi_msghandler: allocate link from BMC: %d\n", + rv); + goto out_err; + } + snprintf(intf->my_dev_name, size+1, "ipmi%d", intf->intf_num); + + rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj, + intf->my_dev_name); + if (rv) { + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to create symlink to bmc: %d\n", + rv); + goto out_err; + } + + return 0; + +out_err: + ipmi_bmc_unregister(intf); + return rv; +} + +static int +send_guid_cmd(ipmi_smi_t intf, int chan) +{ + struct kernel_ipmi_msg msg; + struct ipmi_system_interface_addr si; + + si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + si.channel = IPMI_BMC_CHANNEL; + si.lun = 0; + + msg.netfn = IPMI_NETFN_APP_REQUEST; + msg.cmd = IPMI_GET_DEVICE_GUID_CMD; + msg.data = NULL; + msg.data_len = 0; + return i_ipmi_request(NULL, + intf, + (struct ipmi_addr *) &si, + 0, + &msg, + intf, + NULL, + NULL, + 0, + intf->channels[0].address, + intf->channels[0].lun, + -1, 0); +} + +static void +guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +{ + if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) + || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) + || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) + /* Not for me */ + return; + + if (msg->msg.data[0] != 0) { + /* Error from getting the GUID, the BMC doesn't have one. */ + intf->bmc->guid_set = 0; + goto out; + } + + if (msg->msg.data_len < 17) { + intf->bmc->guid_set = 0; + printk(KERN_WARNING PFX + "guid_handler: The GUID response from the BMC was too" + " short, it was %d but should have been 17. Assuming" + " GUID is not available.\n", + msg->msg.data_len); + goto out; + } + + memcpy(intf->bmc->guid, msg->msg.data, 16); + intf->bmc->guid_set = 1; + out: + wake_up(&intf->waitq); +} + +static void +get_guid(ipmi_smi_t intf) +{ + int rv; + + intf->bmc->guid_set = 0x2; + intf->null_user_handler = guid_handler; + rv = send_guid_cmd(intf, 0); + if (rv) + /* Send failed, no GUID available. */ + intf->bmc->guid_set = 0; + wait_event(intf->waitq, intf->bmc->guid_set != 2); + intf->null_user_handler = NULL; +} + static int send_channel_info_cmd(ipmi_smi_t intf, int chan) { @@ -1804,8 +2303,8 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, - unsigned char version_major, - unsigned char version_minor, + struct ipmi_device_id *device_id, + struct device *si_dev, unsigned char slave_addr, ipmi_smi_t *new_intf) { @@ -1813,7 +2312,11 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, int rv; ipmi_smi_t intf; unsigned long flags; + int version_major; + int version_minor; + version_major = ipmi_version_major(device_id); + version_minor = ipmi_version_minor(device_id); /* Make sure the driver is actually initialized, this handles problems with initialization order. */ @@ -1831,10 +2334,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (!intf) return -ENOMEM; memset(intf, 0, sizeof(*intf)); + intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); + if (!intf->bmc) { + kfree(intf); + return -ENOMEM; + } intf->intf_num = -1; kref_init(&intf->refcount); - intf->version_major = version_major; - intf->version_minor = version_minor; + intf->bmc->id = *device_id; + intf->si_dev = si_dev; for (j = 0; j < IPMI_MAX_CHANNELS; j++) { intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; intf->channels[j].lun = 2; @@ -1884,6 +2392,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, caller before sending any messages with it. */ *new_intf = intf; + get_guid(intf); + if ((version_major > 1) || ((version_major == 1) && (version_minor >= 5))) { @@ -1898,6 +2408,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, /* Wait for the channel info to be read. */ wait_event(intf->waitq, intf->curr_channel >= IPMI_MAX_CHANNELS); + intf->null_user_handler = NULL; } else { /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; @@ -1907,6 +2418,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (rv == 0) rv = add_proc_entries(intf, i); + rv = ipmi_bmc_register(intf); + out: if (rv) { if (intf->proc_dir) @@ -1921,7 +2434,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, spin_lock_irqsave(&interfaces_lock, flags); ipmi_interfaces[i] = intf; spin_unlock_irqrestore(&interfaces_lock, flags); - call_smi_watchers(i); + call_smi_watchers(i, intf->si_dev); } return rv; @@ -1933,6 +2446,8 @@ int ipmi_unregister_smi(ipmi_smi_t intf) struct ipmi_smi_watcher *w; unsigned long flags; + ipmi_bmc_unregister(intf); + spin_lock_irqsave(&interfaces_lock, flags); for (i = 0; i < MAX_IPMI_INTERFACES; i++) { if (ipmi_interfaces[i] == intf) { @@ -3196,10 +3711,17 @@ static struct notifier_block panic_block = { static int ipmi_init_msghandler(void) { int i; + int rv; if (initialized) return 0; + rv = driver_register(&ipmidriver); + if (rv) { + printk(KERN_ERR PFX "Could not register IPMI driver\n"); + return rv; + } + printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); @@ -3256,6 +3778,8 @@ static __exit void cleanup_ipmi(void) remove_proc_entry(proc_ipmi_root->name, &proc_root); #endif /* CONFIG_PROC_FS */ + driver_unregister(&ipmidriver); + initialized = 0; /* Check for buffer leaks. */ diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index e8ed26b77d4c..786a2802ca34 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -464,7 +464,7 @@ static void ipmi_poweroff_function (void) /* Wait for an IPMI interface to be installed, the first one installed will be grabbed by this code and used to perform the powerdown. */ -static void ipmi_po_new_smi(int if_num) +static void ipmi_po_new_smi(int if_num, struct device *device) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f3b3b23c5330..12f858dc9994 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -112,20 +112,13 @@ enum si_type { }; static char *si_to_str[] = { "KCS", "SMIC", "BT" }; -struct ipmi_device_id { - unsigned char device_id; - unsigned char device_revision; - unsigned char firmware_revision_1; - unsigned char firmware_revision_2; - unsigned char ipmi_version; - unsigned char additional_device_support; - unsigned char manufacturer_id[3]; - unsigned char product_id[2]; - unsigned char aux_firmware_revision[4]; -} __attribute__((packed)); - -#define ipmi_version_major(v) ((v)->ipmi_version & 0xf) -#define ipmi_version_minor(v) ((v)->ipmi_version >> 4) +#define DEVICE_NAME "ipmi_si" + +static struct device_driver ipmi_driver = +{ + .name = DEVICE_NAME, + .bus = &platform_bus_type +}; struct smi_info { @@ -208,8 +201,17 @@ struct smi_info interrupts. */ int interrupt_disabled; + /* From the get device id response... */ struct ipmi_device_id device_id; + /* Driver model stuff. */ + struct device *dev; + struct platform_device *pdev; + + /* True if we allocated the device, false if it came from + * someplace else (like PCI). */ + int dev_registered; + /* Slave address, could be reported from DMI. */ unsigned char slave_addr; @@ -987,8 +989,6 @@ static LIST_HEAD(smi_infos); static DECLARE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ -#define DEVICE_NAME "ipmi_si" - #define DEFAULT_REGSPACING 1 static int si_trydefaults = 1; @@ -1164,7 +1164,6 @@ static void port_cleanup(struct smi_info *info) release_region (addr, mapsize); } - kfree(info); } static int port_setup(struct smi_info *info) @@ -1273,7 +1272,6 @@ static void mem_cleanup(struct smi_info *info) release_mem_region(addr, mapsize); } - kfree(info); } static int mem_setup(struct smi_info *info) @@ -1858,6 +1856,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, if (info->irq) info->irq_setup = std_irq_setup; + info->dev = &pdev->dev; + return try_smi_init(info); } @@ -1898,11 +1898,11 @@ static struct pci_driver ipmi_pci_driver = { static int try_get_dev_id(struct smi_info *smi_info) { - unsigned char msg[2]; - unsigned char *resp; - unsigned long resp_len; - enum si_sm_result smi_result; - int rv = 0; + unsigned char msg[2]; + unsigned char *resp; + unsigned long resp_len; + enum si_sm_result smi_result; + int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) @@ -1941,7 +1941,7 @@ static int try_get_dev_id(struct smi_info *smi_info) /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); - if (resp_len < 6) { + if (resp_len < 14) { /* That's odd, it should be longer. */ rv = -EINVAL; goto out; @@ -1954,8 +1954,7 @@ static int try_get_dev_id(struct smi_info *smi_info) } /* Record info from the get device id, in case we need it. */ - memcpy(&smi_info->device_id, &resp[3], - min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id))); + ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id); out: kfree(resp); @@ -2058,15 +2057,14 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 #define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51 -#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} +#define DELL_IANA_MFR_ID 0x0002a2 static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - const char mfr[3]=DELL_IANA_MFR_ID; - if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { + if (id->manufacturer_id == DELL_IANA_MFR_ID) { if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && - id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { + id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } @@ -2138,8 +2136,7 @@ static void setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - const char mfr[3]=DELL_IANA_MFR_ID; - if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && + if (id->manufacturer_id == DELL_IANA_MFR_ID && smi_info->si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -2358,10 +2355,36 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); + if (!new_smi->dev) { + /* If we don't already have a device from something + * else (like PCI), then register a new one. */ + new_smi->pdev = platform_device_alloc("ipmi_si", + new_smi->intf_num); + if (rv) { + printk(KERN_ERR + "ipmi_si_intf:" + " Unable to allocate platform device\n"); + goto out_err_stop_timer; + } + new_smi->dev = &new_smi->pdev->dev; + new_smi->dev->driver = &ipmi_driver; + + rv = platform_device_register(new_smi->pdev); + if (rv) { + printk(KERN_ERR + "ipmi_si_intf:" + " Unable to register system interface device:" + " %d\n", + rv); + goto out_err_stop_timer; + } + new_smi->dev_registered = 1; + } + rv = ipmi_register_smi(&handlers, new_smi, - ipmi_version_major(&new_smi->device_id), - ipmi_version_minor(&new_smi->device_id), + &new_smi->device_id, + new_smi->dev, new_smi->slave_addr, &(new_smi->intf)); if (rv) { @@ -2425,6 +2448,11 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->io_cleanup) new_smi->io_cleanup(new_smi); + if (new_smi->dev_registered) + platform_device_unregister(new_smi->pdev); + + kfree(new_smi); + up(&smi_infos_lock); return rv; @@ -2434,11 +2462,22 @@ static __devinit int init_ipmi_si(void) { int i; char *str; + int rv; if (initialized) return 0; initialized = 1; + /* Register the device drivers. */ + rv = driver_register(&ipmi_driver); + if (rv) { + printk(KERN_ERR + "init_ipmi_si: Unable to register driver: %d\n", + rv); + return rv; + } + + /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { @@ -2549,6 +2588,11 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) to_clean->addr_source_cleanup(to_clean); if (to_clean->io_cleanup) to_clean->io_cleanup(to_clean); + + if (to_clean->dev_registered) + platform_device_unregister(to_clean->pdev); + + kfree(to_clean); } static __exit void cleanup_ipmi_si(void) @@ -2566,6 +2610,8 @@ static __exit void cleanup_ipmi_si(void) list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); up(&smi_infos_lock); + + driver_unregister(&ipmi_driver); } module_exit(cleanup_ipmi_si); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 1f3159eb1ede..616539310d9a 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -996,7 +996,7 @@ static struct notifier_block wdog_panic_notifier = { }; -static void ipmi_new_smi(int if_num) +static void ipmi_new_smi(int if_num, struct device *device) { ipmi_register_watchdog(if_num); } diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index d6276e60b3bf..0a84b56935c2 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -36,6 +36,7 @@ #include #include +#include /* * This file describes an interface to an IPMI driver. You have to @@ -397,7 +398,7 @@ struct ipmi_smi_watcher the watcher list. So you can add and remove users from the IPMI interface, send messages, etc., but you cannot add or remove SMI watchers or SMI interfaces. */ - void (*new_smi)(int if_num); + void (*new_smi)(int if_num, struct device *dev); void (*smi_gone)(int if_num); }; diff --git a/include/linux/ipmi_msgdefs.h b/include/linux/ipmi_msgdefs.h index 03bc64dc2ec1..22f5e2afda4f 100644 --- a/include/linux/ipmi_msgdefs.h +++ b/include/linux/ipmi_msgdefs.h @@ -47,6 +47,7 @@ #define IPMI_NETFN_APP_RESPONSE 0x07 #define IPMI_GET_DEVICE_ID_CMD 0x01 #define IPMI_CLEAR_MSG_FLAGS_CMD 0x30 +#define IPMI_GET_DEVICE_GUID_CMD 0x08 #define IPMI_GET_MSG_FLAGS_CMD 0x31 #define IPMI_SEND_MSG_CMD 0x34 #define IPMI_GET_MSG_CMD 0x33 diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index e36ee157ad67..53571288a9fc 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include /* This files describes the interface for IPMI system management interface drivers to bind into the IPMI message handler. */ @@ -113,12 +116,52 @@ struct ipmi_smi_handlers void (*dec_usecount)(void *send_info); }; +struct ipmi_device_id { + unsigned char device_id; + unsigned char device_revision; + unsigned char firmware_revision_1; + unsigned char firmware_revision_2; + unsigned char ipmi_version; + unsigned char additional_device_support; + unsigned int manufacturer_id; + unsigned int product_id; + unsigned char aux_firmware_revision[4]; + unsigned int aux_firmware_revision_set : 1; +}; + +#define ipmi_version_major(v) ((v)->ipmi_version & 0xf) +#define ipmi_version_minor(v) ((v)->ipmi_version >> 4) + +/* Take a pointer to a raw data buffer and a length and extract device + id information from it. The first byte of data must point to the + byte from the get device id response after the completion code. + The caller is responsible for making sure the length is at least + 11 and the command completed without error. */ +static inline void ipmi_demangle_device_id(unsigned char *data, + unsigned int data_len, + struct ipmi_device_id *id) +{ + id->device_id = data[0]; + id->device_revision = data[1]; + id->firmware_revision_1 = data[2]; + id->firmware_revision_2 = data[3]; + id->ipmi_version = data[4]; + id->additional_device_support = data[5]; + id->manufacturer_id = data[6] | (data[7] << 8) | (data[8] << 16); + id->product_id = data[9] | (data[10] << 8); + if (data_len >= 15) { + memcpy(id->aux_firmware_revision, data+11, 4); + id->aux_firmware_revision_set = 1; + } else + id->aux_firmware_revision_set = 0; +} + /* Add a low-level interface to the IPMI driver. Note that if the interface doesn't know its slave address, it should pass in zero. */ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, - unsigned char version_major, - unsigned char version_minor, + struct ipmi_device_id *device_id, + struct device *dev, unsigned char slave_addr, ipmi_smi_t *intf); -- cgit v1.2.3-59-g8ed1b From e041c683412d5bf44dc2b109053e3b837b71742d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 27 Mar 2006 01:16:30 -0800 Subject: [PATCH] Notifier chain update: API changes The kernel's implementation of notifier chains is unsafe. There is no protection against entries being added to or removed from a chain while the chain is in use. The issues were discussed in this thread: http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2 We noticed that notifier chains in the kernel fall into two basic usage classes: "Blocking" chains are always called from a process context and the callout routines are allowed to sleep; "Atomic" chains can be called from an atomic context and the callout routines are not allowed to sleep. We decided to codify this distinction and make it part of the API. Therefore this set of patches introduces three new, parallel APIs: one for blocking notifiers, one for atomic notifiers, and one for "raw" notifiers (which is really just the old API under a new name). New kinds of data structures are used for the heads of the chains, and new routines are defined for registration, unregistration, and calling a chain. The three APIs are explained in include/linux/notifier.h and their implementation is in kernel/sys.c. With atomic and blocking chains, the implementation guarantees that the chain links will not be corrupted and that chain callers will not get messed up by entries being added or removed. For raw chains the implementation provides no guarantees at all; users of this API must provide their own protections. (The idea was that situations may come up where the assumptions of the atomic and blocking APIs are not appropriate, so it should be possible for users to handle these things in their own way.) There are some limitations, which should not be too hard to live with. For atomic/blocking chains, registration and unregistration must always be done in a process context since the chain is protected by a mutex/rwsem. Also, a callout routine for a non-raw chain must not try to register or unregister entries on its own chain. (This did happen in a couple of places and the code had to be changed to avoid it.) Since atomic chains may be called from within an NMI handler, they cannot use spinlocks for synchronization. Instead we use RCU. The overhead falls almost entirely in the unregister routine, which is okay since unregistration is much less frequent that calling a chain. Here is the list of chains that we adjusted and their classifications. None of them use the raw API, so for the moment it is only a placeholder. ATOMIC CHAINS ------------- arch/i386/kernel/traps.c: i386die_chain arch/ia64/kernel/traps.c: ia64die_chain arch/powerpc/kernel/traps.c: powerpc_die_chain arch/sparc64/kernel/traps.c: sparc64die_chain arch/x86_64/kernel/traps.c: die_chain drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list kernel/panic.c: panic_notifier_list kernel/profile.c: task_free_notifier net/bluetooth/hci_core.c: hci_notifier net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain net/ipv6/addrconf.c: inet6addr_chain net/netfilter/nf_conntrack_core.c: nf_conntrack_chain net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain net/netlink/af_netlink.c: netlink_chain BLOCKING CHAINS --------------- arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain arch/s390/kernel/process.c: idle_chain arch/x86_64/kernel/process.c idle_notifier drivers/base/memory.c: memory_chain drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list drivers/macintosh/adb.c: adb_client_list drivers/macintosh/via-pmu.c sleep_notifier_list drivers/macintosh/via-pmu68k.c sleep_notifier_list drivers/macintosh/windfarm_core.c wf_client_list drivers/usb/core/notify.c usb_notifier_list drivers/video/fbmem.c fb_notifier_list kernel/cpu.c cpu_chain kernel/module.c module_notify_list kernel/profile.c munmap_notifier kernel/profile.c task_exit_notifier kernel/sys.c reboot_notifier_list net/core/dev.c netdev_chain net/decnet/dn_dev.c: dnaddr_chain net/ipv4/devinet.c: inetaddr_chain It's possible that some of these classifications are wrong. If they are, please let us know or submit a patch to fix them. Note that any chain that gets called very frequently should be atomic, because the rwsem read-locking used for blocking chains is very likely to incur cache misses on SMP systems. (However, if the chain's callout routines may sleep then the chain cannot be atomic.) The patch set was written by Alan Stern and Chandra Seetharaman, incorporating material written by Keith Owens and suggestions from Paul McKenney and Andrew Morton. [jes@sgi.com: restructure the notifier chain initialization macros] Signed-off-by: Alan Stern Signed-off-by: Chandra Seetharaman Signed-off-by: Jes Sorensen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/setup.c | 5 +- arch/arm/mach-omap1/board-netstar.c | 2 +- arch/arm/mach-omap1/board-voiceblue.c | 2 +- arch/i386/kernel/traps.c | 17 +- arch/ia64/kernel/traps.c | 6 +- arch/mips/lasat/setup.c | 3 +- arch/mips/sgi-ip22/ip22-reset.c | 2 +- arch/mips/sgi-ip32/ip32-reset.c | 2 +- arch/parisc/kernel/pdc_chassis.c | 3 +- arch/powerpc/kernel/setup_64.c | 3 +- arch/powerpc/kernel/traps.c | 16 +- arch/powerpc/platforms/pseries/reconfig.c | 10 +- arch/ppc/platforms/prep_setup.c | 2 +- arch/s390/kernel/process.c | 11 +- arch/sparc64/kernel/traps.c | 17 +- arch/um/drivers/mconsole_kern.c | 3 +- arch/um/kernel/um_arch.c | 3 +- arch/x86_64/kernel/process.c | 17 +- arch/x86_64/kernel/traps.c | 18 +- arch/xtensa/platform-iss/setup.c | 2 +- drivers/base/memory.c | 8 +- drivers/char/ipmi/ipmi_msghandler.c | 4 +- drivers/char/ipmi/ipmi_si_intf.c | 7 +- drivers/char/ipmi/ipmi_watchdog.c | 6 +- drivers/cpufreq/cpufreq.c | 61 +++--- drivers/firmware/dcdbas.c | 19 +- drivers/macintosh/adb.c | 11 +- drivers/macintosh/adbhid.c | 3 +- drivers/macintosh/via-pmu.c | 2 +- drivers/macintosh/via-pmu68k.c | 7 +- drivers/macintosh/windfarm_core.c | 8 +- drivers/misc/ibmasm/heartbeat.c | 5 +- drivers/net/bonding/bond_main.c | 2 +- drivers/parisc/led.c | 14 +- drivers/parisc/power.c | 6 +- drivers/scsi/gdth.c | 9 +- drivers/usb/core/notify.c | 65 +----- drivers/video/fbmem.c | 31 +-- include/asm-i386/kdebug.h | 10 +- include/asm-ia64/kdebug.h | 4 +- include/asm-powerpc/kdebug.h | 12 +- include/asm-sparc64/kdebug.h | 11 +- include/asm-x86_64/kdebug.h | 23 +- include/linux/adb.h | 2 +- include/linux/kernel.h | 2 +- include/linux/memory.h | 1 - include/linux/netfilter_ipv4/ip_conntrack.h | 17 +- include/linux/notifier.h | 96 +++++++- include/net/netfilter/nf_conntrack.h | 17 +- kernel/cpu.c | 29 +-- kernel/module.c | 20 +- kernel/panic.c | 4 +- kernel/profile.c | 53 ++--- kernel/softlockup.c | 2 +- kernel/sys.c | 327 ++++++++++++++++++++++------ net/bluetooth/hci_core.c | 8 +- net/core/dev.c | 42 ++-- net/decnet/dn_dev.c | 10 +- net/ipv4/devinet.c | 16 +- net/ipv4/netfilter/ip_conntrack_core.c | 6 +- net/ipv6/addrconf.c | 10 +- net/netfilter/nf_conntrack_core.c | 6 +- net/netlink/af_netlink.c | 9 +- 63 files changed, 677 insertions(+), 472 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 9402624453c2..dd8769670596 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -43,7 +43,7 @@ #include #include -extern struct notifier_block *panic_notifier_list; +extern struct atomic_notifier_head panic_notifier_list; static int alpha_panic_event(struct notifier_block *, unsigned long, void *); static struct notifier_block alpha_panic_block = { alpha_panic_event, @@ -500,7 +500,8 @@ setup_arch(char **cmdline_p) } /* Register a call for panic conditions. */ - notifier_chain_register(&panic_notifier_list, &alpha_panic_block); + atomic_notifier_chain_register(&panic_notifier_list, + &alpha_panic_block); #ifdef CONFIG_ALPHA_GENERIC /* Assume that we've booted from SRM if we haven't booted from MILO. diff --git a/arch/arm/mach-omap1/board-netstar.c b/arch/arm/mach-omap1/board-netstar.c index 60d5f8a3339c..7520e602d7a2 100644 --- a/arch/arm/mach-omap1/board-netstar.c +++ b/arch/arm/mach-omap1/board-netstar.c @@ -141,7 +141,7 @@ static int __init netstar_late_init(void) /* TODO: Setup front panel switch here */ /* Setup panic notifier */ - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); return 0; } diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index bfd5fdd1a875..52e4a9d69642 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c @@ -235,7 +235,7 @@ static struct notifier_block panic_block = { static int __init voiceblue_setup(void) { /* Setup panic notifier */ - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); return 0; } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 4624f8ca2459..6b63a5aa1e46 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -92,22 +92,21 @@ asmlinkage void spurious_interrupt_bug(void); asmlinkage void machine_check(void); static int kstack_depth_to_print = 24; -struct notifier_block *i386die_chain; -static DEFINE_SPINLOCK(die_notifier_lock); +ATOMIC_NOTIFIER_HEAD(i386die_chain); int register_die_notifier(struct notifier_block *nb) { - int err = 0; - unsigned long flags; - vmalloc_sync_all(); - spin_lock_irqsave(&die_notifier_lock, flags); - err = notifier_chain_register(&i386die_chain, nb); - spin_unlock_irqrestore(&die_notifier_lock, flags); - return err; + return atomic_notifier_chain_register(&i386die_chain, nb); } EXPORT_SYMBOL(register_die_notifier); +int unregister_die_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&i386die_chain, nb); +} +EXPORT_SYMBOL(unregister_die_notifier); + static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { return p > (void *)tinfo && diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index dabd6c32641e..7c1ddc8ac443 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -30,19 +30,19 @@ extern spinlock_t timerlist_lock; fpswa_interface_t *fpswa_interface; EXPORT_SYMBOL(fpswa_interface); -struct notifier_block *ia64die_chain; +ATOMIC_NOTIFIER_HEAD(ia64die_chain); int register_die_notifier(struct notifier_block *nb) { - return notifier_chain_register(&ia64die_chain, nb); + return atomic_notifier_chain_register(&ia64die_chain, nb); } EXPORT_SYMBOL_GPL(register_die_notifier); int unregister_die_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&ia64die_chain, nb); + return atomic_notifier_chain_unregister(&ia64die_chain, nb); } EXPORT_SYMBOL_GPL(unregister_die_notifier); diff --git a/arch/mips/lasat/setup.c b/arch/mips/lasat/setup.c index 83eb08b7a072..e9e9a89c6741 100644 --- a/arch/mips/lasat/setup.c +++ b/arch/mips/lasat/setup.c @@ -165,7 +165,8 @@ void __init plat_setup(void) /* Set up panic notifier */ for (i = 0; i < sizeof(lasat_panic_block) / sizeof(struct notifier_block); i++) - notifier_chain_register(&panic_notifier_list, &lasat_panic_block[i]); + atomic_notifier_chain_register(&panic_notifier_list, + &lasat_panic_block[i]); lasat_reboot_setup(); diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c index 92a3b3c15ed3..a9c58e067b53 100644 --- a/arch/mips/sgi-ip22/ip22-reset.c +++ b/arch/mips/sgi-ip22/ip22-reset.c @@ -238,7 +238,7 @@ static int __init reboot_setup(void) request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); init_timer(&blink_timer); blink_timer.function = blink_timeout; - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); return 0; } diff --git a/arch/mips/sgi-ip32/ip32-reset.c b/arch/mips/sgi-ip32/ip32-reset.c index 0c948008b023..ab9d9cef089e 100644 --- a/arch/mips/sgi-ip32/ip32-reset.c +++ b/arch/mips/sgi-ip32/ip32-reset.c @@ -193,7 +193,7 @@ static __init int ip32_reboot_setup(void) init_timer(&blink_timer); blink_timer.function = blink_timeout; - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); request_irq(MACEISA_RTC_IRQ, ip32_rtc_int, 0, "rtc", NULL); diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c index 2a01fe1bdc98..0cea6958f427 100644 --- a/arch/parisc/kernel/pdc_chassis.c +++ b/arch/parisc/kernel/pdc_chassis.c @@ -150,7 +150,8 @@ void __init parisc_pdc_chassis_init(void) if (handle) { /* initialize panic notifier chain */ - notifier_chain_register(&panic_notifier_list, &pdc_chassis_panic_block); + atomic_notifier_chain_register(&panic_notifier_list, + &pdc_chassis_panic_block); /* initialize reboot notifier chain */ register_reboot_notifier(&pdc_chassis_reboot_block); diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 2f3fdad35594..e20c1fae3423 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -579,7 +579,8 @@ void __init setup_arch(char **cmdline_p) panic_timeout = 180; if (ppc_md.panic) - notifier_chain_register(&panic_notifier_list, &ppc64_panic_block); + atomic_notifier_chain_register(&panic_notifier_list, + &ppc64_panic_block); init_mm.start_code = PAGE_OFFSET; init_mm.end_code = (unsigned long) _etext; diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 98660aedeeb7..9763faab6739 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -74,19 +74,19 @@ EXPORT_SYMBOL(__debugger_dabr_match); EXPORT_SYMBOL(__debugger_fault_handler); #endif -struct notifier_block *powerpc_die_chain; -static DEFINE_SPINLOCK(die_notifier_lock); +ATOMIC_NOTIFIER_HEAD(powerpc_die_chain); int register_die_notifier(struct notifier_block *nb) { - int err = 0; - unsigned long flags; + return atomic_notifier_chain_register(&powerpc_die_chain, nb); +} +EXPORT_SYMBOL(register_die_notifier); - spin_lock_irqsave(&die_notifier_lock, flags); - err = notifier_chain_register(&powerpc_die_chain, nb); - spin_unlock_irqrestore(&die_notifier_lock, flags); - return err; +int unregister_die_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&powerpc_die_chain, nb); } +EXPORT_SYMBOL(unregister_die_notifier); /* * Trap & Exception support diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 86cfa6ecdcf3..5ad90676567a 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -94,16 +94,16 @@ static struct device_node *derive_parent(const char *path) return parent; } -static struct notifier_block *pSeries_reconfig_chain; +static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain); int pSeries_reconfig_notifier_register(struct notifier_block *nb) { - return notifier_chain_register(&pSeries_reconfig_chain, nb); + return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb); } void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) { - notifier_chain_unregister(&pSeries_reconfig_chain, nb); + blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb); } static int pSeries_reconfig_add_node(const char *path, struct property *proplist) @@ -131,7 +131,7 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist goto out_err; } - err = notifier_call_chain(&pSeries_reconfig_chain, + err = blocking_notifier_call_chain(&pSeries_reconfig_chain, PSERIES_RECONFIG_ADD, np); if (err == NOTIFY_BAD) { printk(KERN_ERR "Failed to add device node %s\n", path); @@ -171,7 +171,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) remove_node_proc_entries(np); - notifier_call_chain(&pSeries_reconfig_chain, + blocking_notifier_call_chain(&pSeries_reconfig_chain, PSERIES_RECONFIG_REMOVE, np); of_detach_node(np); diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index a0fc628ffb1e..d95c05d9824d 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c @@ -736,7 +736,7 @@ ibm_statusled_progress(char *s, unsigned short hex) hex = 0xfff; if (!notifier_installed) { ++notifier_installed; - notifier_chain_register(&panic_notifier_list, + atomic_notifier_chain_register(&panic_notifier_list, &ibm_statusled_block); } } diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 99182a415fe7..4a0f5a1551ea 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -76,17 +76,17 @@ unsigned long thread_saved_pc(struct task_struct *tsk) /* * Need to know about CPUs going idle? */ -static struct notifier_block *idle_chain; +static ATOMIC_NOTIFIER_HEAD(idle_chain); int register_idle_notifier(struct notifier_block *nb) { - return notifier_chain_register(&idle_chain, nb); + return atomic_notifier_chain_register(&idle_chain, nb); } EXPORT_SYMBOL(register_idle_notifier); int unregister_idle_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&idle_chain, nb); + return atomic_notifier_chain_unregister(&idle_chain, nb); } EXPORT_SYMBOL(unregister_idle_notifier); @@ -95,7 +95,7 @@ void do_monitor_call(struct pt_regs *regs, long interruption_code) /* disable monitor call class 0 */ __ctl_clear_bit(8, 15); - notifier_call_chain(&idle_chain, CPU_NOT_IDLE, + atomic_notifier_call_chain(&idle_chain, CPU_NOT_IDLE, (void *)(long) smp_processor_id()); } @@ -116,7 +116,8 @@ static void default_idle(void) return; } - rc = notifier_call_chain(&idle_chain, CPU_IDLE, (void *)(long) cpu); + rc = atomic_notifier_call_chain(&idle_chain, + CPU_IDLE, (void *)(long) cpu); if (rc != NOTIFY_OK && rc != NOTIFY_DONE) BUG(); if (rc != NOTIFY_OK) { diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index df612e4f75f9..ff090bb9734b 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -43,18 +43,19 @@ #include #endif -struct notifier_block *sparc64die_chain; -static DEFINE_SPINLOCK(die_notifier_lock); +ATOMIC_NOTIFIER_HEAD(sparc64die_chain); int register_die_notifier(struct notifier_block *nb) { - int err = 0; - unsigned long flags; - spin_lock_irqsave(&die_notifier_lock, flags); - err = notifier_chain_register(&sparc64die_chain, nb); - spin_unlock_irqrestore(&die_notifier_lock, flags); - return err; + return atomic_notifier_chain_register(&sparc64die_chain, nb); } +EXPORT_SYMBOL(register_die_notifier); + +int unregister_die_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&sparc64die_chain, nb); +} +EXPORT_SYMBOL(unregister_die_notifier); /* When an irrecoverable trap occurs at tl > 0, the trap entry * code logs the trap state registers at every level in the trap diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 54388d10bcf9..1488816588ea 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -762,7 +762,8 @@ static struct notifier_block panic_exit_notifier = { static int add_notifier(void) { - notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); + atomic_notifier_chain_register(&panic_notifier_list, + &panic_exit_notifier); return(0); } diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index bb1c87211ac1..7d51dd7201c3 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -477,7 +477,8 @@ static struct notifier_block panic_exit_notifier = { void __init setup_arch(char **cmdline_p) { - notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); + atomic_notifier_chain_register(&panic_notifier_list, + &panic_exit_notifier); paging_init(); strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 0370720515f1..70dd8e5c6889 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c @@ -66,24 +66,17 @@ EXPORT_SYMBOL(boot_option_idle_override); void (*pm_idle)(void); static DEFINE_PER_CPU(unsigned int, cpu_idle_state); -static struct notifier_block *idle_notifier; -static DEFINE_SPINLOCK(idle_notifier_lock); +static ATOMIC_NOTIFIER_HEAD(idle_notifier); void idle_notifier_register(struct notifier_block *n) { - unsigned long flags; - spin_lock_irqsave(&idle_notifier_lock, flags); - notifier_chain_register(&idle_notifier, n); - spin_unlock_irqrestore(&idle_notifier_lock, flags); + atomic_notifier_chain_register(&idle_notifier, n); } EXPORT_SYMBOL_GPL(idle_notifier_register); void idle_notifier_unregister(struct notifier_block *n) { - unsigned long flags; - spin_lock_irqsave(&idle_notifier_lock, flags); - notifier_chain_unregister(&idle_notifier, n); - spin_unlock_irqrestore(&idle_notifier_lock, flags); + atomic_notifier_chain_unregister(&idle_notifier, n); } EXPORT_SYMBOL(idle_notifier_unregister); @@ -93,13 +86,13 @@ static DEFINE_PER_CPU(enum idle_state, idle_state) = CPU_NOT_IDLE; void enter_idle(void) { __get_cpu_var(idle_state) = CPU_IDLE; - notifier_call_chain(&idle_notifier, IDLE_START, NULL); + atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); } static void __exit_idle(void) { __get_cpu_var(idle_state) = CPU_NOT_IDLE; - notifier_call_chain(&idle_notifier, IDLE_END, NULL); + atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); } /* Called from interrupts to signify idle end */ diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 7b148309c529..edaa9fe654dc 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -69,20 +69,20 @@ asmlinkage void alignment_check(void); asmlinkage void machine_check(void); asmlinkage void spurious_interrupt_bug(void); -struct notifier_block *die_chain; -static DEFINE_SPINLOCK(die_notifier_lock); +ATOMIC_NOTIFIER_HEAD(die_chain); int register_die_notifier(struct notifier_block *nb) { - int err = 0; - unsigned long flags; - vmalloc_sync_all(); - spin_lock_irqsave(&die_notifier_lock, flags); - err = notifier_chain_register(&die_chain, nb); - spin_unlock_irqrestore(&die_notifier_lock, flags); - return err; + return atomic_notifier_chain_register(&die_chain, nb); +} +EXPORT_SYMBOL(register_die_notifier); + +int unregister_die_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&die_chain, nb); } +EXPORT_SYMBOL(unregister_die_notifier); static inline void conditional_sti(struct pt_regs *regs) { diff --git a/arch/xtensa/platform-iss/setup.c b/arch/xtensa/platform-iss/setup.c index 2e6dcbf0cc04..23790a5610e2 100644 --- a/arch/xtensa/platform-iss/setup.c +++ b/arch/xtensa/platform-iss/setup.c @@ -108,5 +108,5 @@ static struct notifier_block iss_panic_block = { void __init platform_setup(char **p_cmdline) { - notifier_chain_register(&panic_notifier_list, &iss_panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &iss_panic_block); } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 105a0d61eb1f..dd547af4681a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -47,16 +47,16 @@ static struct kset_uevent_ops memory_uevent_ops = { .uevent = memory_uevent, }; -static struct notifier_block *memory_chain; +static BLOCKING_NOTIFIER_HEAD(memory_chain); int register_memory_notifier(struct notifier_block *nb) { - return notifier_chain_register(&memory_chain, nb); + return blocking_notifier_chain_register(&memory_chain, nb); } void unregister_memory_notifier(struct notifier_block *nb) { - notifier_chain_unregister(&memory_chain, nb); + blocking_notifier_chain_unregister(&memory_chain, nb); } /* @@ -140,7 +140,7 @@ static ssize_t show_mem_state(struct sys_device *dev, char *buf) static inline int memory_notify(unsigned long val, void *v) { - return notifier_call_chain(&memory_chain, val, v); + return blocking_notifier_call_chain(&memory_chain, val, v); } /* diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index b8fb87c6c29f..40eb005b9d77 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3744,7 +3744,7 @@ static int ipmi_init_msghandler(void) ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES; add_timer(&ipmi_timer); - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); initialized = 1; @@ -3764,7 +3764,7 @@ static __exit void cleanup_ipmi(void) if (!initialized) return; - notifier_chain_unregister(&panic_notifier_list, &panic_block); + atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); /* This can't be called if any interfaces exist, so no worry about shutting down the interfaces. */ diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 12f858dc9994..35fbd4d8ed4b 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -237,10 +237,10 @@ struct smi_info static int try_smi_init(struct smi_info *smi); -static struct notifier_block *xaction_notifier_list; +static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); static int register_xaction_notifier(struct notifier_block * nb) { - return notifier_chain_register(&xaction_notifier_list, nb); + return atomic_notifier_chain_register(&xaction_notifier_list, nb); } static void si_restart_short_timer(struct smi_info *smi_info); @@ -302,7 +302,8 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) do_gettimeofday(&t); printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif - err = notifier_call_chain(&xaction_notifier_list, 0, smi_info); + err = atomic_notifier_call_chain(&xaction_notifier_list, + 0, smi_info); if (err & NOTIFY_STOP_MASK) { rv = SI_SM_CALL_WITHOUT_DELAY; goto out; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 616539310d9a..7ece9f3c8f70 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1158,7 +1158,8 @@ static int __init ipmi_wdog_init(void) } register_reboot_notifier(&wdog_reboot_notifier); - notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); + atomic_notifier_chain_register(&panic_notifier_list, + &wdog_panic_notifier); printk(KERN_INFO PFX "driver initialized\n"); @@ -1176,7 +1177,8 @@ static __exit void ipmi_unregister_watchdog(void) release_nmi(&ipmi_nmi_handler); #endif - notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, + &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); if (! watchdog_user) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index aed80e6aec6d..9b6ae7dc8b8a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -52,9 +52,8 @@ static void handle_update(void *data); * changes to devices when the CPU clock speed changes. * The mutex locks both lists. */ -static struct notifier_block *cpufreq_policy_notifier_list; -static struct notifier_block *cpufreq_transition_notifier_list; -static DECLARE_RWSEM (cpufreq_notifier_rwsem); +static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list); +static BLOCKING_NOTIFIER_HEAD(cpufreq_transition_notifier_list); static LIST_HEAD(cpufreq_governor_list); @@ -247,8 +246,6 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) dprintk("notification %u of frequency transition to %u kHz\n", state, freqs->new); - down_read(&cpufreq_notifier_rwsem); - policy = cpufreq_cpu_data[freqs->cpu]; switch (state) { @@ -266,20 +263,19 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) freqs->old = policy->cur; } } - notifier_call_chain(&cpufreq_transition_notifier_list, - CPUFREQ_PRECHANGE, freqs); + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, + CPUFREQ_PRECHANGE, freqs); adjust_jiffies(CPUFREQ_PRECHANGE, freqs); break; case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); - notifier_call_chain(&cpufreq_transition_notifier_list, - CPUFREQ_POSTCHANGE, freqs); + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, + CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) policy->cur = freqs->new; break; } - up_read(&cpufreq_notifier_rwsem); } EXPORT_SYMBOL_GPL(cpufreq_notify_transition); @@ -1007,7 +1003,7 @@ static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - notifier_call_chain(&cpufreq_transition_notifier_list, + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_SUSPENDCHANGE, &freqs); adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); @@ -1088,7 +1084,8 @@ static int cpufreq_resume(struct sys_device * sysdev) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - notifier_call_chain(&cpufreq_transition_notifier_list, + blocking_notifier_call_chain( + &cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); @@ -1125,24 +1122,24 @@ static struct sysdev_driver cpufreq_sysdev_driver = { * changes in cpufreq policy. * * This function may sleep, and has the same return conditions as - * notifier_chain_register. + * blocking_notifier_chain_register. */ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) { int ret; - down_write(&cpufreq_notifier_rwsem); switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); + ret = blocking_notifier_chain_register( + &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: - ret = notifier_chain_register(&cpufreq_policy_notifier_list, nb); + ret = blocking_notifier_chain_register( + &cpufreq_policy_notifier_list, nb); break; default: ret = -EINVAL; } - up_write(&cpufreq_notifier_rwsem); return ret; } @@ -1157,24 +1154,24 @@ EXPORT_SYMBOL(cpufreq_register_notifier); * Remove a driver from the CPU frequency notifier list. * * This function may sleep, and has the same return conditions as - * notifier_chain_unregister. + * blocking_notifier_chain_unregister. */ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) { int ret; - down_write(&cpufreq_notifier_rwsem); switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); + ret = blocking_notifier_chain_unregister( + &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: - ret = notifier_chain_unregister(&cpufreq_policy_notifier_list, nb); + ret = blocking_notifier_chain_unregister( + &cpufreq_policy_notifier_list, nb); break; default: ret = -EINVAL; } - up_write(&cpufreq_notifier_rwsem); return ret; } @@ -1346,29 +1343,23 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli if (ret) goto error_out; - down_read(&cpufreq_notifier_rwsem); - /* adjust if necessary - all reasons */ - notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, - policy); + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_ADJUST, policy); /* adjust if necessary - hardware incompatibility*/ - notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE, - policy); + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_INCOMPATIBLE, policy); /* verify the cpu speed can be set within this limit, which might be different to the first one */ ret = cpufreq_driver->verify(policy); - if (ret) { - up_read(&cpufreq_notifier_rwsem); + if (ret) goto error_out; - } /* notification of the new policy */ - notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, - policy); - - up_read(&cpufreq_notifier_rwsem); + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_NOTIFY, policy); data->min = policy->min; data->max = policy->max; diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index d6543fc4a923..339f405ff708 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -484,26 +484,15 @@ static void dcdbas_host_control(void) static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { - static unsigned int notify_cnt = 0; - switch (code) { case SYS_DOWN: case SYS_HALT: case SYS_POWER_OFF: if (host_control_on_shutdown) { /* firmware is going to perform host control action */ - if (++notify_cnt == 2) { - printk(KERN_WARNING - "Please wait for shutdown " - "action to complete...\n"); - dcdbas_host_control(); - } - /* - * register again and initiate the host control - * action on the second notification to allow - * everyone that registered to be notified - */ - register_reboot_notifier(nb); + printk(KERN_WARNING "Please wait for shutdown " + "action to complete...\n"); + dcdbas_host_control(); } break; } @@ -514,7 +503,7 @@ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, static struct notifier_block dcdbas_reboot_nb = { .notifier_call = dcdbas_reboot_notify, .next = NULL, - .priority = 0 + .priority = INT_MIN }; static DCDBAS_BIN_ATTR_RW(smi_data); diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index d2ead1776c16..34fcabac5fdb 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -80,7 +80,7 @@ static struct adb_driver *adb_driver_list[] = { static struct class *adb_dev_class; struct adb_driver *adb_controller; -struct notifier_block *adb_client_list = NULL; +BLOCKING_NOTIFIER_HEAD(adb_client_list); static int adb_got_sleep; static int adb_inited; static pid_t adb_probe_task_pid; @@ -354,7 +354,8 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when) /* Stop autopoll */ if (adb_controller->autopoll) adb_controller->autopoll(0); - ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); + ret = blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POWERDOWN, NULL); if (ret & NOTIFY_STOP_MASK) { up(&adb_probe_mutex); return PBOOK_SLEEP_REFUSE; @@ -391,7 +392,8 @@ do_adb_reset_bus(void) if (adb_controller->autopoll) adb_controller->autopoll(0); - nret = notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL); + nret = blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_PRE_RESET, NULL); if (nret & NOTIFY_STOP_MASK) { if (adb_controller->autopoll) adb_controller->autopoll(autopoll_devs); @@ -426,7 +428,8 @@ do_adb_reset_bus(void) } up(&adb_handler_sem); - nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL); + nret = blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POST_RESET, NULL); if (nret & NOTIFY_STOP_MASK) return -EBUSY; diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index c0b46bceb5df..f5779a73184d 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c @@ -1214,7 +1214,8 @@ static int __init adbhid_init(void) adbhid_probe(); - notifier_chain_register(&adb_client_list, &adbhid_adb_notifier); + blocking_notifier_chain_register(&adb_client_list, + &adbhid_adb_notifier); return 0; } diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 4f5f3abc9cb3..0b5ff553e39a 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -187,7 +187,7 @@ extern int disable_kernel_backlight; int __fake_sleep; int asleep; -struct notifier_block *sleep_notifier_list; +BLOCKING_NOTIFIER_HEAD(sleep_notifier_list); #ifdef CONFIG_ADB static int adb_dev_map = 0; diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index f08e52f2107b..35b70323e7e3 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c @@ -102,7 +102,7 @@ static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; int asleep; -struct notifier_block *sleep_notifier_list; +BLOCKING_NOTIFIER_HEAD(sleep_notifier_list); static int pmu_probe(void); static int pmu_init(void); @@ -913,7 +913,8 @@ int powerbook_sleep(void) struct adb_request sleep_req; /* Notify device drivers */ - ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL); + ret = blocking_notifier_call_chain(&sleep_notifier_list, + PBOOK_SLEEP, NULL); if (ret & NOTIFY_STOP_MASK) return -EBUSY; @@ -984,7 +985,7 @@ int powerbook_sleep(void) enable_irq(i); /* Notify drivers */ - notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); + blocking_notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); /* reenable ADB autopoll */ pmu_adb_autopoll(adb_dev_map); diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 6c0ba04bc57a..ab3faa702d58 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -52,7 +52,7 @@ static LIST_HEAD(wf_controls); static LIST_HEAD(wf_sensors); static DEFINE_MUTEX(wf_lock); -static struct notifier_block *wf_client_list; +static BLOCKING_NOTIFIER_HEAD(wf_client_list); static int wf_client_count; static unsigned int wf_overtemp; static unsigned int wf_overtemp_counter; @@ -68,7 +68,7 @@ static struct platform_device wf_platform_device = { static inline void wf_notify(int event, void *param) { - notifier_call_chain(&wf_client_list, event, param); + blocking_notifier_call_chain(&wf_client_list, event, param); } int wf_critical_overtemp(void) @@ -398,7 +398,7 @@ int wf_register_client(struct notifier_block *nb) struct wf_sensor *sr; mutex_lock(&wf_lock); - rc = notifier_chain_register(&wf_client_list, nb); + rc = blocking_notifier_chain_register(&wf_client_list, nb); if (rc != 0) goto bail; wf_client_count++; @@ -417,7 +417,7 @@ EXPORT_SYMBOL_GPL(wf_register_client); int wf_unregister_client(struct notifier_block *nb) { mutex_lock(&wf_lock); - notifier_chain_unregister(&wf_client_list, nb); + blocking_notifier_chain_unregister(&wf_client_list, nb); wf_client_count++; if (wf_client_count == 0) wf_stop_thread(); diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c index f295401fac21..7fd7a43e38de 100644 --- a/drivers/misc/ibmasm/heartbeat.c +++ b/drivers/misc/ibmasm/heartbeat.c @@ -52,12 +52,13 @@ static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; void ibmasm_register_panic_notifier(void) { - notifier_chain_register(&panic_notifier_list, &panic_notifier); + atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); } void ibmasm_unregister_panic_notifier(void) { - notifier_chain_unregister(&panic_notifier_list, &panic_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, + &panic_notifier); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2d0ac169a86c..f13a539dc169 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3159,7 +3159,7 @@ static int bond_slave_netdev_event(unsigned long event, struct net_device *slave * bond_netdev_event: handle netdev notifier chain events. * * This function receives events for the netdev chain. The caller (an - * ioctl handler calling notifier_call_chain) holds the necessary + * ioctl handler calling blocking_notifier_call_chain) holds the necessary * locks for us to safely manipulate the slave devices (RTNL lock, * dev_probe_lock). */ diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 3627a2d7f79f..298f2ddb2c17 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -499,11 +499,16 @@ static int led_halt(struct notifier_block *, unsigned long, void *); static struct notifier_block led_notifier = { .notifier_call = led_halt, }; +static int notifier_disabled = 0; static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) { char *txt; - + + if (notifier_disabled) + return NOTIFY_OK; + + notifier_disabled = 1; switch (event) { case SYS_RESTART: txt = "SYSTEM RESTART"; break; @@ -527,7 +532,6 @@ static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) if (led_func_ptr) led_func_ptr(0xff); /* turn all LEDs ON */ - unregister_reboot_notifier(&led_notifier); return NOTIFY_OK; } @@ -758,6 +762,12 @@ not_found: return 1; } +static void __exit led_exit(void) +{ + unregister_reboot_notifier(&led_notifier); + return; +} + #ifdef CONFIG_PROC_FS module_init(led_create_procfs) #endif diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index 54b2b7f20b96..0bcab83b4080 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -251,7 +251,8 @@ static int __init power_init(void) } /* Register a call for panic conditions. */ - notifier_chain_register(&panic_notifier_list, &parisc_panic_block); + atomic_notifier_chain_register(&panic_notifier_list, + &parisc_panic_block); tasklet_enable(&power_tasklet); @@ -264,7 +265,8 @@ static void __exit power_exit(void) return; tasklet_disable(&power_tasklet); - notifier_chain_unregister(&panic_notifier_list, &parisc_panic_block); + atomic_notifier_chain_unregister(&panic_notifier_list, + &parisc_panic_block); power_tasklet.func = NULL; pdc_soft_power_button(0); } diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 62e3cda859af..7f7013e80a88 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -671,7 +671,7 @@ static struct file_operations gdth_fops = { static struct notifier_block gdth_notifier = { gdth_halt, NULL, 0 }; - +static int notifier_disabled = 0; static void gdth_delay(int milliseconds) { @@ -4595,13 +4595,13 @@ static int __init gdth_detect(struct scsi_host_template *shtp) add_timer(&gdth_timer); #endif major = register_chrdev(0,"gdth",&gdth_fops); + notifier_disabled = 0; register_reboot_notifier(&gdth_notifier); } gdth_polling = FALSE; return gdth_ctr_vcount; } - static int gdth_release(struct Scsi_Host *shp) { int hanum; @@ -5632,10 +5632,14 @@ static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) char cmnd[MAX_COMMAND_SIZE]; #endif + if (notifier_disabled) + return NOTIFY_OK; + TRACE2(("gdth_halt() event %d\n",(int)event)); if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) return NOTIFY_DONE; + notifier_disabled = 1; printk("GDT-HA: Flushing all host drives .. "); for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { gdth_flush(hanum); @@ -5679,7 +5683,6 @@ static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) #ifdef GDTH_STATISTICS del_timer(&gdth_timer); #endif - unregister_reboot_notifier(&gdth_notifier); return NOTIFY_OK; } diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c index 4b55285de9a0..fe0ed54fa0ae 100644 --- a/drivers/usb/core/notify.c +++ b/drivers/usb/core/notify.c @@ -16,57 +16,7 @@ #include #include "usb.h" - -static struct notifier_block *usb_notifier_list; -static DEFINE_MUTEX(usb_notifier_lock); - -static void usb_notifier_chain_register(struct notifier_block **list, - struct notifier_block *n) -{ - mutex_lock(&usb_notifier_lock); - while (*list) { - if (n->priority > (*list)->priority) - break; - list = &((*list)->next); - } - n->next = *list; - *list = n; - mutex_unlock(&usb_notifier_lock); -} - -static void usb_notifier_chain_unregister(struct notifier_block **nl, - struct notifier_block *n) -{ - mutex_lock(&usb_notifier_lock); - while ((*nl)!=NULL) { - if ((*nl)==n) { - *nl = n->next; - goto exit; - } - nl=&((*nl)->next); - } -exit: - mutex_unlock(&usb_notifier_lock); -} - -static int usb_notifier_call_chain(struct notifier_block **n, - unsigned long val, void *v) -{ - int ret=NOTIFY_DONE; - struct notifier_block *nb = *n; - - mutex_lock(&usb_notifier_lock); - while (nb) { - ret = nb->notifier_call(nb,val,v); - if (ret&NOTIFY_STOP_MASK) { - goto exit; - } - nb = nb->next; - } -exit: - mutex_unlock(&usb_notifier_lock); - return ret; -} +static BLOCKING_NOTIFIER_HEAD(usb_notifier_list); /** * usb_register_notify - register a notifier callback whenever a usb change happens @@ -76,7 +26,7 @@ exit: */ void usb_register_notify(struct notifier_block *nb) { - usb_notifier_chain_register(&usb_notifier_list, nb); + blocking_notifier_chain_register(&usb_notifier_list, nb); } EXPORT_SYMBOL_GPL(usb_register_notify); @@ -89,27 +39,28 @@ EXPORT_SYMBOL_GPL(usb_register_notify); */ void usb_unregister_notify(struct notifier_block *nb) { - usb_notifier_chain_unregister(&usb_notifier_list, nb); + blocking_notifier_chain_unregister(&usb_notifier_list, nb); } EXPORT_SYMBOL_GPL(usb_unregister_notify); void usb_notify_add_device(struct usb_device *udev) { - usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev); + blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev); } void usb_notify_remove_device(struct usb_device *udev) { - usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_REMOVE, udev); + blocking_notifier_call_chain(&usb_notifier_list, + USB_DEVICE_REMOVE, udev); } void usb_notify_add_bus(struct usb_bus *ubus) { - usb_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus); + blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus); } void usb_notify_remove_bus(struct usb_bus *ubus) { - usb_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus); + blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus); } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 07d882b14396..b1a8dca76430 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -55,7 +55,7 @@ #define FBPIXMAPSIZE (1024 * 8) -static struct notifier_block *fb_notifier_list; +static BLOCKING_NOTIFIER_HEAD(fb_notifier_list); struct fb_info *registered_fb[FB_MAX]; int num_registered_fb; @@ -784,7 +784,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) event.info = info; event.data = &mode1; - ret = notifier_call_chain(&fb_notifier_list, + ret = blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_MODE_DELETE, &event); } @@ -830,8 +830,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; - notifier_call_chain(&fb_notifier_list, evnt, - &event); + blocking_notifier_call_chain(&fb_notifier_list, + evnt, &event); } } } @@ -854,7 +854,8 @@ fb_blank(struct fb_info *info, int blank) event.info = info; event.data = ␣ - notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_BLANK, &event); } return ret; @@ -925,7 +926,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, con2fb.framebuffer = -1; event.info = info; event.data = &con2fb; - notifier_call_chain(&fb_notifier_list, + blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_GET_CONSOLE_MAP, &event); return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; @@ -944,7 +945,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EINVAL; event.info = info; event.data = &con2fb; - return notifier_call_chain(&fb_notifier_list, + return blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_SET_CONSOLE_MAP, &event); case FBIOBLANK: @@ -1324,7 +1325,7 @@ register_framebuffer(struct fb_info *fb_info) devfs_mk_cdev(MKDEV(FB_MAJOR, i), S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); event.info = fb_info; - notifier_call_chain(&fb_notifier_list, + blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_FB_REGISTERED, &event); return 0; } @@ -1366,7 +1367,7 @@ unregister_framebuffer(struct fb_info *fb_info) */ int fb_register_client(struct notifier_block *nb) { - return notifier_chain_register(&fb_notifier_list, nb); + return blocking_notifier_chain_register(&fb_notifier_list, nb); } /** @@ -1375,7 +1376,7 @@ int fb_register_client(struct notifier_block *nb) */ int fb_unregister_client(struct notifier_block *nb) { - return notifier_chain_unregister(&fb_notifier_list, nb); + return blocking_notifier_chain_unregister(&fb_notifier_list, nb); } /** @@ -1393,11 +1394,13 @@ void fb_set_suspend(struct fb_info *info, int state) event.info = info; if (state) { - notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_SUSPEND, &event); info->state = FBINFO_STATE_SUSPENDED; } else { info->state = FBINFO_STATE_RUNNING; - notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_RESUME, &event); } } @@ -1469,7 +1472,7 @@ int fb_new_modelist(struct fb_info *info) if (!list_empty(&info->modelist)) { event.info = info; - err = notifier_call_chain(&fb_notifier_list, + err = blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_NEW_MODELIST, &event); } @@ -1495,7 +1498,7 @@ int fb_con_duit(struct fb_info *info, int event, void *data) evnt.info = info; evnt.data = data; - return notifier_call_chain(&fb_notifier_list, event, &evnt); + return blocking_notifier_call_chain(&fb_notifier_list, event, &evnt); } EXPORT_SYMBOL(fb_con_duit); diff --git a/include/asm-i386/kdebug.h b/include/asm-i386/kdebug.h index 316138e89910..96d0828ce096 100644 --- a/include/asm-i386/kdebug.h +++ b/include/asm-i386/kdebug.h @@ -17,11 +17,9 @@ struct die_args { int signr; }; -/* Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_sched - then free. - */ -int register_die_notifier(struct notifier_block *nb); -extern struct notifier_block *i386die_chain; +extern int register_die_notifier(struct notifier_block *); +extern int unregister_die_notifier(struct notifier_block *); +extern struct atomic_notifier_head i386die_chain; /* Grossly misnamed. */ @@ -51,7 +49,7 @@ static inline int notify_die(enum die_val val, const char *str, .trapnr = trap, .signr = sig }; - return notifier_call_chain(&i386die_chain, val, &args); + return atomic_notifier_call_chain(&i386die_chain, val, &args); } #endif diff --git a/include/asm-ia64/kdebug.h b/include/asm-ia64/kdebug.h index 8b01a083dde6..218c458ab60c 100644 --- a/include/asm-ia64/kdebug.h +++ b/include/asm-ia64/kdebug.h @@ -40,7 +40,7 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); -extern struct notifier_block *ia64die_chain; +extern struct atomic_notifier_head ia64die_chain; enum die_val { DIE_BREAK = 1, @@ -81,7 +81,7 @@ static inline int notify_die(enum die_val val, char *str, struct pt_regs *regs, .signr = sig }; - return notifier_call_chain(&ia64die_chain, val, &args); + return atomic_notifier_call_chain(&ia64die_chain, val, &args); } #endif diff --git a/include/asm-powerpc/kdebug.h b/include/asm-powerpc/kdebug.h index 7c16265568e0..c01786ab5fa6 100644 --- a/include/asm-powerpc/kdebug.h +++ b/include/asm-powerpc/kdebug.h @@ -16,13 +16,9 @@ struct die_args { int signr; }; -/* - Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_sched - - then free. - */ -int register_die_notifier(struct notifier_block *nb); -extern struct notifier_block *powerpc_die_chain; +extern int register_die_notifier(struct notifier_block *); +extern int unregister_die_notifier(struct notifier_block *); +extern struct atomic_notifier_head powerpc_die_chain; /* Grossly misnamed. */ enum die_val { @@ -37,7 +33,7 @@ enum die_val { static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig) { struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig }; - return notifier_call_chain(&powerpc_die_chain, val, &args); + return atomic_notifier_call_chain(&powerpc_die_chain, val, &args); } #endif /* __KERNEL__ */ diff --git a/include/asm-sparc64/kdebug.h b/include/asm-sparc64/kdebug.h index 6321f5a0198d..4040d127ac3e 100644 --- a/include/asm-sparc64/kdebug.h +++ b/include/asm-sparc64/kdebug.h @@ -15,12 +15,9 @@ struct die_args { int signr; }; -/* Note - you should never unregister because that can race with NMIs. - * If you really want to do it first unregister - then synchronize_sched - * - then free. - */ -int register_die_notifier(struct notifier_block *nb); -extern struct notifier_block *sparc64die_chain; +extern int register_die_notifier(struct notifier_block *); +extern int unregister_die_notifier(struct notifier_block *); +extern struct atomic_notifier_head sparc64die_chain; extern void bad_trap(struct pt_regs *, long); @@ -46,7 +43,7 @@ static inline int notify_die(enum die_val val,char *str, struct pt_regs *regs, .trapnr = trap, .signr = sig }; - return notifier_call_chain(&sparc64die_chain, val, &args); + return atomic_notifier_call_chain(&sparc64die_chain, val, &args); } #endif diff --git a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h index b9ed4c0c8783..cf795631d9b4 100644 --- a/include/asm-x86_64/kdebug.h +++ b/include/asm-x86_64/kdebug.h @@ -5,21 +5,20 @@ struct pt_regs; -struct die_args { +struct die_args { struct pt_regs *regs; const char *str; - long err; + long err; int trapnr; int signr; -}; +}; + +extern int register_die_notifier(struct notifier_block *); +extern int unregister_die_notifier(struct notifier_block *); +extern struct atomic_notifier_head die_chain; -/* Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_sched - then free. - */ -int register_die_notifier(struct notifier_block *nb); -extern struct notifier_block *die_chain; /* Grossly misnamed. */ -enum die_val { +enum die_val { DIE_OOPS = 1, DIE_INT3, DIE_DEBUG, @@ -33,8 +32,8 @@ enum die_val { DIE_CALL, DIE_NMI_IPI, DIE_PAGE_FAULT, -}; - +}; + static inline int notify_die(enum die_val val, const char *str, struct pt_regs *regs, long err, int trap, int sig) { @@ -45,7 +44,7 @@ static inline int notify_die(enum die_val val, const char *str, .trapnr = trap, .signr = sig }; - return notifier_call_chain(&die_chain, val, &args); + return atomic_notifier_call_chain(&die_chain, val, &args); } extern int printk_address(unsigned long address); diff --git a/include/linux/adb.h b/include/linux/adb.h index e9fdc63483c7..b7305b178279 100644 --- a/include/linux/adb.h +++ b/include/linux/adb.h @@ -85,7 +85,7 @@ enum adb_message { ADB_MSG_POST_RESET /* Called after resetting the bus (re-do init & register) */ }; extern struct adb_driver *adb_controller; -extern struct notifier_block *adb_client_list; +extern struct blocking_notifier_head adb_client_list; int adb_request(struct adb_request *req, void (*done)(struct adb_request *), int flags, int nbytes, ...); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 03d6cfaa5b8a..a3720f973ea5 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -87,7 +87,7 @@ extern int cond_resched(void); (__x < 0) ? -__x : __x; \ }) -extern struct notifier_block *panic_notifier_list; +extern struct atomic_notifier_head panic_notifier_list; extern long (*panic_blink)(long time); NORET_TYPE void panic(const char * fmt, ...) __attribute__ ((NORET_AND format (printf, 1, 2))); diff --git a/include/linux/memory.h b/include/linux/memory.h index e251dc43d0f5..8f04143ca363 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -77,7 +77,6 @@ extern int remove_memory_block(unsigned long, struct mem_section *, int); #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION< +#include +#include -struct notifier_block -{ - int (*notifier_call)(struct notifier_block *self, unsigned long, void *); +/* + * Notifier chains are of three types: + * + * Atomic notifier chains: Chain callbacks run in interrupt/atomic + * context. Callouts are not allowed to block. + * Blocking notifier chains: Chain callbacks run in process context. + * Callouts are allowed to block. + * Raw notifier chains: There are no restrictions on callbacks, + * registration, or unregistration. All locking and protection + * must be provided by the caller. + * + * atomic_notifier_chain_register() may be called from an atomic context, + * but blocking_notifier_chain_register() must be called from a process + * context. Ditto for the corresponding _unregister() routines. + * + * atomic_notifier_chain_unregister() and blocking_notifier_chain_unregister() + * _must not_ be called from within the call chain. + */ + +struct notifier_block { + int (*notifier_call)(struct notifier_block *, unsigned long, void *); struct notifier_block *next; int priority; }; +struct atomic_notifier_head { + spinlock_t lock; + struct notifier_block *head; +}; + +struct blocking_notifier_head { + struct rw_semaphore rwsem; + struct notifier_block *head; +}; + +struct raw_notifier_head { + struct notifier_block *head; +}; + +#define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ + spin_lock_init(&(name)->lock); \ + (name)->head = NULL; \ + } while (0) +#define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \ + init_rwsem(&(name)->rwsem); \ + (name)->head = NULL; \ + } while (0) +#define RAW_INIT_NOTIFIER_HEAD(name) do { \ + (name)->head = NULL; \ + } while (0) + +#define ATOMIC_NOTIFIER_INIT(name) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .head = NULL } +#define BLOCKING_NOTIFIER_INIT(name) { \ + .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ + .head = NULL } +#define RAW_NOTIFIER_INIT(name) { \ + .head = NULL } + +#define ATOMIC_NOTIFIER_HEAD(name) \ + struct atomic_notifier_head name = \ + ATOMIC_NOTIFIER_INIT(name) +#define BLOCKING_NOTIFIER_HEAD(name) \ + struct blocking_notifier_head name = \ + BLOCKING_NOTIFIER_INIT(name) +#define RAW_NOTIFIER_HEAD(name) \ + struct raw_notifier_head name = \ + RAW_NOTIFIER_INIT(name) #ifdef __KERNEL__ -extern int notifier_chain_register(struct notifier_block **list, struct notifier_block *n); -extern int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n); -extern int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v); +extern int atomic_notifier_chain_register(struct atomic_notifier_head *, + struct notifier_block *); +extern int blocking_notifier_chain_register(struct blocking_notifier_head *, + struct notifier_block *); +extern int raw_notifier_chain_register(struct raw_notifier_head *, + struct notifier_block *); + +extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, + struct notifier_block *); +extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, + struct notifier_block *); +extern int raw_notifier_chain_unregister(struct raw_notifier_head *, + struct notifier_block *); + +extern int atomic_notifier_call_chain(struct atomic_notifier_head *, + unsigned long val, void *v); +extern int blocking_notifier_call_chain(struct blocking_notifier_head *, + unsigned long val, void *v); +extern int raw_notifier_call_chain(struct raw_notifier_head *, + unsigned long val, void *v); #define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_OK 0x0001 /* Suits me */ #define NOTIFY_STOP_MASK 0x8000 /* Don't call further */ -#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */ +#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) + /* Bad/Veto action */ /* * Clean way to return from the notifier and stop further calls. */ diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index b6f0905a4ee2..916013ca4a5c 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -300,29 +300,30 @@ DECLARE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache); #define CONNTRACK_ECACHE(x) (__get_cpu_var(nf_conntrack_ecache).x) -extern struct notifier_block *nf_conntrack_chain; -extern struct notifier_block *nf_conntrack_expect_chain; +extern struct atomic_notifier_head nf_conntrack_chain; +extern struct atomic_notifier_head nf_conntrack_expect_chain; static inline int nf_conntrack_register_notifier(struct notifier_block *nb) { - return notifier_chain_register(&nf_conntrack_chain, nb); + return atomic_notifier_chain_register(&nf_conntrack_chain, nb); } static inline int nf_conntrack_unregister_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&nf_conntrack_chain, nb); + return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); } static inline int nf_conntrack_expect_register_notifier(struct notifier_block *nb) { - return notifier_chain_register(&nf_conntrack_expect_chain, nb); + return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb); } static inline int nf_conntrack_expect_unregister_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&nf_conntrack_expect_chain, nb); + return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain, + nb); } extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); @@ -347,14 +348,14 @@ static inline void nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) { if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) - notifier_call_chain(&nf_conntrack_chain, event, ct); + atomic_notifier_call_chain(&nf_conntrack_chain, event, ct); } static inline void nf_conntrack_expect_event(enum ip_conntrack_expect_events event, struct nf_conntrack_expect *exp) { - notifier_call_chain(&nf_conntrack_expect_chain, event, exp); + atomic_notifier_call_chain(&nf_conntrack_expect_chain, event, exp); } #else /* CONFIG_NF_CONNTRACK_EVENTS */ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, diff --git a/kernel/cpu.c b/kernel/cpu.c index 8be22bd80933..fe2b8d0bfe4c 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -18,7 +18,7 @@ /* This protects CPUs going up and down... */ static DECLARE_MUTEX(cpucontrol); -static struct notifier_block *cpu_chain; +static BLOCKING_NOTIFIER_HEAD(cpu_chain); #ifdef CONFIG_HOTPLUG_CPU static struct task_struct *lock_cpu_hotplug_owner; @@ -71,21 +71,13 @@ EXPORT_SYMBOL_GPL(lock_cpu_hotplug_interruptible); /* Need to know about CPUs going up/down? */ int register_cpu_notifier(struct notifier_block *nb) { - int ret; - - if ((ret = lock_cpu_hotplug_interruptible()) != 0) - return ret; - ret = notifier_chain_register(&cpu_chain, nb); - unlock_cpu_hotplug(); - return ret; + return blocking_notifier_chain_register(&cpu_chain, nb); } EXPORT_SYMBOL(register_cpu_notifier); void unregister_cpu_notifier(struct notifier_block *nb) { - lock_cpu_hotplug(); - notifier_chain_unregister(&cpu_chain, nb); - unlock_cpu_hotplug(); + blocking_notifier_chain_unregister(&cpu_chain, nb); } EXPORT_SYMBOL(unregister_cpu_notifier); @@ -141,7 +133,7 @@ int cpu_down(unsigned int cpu) goto out; } - err = notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, + err = blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, (void *)(long)cpu); if (err == NOTIFY_BAD) { printk("%s: attempt to take down CPU %u failed\n", @@ -159,7 +151,7 @@ int cpu_down(unsigned int cpu) p = __stop_machine_run(take_cpu_down, NULL, cpu); if (IS_ERR(p)) { /* CPU didn't die: tell everyone. Can't complain. */ - if (notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, + if (blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, (void *)(long)cpu) == NOTIFY_BAD) BUG(); @@ -182,8 +174,8 @@ int cpu_down(unsigned int cpu) put_cpu(); /* CPU is completely dead: tell everyone. Too late to complain. */ - if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu) - == NOTIFY_BAD) + if (blocking_notifier_call_chain(&cpu_chain, CPU_DEAD, + (void *)(long)cpu) == NOTIFY_BAD) BUG(); check_for_tasks(cpu); @@ -211,7 +203,7 @@ int __devinit cpu_up(unsigned int cpu) goto out; } - ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); + ret = blocking_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); if (ret == NOTIFY_BAD) { printk("%s: attempt to bring up CPU %u failed\n", __FUNCTION__, cpu); @@ -226,11 +218,12 @@ int __devinit cpu_up(unsigned int cpu) BUG_ON(!cpu_online(cpu)); /* Now call notifier in preparation. */ - notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu); + blocking_notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu); out_notify: if (ret != 0) - notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu); + blocking_notifier_call_chain(&cpu_chain, + CPU_UP_CANCELED, hcpu); out: unlock_cpu_hotplug(); return ret; diff --git a/kernel/module.c b/kernel/module.c index ddfe45ac2fd1..4fafd58038a0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -64,26 +64,17 @@ static DEFINE_SPINLOCK(modlist_lock); static DEFINE_MUTEX(module_mutex); static LIST_HEAD(modules); -static DEFINE_MUTEX(notify_mutex); -static struct notifier_block * module_notify_list; +static BLOCKING_NOTIFIER_HEAD(module_notify_list); int register_module_notifier(struct notifier_block * nb) { - int err; - mutex_lock(¬ify_mutex); - err = notifier_chain_register(&module_notify_list, nb); - mutex_unlock(¬ify_mutex); - return err; + return blocking_notifier_chain_register(&module_notify_list, nb); } EXPORT_SYMBOL(register_module_notifier); int unregister_module_notifier(struct notifier_block * nb) { - int err; - mutex_lock(¬ify_mutex); - err = notifier_chain_unregister(&module_notify_list, nb); - mutex_unlock(¬ify_mutex); - return err; + return blocking_notifier_chain_unregister(&module_notify_list, nb); } EXPORT_SYMBOL(unregister_module_notifier); @@ -1816,9 +1807,8 @@ sys_init_module(void __user *umod, /* Drop lock so they can recurse */ mutex_unlock(&module_mutex); - mutex_lock(¬ify_mutex); - notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); - mutex_unlock(¬ify_mutex); + blocking_notifier_call_chain(&module_notify_list, + MODULE_STATE_COMING, mod); /* Start the module */ if (mod->init != NULL) diff --git a/kernel/panic.c b/kernel/panic.c index acd95adddb93..f895c7c01d5b 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -29,7 +29,7 @@ static DEFINE_SPINLOCK(pause_on_oops_lock); int panic_timeout; EXPORT_SYMBOL(panic_timeout); -struct notifier_block *panic_notifier_list; +ATOMIC_NOTIFIER_HEAD(panic_notifier_list); EXPORT_SYMBOL(panic_notifier_list); @@ -97,7 +97,7 @@ NORET_TYPE void panic(const char * fmt, ...) smp_send_stop(); #endif - notifier_call_chain(&panic_notifier_list, 0, buf); + atomic_notifier_call_chain(&panic_notifier_list, 0, buf); if (!panic_blink) panic_blink = no_blink; diff --git a/kernel/profile.c b/kernel/profile.c index ad81f799a9b4..5a730fdb1a2c 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -87,72 +87,52 @@ void __init profile_init(void) #ifdef CONFIG_PROFILING -static DECLARE_RWSEM(profile_rwsem); -static DEFINE_RWLOCK(handoff_lock); -static struct notifier_block * task_exit_notifier; -static struct notifier_block * task_free_notifier; -static struct notifier_block * munmap_notifier; +static BLOCKING_NOTIFIER_HEAD(task_exit_notifier); +static ATOMIC_NOTIFIER_HEAD(task_free_notifier); +static BLOCKING_NOTIFIER_HEAD(munmap_notifier); void profile_task_exit(struct task_struct * task) { - down_read(&profile_rwsem); - notifier_call_chain(&task_exit_notifier, 0, task); - up_read(&profile_rwsem); + blocking_notifier_call_chain(&task_exit_notifier, 0, task); } int profile_handoff_task(struct task_struct * task) { int ret; - read_lock(&handoff_lock); - ret = notifier_call_chain(&task_free_notifier, 0, task); - read_unlock(&handoff_lock); + ret = atomic_notifier_call_chain(&task_free_notifier, 0, task); return (ret == NOTIFY_OK) ? 1 : 0; } void profile_munmap(unsigned long addr) { - down_read(&profile_rwsem); - notifier_call_chain(&munmap_notifier, 0, (void *)addr); - up_read(&profile_rwsem); + blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr); } int task_handoff_register(struct notifier_block * n) { - int err = -EINVAL; - - write_lock(&handoff_lock); - err = notifier_chain_register(&task_free_notifier, n); - write_unlock(&handoff_lock); - return err; + return atomic_notifier_chain_register(&task_free_notifier, n); } int task_handoff_unregister(struct notifier_block * n) { - int err = -EINVAL; - - write_lock(&handoff_lock); - err = notifier_chain_unregister(&task_free_notifier, n); - write_unlock(&handoff_lock); - return err; + return atomic_notifier_chain_unregister(&task_free_notifier, n); } int profile_event_register(enum profile_type type, struct notifier_block * n) { int err = -EINVAL; - down_write(&profile_rwsem); - switch (type) { case PROFILE_TASK_EXIT: - err = notifier_chain_register(&task_exit_notifier, n); + err = blocking_notifier_chain_register( + &task_exit_notifier, n); break; case PROFILE_MUNMAP: - err = notifier_chain_register(&munmap_notifier, n); + err = blocking_notifier_chain_register( + &munmap_notifier, n); break; } - up_write(&profile_rwsem); - return err; } @@ -161,18 +141,17 @@ int profile_event_unregister(enum profile_type type, struct notifier_block * n) { int err = -EINVAL; - down_write(&profile_rwsem); - switch (type) { case PROFILE_TASK_EXIT: - err = notifier_chain_unregister(&task_exit_notifier, n); + err = blocking_notifier_chain_unregister( + &task_exit_notifier, n); break; case PROFILE_MUNMAP: - err = notifier_chain_unregister(&munmap_notifier, n); + err = blocking_notifier_chain_unregister( + &munmap_notifier, n); break; } - up_write(&profile_rwsem); return err; } diff --git a/kernel/softlockup.c b/kernel/softlockup.c index d9b3d5847ed8..ced91e1ff564 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -152,5 +152,5 @@ __init void spawn_softlockup_task(void) cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); register_cpu_notifier(&cpu_nfb); - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); } diff --git a/kernel/sys.c b/kernel/sys.c index 38bc73ede2ba..c93d37f71aef 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -95,99 +95,304 @@ int cad_pid = 1; * and the like. */ -static struct notifier_block *reboot_notifier_list; -static DEFINE_RWLOCK(notifier_lock); +static BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); + +/* + * Notifier chain core routines. The exported routines below + * are layered on top of these, with appropriate locking added. + */ + +static int notifier_chain_register(struct notifier_block **nl, + struct notifier_block *n) +{ + while ((*nl) != NULL) { + if (n->priority > (*nl)->priority) + break; + nl = &((*nl)->next); + } + n->next = *nl; + rcu_assign_pointer(*nl, n); + return 0; +} + +static int notifier_chain_unregister(struct notifier_block **nl, + struct notifier_block *n) +{ + while ((*nl) != NULL) { + if ((*nl) == n) { + rcu_assign_pointer(*nl, n->next); + return 0; + } + nl = &((*nl)->next); + } + return -ENOENT; +} + +static int __kprobes notifier_call_chain(struct notifier_block **nl, + unsigned long val, void *v) +{ + int ret = NOTIFY_DONE; + struct notifier_block *nb; + + nb = rcu_dereference(*nl); + while (nb) { + ret = nb->notifier_call(nb, val, v); + if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) + break; + nb = rcu_dereference(nb->next); + } + return ret; +} + +/* + * Atomic notifier chain routines. Registration and unregistration + * use a mutex, and call_chain is synchronized by RCU (no locks). + */ /** - * notifier_chain_register - Add notifier to a notifier chain - * @list: Pointer to root list pointer + * atomic_notifier_chain_register - Add notifier to an atomic notifier chain + * @nh: Pointer to head of the atomic notifier chain * @n: New entry in notifier chain * - * Adds a notifier to a notifier chain. + * Adds a notifier to an atomic notifier chain. * * Currently always returns zero. */ + +int atomic_notifier_chain_register(struct atomic_notifier_head *nh, + struct notifier_block *n) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&nh->lock, flags); + ret = notifier_chain_register(&nh->head, n); + spin_unlock_irqrestore(&nh->lock, flags); + return ret; +} + +EXPORT_SYMBOL_GPL(atomic_notifier_chain_register); + +/** + * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain + * @nh: Pointer to head of the atomic notifier chain + * @n: Entry to remove from notifier chain + * + * Removes a notifier from an atomic notifier chain. + * + * Returns zero on success or %-ENOENT on failure. + */ +int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, + struct notifier_block *n) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&nh->lock, flags); + ret = notifier_chain_unregister(&nh->head, n); + spin_unlock_irqrestore(&nh->lock, flags); + synchronize_rcu(); + return ret; +} + +EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); + +/** + * atomic_notifier_call_chain - Call functions in an atomic notifier chain + * @nh: Pointer to head of the atomic notifier chain + * @val: Value passed unmodified to notifier function + * @v: Pointer passed unmodified to notifier function + * + * Calls each function in a notifier chain in turn. The functions + * run in an atomic context, so they must not block. + * This routine uses RCU to synchronize with changes to the chain. + * + * If the return value of the notifier can be and'ed + * with %NOTIFY_STOP_MASK then atomic_notifier_call_chain + * will return immediately, with the return value of + * the notifier function which halted execution. + * Otherwise the return value is the return value + * of the last notifier function called. + */ -int notifier_chain_register(struct notifier_block **list, struct notifier_block *n) +int atomic_notifier_call_chain(struct atomic_notifier_head *nh, + unsigned long val, void *v) { - write_lock(¬ifier_lock); - while(*list) - { - if(n->priority > (*list)->priority) - break; - list= &((*list)->next); - } - n->next = *list; - *list=n; - write_unlock(¬ifier_lock); - return 0; + int ret; + + rcu_read_lock(); + ret = notifier_call_chain(&nh->head, val, v); + rcu_read_unlock(); + return ret; } -EXPORT_SYMBOL(notifier_chain_register); +EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); + +/* + * Blocking notifier chain routines. All access to the chain is + * synchronized by an rwsem. + */ /** - * notifier_chain_unregister - Remove notifier from a notifier chain - * @nl: Pointer to root list pointer + * blocking_notifier_chain_register - Add notifier to a blocking notifier chain + * @nh: Pointer to head of the blocking notifier chain * @n: New entry in notifier chain * - * Removes a notifier from a notifier chain. + * Adds a notifier to a blocking notifier chain. + * Must be called in process context. * - * Returns zero on success, or %-ENOENT on failure. + * Currently always returns zero. */ -int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) +int blocking_notifier_chain_register(struct blocking_notifier_head *nh, + struct notifier_block *n) { - write_lock(¬ifier_lock); - while((*nl)!=NULL) - { - if((*nl)==n) - { - *nl=n->next; - write_unlock(¬ifier_lock); - return 0; - } - nl=&((*nl)->next); - } - write_unlock(¬ifier_lock); - return -ENOENT; + int ret; + + /* + * This code gets used during boot-up, when task switching is + * not yet working and interrupts must remain disabled. At + * such times we must not call down_write(). + */ + if (unlikely(system_state == SYSTEM_BOOTING)) + return notifier_chain_register(&nh->head, n); + + down_write(&nh->rwsem); + ret = notifier_chain_register(&nh->head, n); + up_write(&nh->rwsem); + return ret; } -EXPORT_SYMBOL(notifier_chain_unregister); +EXPORT_SYMBOL_GPL(blocking_notifier_chain_register); /** - * notifier_call_chain - Call functions in a notifier chain - * @n: Pointer to root pointer of notifier chain + * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain + * @nh: Pointer to head of the blocking notifier chain + * @n: Entry to remove from notifier chain + * + * Removes a notifier from a blocking notifier chain. + * Must be called from process context. + * + * Returns zero on success or %-ENOENT on failure. + */ +int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, + struct notifier_block *n) +{ + int ret; + + /* + * This code gets used during boot-up, when task switching is + * not yet working and interrupts must remain disabled. At + * such times we must not call down_write(). + */ + if (unlikely(system_state == SYSTEM_BOOTING)) + return notifier_chain_unregister(&nh->head, n); + + down_write(&nh->rwsem); + ret = notifier_chain_unregister(&nh->head, n); + up_write(&nh->rwsem); + return ret; +} + +EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); + +/** + * blocking_notifier_call_chain - Call functions in a blocking notifier chain + * @nh: Pointer to head of the blocking notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function * - * Calls each function in a notifier chain in turn. + * Calls each function in a notifier chain in turn. The functions + * run in a process context, so they are allowed to block. * - * If the return value of the notifier can be and'd - * with %NOTIFY_STOP_MASK, then notifier_call_chain + * If the return value of the notifier can be and'ed + * with %NOTIFY_STOP_MASK then blocking_notifier_call_chain * will return immediately, with the return value of * the notifier function which halted execution. - * Otherwise, the return value is the return value + * Otherwise the return value is the return value * of the last notifier function called. */ -int __kprobes notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) +int blocking_notifier_call_chain(struct blocking_notifier_head *nh, + unsigned long val, void *v) { - int ret=NOTIFY_DONE; - struct notifier_block *nb = *n; + int ret; - while(nb) - { - ret=nb->notifier_call(nb,val,v); - if(ret&NOTIFY_STOP_MASK) - { - return ret; - } - nb=nb->next; - } + down_read(&nh->rwsem); + ret = notifier_call_chain(&nh->head, val, v); + up_read(&nh->rwsem); return ret; } -EXPORT_SYMBOL(notifier_call_chain); +EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); + +/* + * Raw notifier chain routines. There is no protection; + * the caller must provide it. Use at your own risk! + */ + +/** + * raw_notifier_chain_register - Add notifier to a raw notifier chain + * @nh: Pointer to head of the raw notifier chain + * @n: New entry in notifier chain + * + * Adds a notifier to a raw notifier chain. + * All locking must be provided by the caller. + * + * Currently always returns zero. + */ + +int raw_notifier_chain_register(struct raw_notifier_head *nh, + struct notifier_block *n) +{ + return notifier_chain_register(&nh->head, n); +} + +EXPORT_SYMBOL_GPL(raw_notifier_chain_register); + +/** + * raw_notifier_chain_unregister - Remove notifier from a raw notifier chain + * @nh: Pointer to head of the raw notifier chain + * @n: Entry to remove from notifier chain + * + * Removes a notifier from a raw notifier chain. + * All locking must be provided by the caller. + * + * Returns zero on success or %-ENOENT on failure. + */ +int raw_notifier_chain_unregister(struct raw_notifier_head *nh, + struct notifier_block *n) +{ + return notifier_chain_unregister(&nh->head, n); +} + +EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); + +/** + * raw_notifier_call_chain - Call functions in a raw notifier chain + * @nh: Pointer to head of the raw notifier chain + * @val: Value passed unmodified to notifier function + * @v: Pointer passed unmodified to notifier function + * + * Calls each function in a notifier chain in turn. The functions + * run in an undefined context. + * All locking must be provided by the caller. + * + * If the return value of the notifier can be and'ed + * with %NOTIFY_STOP_MASK then raw_notifier_call_chain + * will return immediately, with the return value of + * the notifier function which halted execution. + * Otherwise the return value is the return value + * of the last notifier function called. + */ + +int raw_notifier_call_chain(struct raw_notifier_head *nh, + unsigned long val, void *v) +{ + return notifier_call_chain(&nh->head, val, v); +} + +EXPORT_SYMBOL_GPL(raw_notifier_call_chain); /** * register_reboot_notifier - Register function to be called at reboot time @@ -196,13 +401,13 @@ EXPORT_SYMBOL(notifier_call_chain); * Registers a function with the list of functions * to be called at reboot time. * - * Currently always returns zero, as notifier_chain_register + * Currently always returns zero, as blocking_notifier_chain_register * always returns zero. */ int register_reboot_notifier(struct notifier_block * nb) { - return notifier_chain_register(&reboot_notifier_list, nb); + return blocking_notifier_chain_register(&reboot_notifier_list, nb); } EXPORT_SYMBOL(register_reboot_notifier); @@ -219,7 +424,7 @@ EXPORT_SYMBOL(register_reboot_notifier); int unregister_reboot_notifier(struct notifier_block * nb) { - return notifier_chain_unregister(&reboot_notifier_list, nb); + return blocking_notifier_chain_unregister(&reboot_notifier_list, nb); } EXPORT_SYMBOL(unregister_reboot_notifier); @@ -380,7 +585,7 @@ EXPORT_SYMBOL_GPL(emergency_restart); void kernel_restart_prepare(char *cmd) { - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); + blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; device_shutdown(); } @@ -430,7 +635,7 @@ EXPORT_SYMBOL_GPL(kernel_kexec); void kernel_shutdown_prepare(enum system_states state) { - notifier_call_chain(&reboot_notifier_list, + blocking_notifier_call_chain(&reboot_notifier_list, (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL); system_state = state; device_shutdown(); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9106354c781e..a49a6975092d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -73,23 +73,23 @@ DEFINE_RWLOCK(hci_cb_list_lock); struct hci_proto *hci_proto[HCI_MAX_PROTO]; /* HCI notifiers list */ -static struct notifier_block *hci_notifier; +static ATOMIC_NOTIFIER_HEAD(hci_notifier); /* ---- HCI notifications ---- */ int hci_register_notifier(struct notifier_block *nb) { - return notifier_chain_register(&hci_notifier, nb); + return atomic_notifier_chain_register(&hci_notifier, nb); } int hci_unregister_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&hci_notifier, nb); + return atomic_notifier_chain_unregister(&hci_notifier, nb); } static void hci_notify(struct hci_dev *hdev, int event) { - notifier_call_chain(&hci_notifier, event, hdev); + atomic_notifier_call_chain(&hci_notifier, event, hdev); } /* ---- HCI requests ---- */ diff --git a/net/core/dev.c b/net/core/dev.c index 8e1dc3051222..a3ab11f34153 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -193,7 +193,7 @@ static inline struct hlist_head *dev_index_hash(int ifindex) * Our notifier list */ -static struct notifier_block *netdev_chain; +static BLOCKING_NOTIFIER_HEAD(netdev_chain); /* * Device drivers call our routines to queue packets here. We empty the @@ -736,7 +736,8 @@ int dev_change_name(struct net_device *dev, char *newname) if (!err) { hlist_del(&dev->name_hlist); hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); - notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + blocking_notifier_call_chain(&netdev_chain, + NETDEV_CHANGENAME, dev); } return err; @@ -750,7 +751,7 @@ int dev_change_name(struct net_device *dev, char *newname) */ void netdev_features_change(struct net_device *dev) { - notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev); } EXPORT_SYMBOL(netdev_features_change); @@ -765,7 +766,8 @@ EXPORT_SYMBOL(netdev_features_change); void netdev_state_change(struct net_device *dev) { if (dev->flags & IFF_UP) { - notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); + blocking_notifier_call_chain(&netdev_chain, + NETDEV_CHANGE, dev); rtmsg_ifinfo(RTM_NEWLINK, dev, 0); } } @@ -862,7 +864,7 @@ int dev_open(struct net_device *dev) /* * ... and announce new interface. */ - notifier_call_chain(&netdev_chain, NETDEV_UP, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_UP, dev); } return ret; } @@ -885,7 +887,7 @@ int dev_close(struct net_device *dev) * Tell people we are going down, so that they can * prepare to death, when device is still operating. */ - notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev); dev_deactivate(dev); @@ -922,7 +924,7 @@ int dev_close(struct net_device *dev) /* * Tell people we are down */ - notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); return 0; } @@ -953,7 +955,7 @@ int register_netdevice_notifier(struct notifier_block *nb) int err; rtnl_lock(); - err = notifier_chain_register(&netdev_chain, nb); + err = blocking_notifier_chain_register(&netdev_chain, nb); if (!err) { for (dev = dev_base; dev; dev = dev->next) { nb->notifier_call(nb, NETDEV_REGISTER, dev); @@ -981,7 +983,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb) int err; rtnl_lock(); - err = notifier_chain_unregister(&netdev_chain, nb); + err = blocking_notifier_chain_unregister(&netdev_chain, nb); rtnl_unlock(); return err; } @@ -992,12 +994,12 @@ int unregister_netdevice_notifier(struct notifier_block *nb) * @v: pointer passed unmodified to notifier function * * Call all network notifier blocks. Parameters and return value - * are as for notifier_call_chain(). + * are as for blocking_notifier_call_chain(). */ int call_netdevice_notifiers(unsigned long val, void *v) { - return notifier_call_chain(&netdev_chain, val, v); + return blocking_notifier_call_chain(&netdev_chain, val, v); } /* When > 0 there are consumers of rx skb time stamps */ @@ -2242,7 +2244,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags) if (dev->flags & IFF_UP && ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) - notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); + blocking_notifier_call_chain(&netdev_chain, + NETDEV_CHANGE, dev); if ((flags ^ dev->gflags) & IFF_PROMISC) { int inc = (flags & IFF_PROMISC) ? +1 : -1; @@ -2286,8 +2289,8 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) else dev->mtu = new_mtu; if (!err && dev->flags & IFF_UP) - notifier_call_chain(&netdev_chain, - NETDEV_CHANGEMTU, dev); + blocking_notifier_call_chain(&netdev_chain, + NETDEV_CHANGEMTU, dev); return err; } @@ -2303,7 +2306,8 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) return -ENODEV; err = dev->set_mac_address(dev, sa); if (!err) - notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); + blocking_notifier_call_chain(&netdev_chain, + NETDEV_CHANGEADDR, dev); return err; } @@ -2359,7 +2363,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) return -EINVAL; memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); - notifier_call_chain(&netdev_chain, + blocking_notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); return 0; @@ -2813,7 +2817,7 @@ int register_netdevice(struct net_device *dev) write_unlock_bh(&dev_base_lock); /* Notify protocols, that a new device appeared. */ - notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); /* Finish registration after unlock */ net_set_todo(dev); @@ -2892,7 +2896,7 @@ static void netdev_wait_allrefs(struct net_device *dev) rtnl_lock(); /* Rebroadcast unregister notification */ - notifier_call_chain(&netdev_chain, + blocking_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); if (test_bit(__LINK_STATE_LINKWATCH_PENDING, @@ -3148,7 +3152,7 @@ int unregister_netdevice(struct net_device *dev) /* Notify protocols, that we are about to destroy this device. They should clean all the things. */ - notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); + blocking_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); /* * Flush the multicast chain diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index cc7b9d9255ef..d2ae9893ca17 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -68,7 +68,7 @@ __le16 decnet_address = 0; static DEFINE_RWLOCK(dndev_lock); static struct net_device *decnet_default_device; -static struct notifier_block *dnaddr_chain; +static BLOCKING_NOTIFIER_HEAD(dnaddr_chain); static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); static void dn_dev_delete(struct net_device *dev); @@ -446,7 +446,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de } rtmsg_ifa(RTM_DELADDR, ifa1); - notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); + blocking_notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); if (destroy) { dn_dev_free_ifa(ifa1); @@ -481,7 +481,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) dn_db->ifa_list = ifa; rtmsg_ifa(RTM_NEWADDR, ifa); - notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); + blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); return 0; } @@ -1285,12 +1285,12 @@ void dn_dev_devices_on(void) int register_dnaddr_notifier(struct notifier_block *nb) { - return notifier_chain_register(&dnaddr_chain, nb); + return blocking_notifier_chain_register(&dnaddr_chain, nb); } int unregister_dnaddr_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&dnaddr_chain, nb); + return blocking_notifier_chain_unregister(&dnaddr_chain, nb); } #ifdef CONFIG_PROC_FS diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 44fdf1413e2c..81c2f7885292 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -81,7 +81,7 @@ static struct ipv4_devconf ipv4_devconf_dflt = { static void rtmsg_ifa(int event, struct in_ifaddr *); -static struct notifier_block *inetaddr_chain; +static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy); #ifdef CONFIG_SYSCTL @@ -267,7 +267,8 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, *ifap1 = ifa->ifa_next; rtmsg_ifa(RTM_DELADDR, ifa); - notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); + blocking_notifier_call_chain(&inetaddr_chain, + NETDEV_DOWN, ifa); inet_free_ifa(ifa); } else { promote = ifa; @@ -291,7 +292,7 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, So that, this order is correct. */ rtmsg_ifa(RTM_DELADDR, ifa1); - notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); + blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); if (promote) { @@ -303,7 +304,8 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, promote->ifa_flags &= ~IFA_F_SECONDARY; rtmsg_ifa(RTM_NEWADDR, promote); - notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); + blocking_notifier_call_chain(&inetaddr_chain, + NETDEV_UP, promote); for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { if (ifa1->ifa_mask != ifa->ifa_mask || !inet_ifa_match(ifa1->ifa_address, ifa)) @@ -366,7 +368,7 @@ static int inet_insert_ifa(struct in_ifaddr *ifa) Notifier will trigger FIB update, so that listeners of netlink will know about new ifaddr */ rtmsg_ifa(RTM_NEWADDR, ifa); - notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); + blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); return 0; } @@ -938,12 +940,12 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop int register_inetaddr_notifier(struct notifier_block *nb) { - return notifier_chain_register(&inetaddr_chain, nb); + return blocking_notifier_chain_register(&inetaddr_chain, nb); } int unregister_inetaddr_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&inetaddr_chain, nb); + return blocking_notifier_chain_unregister(&inetaddr_chain, nb); } /* Rename ifa_labels for a device name change. Make some effort to preserve existing diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index 9e34034729a6..ceaabc18202b 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -80,8 +80,8 @@ static int ip_conntrack_vmalloc; static unsigned int ip_conntrack_next_id; static unsigned int ip_conntrack_expect_next_id; #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -struct notifier_block *ip_conntrack_chain; -struct notifier_block *ip_conntrack_expect_chain; +ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain); +ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain); DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); @@ -92,7 +92,7 @@ __ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache) { DEBUGP("ecache: delivering events for %p\n", ecache->ct); if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events) - notifier_call_chain(&ip_conntrack_chain, ecache->events, + atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events, ecache->ct); ecache->events = 0; ip_conntrack_put(ecache->ct); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 01c62a0d3742..445006ee4522 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -143,7 +143,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev, struct prefix_info *pinfo); static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev); -static struct notifier_block *inet6addr_chain; +static ATOMIC_NOTIFIER_HEAD(inet6addr_chain); struct ipv6_devconf ipv6_devconf = { .forwarding = 0, @@ -593,7 +593,7 @@ out2: read_unlock_bh(&addrconf_lock); if (likely(err == 0)) - notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); + atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); else { kfree(ifa); ifa = ERR_PTR(err); @@ -688,7 +688,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) ipv6_ifa_notify(RTM_DELADDR, ifp); - notifier_call_chain(&inet6addr_chain,NETDEV_DOWN,ifp); + atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp); addrconf_del_timer(ifp); @@ -3767,12 +3767,12 @@ static void addrconf_sysctl_unregister(struct ipv6_devconf *p) int register_inet6addr_notifier(struct notifier_block *nb) { - return notifier_chain_register(&inet6addr_chain, nb); + return atomic_notifier_chain_register(&inet6addr_chain, nb); } int unregister_inet6addr_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&inet6addr_chain,nb); + return atomic_notifier_chain_unregister(&inet6addr_chain,nb); } /* diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0ae281d9bfc3..56389c83557c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -90,8 +90,8 @@ static int nf_conntrack_vmalloc; static unsigned int nf_conntrack_next_id; static unsigned int nf_conntrack_expect_next_id; #ifdef CONFIG_NF_CONNTRACK_EVENTS -struct notifier_block *nf_conntrack_chain; -struct notifier_block *nf_conntrack_expect_chain; +ATOMIC_NOTIFIER_HEAD(nf_conntrack_chain); +ATOMIC_NOTIFIER_HEAD(nf_conntrack_expect_chain); DEFINE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache); @@ -103,7 +103,7 @@ __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache) DEBUGP("ecache: delivering events for %p\n", ecache->ct); if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct) && ecache->events) - notifier_call_chain(&nf_conntrack_chain, ecache->events, + atomic_notifier_call_chain(&nf_conntrack_chain, ecache->events, ecache->ct); ecache->events = 0; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d00a9034cb5f..2a233ffcf618 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -123,7 +123,7 @@ static void netlink_destroy_callback(struct netlink_callback *cb); static DEFINE_RWLOCK(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); -static struct notifier_block *netlink_chain; +static ATOMIC_NOTIFIER_HEAD(netlink_chain); static u32 netlink_group_mask(u32 group) { @@ -469,7 +469,8 @@ static int netlink_release(struct socket *sock) .protocol = sk->sk_protocol, .pid = nlk->pid, }; - notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); + atomic_notifier_call_chain(&netlink_chain, + NETLINK_URELEASE, &n); } if (nlk->module) @@ -1695,12 +1696,12 @@ static struct file_operations netlink_seq_fops = { int netlink_register_notifier(struct notifier_block *nb) { - return notifier_chain_register(&netlink_chain, nb); + return atomic_notifier_chain_register(&netlink_chain, nb); } int netlink_unregister_notifier(struct notifier_block *nb) { - return notifier_chain_unregister(&netlink_chain, nb); + return atomic_notifier_chain_unregister(&netlink_chain, nb); } static const struct proto_ops netlink_ops = { -- cgit v1.2.3-59-g8ed1b From 453823ba08ba762b3d58934b6dce75edce37169e Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 31 Mar 2006 02:30:39 -0800 Subject: [PATCH] IPMI: fix startup race condition Matt Domsch noticed a startup race with the IPMI kernel thread, it was possible (though extraordinarly unlikely) that a message could come in before the upper layer was ready to handle it. This patch splits the startup processing of an IPMI interface into two parts, one to get ready and one to actually start the processes to receive messages from the interface. [akpm@osdl.org: cleanups] Signed-off-by: Corey Minyard Cc: Matt Domsch Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_msghandler.c | 9 +++--- drivers/char/ipmi/ipmi_si_intf.c | 59 +++++++++++++++++++++++-------------- include/linux/ipmi_smi.h | 16 ++++++++-- 3 files changed, 54 insertions(+), 30 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 40eb005b9d77..a0b6f797d97d 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2305,8 +2305,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *si_dev, - unsigned char slave_addr, - ipmi_smi_t *new_intf) + unsigned char slave_addr) { int i, j; int rv; @@ -2388,9 +2387,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (rv) goto out; - /* FIXME - this is an ugly kludge, this sets the intf for the - caller before sending any messages with it. */ - *new_intf = intf; + rv = handlers->start_processing(send_info, intf); + if (rv) + goto out; get_guid(intf); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 35fbd4d8ed4b..d48d86bd2c2b 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -972,10 +972,37 @@ static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs) return si_irq_handler(irq, data, regs); } +static int smi_start_processing(void *send_info, + ipmi_smi_t intf) +{ + struct smi_info *new_smi = send_info; + + new_smi->intf = intf; + + /* Set up the timer that drives the interface. */ + setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi); + new_smi->last_timeout_jiffies = jiffies; + mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES); + + if (new_smi->si_type != SI_BT) { + new_smi->thread = kthread_run(ipmi_thread, new_smi, + "kipmi%d", new_smi->intf_num); + if (IS_ERR(new_smi->thread)) { + printk(KERN_NOTICE "ipmi_si_intf: Could not start" + " kernel thread due to error %ld, only using" + " timers to drive the interface\n", + PTR_ERR(new_smi->thread)); + new_smi->thread = NULL; + } + } + + return 0; +} static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, + .start_processing = smi_start_processing, .sender = sender, .request_events = request_events, .set_run_to_completion = set_run_to_completion, @@ -2162,9 +2189,13 @@ static void setup_xaction_handlers(struct smi_info *smi_info) static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { - if (smi_info->thread != NULL && smi_info->thread != ERR_PTR(-ENOMEM)) - kthread_stop(smi_info->thread); - del_timer_sync(&smi_info->si_timer); + if (smi_info->intf) { + /* The timer and thread are only running if the + interface has been started up and registered. */ + if (smi_info->thread != NULL) + kthread_stop(smi_info->thread); + del_timer_sync(&smi_info->si_timer); + } } static struct ipmi_default_vals @@ -2341,21 +2372,6 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->irq) new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; - /* The ipmi_register_smi() code does some operations to - determine the channel information, so we must be ready to - handle operations before it is called. This means we have - to stop the timer if we get an error after this point. */ - init_timer(&(new_smi->si_timer)); - new_smi->si_timer.data = (long) new_smi; - new_smi->si_timer.function = smi_timeout; - new_smi->last_timeout_jiffies = jiffies; - new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; - - add_timer(&(new_smi->si_timer)); - if (new_smi->si_type != SI_BT) - new_smi->thread = kthread_run(ipmi_thread, new_smi, - "kipmi%d", new_smi->intf_num); - if (!new_smi->dev) { /* If we don't already have a device from something * else (like PCI), then register a new one. */ @@ -2365,7 +2381,7 @@ static int try_smi_init(struct smi_info *new_smi) printk(KERN_ERR "ipmi_si_intf:" " Unable to allocate platform device\n"); - goto out_err_stop_timer; + goto out_err; } new_smi->dev = &new_smi->pdev->dev; new_smi->dev->driver = &ipmi_driver; @@ -2377,7 +2393,7 @@ static int try_smi_init(struct smi_info *new_smi) " Unable to register system interface device:" " %d\n", rv); - goto out_err_stop_timer; + goto out_err; } new_smi->dev_registered = 1; } @@ -2386,8 +2402,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi, &new_smi->device_id, new_smi->dev, - new_smi->slave_addr, - &(new_smi->intf)); + new_smi->slave_addr); if (rv) { printk(KERN_ERR "ipmi_si: Unable to register device: error %d\n", diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 53571288a9fc..6d9c7e4da472 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -82,6 +82,13 @@ struct ipmi_smi_handlers { struct module *owner; + /* The low-level interface cannot start sending messages to + the upper layer until this function is called. This may + not be NULL, the lower layer must take the interface from + this call. */ + int (*start_processing)(void *send_info, + ipmi_smi_t new_intf); + /* Called to enqueue an SMI message to be sent. This operation is not allowed to fail. If an error occurs, it should report back the error in a received message. It may @@ -157,13 +164,16 @@ static inline void ipmi_demangle_device_id(unsigned char *data, } /* Add a low-level interface to the IPMI driver. Note that if the - interface doesn't know its slave address, it should pass in zero. */ + interface doesn't know its slave address, it should pass in zero. + The low-level interface should not deliver any messages to the + upper layer until the start_processing() function in the handlers + is called, and the lower layer must get the interface from that + call. */ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *dev, - unsigned char slave_addr, - ipmi_smi_t *intf); + unsigned char slave_addr); /* * Remove a low-level interface from the IPMI driver. This will -- cgit v1.2.3-59-g8ed1b From 8a3628d53fe5eb1d1401dd1ce16655182c1c5ffc Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 31 Mar 2006 02:30:40 -0800 Subject: [PATCH] IPMI: tidy up various things Tidy up various coding standard things, mostly removing the space after !, but also break some long lines and fix a few other spacing inconsistencies. Also fixes some bad error reporting when deleting an IPMI user. Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_kcs_sm.c | 8 +++---- drivers/char/ipmi/ipmi_msghandler.c | 48 +++++++++++++++++++------------------ drivers/char/ipmi/ipmi_poweroff.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 2 +- drivers/char/ipmi/ipmi_watchdog.c | 8 +++---- 5 files changed, 35 insertions(+), 33 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index da1554194d3d..2062675f9e99 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -227,7 +227,7 @@ static inline int check_ibf(struct si_sm_data *kcs, unsigned char status, static inline int check_obf(struct si_sm_data *kcs, unsigned char status, long time) { - if (! GET_STATUS_OBF(status)) { + if (!GET_STATUS_OBF(status)) { kcs->obf_timeout -= time; if (kcs->obf_timeout < 0) { start_error_recovery(kcs, "OBF not ready in time"); @@ -407,7 +407,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) } if (state == KCS_READ_STATE) { - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; read_next_byte(kcs); } else { @@ -447,7 +447,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) "Not in read state for error2"); break; } - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); @@ -462,7 +462,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) break; } - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index a0b6f797d97d..c0c862cce1c3 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -557,7 +557,7 @@ unsigned int ipmi_addr_length(int addr_type) static void deliver_response(struct ipmi_recv_msg *msg) { - if (! msg->user) { + if (!msg->user) { ipmi_smi_t intf = msg->user_msg_data; unsigned long flags; @@ -598,11 +598,11 @@ static int intf_next_seq(ipmi_smi_t intf, (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; i = (i+1)%IPMI_IPMB_NUM_SEQ) { - if (! intf->seq_table[i].inuse) + if (!intf->seq_table[i].inuse) break; } - if (! intf->seq_table[i].inuse) { + if (!intf->seq_table[i].inuse) { intf->seq_table[i].recv_msg = recv_msg; /* Start with the maximum timeout, when the send response @@ -763,7 +763,7 @@ int ipmi_create_user(unsigned int if_num, } new_user = kmalloc(sizeof(*new_user), GFP_KERNEL); - if (! new_user) + if (!new_user) return -ENOMEM; spin_lock_irqsave(&interfaces_lock, flags); @@ -819,14 +819,13 @@ static void free_user(struct kref *ref) int ipmi_destroy_user(ipmi_user_t user) { - int rv = -ENODEV; ipmi_smi_t intf = user->intf; int i; unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - user->valid = 1; + user->valid = 0; /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); @@ -871,7 +870,7 @@ int ipmi_destroy_user(ipmi_user_t user) kref_put(&user->refcount, free_user); - return rv; + return 0; } void ipmi_get_version(ipmi_user_t user, @@ -936,7 +935,8 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) if (val) { /* Deliver any queued events. */ - list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) { + list_for_each_entry_safe(msg, msg2, &intf->waiting_events, + link) { list_del(&msg->link); list_add_tail(&msg->link, &msgs); } @@ -978,7 +978,7 @@ int ipmi_register_for_cmd(ipmi_user_t user, rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); - if (! rcvr) + if (!rcvr) return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; @@ -1514,7 +1514,7 @@ int ipmi_request_settime(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1545,7 +1545,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1570,7 +1570,7 @@ static int ipmb_file_read_proc(char *page, char **start, off_t off, char *out = (char *) page; ipmi_smi_t intf = data; int i; - int rv= 0; + int rv = 0; for (i = 0; i < IPMI_MAX_CHANNELS; i++) rv += sprintf(out+rv, "%x ", intf->channels[i].address); @@ -1989,7 +1989,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf) } else { bmc->dev = platform_device_alloc("ipmi_bmc", bmc->id.device_id); - if (! bmc->dev) { + if (!bmc->dev) { printk(KERN_ERR "ipmi_msghandler:" " Unable to allocate platform device\n"); @@ -2621,7 +2621,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, spin_unlock_irqrestore(&intf->counter_lock, flags); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -2776,7 +2776,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, spin_unlock_irqrestore(&intf->counter_lock, flags); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -2868,13 +2868,14 @@ static int handle_read_event_rsp(ipmi_smi_t intf, events. */ rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->gets_events) + if (!user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { rcu_read_unlock(); - list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { + list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, + link) { list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); } @@ -2904,7 +2905,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, /* No one to receive the message, put it in queue if there's not already too many things in the queue. */ recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -3189,7 +3190,7 @@ void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->handler->ipmi_watchdog_pretimeout) + if (!user->handler->ipmi_watchdog_pretimeout) continue; user->handler->ipmi_watchdog_pretimeout(user->handler_data); @@ -3277,7 +3278,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); - if (! smi_msg) + if (!smi_msg) return; spin_unlock_irqrestore(&intf->seq_lock, *flags); @@ -3313,8 +3314,9 @@ static void ipmi_timeout_handler(long timeout_period) /* See if any waiting messages need to be processed. */ spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, &intf->waiting_msgs, link) { - if (! handle_new_recv_msg(intf, smi_msg)) { + list_for_each_entry_safe(smi_msg, smi_msg2, + &intf->waiting_msgs, link) { + if (!handle_new_recv_msg(intf, smi_msg)) { list_del(&smi_msg->link); ipmi_free_smi_msg(smi_msg); } else { diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 786a2802ca34..d0b5c08e7b4e 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -346,7 +346,7 @@ static int ipmi_dell_chassis_detect (ipmi_user_t user) { const char ipmi_version_major = ipmi_version & 0xF; const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; - const char mfr[3]=DELL_IANA_MFR_ID; + const char mfr[3] = DELL_IANA_MFR_ID; if (!memcmp(mfr, &mfg_id, sizeof(mfr)) && ipmi_version_major <= 1 && ipmi_version_minor < 5) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index d48d86bd2c2b..2da5845a383a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -803,7 +803,7 @@ static int ipmi_thread(void *data) set_user_nice(current, 19); while (!kthread_should_stop()) { spin_lock_irqsave(&(smi_info->si_lock), flags); - smi_result=smi_event_handler(smi_info, 0); + smi_result = smi_event_handler(smi_info, 0); spin_unlock_irqrestore(&(smi_info->si_lock), flags); if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { /* do nothing */ diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 7ece9f3c8f70..2872c9575528 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -589,7 +589,7 @@ static void panic_halt_ipmi_heartbeat(void) 1); } -static struct watchdog_info ident= +static struct watchdog_info ident = { .options = 0, /* WDIOF_SETTIMEOUT, */ .firmware_version = 1, @@ -790,13 +790,13 @@ static int ipmi_fasync(int fd, struct file *file, int on) static int ipmi_close(struct inode *ino, struct file *filep) { - if (iminor(ino)==WATCHDOG_MINOR) - { + if (iminor(ino) == WATCHDOG_MINOR) { if (expect_close == 42) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + printk(KERN_CRIT PFX + "Unexpected close, not stopping watchdog!\n"); ipmi_heartbeat(); } clear_bit(0, &ipmi_wdog_open); -- cgit v1.2.3-59-g8ed1b From d6dfd1310d3562698fd7c3c086f6c239f96394ac Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 31 Mar 2006 02:30:41 -0800 Subject: [PATCH] IPMI: convert from semaphores to mutexes Convert the remaining semaphores to mutexes in the IPMI driver. The watchdog was using a semaphore as a real semaphore (for IPC), so the conversion there required adding a completion. Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_devintf.c | 18 ++++++------- drivers/char/ipmi/ipmi_msghandler.c | 23 ++++++++-------- drivers/char/ipmi/ipmi_si_intf.c | 24 ++++++++--------- drivers/char/ipmi/ipmi_watchdog.c | 53 +++++++++++++++++++++---------------- 4 files changed, 63 insertions(+), 55 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 932feedda262..e1c95374984c 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,7 +55,7 @@ struct ipmi_file_private struct file *file; struct fasync_struct *fasync_queue; wait_queue_head_t wait; - struct semaphore recv_sem; + struct mutex recv_mutex; int default_retries; unsigned int default_retry_time_ms; }; @@ -141,7 +141,7 @@ static int ipmi_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&(priv->recv_msgs)); init_waitqueue_head(&priv->wait); priv->fasync_queue = NULL; - sema_init(&(priv->recv_sem), 1); + mutex_init(&priv->recv_mutex); /* Use the low-level defaults. */ priv->default_retries = -1; @@ -285,15 +285,15 @@ static int ipmi_ioctl(struct inode *inode, break; } - /* We claim a semaphore because we don't want two + /* We claim a mutex because we don't want two users getting something from the queue at a time. Since we have to release the spinlock before we can copy the data to the user, it's possible another user will grab something from the queue, too. Then the messages might get out of order if something fails and the message gets put back onto the - queue. This semaphore prevents that problem. */ - down(&(priv->recv_sem)); + queue. This mutex prevents that problem. */ + mutex_lock(&priv->recv_mutex); /* Grab the message off the list. */ spin_lock_irqsave(&(priv->recv_msg_lock), flags); @@ -352,7 +352,7 @@ static int ipmi_ioctl(struct inode *inode, goto recv_putback_on_err; } - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); ipmi_free_recv_msg(msg); break; @@ -362,11 +362,11 @@ static int ipmi_ioctl(struct inode *inode, spin_lock_irqsave(&(priv->recv_msg_lock), flags); list_add(entry, &(priv->recv_msgs)); spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); break; recv_err: - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); break; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c0c862cce1c3..0ded046d5aa8 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -234,7 +235,7 @@ struct ipmi_smi /* The list of command receivers that are registered for commands on this interface. */ - struct semaphore cmd_rcvrs_lock; + struct mutex cmd_rcvrs_mutex; struct list_head cmd_rcvrs; /* Events that were queues because no one was there to receive @@ -387,10 +388,10 @@ static void clean_up_interface_data(ipmi_smi_t intf) /* Wholesale remove all the entries from the list in the * interface and wait for RCU to know that none are in use. */ - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); list_add_rcu(&list, &intf->cmd_rcvrs); list_del_rcu(&intf->cmd_rcvrs); - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); list_for_each_entry_safe(rcvr, rcvr2, &list, link) @@ -846,7 +847,7 @@ int ipmi_destroy_user(ipmi_user_t user) * since other things may be using it till we do * synchronize_rcu()) then free everything in that list. */ - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if (rcvr->user == user) { list_del_rcu(&rcvr->link); @@ -854,7 +855,7 @@ int ipmi_destroy_user(ipmi_user_t user) rcvrs = rcvr; } } - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; @@ -984,7 +985,7 @@ int ipmi_register_for_cmd(ipmi_user_t user, rcvr->netfn = netfn; rcvr->user = user; - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ entry = find_cmd_rcvr(intf, netfn, cmd); if (entry) { @@ -995,7 +996,7 @@ int ipmi_register_for_cmd(ipmi_user_t user, list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); @@ -1009,17 +1010,17 @@ int ipmi_unregister_for_cmd(ipmi_user_t user, ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ rcvr = find_cmd_rcvr(intf, netfn, cmd); if ((rcvr) && (rcvr->user == user)) { list_del_rcu(&rcvr->link); - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); kfree(rcvr); return 0; } else { - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); return -ENOENT; } } @@ -2365,7 +2366,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, spin_lock_init(&intf->events_lock); INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; - init_MUTEX(&intf->cmd_rcvrs_lock); + mutex_init(&intf->cmd_rcvrs_mutex); INIT_LIST_HEAD(&intf->cmd_rcvrs); init_waitqueue_head(&intf->waitq); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 2da5845a383a..a86c0f29953e 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1014,7 +1014,7 @@ static struct ipmi_smi_handlers handlers = #define SI_MAX_PARMS 4 static LIST_HEAD(smi_infos); -static DECLARE_MUTEX(smi_infos_lock); +static DEFINE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSPACING 1 @@ -2276,7 +2276,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->slave_addr, new_smi->irq); } - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { printk(KERN_WARNING "ipmi_si: duplicate interface\n"); rv = -EBUSY; @@ -2432,7 +2432,7 @@ static int try_smi_init(struct smi_info *new_smi) list_add_tail(&new_smi->link, &smi_infos); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]); @@ -2469,7 +2469,7 @@ static int try_smi_init(struct smi_info *new_smi) kfree(new_smi); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); return rv; } @@ -2527,26 +2527,26 @@ static __devinit int init_ipmi_si(void) #endif if (si_trydefaults) { - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { /* No BMC was found, try defaults. */ - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); default_find_bmc(); } else { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); } } - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); #ifdef CONFIG_PCI pci_unregister_driver(&ipmi_pci_driver); #endif printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; } else { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); return 0; } } @@ -2622,10 +2622,10 @@ static __exit void cleanup_ipmi_si(void) pci_unregister_driver(&ipmi_pci_driver); #endif - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); driver_unregister(&ipmi_driver); } diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 2872c9575528..2d11ddd99e55 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -303,21 +304,22 @@ static int ipmi_heartbeat(void); static void panic_halt_ipmi_heartbeat(void); -/* We use a semaphore to make sure that only one thing can send a set +/* We use a mutex to make sure that only one thing can send a set timeout at one time, because we only have one copy of the data. - The semaphore is claimed when the set_timeout is sent and freed + The mutex is claimed when the set_timeout is sent and freed when both messages are free. */ static atomic_t set_timeout_tofree = ATOMIC_INIT(0); -static DECLARE_MUTEX(set_timeout_lock); +static DEFINE_MUTEX(set_timeout_lock); +static DECLARE_COMPLETION(set_timeout_wait); static void set_timeout_free_smi(struct ipmi_smi_msg *msg) { if (atomic_dec_and_test(&set_timeout_tofree)) - up(&set_timeout_lock); + complete(&set_timeout_wait); } static void set_timeout_free_recv(struct ipmi_recv_msg *msg) { if (atomic_dec_and_test(&set_timeout_tofree)) - up(&set_timeout_lock); + complete(&set_timeout_wait); } static struct ipmi_smi_msg set_timeout_smi_msg = { @@ -399,7 +401,7 @@ static int ipmi_set_timeout(int do_heartbeat) /* We can only send one of these at a time. */ - down(&set_timeout_lock); + mutex_lock(&set_timeout_lock); atomic_set(&set_timeout_tofree, 2); @@ -407,16 +409,21 @@ static int ipmi_set_timeout(int do_heartbeat) &set_timeout_recv_msg, &send_heartbeat_now); if (rv) { - up(&set_timeout_lock); - } else { - if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) - || ((send_heartbeat_now) - && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) - { - rv = ipmi_heartbeat(); - } + mutex_unlock(&set_timeout_lock); + goto out; + } + + wait_for_completion(&set_timeout_wait); + + if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) + || ((send_heartbeat_now) + && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) + { + rv = ipmi_heartbeat(); } + mutex_unlock(&set_timeout_lock); +out: return rv; } @@ -458,17 +465,17 @@ static void panic_halt_ipmi_set_timeout(void) The semaphore is claimed when the set_timeout is sent and freed when both messages are free. */ static atomic_t heartbeat_tofree = ATOMIC_INIT(0); -static DECLARE_MUTEX(heartbeat_lock); -static DECLARE_MUTEX_LOCKED(heartbeat_wait_lock); +static DEFINE_MUTEX(heartbeat_lock); +static DECLARE_COMPLETION(heartbeat_wait); static void heartbeat_free_smi(struct ipmi_smi_msg *msg) { if (atomic_dec_and_test(&heartbeat_tofree)) - up(&heartbeat_wait_lock); + complete(&heartbeat_wait); } static void heartbeat_free_recv(struct ipmi_recv_msg *msg) { if (atomic_dec_and_test(&heartbeat_tofree)) - up(&heartbeat_wait_lock); + complete(&heartbeat_wait); } static struct ipmi_smi_msg heartbeat_smi_msg = { @@ -511,14 +518,14 @@ static int ipmi_heartbeat(void) return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } - down(&heartbeat_lock); + mutex_lock(&heartbeat_lock); atomic_set(&heartbeat_tofree, 2); /* Don't reset the timer if we have the timer turned off, that re-enables the watchdog. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) { - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); return 0; } @@ -539,14 +546,14 @@ static int ipmi_heartbeat(void) &heartbeat_recv_msg, 1); if (rv) { - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); printk(KERN_WARNING PFX "heartbeat failure: %d\n", rv); return rv; } /* Wait for the heartbeat to be sent. */ - down(&heartbeat_wait_lock); + wait_for_completion(&heartbeat_wait); if (heartbeat_recv_msg.msg.data[0] != 0) { /* Got an error in the heartbeat response. It was already @@ -555,7 +562,7 @@ static int ipmi_heartbeat(void) rv = -EINVAL; } - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); return rv; } -- cgit v1.2.3-59-g8ed1b