diff options
Diffstat (limited to 'drivers')
171 files changed, 3562 insertions, 2532 deletions
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 5e41e6dd657..db195abad69 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -155,7 +155,7 @@ struct aoedev { u16 fw_ver; /* version of blade's firmware */ struct work_struct work;/* disk create work struct */ struct gendisk *gd; - struct request_queue blkq; + struct request_queue *blkq; struct hd_geometry geo; sector_t ssize; struct timer_list timer; diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 2307a271bdc..95d344971ed 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -264,9 +264,13 @@ aoeblk_gdalloc(void *vp) goto err_disk; } - blk_queue_make_request(&d->blkq, aoeblk_make_request); - if (bdi_init(&d->blkq.backing_dev_info)) + d->blkq = blk_alloc_queue(GFP_KERNEL); + if (!d->blkq) goto err_mempool; + blk_queue_make_request(d->blkq, aoeblk_make_request); + d->blkq->backing_dev_info.name = "aoe"; + if (bdi_init(&d->blkq->backing_dev_info)) + goto err_blkq; spin_lock_irqsave(&d->lock, flags); gd->major = AOE_MAJOR; gd->first_minor = d->sysminor * AOE_PARTITIONS; @@ -276,7 +280,7 @@ aoeblk_gdalloc(void *vp) snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d", d->aoemajor, d->aoeminor); - gd->queue = &d->blkq; + gd->queue = d->blkq; d->gd = gd; d->flags &= ~DEVFL_GDALLOC; d->flags |= DEVFL_UP; @@ -287,6 +291,9 @@ aoeblk_gdalloc(void *vp) aoedisk_add_sysfs(d); return; +err_blkq: + blk_cleanup_queue(d->blkq); + d->blkq = NULL; err_mempool: mempool_destroy(d->bufpool); err_disk: diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index eeea477d960..fa67027789a 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -113,6 +113,7 @@ aoedev_freedev(struct aoedev *d) if (d->bufpool) mempool_destroy(d->bufpool); skbpoolfree(d); + blk_cleanup_queue(d->blkq); kfree(d); } diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 8c9d50db5c3..c5855779058 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -49,6 +49,7 @@ #define PCI_DEVICE_ID_INTEL_IGDNG_D_HB 0x0040 #define PCI_DEVICE_ID_INTEL_IGDNG_D_IG 0x0042 #define PCI_DEVICE_ID_INTEL_IGDNG_M_HB 0x0044 +#define PCI_DEVICE_ID_INTEL_IGDNG_MA_HB 0x0062 #define PCI_DEVICE_ID_INTEL_IGDNG_M_IG 0x0046 /* cover 915 and 945 variants */ @@ -81,7 +82,8 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB) extern int agp_memory_reserved; @@ -1216,6 +1218,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) case PCI_DEVICE_ID_INTEL_G41_HB: case PCI_DEVICE_ID_INTEL_IGDNG_D_HB: case PCI_DEVICE_ID_INTEL_IGDNG_M_HB: + case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB: *gtt_offset = *gtt_size = MB(2); break; default: @@ -2195,6 +2198,8 @@ static const struct intel_driver_description { "IGDNG/D", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IGDNG_M_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0, "IGDNG/M", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_IGDNG_MA_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0, + "IGDNG/MA", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; @@ -2398,6 +2403,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_G41_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB), + ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB), { } }; diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 86105efb4eb..0ecac7e532f 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -1006,7 +1006,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) priv->dev->release = (void (*)(struct device *)) kfree; rc = device_register(priv->dev); if (rc) { - kfree(priv->dev); + put_device(priv->dev); goto out_error_dev; } diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c index cd0ba51f7c8..0d8c5788b8e 100644 --- a/drivers/char/hw_random/amd-rng.c +++ b/drivers/char/hw_random/amd-rng.c @@ -44,8 +44,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VDEVICE(AMD, 0x7443), 0, }, + { PCI_VDEVICE(AMD, 0x746b), 0, }, { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c index 64d513f6836..4c4d4e140f9 100644 --- a/drivers/char/hw_random/geode-rng.c +++ b/drivers/char/hw_random/geode-rng.c @@ -46,8 +46,7 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, }, { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index afa8813e737..645237bda68 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -822,6 +822,7 @@ static const struct file_operations zero_fops = { * - permits private mappings, "copies" are taken of the source of zeros */ static struct backing_dev_info zero_bdi = { + .name = "char/mem", .capabilities = BDI_CAP_MAP_COPY, }; diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 973be2f4419..4e28b35024e 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -300,8 +300,7 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) if (space < 2) return -1; tty->canon_column = tty->column = 0; - tty_put_char(tty, '\r'); - tty_put_char(tty, c); + tty->ops->write(tty, "\r\n", 2); return 2; } tty->canon_column = tty->column; diff --git a/drivers/char/pty.c b/drivers/char/pty.c index d083c73d784..b33d6688e91 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -109,21 +109,13 @@ static int pty_space(struct tty_struct *to) * the other side of the pty/tty pair. */ -static int pty_write(struct tty_struct *tty, const unsigned char *buf, - int count) +static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) { struct tty_struct *to = tty->link; - int c; if (tty->stopped) return 0; - /* This isn't locked but our 8K is quite sloppy so no - big deal */ - - c = pty_space(to); - if (c > count) - c = count; if (c > 0) { /* Stuff the data into the input queue of the other end */ c = tty_insert_flip_string(to, buf, c); diff --git a/drivers/char/random.c b/drivers/char/random.c index 8c7444857a4..d8a9255e1a3 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -240,6 +240,7 @@ #include <linux/spinlock.h> #include <linux/percpu.h> #include <linux/cryptohash.h> +#include <linux/fips.h> #ifdef CONFIG_GENERIC_HARDIRQS # include <linux/irq.h> @@ -413,6 +414,7 @@ struct entropy_store { unsigned add_ptr; int entropy_count; int input_rotate; + __u8 *last_data; }; static __u32 input_pool_data[INPUT_POOL_WORDS]; @@ -852,12 +854,21 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, { ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; + unsigned long flags; xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, min, reserved); while (nbytes) { extract_buf(r, tmp); + + if (r->last_data) { + spin_lock_irqsave(&r->lock, flags); + if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) + panic("Hardware RNG duplicated output!\n"); + memcpy(r->last_data, tmp, EXTRACT_SIZE); + spin_unlock_irqrestore(&r->lock, flags); + } i = min_t(int, nbytes, EXTRACT_SIZE); memcpy(buf, tmp, i); nbytes -= i; @@ -940,6 +951,9 @@ static void init_std_data(struct entropy_store *r) now = ktime_get_real(); mix_pool_bytes(r, &now, sizeof(now)); mix_pool_bytes(r, utsname(), sizeof(*(utsname()))); + /* Enable continuous test in fips mode */ + if (fips_enabled) + r->last_data = kmalloc(EXTRACT_SIZE, GFP_KERNEL); } static int rand_initialize(void) diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 5d7a02f63e1..50eecfe1d72 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -24,6 +24,7 @@ #include <linux/sysrq.h> #include <linux/kbd_kern.h> #include <linux/proc_fs.h> +#include <linux/nmi.h> #include <linux/quotaops.h> #include <linux/perf_counter.h> #include <linux/kernel.h> @@ -222,12 +223,20 @@ static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus); static void sysrq_handle_showallcpus(int key, struct tty_struct *tty) { - struct pt_regs *regs = get_irq_regs(); - if (regs) { - printk(KERN_INFO "CPU%d:\n", smp_processor_id()); - show_regs(regs); + /* + * Fall back to the workqueue based printing if the + * backtrace printing did not succeed or the + * architecture has no support for it: + */ + if (!trigger_all_cpu_backtrace()) { + struct pt_regs *regs = get_irq_regs(); + + if (regs) { + printk(KERN_INFO "CPU%d:\n", smp_processor_id()); + show_regs(regs); + } + schedule_work(&sysrq_showallcpus); } - schedule_work(&sysrq_showallcpus); } static struct sysrq_key_op sysrq_showallcpus_op = { diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index aec1931608a..0b73e4ec1ad 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -450,6 +450,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, goto out_err; } + /* Default timeouts */ + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + if (request_locality(chip, 0) != 0) { rc = -ENODEV; goto out_err; @@ -457,12 +463,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); - /* Default timeouts */ - chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - dev_info(dev, "1.2 TPM (device-id 0x%X, rev-id %d)\n", vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index fd69086d08d..2968ed6a9c4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1250,20 +1250,11 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) { int ret = 0; -#ifdef __powerpc__ int cpu = sysdev->id; - unsigned int cur_freq = 0; struct cpufreq_policy *cpu_policy; dprintk("suspending cpu %u\n", cpu); - /* - * This whole bogosity is here because Powerbooks are made of fail. - * No sane platform should need any of the code below to be run. - * (it's entirely the wrong thing to do, as driver->get may - * reenable interrupts on some architectures). - */ - if (!cpu_online(cpu)) return 0; @@ -1282,47 +1273,13 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) if (cpufreq_driver->suspend) { ret = cpufreq_driver->suspend(cpu_policy, pmsg); - if (ret) { + if (ret) printk(KERN_ERR "cpufreq: suspend failed in ->suspend " "step on CPU %u\n", cpu_policy->cpu); - goto out; - } - } - - if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS) - goto out; - - if (cpufreq_driver->get) - cur_freq = cpufreq_driver->get(cpu_policy->cpu); - - if (!cur_freq || !cpu_policy->cur) { - printk(KERN_ERR "cpufreq: suspend failed to assert current " - "frequency is what timing core thinks it is.\n"); - goto out; - } - - if (unlikely(cur_freq != cpu_policy->cur)) { - struct cpufreq_freqs freqs; - - if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN)) - dprintk("Warning: CPU frequency is %u, " - "cpufreq assumed %u kHz.\n", - cur_freq, cpu_policy->cur); - - freqs.cpu = cpu; - freqs.old = cpu_policy->cur; - freqs.new = cur_freq; - - srcu_notifier_call_chain(&cpufreq_transition_notifier_list, - CPUFREQ_SUSPENDCHANGE, &freqs); - adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); - - cpu_policy->cur = cur_freq; } out: cpufreq_cpu_put(cpu_policy); -#endif /* __powerpc__ */ return ret; } @@ -1330,24 +1287,21 @@ out: * cpufreq_resume - restore proper CPU frequency handling after resume * * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) - * 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync - * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are - * restored. + * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are + * restored. It will verify that the current freq is in sync with + * what we believe it to be. This is a bit later than when it + * should be, but nonethteless it's better than calling + * cpufreq_driver->get() here which might re-enable interrupts... */ static int cpufreq_resume(struct sys_device *sysdev) { int ret = 0; -#ifdef __powerpc__ int cpu = sysdev->id; struct cpufreq_policy *cpu_policy; dprintk("resuming cpu %u\n", cpu); - /* As with the ->suspend method, all the code below is - * only necessary because Powerbooks suck. - * See commit 42d4dc3f4e1e for jokes. */ - if (!cpu_online(cpu)) return 0; @@ -1373,45 +1327,10 @@ static int cpufreq_resume(struct sys_device *sysdev) } } - if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { - unsigned int cur_freq = 0; - - if (cpufreq_driver->get) - cur_freq = cpufreq_driver->get(cpu_policy->cpu); - - if (!cur_freq || !cpu_policy->cur) { - printk(KERN_ERR "cpufreq: resume failed to assert " - "current frequency is what timing core " - "thinks it is.\n"); - goto out; - } - - if (unlikely(cur_freq != cpu_policy->cur)) { - struct cpufreq_freqs freqs; - - if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN)) - dprintk("Warning: CPU frequency " - "is %u, cpufreq assumed %u kHz.\n", - cur_freq, cpu_policy->cur); - - freqs.cpu = cpu; - freqs.old = cpu_policy->cur; - freqs.new = cur_freq; - - srcu_notifier_call_chain( - &cpufreq_transition_notifier_list, - CPUFREQ_RESUMECHANGE, &freqs); - adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); - - cpu_policy->cur = cur_freq; - } - } - -out: schedule_work(&cpu_policy->update); + fail: cpufreq_cpu_put(cpu_policy); -#endif /* __powerpc__ */ return ret; } diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 5b27692372b..b08403d7d1c 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -13,7 +13,6 @@ if CRYPTO_HW config CRYPTO_DEV_PADLOCK tristate "Support for VIA PadLock ACE" depends on X86 && !UML - select CRYPTO_ALGAPI help Some VIA processors come with an integrated crypto engine (so called VIA PadLock ACE, Advanced Cryptography Engine) @@ -39,6 +38,7 @@ config CRYPTO_DEV_PADLOCK_AES config CRYPTO_DEV_PADLOCK_SHA tristate "PadLock driver for SHA1 and SHA256 algorithms" depends on CRYPTO_DEV_PADLOCK + select CRYPTO_HASH select CRYPTO_SHA1 select CRYPTO_SHA256 help @@ -157,6 +157,19 @@ config S390_PRNG ANSI X9.17 standard. The PRNG is usable via the char device /dev/prandom. +config CRYPTO_DEV_MV_CESA + tristate "Marvell's Cryptographic Engine" + depends on PLAT_ORION + select CRYPTO_ALGAPI + select CRYPTO_AES + select CRYPTO_BLKCIPHER2 + help + This driver allows you to utilize the Cryptographic Engines and + Security Accelerator (CESA) which can be found on the Marvell Orion + and Kirkwood SoCs, such as QNAP's TS-209. + + Currently the driver supports AES in ECB and CBC mode without DMA. + config CRYPTO_DEV_HIFN_795X tristate "Driver HIFN 795x crypto accelerator chips" select CRYPTO_DES diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 9bf4a2bc884..6ffcb3f7f94 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o +obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/ diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c index 61b6e1bec8c..a33243c17b0 100644 --- a/drivers/crypto/amcc/crypto4xx_alg.c +++ b/drivers/crypto/amcc/crypto4xx_alg.c @@ -208,7 +208,8 @@ static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm, } } - tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx); + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct crypto4xx_ctx)); sa = (struct dynamic_sa_ctl *) ctx->sa_in; set_dynamic_sa_command_0(sa, SA_SAVE_HASH, SA_NOT_SAVE_IV, SA_NOT_LOAD_HASH, SA_LOAD_IV_FROM_SA, diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 4c0dfb2b872..46e899ac924 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -31,8 +31,6 @@ #include <asm/dcr.h> #include <asm/dcr-regs.h> #include <asm/cacheflush.h> -#include <crypto/internal/hash.h> -#include <crypto/algapi.h> #include <crypto/aes.h> #include <crypto/sha.h> #include "crypto4xx_reg_def.h" @@ -998,10 +996,15 @@ static int crypto4xx_alg_init(struct crypto_tfm *tfm) ctx->sa_out_dma_addr = 0; ctx->sa_len = 0; - if (alg->cra_type == &crypto_ablkcipher_type) + switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) { + default: tfm->crt_ablkcipher.reqsize = sizeof(struct crypto4xx_ctx); - else if (alg->cra_type == &crypto_ahash_type) - tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx); + break; + case CRYPTO_ALG_TYPE_AHASH: + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct crypto4xx_ctx)); + break; + } return 0; } @@ -1015,7 +1018,8 @@ static void crypto4xx_alg_exit(struct crypto_tfm *tfm) } int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, - struct crypto_alg *crypto_alg, int array_size) + struct crypto4xx_alg_common *crypto_alg, + int array_size) { struct crypto4xx_alg *alg; int i; @@ -1027,13 +1031,18 @@ int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, return -ENOMEM; alg->alg = crypto_alg[i]; - INIT_LIST_HEAD(&alg->alg.cra_list); - if (alg->alg.cra_init == NULL) - alg->alg.cra_init = crypto4xx_alg_init; - if (alg->alg.cra_exit == NULL) - alg->alg.cra_exit = crypto4xx_alg_exit; alg->dev = sec_dev; - rc = crypto_register_alg(&alg->alg); + + switch (alg->alg.type) { + case CRYPTO_ALG_TYPE_AHASH: + rc = crypto_register_ahash(&alg->alg.u.hash); + break; + + default: + rc = crypto_register_alg(&alg->alg.u.cipher); + break; + } + if (rc) { list_del(&alg->entry); kfree(alg); @@ -1051,7 +1060,14 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev) list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) { list_del(&alg->entry); - crypto_unregister_alg(&alg->alg); + switch (alg->alg.type) { + case CRYPTO_ALG_TYPE_AHASH: + crypto_unregister_ahash(&alg->alg.u.hash); + break; + + default: + crypto_unregister_alg(&alg->alg.u.cipher); + } kfree(alg); } } @@ -1104,17 +1120,18 @@ static irqreturn_t crypto4xx_ce_interrupt_handler(int irq, void *data) /** * Supported Crypto Algorithms */ -struct crypto_alg crypto4xx_alg[] = { +struct crypto4xx_alg_common crypto4xx_alg[] = { /* Crypto AES modes */ - { + { .type = CRYPTO_ALG_TYPE_ABLKCIPHER, .u.cipher = { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-ppc4xx", .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto4xx_ctx), - .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, + .cra_init = crypto4xx_alg_init, + .cra_exit = crypto4xx_alg_exit, .cra_module = THIS_MODULE, .cra_u = { .ablkcipher = { @@ -1126,29 +1143,7 @@ struct crypto_alg crypto4xx_alg[] = { .decrypt = crypto4xx_decrypt, } } - }, - /* Hash SHA1 */ - { - .cra_name = "sha1", - .cra_driver_name = "sha1-ppc4xx", - .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct crypto4xx_ctx), - .cra_alignmask = 0, - .cra_type = &crypto_ahash_type, - .cra_init = crypto4xx_sha1_alg_init, - .cra_module = THIS_MODULE, - .cra_u = { - .ahash = { - .digestsize = SHA1_DIGEST_SIZE, - .init = crypto4xx_hash_init, - .update = crypto4xx_hash_update, - .final = crypto4xx_hash_final, - .digest = crypto4xx_hash_digest, - } - } - }, + }}, }; /** diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index 1ef10344936..da9cbe3b9fc 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -22,6 +22,8 @@ #ifndef __CRYPTO4XX_CORE_H__ #define __CRYPTO4XX_CORE_H__ +#include <crypto/internal/hash.h> + #define PPC460SX_SDR0_SRST 0x201 #define PPC405EX_SDR0_SRST 0x200 #define PPC460EX_SDR0_SRST 0x201 @@ -138,14 +140,31 @@ struct crypto4xx_req_ctx { u16 sa_len; }; +struct crypto4xx_alg_common { + u32 type; + union { + struct crypto_alg cipher; + struct ahash_alg hash; + } u; +}; + struct crypto4xx_alg { struct list_head entry; - struct crypto_alg alg; + struct crypto4xx_alg_common alg; struct crypto4xx_device *dev; }; -#define crypto_alg_to_crypto4xx_alg(x) \ - container_of(x, struct crypto4xx_alg, alg) +static inline struct crypto4xx_alg *crypto_alg_to_crypto4xx_alg( + struct crypto_alg *x) +{ + switch (x->cra_flags & CRYPTO_ALG_TYPE_MASK) { + case CRYPTO_ALG_TYPE_AHASH: + return container_of(__crypto_ahash_alg(x), + struct crypto4xx_alg, alg.u.hash); + } + + return container_of(x, struct crypto4xx_alg, alg.u.cipher); +} extern int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size); extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx); diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c new file mode 100644 index 00000000000..b21ef635f35 --- /dev/null +++ b/drivers/crypto/mv_cesa.c @@ -0,0 +1,606 @@ +/* + * Support for Marvell's crypto engine which can be found on some Orion5X + * boards. + * + * Author: Sebastian Andrzej Siewior < sebastian at breakpoint dot cc > + * License: GPLv2 + * + */ +#include <crypto/aes.h> +#include <crypto/algapi.h> +#include <linux/crypto.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> + +#include "mv_cesa.h" +/* + * STM: + * /---------------------------------------\ + * | | request complete + * \./ | + * IDLE -> new request -> BUSY -> done -> DEQUEUE + * /°\ | + * | | more scatter entries + * \________________/ + */ +enum engine_status { + ENGINE_IDLE, + ENGINE_BUSY, + ENGINE_W_DEQUEUE, +}; + +/** + * struct req_progress - used for every crypt request + * @src_sg_it: sg iterator for src + * @dst_sg_it: sg iterator for dst + * @sg_src_left: bytes left in src to process (scatter list) + * @src_start: offset to add to src start position (scatter list) + * @crypt_len: length of current crypt process + * @sg_dst_left: bytes left dst to process in this scatter list + * @dst_start: offset to add to dst start position (scatter list) + * @total_req_bytes: total number of bytes processed (request). + * + * sg helper are used to iterate over the scatterlist. Since the size of the + * SRAM may be less than the scatter size, this struct struct is used to keep + * track of progress within current scatterlist. + */ +struct req_progress { + struct sg_mapping_iter src_sg_it; + struct sg_mapping_iter dst_sg_it; + + /* src mostly */ + int sg_src_left; + int src_start; + int crypt_len; + /* dst mostly */ + int sg_dst_left; + int dst_start; + int total_req_bytes; +}; + +struct crypto_priv { + void __iomem *reg; + void __iomem *sram; + int irq; + struct task_struct *queue_th; + + /* the lock protects queue and eng_st */ + spinlock_t lock; + struct crypto_queue queue; + enum engine_status eng_st; + struct ablkcipher_request *cur_req; + struct req_progress p; + int max_req_size; + int sram_size; +}; + +static struct crypto_priv *cpg; + +struct mv_ctx { + u8 aes_enc_key[AES_KEY_LEN]; + u32 aes_dec_key[8]; + int key_len; + u32 need_calc_aes_dkey; +}; + +enum crypto_op { + COP_AES_ECB, + COP_AES_CBC, +}; + +struct mv_req_ctx { + enum crypto_op op; + int decrypt; +}; + +static void compute_aes_dec_key(struct mv_ctx *ctx) +{ + struct crypto_aes_ctx gen_aes_key; + int key_pos; + + if (!ctx->need_calc_aes_dkey) + return; + + crypto_aes_expand_key(&gen_aes_key, ctx->aes_enc_key, ctx->key_len); + + key_pos = ctx->key_len + 24; + memcpy(ctx->aes_dec_key, &gen_aes_key.key_enc[key_pos], 4 * 4); + switch (ctx->key_len) { + case AES_KEYSIZE_256: + key_pos -= 2; + /* fall */ + case AES_KEYSIZE_192: + key_pos -= 2; + memcpy(&ctx->aes_dec_key[4], &gen_aes_key.key_enc[key_pos], + 4 * 4); + break; + } + ctx->need_calc_aes_dkey = 0; +} + +static int mv_setkey_aes(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int len) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct mv_ctx *ctx = crypto_tfm_ctx(tfm); + + switch (len) { + case AES_KEYSIZE_128: + case AES_KEYSIZE_192: + case AES_KEYSIZE_256: + break; + default: + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + ctx->key_len = len; + ctx->need_calc_aes_dkey = 1; + + memcpy(ctx->aes_enc_key, key, AES_KEY_LEN); + return 0; +} + +static void setup_data_in(struct ablkcipher_request *req) +{ + int ret; + void *buf; + + if (!cpg->p.sg_src_left) { + ret = sg_miter_next(&cpg->p.src_sg_it); + BUG_ON(!ret); + cpg->p.sg_src_left = cpg->p.src_sg_it.length; + cpg->p.src_start = 0; + } + + cpg->p.crypt_len = min(cpg->p.sg_src_left, cpg->max_req_size); + + buf = cpg->p.src_sg_it.addr; + buf += cpg->p.src_start; + + memcpy(cpg->sram + SRAM_DATA_IN_START, buf, cpg->p.crypt_len); + + cpg->p.sg_src_left -= cpg->p.crypt_len; + cpg->p.src_start += cpg->p.crypt_len; +} + +static void mv_process_current_q(int first_block) +{ + struct ablkcipher_request *req = cpg->cur_req; + struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + struct sec_accel_config op; + + switch (req_ctx->op) { + case COP_AES_ECB: + op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_ECB; + break; + case COP_AES_CBC: + op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_CBC; + op.enc_iv = ENC_IV_POINT(SRAM_DATA_IV) | + ENC_IV_BUF_POINT(SRAM_DATA_IV_BUF); + if (first_block) + memcpy(cpg->sram + SRAM_DATA_IV, req->info, 16); + break; + } + if (req_ctx->decrypt) { + op.config |= CFG_DIR_DEC; + memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_dec_key, + AES_KEY_LEN); + } else { + op.config |= CFG_DIR_ENC; + memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_enc_key, + AES_KEY_LEN); + } + + switch (ctx->key_len) { + case AES_KEYSIZE_128: + op.config |= CFG_AES_LEN_128; + break; + case AES_KEYSIZE_192: + op.config |= CFG_AES_LEN_192; + break; + case AES_KEYSIZE_256: + op.config |= CFG_AES_LEN_256; + break; + } + op.enc_p = ENC_P_SRC(SRAM_DATA_IN_START) | + ENC_P_DST(SRAM_DATA_OUT_START); + op.enc_key_p = SRAM_DATA_KEY_P; + + setup_data_in(req); + op.enc_len = cpg->p.crypt_len; + memcpy(cpg->sram + SRAM_CONFIG, &op, + sizeof(struct sec_accel_config)); + + writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0); + /* GO */ + writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD); + + /* + * XXX: add timer if the interrupt does not occur for some mystery + * reason + */ +} + +static void mv_crypto_algo_completion(void) +{ + struct ablkcipher_request *req = cpg->cur_req; + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + + if (req_ctx->op != COP_AES_CBC) + return ; + + memcpy(req->info, cpg->sram + SRAM_DATA_IV_BUF, 16); +} + +static void dequeue_complete_req(void) +{ + struct ablkcipher_request *req = cpg->cur_req; + void *buf; + int ret; + + cpg->p.total_req_bytes += cpg->p.crypt_len; + do { + int dst_copy; + + if (!cpg->p.sg_dst_left) { + ret = sg_miter_next(&cpg->p.dst_sg_it); + BUG_ON(!ret); + cpg->p.sg_dst_left = cpg->p.dst_sg_it.length; + cpg->p.dst_start = 0; + } + + buf = cpg->p.dst_sg_it.addr; + buf += cpg->p.dst_start; + + dst_copy = min(cpg->p.crypt_len, cpg->p.sg_dst_left); + + memcpy(buf, cpg->sram + SRAM_DATA_OUT_START, dst_copy); + + cpg->p.sg_dst_left -= dst_copy; + cpg->p.crypt_len -= dst_copy; + cpg->p.dst_start += dst_copy; + } while (cpg->p.crypt_len > 0); + + BUG_ON(cpg->eng_st != ENGINE_W_DEQUEUE); + if (cpg->p.total_req_bytes < req->nbytes) { + /* process next scatter list entry */ + cpg->eng_st = ENGINE_BUSY; + mv_process_current_q(0); + } else { + sg_miter_stop(&cpg->p.src_sg_it); + sg_miter_stop(&cpg->p.dst_sg_it); + mv_crypto_algo_completion(); + cpg->eng_st = ENGINE_IDLE; + req->base.complete(&req->base, 0); + } +} + +static int count_sgs(struct scatterlist *sl, unsigned int total_bytes) +{ + int i = 0; + + do { + total_bytes -= sl[i].length; + i++; + + } while (total_bytes > 0); + + return i; +} + +static void mv_enqueue_new_req(struct ablkcipher_request *req) +{ + int num_sgs; + + cpg->cur_req = req; + memset(&cpg->p, 0, sizeof(struct req_progress)); + + num_sgs = count_sgs(req->src, req->nbytes); + sg_miter_start(&cpg->p.src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG); + + num_sgs = count_sgs(req->dst, req->nbytes); + sg_miter_start(&cpg->p.dst_sg_it, req->dst, num_sgs, SG_MITER_TO_SG); + mv_process_current_q(1); +} + +static int queue_manag(void *data) +{ + cpg->eng_st = ENGINE_IDLE; + do { + struct ablkcipher_request *req; + struct crypto_async_request *async_req = NULL; + struct crypto_async_request *backlog; + + __set_current_state(TASK_INTERRUPTIBLE); + + if (cpg->eng_st == ENGINE_W_DEQUEUE) + dequeue_complete_req(); + + spin_lock_irq(&cpg->lock); + if (cpg->eng_st == ENGINE_IDLE) { + backlog = crypto_get_backlog(&cpg->queue); + async_req = crypto_dequeue_request(&cpg->queue); + if (async_req) { + BUG_ON(cpg->eng_st != ENGINE_IDLE); + cpg->eng_st = ENGINE_BUSY; + } + } + spin_unlock_irq(&cpg->lock); + + if (backlog) { + backlog->complete(backlog, -EINPROGRESS); + backlog = NULL; + } + + if (async_req) { + req = container_of(async_req, + struct ablkcipher_request, base); + mv_enqueue_new_req(req); + async_req = NULL; + } + + schedule(); + + } while (!kthread_should_stop()); + return 0; +} + +static int mv_handle_req(struct ablkcipher_request *req) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&cpg->lock, flags); + ret = ablkcipher_enqueue_request(&cpg->queue, req); + spin_unlock_irqrestore(&cpg->lock, flags); + wake_up_process(cpg->queue_th); + return ret; +} + +static int mv_enc_aes_ecb(struct ablkcipher_request *req) +{ + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + + req_ctx->op = COP_AES_ECB; + req_ctx->decrypt = 0; + + return mv_handle_req(req); +} + +static int mv_dec_aes_ecb(struct ablkcipher_request *req) +{ + struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + + req_ctx->op = COP_AES_ECB; + req_ctx->decrypt = 1; + + compute_aes_dec_key(ctx); + return mv_handle_req(req); +} + +static int mv_enc_aes_cbc(struct ablkcipher_request *req) +{ + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + + req_ctx->op = COP_AES_CBC; + req_ctx->decrypt = 0; + + return mv_handle_req(req); +} + +static int mv_dec_aes_cbc(struct ablkcipher_request *req) +{ + struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + + req_ctx->op = COP_AES_CBC; + req_ctx->decrypt = 1; + + compute_aes_dec_key(ctx); + return mv_handle_req(req); +} + +static int mv_cra_init(struct crypto_tfm *tfm) +{ + tfm->crt_ablkcipher.reqsize = sizeof(struct mv_req_ctx); + return 0; +} + +irqreturn_t crypto_int(int irq, void *priv) +{ + u32 val; + + val = readl(cpg->reg + SEC_ACCEL_INT_STATUS); + if (!(val & SEC_INT_ACCEL0_DONE)) + return IRQ_NONE; + + val &= ~SEC_INT_ACCEL0_DONE; + writel(val, cpg->reg + FPGA_INT_STATUS); + writel(val, cpg->reg + SEC_ACCEL_INT_STATUS); + BUG_ON(cpg->eng_st != ENGINE_BUSY); + cpg->eng_st = ENGINE_W_DEQUEUE; + wake_up_process(cpg->queue_th); + return IRQ_HANDLED; +} + +struct crypto_alg mv_aes_alg_ecb = { + .cra_name = "ecb(aes)", + .cra_driver_name = "mv-ecb-aes", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = 16, + .cra_ctxsize = sizeof(struct mv_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = mv_cra_init, + .cra_u = { + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = mv_setkey_aes, + .encrypt = mv_enc_aes_ecb, + .decrypt = mv_dec_aes_ecb, + }, + }, +}; + +struct crypto_alg mv_aes_alg_cbc = { + .cra_name = "cbc(aes)", + .cra_driver_name = "mv-cbc-aes", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mv_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = mv_cra_init, + .cra_u = { + .ablkcipher = { + .ivsize = AES_BLOCK_SIZE, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = mv_setkey_aes, + .encrypt = mv_enc_aes_cbc, + .decrypt = mv_dec_aes_cbc, + }, + }, +}; + +static int mv_probe(struct platform_device *pdev) +{ + struct crypto_priv *cp; + struct resource *res; + int irq; + int ret; + + if (cpg) { + printk(KERN_ERR "Second crypto dev?\n"); + return -EEXIST; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) + return -ENXIO; + + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + spin_lock_init(&cp->lock); + crypto_init_queue(&cp->queue, 50); + cp->reg = ioremap(res->start, res->end - res->start + 1); + if (!cp->reg) { + ret = -ENOMEM; + goto err; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!res) { + ret = -ENXIO; + goto err_unmap_reg; + } + cp->sram_size = res->end - res->start + 1; + cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE; + cp->sram = ioremap(res->start, cp->sram_size); + if (!cp->sram) { + ret = -ENOMEM; + goto err_unmap_reg; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0 || irq == NO_IRQ) { + ret = irq; + goto err_unmap_sram; + } + cp->irq = irq; + + platform_set_drvdata(pdev, cp); + cpg = cp; + + cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto"); + if (IS_ERR(cp->queue_th)) { + ret = PTR_ERR(cp->queue_th); + goto err_thread; + } + + ret = request_irq(irq, crypto_int, IRQF_DISABLED, dev_name(&pdev->dev), + cp); + if (ret) + goto err_unmap_sram; + + writel(SEC_INT_ACCEL0_DONE, cpg->reg + SEC_ACCEL_INT_MASK); + writel(SEC_CFG_STOP_DIG_ERR, cpg->reg + SEC_ACCEL_CFG); + + ret = crypto_register_alg(&mv_aes_alg_ecb); + if (ret) + goto err_reg; + + ret = crypto_register_alg(&mv_aes_alg_cbc); + if (ret) + goto err_unreg_ecb; + return 0; +err_unreg_ecb: + crypto_unregister_alg(&mv_aes_alg_ecb); +err_thread: + free_irq(irq, cp); +err_reg: + kthread_stop(cp->queue_th); +err_unmap_sram: + iounmap(cp->sram); +err_unmap_reg: + iounmap(cp->reg); +err: + kfree(cp); + cpg = NULL; + platform_set_drvdata(pdev, NULL); + return ret; +} + +static int mv_remove(struct platform_device *pdev) +{ + struct crypto_priv *cp = platform_get_drvdata(pdev); + + crypto_unregister_alg(&mv_aes_alg_ecb); + crypto_unregister_alg(&mv_aes_alg_cbc); + kthread_stop(cp->queue_th); + free_irq(cp->irq, cp); + memset(cp->sram, 0, cp->sram_size); + iounmap(cp->sram); + iounmap(cp->reg); + kfree(cp); + cpg = NULL; + return 0; +} + +static struct platform_driver marvell_crypto = { + .probe = mv_probe, + .remove = mv_remove, + .driver = { + .owner = THIS_MODULE, + .name = "mv_crypto", + }, +}; +MODULE_ALIAS("platform:mv_crypto"); + +static int __init mv_crypto_init(void) +{ + return platform_driver_register(&marvell_crypto); +} +module_init(mv_crypto_init); + +static void __exit mv_crypto_exit(void) +{ + platform_driver_unregister(&marvell_crypto); +} +module_exit(mv_crypto_exit); + +MODULE_AUTHOR("Sebastian Andrzej Siewior <sebastian@breakpoint.cc>"); +MODULE_DESCRIPTION("Support for Marvell's cryptographic engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/mv_cesa.h b/drivers/crypto/mv_cesa.h new file mode 100644 index 00000000000..c3e25d3bb17 --- /dev/null +++ b/drivers/crypto/mv_cesa.h @@ -0,0 +1,119 @@ +#ifndef __MV_CRYPTO_H__ + +#define DIGEST_INITIAL_VAL_A 0xdd00 +#define DES_CMD_REG 0xdd58 + +#define SEC_ACCEL_CMD 0xde00 +#define SEC_CMD_EN_SEC_ACCL0 (1 << 0) +#define SEC_CMD_EN_SEC_ACCL1 (1 << 1) +#define SEC_CMD_DISABLE_SEC (1 << 2) + +#define SEC_ACCEL_DESC_P0 0xde04 +#define SEC_DESC_P0_PTR(x) (x) + +#define SEC_ACCEL_DESC_P1 0xde14 +#define SEC_DESC_P1_PTR(x) (x) + +#define SEC_ACCEL_CFG 0xde08 +#define SEC_CFG_STOP_DIG_ERR (1 << 0) +#define SEC_CFG_CH0_W_IDMA (1 << 7) +#define SEC_CFG_CH1_W_IDMA (1 << 8) +#define SEC_CFG_ACT_CH0_IDMA (1 << 9) +#define SEC_CFG_ACT_CH1_IDMA (1 << 10) + +#define SEC_ACCEL_STATUS 0xde0c +#define SEC_ST_ACT_0 (1 << 0) +#define SEC_ST_ACT_1 (1 << 1) + +/* + * FPGA_INT_STATUS looks like a FPGA leftover and is documented only in Errata + * 4.12. It looks like that it was part of an IRQ-controller in FPGA and + * someone forgot to remove it while switching to the core and moving to + * SEC_ACCEL_INT_STATUS. + */ +#define FPGA_INT_STATUS 0xdd68 +#define SEC_ACCEL_INT_STATUS 0xde20 +#define SEC_INT_AUTH_DONE (1 << 0) +#define SEC_INT_DES_E_DONE (1 << 1) +#define SEC_INT_AES_E_DONE (1 << 2) +#define SEC_INT_AES_D_DONE (1 << 3) +#define SEC_INT_ENC_DONE (1 << 4) +#define SEC_INT_ACCEL0_DONE (1 << 5) +#define SEC_INT_ACCEL1_DONE (1 << 6) +#define SEC_INT_ACC0_IDMA_DONE (1 << 7) +#define SEC_INT_ACC1_IDMA_DONE (1 << 8) + +#define SEC_ACCEL_INT_MASK 0xde24 + +#define AES_KEY_LEN (8 * 4) + +struct sec_accel_config { + + u32 config; +#define CFG_OP_MAC_ONLY 0 +#define CFG_OP_CRYPT_ONLY 1 +#define CFG_OP_MAC_CRYPT 2 +#define CFG_OP_CRYPT_MAC 3 +#define CFG_MACM_MD5 (4 << 4) +#define CFG_MACM_SHA1 (5 << 4) +#define CFG_MACM_HMAC_MD5 (6 << 4) +#define CFG_MACM_HMAC_SHA1 (7 << 4) +#define CFG_ENCM_DES (1 << 8) +#define CFG_ENCM_3DES (2 << 8) +#define CFG_ENCM_AES (3 << 8) +#define CFG_DIR_ENC (0 << 12) +#define CFG_DIR_DEC (1 << 12) +#define CFG_ENC_MODE_ECB (0 << 16) +#define CFG_ENC_MODE_CBC (1 << 16) +#define CFG_3DES_EEE (0 << 20) +#define CFG_3DES_EDE (1 << 20) +#define CFG_AES_LEN_128 (0 << 24) +#define CFG_AES_LEN_192 (1 << 24) +#define CFG_AES_LEN_256 (2 << 24) + + u32 enc_p; +#define ENC_P_SRC(x) (x) +#define ENC_P_DST(x) ((x) << 16) + + u32 enc_len; +#define ENC_LEN(x) (x) + + u32 enc_key_p; +#define ENC_KEY_P(x) (x) + + u32 enc_iv; +#define ENC_IV_POINT(x) ((x) << 0) +#define ENC_IV_BUF_POINT(x) ((x) << 16) + + u32 mac_src_p; +#define MAC_SRC_DATA_P(x) (x) +#define MAC_SRC_TOTAL_LEN(x) ((x) << 16) + + u32 mac_digest; + u32 mac_iv; +}__attribute__ ((packed)); + /* + * /-----------\ 0 + * | ACCEL CFG | 4 * 8 + * |-----------| 0x20 + * | CRYPT KEY | 8 * 4 + * |-----------| 0x40 + * | IV IN | 4 * 4 + * |-----------| 0x40 (inplace) + * | IV BUF | 4 * 4 + * |-----------| 0x50 + * | DATA IN | 16 * x (max ->max_req_size) + * |-----------| 0x50 (inplace operation) + * | DATA OUT | 16 * x (max ->max_req_size) + * \-----------/ SRAM size + */ +#define SRAM_CONFIG 0x00 +#define SRAM_DATA_KEY_P 0x20 +#define SRAM_DATA_IV 0x40 +#define SRAM_DATA_IV_BUF 0x40 +#define SRAM_DATA_IN_START 0x50 +#define SRAM_DATA_OUT_START 0x50 + +#define SRAM_CFG_SPACE 0x50 + +#endif diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index a2c8e8514b6..76cb6b345e7 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -12,81 +12,43 @@ * */ -#include <crypto/algapi.h> +#include <crypto/internal/hash.h> #include <crypto/sha.h> #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> #include <linux/errno.h> -#include <linux/cryptohash.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/scatterlist.h> #include <asm/i387.h> #include "padlock.h" -#define SHA1_DEFAULT_FALLBACK "sha1-generic" -#define SHA256_DEFAULT_FALLBACK "sha256-generic" +struct padlock_sha_desc { + struct shash_desc fallback; +}; struct padlock_sha_ctx { - char *data; - size_t used; - int bypass; - void (*f_sha_padlock)(const char *in, char *out, int count); - struct hash_desc fallback; + struct crypto_shash *fallback; }; -static inline struct padlock_sha_ctx *ctx(struct crypto_tfm *tfm) -{ - return crypto_tfm_ctx(tfm); -} - -/* We'll need aligned address on the stack */ -#define NEAREST_ALIGNED(ptr) \ - ((void *)ALIGN((size_t)(ptr), PADLOCK_ALIGNMENT)) - -static struct crypto_alg sha1_alg, sha256_alg; - -static void padlock_sha_bypass(struct crypto_tfm *tfm) +static int padlock_sha_init(struct shash_desc *desc) { - if (ctx(tfm)->bypass) - return; + struct padlock_sha_desc *dctx = shash_desc_ctx(desc); + struct padlock_sha_ctx *ctx = crypto_shash_ctx(desc->tfm); - crypto_hash_init(&ctx(tfm)->fallback); - if (ctx(tfm)->data && ctx(tfm)->used) { - struct scatterlist sg; - - sg_init_one(&sg, ctx(tfm)->data, ctx(tfm)->used); - crypto_hash_update(&ctx(tfm)->fallback, &sg, sg.length); - } - - ctx(tfm)->used = 0; - ctx(tfm)->bypass = 1; -} - -static void padlock_sha_init(struct crypto_tfm *tfm) -{ - ctx(tfm)->used = 0; - ctx(tfm)->bypass = 0; + dctx->fallback.tfm = ctx->fallback; + dctx->fallback.flags = desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; + return crypto_shash_init(&dctx->fallback); } -static void padlock_sha_update(struct crypto_tfm *tfm, - const uint8_t *data, unsigned int length) +static int padlock_sha_update(struct shash_desc *desc, + const u8 *data, unsigned int length) { - /* Our buffer is always one page. */ - if (unlikely(!ctx(tfm)->bypass && - (ctx(tfm)->used + length > PAGE_SIZE))) - padlock_sha_bypass(tfm); - - if (unlikely(ctx(tfm)->bypass)) { - struct scatterlist sg; - sg_init_one(&sg, (uint8_t *)data, length); - crypto_hash_update(&ctx(tfm)->fallback, &sg, length); - return; - } + struct padlock_sha_desc *dctx = shash_desc_ctx(desc); - memcpy(ctx(tfm)->data + ctx(tfm)->used, data, length); - ctx(tfm)->used += length; + dctx->fallback.flags = desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; + return crypto_shash_update(&dctx->fallback, data, length); } static inline void padlock_output_block(uint32_t *src, @@ -96,165 +58,206 @@ static inline void padlock_output_block(uint32_t *src, *dst++ = swab32(*src++); } -static void padlock_do_sha1(const char *in, char *out, int count) +static int padlock_sha1_finup(struct shash_desc *desc, const u8 *in, + unsigned int count, u8 *out) { /* We can't store directly to *out as it may be unaligned. */ /* BTW Don't reduce the buffer size below 128 Bytes! * PadLock microcode needs it that big. */ - char buf[128+16]; - char *result = NEAREST_ALIGNED(buf); + char result[128] __attribute__ ((aligned(PADLOCK_ALIGNMENT))); + struct padlock_sha_desc *dctx = shash_desc_ctx(desc); + struct sha1_state state; + unsigned int space; + unsigned int leftover; int ts_state; + int err; + + dctx->fallback.flags = desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; + err = crypto_shash_export(&dctx->fallback, &state); + if (err) + goto out; + + if (state.count + count > ULONG_MAX) + return crypto_shash_finup(&dctx->fallback, in, count, out); + + leftover = ((state.count - 1) & (SHA1_BLOCK_SIZE - 1)) + 1; + space = SHA1_BLOCK_SIZE - leftover; + if (space) { + if (count > space) { + err = crypto_shash_update(&dctx->fallback, in, space) ?: + crypto_shash_export(&dctx->fallback, &state); + if (err) + goto out; + count -= space; + in += space; + } else { + memcpy(state.buffer + leftover, in, count); + in = state.buffer; + count += leftover; + state.count &= ~(SHA1_BLOCK_SIZE - 1); + } + } + + memcpy(result, &state.state, SHA1_DIGEST_SIZE); - ((uint32_t *)result)[0] = SHA1_H0; - ((uint32_t *)result)[1] = SHA1_H1; - ((uint32_t *)result)[2] = SHA1_H2; - ((uint32_t *)result)[3] = SHA1_H3; - ((uint32_t *)result)[4] = SHA1_H4; - /* prevent taking the spurious DNA fault with padlock. */ ts_state = irq_ts_save(); asm volatile (".byte 0xf3,0x0f,0xa6,0xc8" /* rep xsha1 */ - : "+S"(in), "+D"(result) - : "c"(count), "a"(0)); + : \ + : "c"((unsigned long)state.count + count), \ + "a"((unsigned long)state.count), \ + "S"(in), "D"(result)); irq_ts_restore(ts_state); padlock_output_block((uint32_t *)result, (uint32_t *)out, 5); + +out: + return err; } -static void padlock_do_sha256(const char *in, char *out, int count) +static int padlock_sha1_final(struct shash_desc *desc, u8 *out) +{ + u8 buf[4]; + + return padlock_sha1_finup(desc, buf, 0, out); +} + +static int padlock_sha256_finup(struct shash_desc *desc, const u8 *in, + unsigned int count, u8 *out) { /* We can't store directly to *out as it may be unaligned. */ /* BTW Don't reduce the buffer size below 128 Bytes! * PadLock microcode needs it that big. */ - char buf[128+16]; - char *result = NEAREST_ALIGNED(buf); + char result[128] __attribute__ ((aligned(PADLOCK_ALIGNMENT))); + struct padlock_sha_desc *dctx = shash_desc_ctx(desc); + struct sha256_state state; + unsigned int space; + unsigned int leftover; int ts_state; + int err; + + dctx->fallback.flags = desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; + err = crypto_shash_export(&dctx->fallback, &state); + if (err) + goto out; + + if (state.count + count > ULONG_MAX) + return crypto_shash_finup(&dctx->fallback, in, count, out); + + leftover = ((state.count - 1) & (SHA256_BLOCK_SIZE - 1)) + 1; + space = SHA256_BLOCK_SIZE - leftover; + if (space) { + if (count > space) { + err = crypto_shash_update(&dctx->fallback, in, space) ?: + crypto_shash_export(&dctx->fallback, &state); + if (err) + goto out; + count -= space; + in += space; + } else { + memcpy(state.buf + leftover, in, count); + in = state.buf; + count += leftover; + state.count &= ~(SHA1_BLOCK_SIZE - 1); + } + } - ((uint32_t *)result)[0] = SHA256_H0; - ((uint32_t *)result)[1] = SHA256_H1; - ((uint32_t *)result)[2] = SHA256_H2; - ((uint32_t *)result)[3] = SHA256_H3; - ((uint32_t *)result)[4] = SHA256_H4; - ((uint32_t *)result)[5] = SHA256_H5; - ((uint32_t *)result)[6] = SHA256_H6; - ((uint32_t *)result)[7] = SHA256_H7; + memcpy(result, &state.state, SHA256_DIGEST_SIZE); /* prevent taking the spurious DNA fault with padlock. */ ts_state = irq_ts_save(); asm volatile (".byte 0xf3,0x0f,0xa6,0xd0" /* rep xsha256 */ - : "+S"(in), "+D"(result) - : "c"(count), "a"(0)); + : \ + : "c"((unsigned long)state.count + count), \ + "a"((unsigned long)state.count), \ + "S"(in), "D"(result)); irq_ts_restore(ts_state); padlock_output_block((uint32_t *)result, (uint32_t *)out, 8); + +out: + return err; } -static void padlock_sha_final(struct crypto_tfm *tfm, uint8_t *out) +static int padlock_sha256_final(struct shash_desc *desc, u8 *out) { - if (unlikely(ctx(tfm)->bypass)) { - crypto_hash_final(&ctx(tfm)->fallback, out); - ctx(tfm)->bypass = 0; - return; - } + u8 buf[4]; - /* Pass the input buffer to PadLock microcode... */ - ctx(tfm)->f_sha_padlock(ctx(tfm)->data, out, ctx(tfm)->used); - - ctx(tfm)->used = 0; + return padlock_sha256_finup(desc, buf, 0, out); } static int padlock_cra_init(struct crypto_tfm *tfm) { + struct crypto_shash *hash = __crypto_shash_cast(tfm); const char *fallback_driver_name = tfm->__crt_alg->cra_name; - struct crypto_hash *fallback_tfm; - - /* For now we'll allocate one page. This - * could eventually be configurable one day. */ - ctx(tfm)->data = (char *)__get_free_page(GFP_KERNEL); - if (!ctx(tfm)->data) - return -ENOMEM; + struct padlock_sha_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_shash *fallback_tfm; + int err = -ENOMEM; /* Allocate a fallback and abort if it failed. */ - fallback_tfm = crypto_alloc_hash(fallback_driver_name, 0, - CRYPTO_ALG_ASYNC | - CRYPTO_ALG_NEED_FALLBACK); + fallback_tfm = crypto_alloc_shash(fallback_driver_name, 0, + CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback_tfm)) { printk(KERN_WARNING PFX "Fallback driver '%s' could not be loaded!\n", fallback_driver_name); - free_page((unsigned long)(ctx(tfm)->data)); - return PTR_ERR(fallback_tfm); + err = PTR_ERR(fallback_tfm); + goto out; } - ctx(tfm)->fallback.tfm = fallback_tfm; + ctx->fallback = fallback_tfm; + hash->descsize += crypto_shash_descsize(fallback_tfm); return 0; -} - -static int padlock_sha1_cra_init(struct crypto_tfm *tfm) -{ - ctx(tfm)->f_sha_padlock = padlock_do_sha1; - return padlock_cra_init(tfm); -} - -static int padlock_sha256_cra_init(struct crypto_tfm *tfm) -{ - ctx(tfm)->f_sha_padlock = padlock_do_sha256; - - return padlock_cra_init(tfm); +out: + return err; } static void padlock_cra_exit(struct crypto_tfm *tfm) { - if (ctx(tfm)->data) { - free_page((unsigned long)(ctx(tfm)->data)); - ctx(tfm)->data = NULL; - } + struct padlock_sha_ctx *ctx = crypto_tfm_ctx(tfm); - crypto_free_hash(ctx(tfm)->fallback.tfm); - ctx(tfm)->fallback.tfm = NULL; + crypto_free_shash(ctx->fallback); } -static struct crypto_alg sha1_alg = { - .cra_name = "sha1", - .cra_driver_name = "sha1-padlock", - .cra_priority = PADLOCK_CRA_PRIORITY, - .cra_flags = CRYPTO_ALG_TYPE_DIGEST | - CRYPTO_ALG_NEED_FALLBACK, - .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct padlock_sha_ctx), - .cra_module = THIS_MODULE, - .cra_list = LIST_HEAD_INIT(sha1_alg.cra_list), - .cra_init = padlock_sha1_cra_init, - .cra_exit = padlock_cra_exit, - .cra_u = { - .digest = { - .dia_digestsize = SHA1_DIGEST_SIZE, - .dia_init = padlock_sha_init, - .dia_update = padlock_sha_update, - .dia_final = padlock_sha_final, - } +static struct shash_alg sha1_alg = { + .digestsize = SHA1_DIGEST_SIZE, + .init = padlock_sha_init, + .update = padlock_sha_update, + .finup = padlock_sha1_finup, + .final = padlock_sha1_final, + .descsize = sizeof(struct padlock_sha_desc), + .base = { + .cra_name = "sha1", + .cra_driver_name = "sha1-padlock", + .cra_priority = PADLOCK_CRA_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct padlock_sha_ctx), + .cra_module = THIS_MODULE, + .cra_init = padlock_cra_init, + .cra_exit = padlock_cra_exit, } }; -static struct crypto_alg sha256_alg = { - .cra_name = "sha256", - .cra_driver_name = "sha256-padlock", - .cra_priority = PADLOCK_CRA_PRIORITY, - .cra_flags = CRYPTO_ALG_TYPE_DIGEST | - CRYPTO_ALG_NEED_FALLBACK, - .cra_blocksize = SHA256_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct padlock_sha_ctx), - .cra_module = THIS_MODULE, - .cra_list = LIST_HEAD_INIT(sha256_alg.cra_list), - .cra_init = padlock_sha256_cra_init, - .cra_exit = padlock_cra_exit, - .cra_u = { - .digest = { - .dia_digestsize = SHA256_DIGEST_SIZE, - .dia_init = padlock_sha_init, - .dia_update = padlock_sha_update, - .dia_final = padlock_sha_final, - } +static struct shash_alg sha256_alg = { + .digestsize = SHA256_DIGEST_SIZE, + .init = padlock_sha_init, + .update = padlock_sha_update, + .finup = padlock_sha256_finup, + .final = padlock_sha256_final, + .descsize = sizeof(struct padlock_sha_desc), + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-padlock", + .cra_priority = PADLOCK_CRA_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct padlock_sha_ctx), + .cra_module = THIS_MODULE, + .cra_init = padlock_cra_init, + .cra_exit = padlock_cra_exit, } }; @@ -272,11 +275,11 @@ static int __init padlock_init(void) return -ENODEV; } - rc = crypto_register_alg(&sha1_alg); + rc = crypto_register_shash(&sha1_alg); if (rc) goto out; - rc = crypto_register_alg(&sha256_alg); + rc = crypto_register_shash(&sha256_alg); if (rc) goto out_unreg1; @@ -285,7 +288,7 @@ static int __init padlock_init(void) return 0; out_unreg1: - crypto_unregister_alg(&sha1_alg); + crypto_unregister_shash(&sha1_alg); out: printk(KERN_ERR PFX "VIA PadLock SHA1/SHA256 initialization failed.\n"); return rc; @@ -293,8 +296,8 @@ out: static void __exit padlock_fini(void) { - crypto_unregister_alg(&sha1_alg); - crypto_unregister_alg(&sha256_alg); + crypto_unregister_shash(&sha1_alg); + crypto_unregister_shash(&sha256_alg); } module_init(padlock_init); diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index c70775fd3ce..c47ffe8a73e 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -86,6 +86,25 @@ struct talitos_request { void *context; }; +/* per-channel fifo management */ +struct talitos_channel { + /* request fifo */ + struct talitos_request *fifo; + + /* number of requests pending in channel h/w fifo */ + atomic_t submit_count ____cacheline_aligned; + + /* request submission (head) lock */ + spinlock_t head_lock ____cacheline_aligned; + /* index to next free descriptor request */ + int head; + + /* request release (tail) lock */ + spinlock_t tail_lock ____cacheline_aligned; + /* index to next in-progress/done descriptor request */ + int tail; +}; + struct talitos_private { struct device *dev; struct of_device *ofdev; @@ -101,15 +120,6 @@ struct talitos_private { /* SEC Compatibility info */ unsigned long features; - /* next channel to be assigned next incoming descriptor */ - atomic_t last_chan; - - /* per-channel number of requests pending in channel h/w fifo */ - atomic_t *submit_count; - - /* per-channel request fifo */ - struct talitos_request **fifo; - /* * length of the request fifo * fifo_len is chfifo_len rounded up to next power of 2 @@ -117,15 +127,10 @@ struct talitos_private { */ unsigned int fifo_len; - /* per-channel index to next free descriptor request */ - int *head; - - /* per-channel index to next in-progress/done descriptor request */ - int *tail; + struct talitos_channel *chan; - /* per-channel request submission (head) and release (tail) locks */ - spinlock_t *head_lock; - spinlock_t *tail_lock; + /* next channel to be assigned next incoming descriptor */ + atomic_t last_chan ____cacheline_aligned; /* request callback tasklet */ struct tasklet_struct done_task; @@ -141,6 +146,12 @@ struct talitos_private { #define TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT 0x00000001 #define TALITOS_FTR_HW_AUTH_CHECK 0x00000002 +static void to_talitos_ptr(struct talitos_ptr *talitos_ptr, dma_addr_t dma_addr) +{ + talitos_ptr->ptr = cpu_to_be32(lower_32_bits(dma_addr)); + talitos_ptr->eptr = cpu_to_be32(upper_32_bits(dma_addr)); +} + /* * map virtual single (contiguous) pointer to h/w descriptor pointer */ @@ -150,8 +161,10 @@ static void map_single_talitos_ptr(struct device *dev, unsigned char extent, enum dma_data_direction dir) { + dma_addr_t dma_addr = dma_map_single(dev, data, len, dir); + talitos_ptr->len = cpu_to_be16(len); - talitos_ptr->ptr = cpu_to_be32(dma_map_single(dev, data, len, dir)); + to_talitos_ptr(talitos_ptr, dma_addr); talitos_ptr->j_extent = extent; } @@ -182,9 +195,9 @@ static int reset_channel(struct device *dev, int ch) return -EIO; } - /* set done writeback and IRQ */ - setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_CDWE | - TALITOS_CCCR_LO_CDIE); + /* set 36-bit addressing, done writeback enable and done IRQ enable */ + setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_EAE | + TALITOS_CCCR_LO_CDWE | TALITOS_CCCR_LO_CDIE); /* and ICCR writeback, if available */ if (priv->features & TALITOS_FTR_HW_AUTH_CHECK) @@ -282,16 +295,16 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc, /* emulate SEC's round-robin channel fifo polling scheme */ ch = atomic_inc_return(&priv->last_chan) & (priv->num_channels - 1); - spin_lock_irqsave(&priv->head_lock[ch], flags); + spin_lock_irqsave(&priv->chan[ch].head_lock, flags); - if (!atomic_inc_not_zero(&priv->submit_count[ch])) { + if (!atomic_inc_not_zero(&priv->chan[ch].submit_count)) { /* h/w fifo is full */ - spin_unlock_irqrestore(&priv->head_lock[ch], flags); + spin_unlock_irqrestore(&priv->chan[ch].head_lock, flags); return -EAGAIN; } - head = priv->head[ch]; - request = &priv->fifo[ch][head]; + head = priv->chan[ch].head; + request = &priv->chan[ch].fifo[head]; /* map descriptor and save caller data */ request->dma_desc = dma_map_single(dev, desc, sizeof(*desc), @@ -300,16 +313,19 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc, request->context = context; /* increment fifo head */ - priv->head[ch] = (priv->head[ch] + 1) & (priv->fifo_len - 1); + priv->chan[ch].head = (priv->chan[ch].head + 1) & (priv->fifo_len - 1); smp_wmb(); request->desc = desc; /* GO! */ wmb(); - out_be32(priv->reg + TALITOS_FF_LO(ch), request->dma_desc); + out_be32(priv->reg + TALITOS_FF(ch), + cpu_to_be32(upper_32_bits(request->dma_desc))); + out_be32(priv->reg + TALITOS_FF_LO(ch), + cpu_to_be32(lower_32_bits(request->dma_desc))); - spin_unlock_irqrestore(&priv->head_lock[ch], flags); + spin_unlock_irqrestore(&priv->chan[ch].head_lock, flags); return -EINPROGRESS; } @@ -324,11 +340,11 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) unsigned long flags; int tail, status; - spin_lock_irqsave(&priv->tail_lock[ch], flags); + spin_lock_irqsave(&priv->chan[ch].tail_lock, flags); - tail = priv->tail[ch]; - while (priv->fifo[ch][tail].desc) { - request = &priv->fifo[ch][tail]; + tail = priv->chan[ch].tail; + while (priv->chan[ch].fifo[tail].desc) { + request = &priv->chan[ch].fifo[tail]; /* descriptors with their done bits set don't get the error */ rmb(); @@ -354,22 +370,22 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) request->desc = NULL; /* increment fifo tail */ - priv->tail[ch] = (tail + 1) & (priv->fifo_len - 1); + priv->chan[ch].tail = (tail + 1) & (priv->fifo_len - 1); - spin_unlock_irqrestore(&priv->tail_lock[ch], flags); + spin_unlock_irqrestore(&priv->chan[ch].tail_lock, flags); - atomic_dec(&priv->submit_count[ch]); + atomic_dec(&priv->chan[ch].submit_count); saved_req.callback(dev, saved_req.desc, saved_req.context, status); /* channel may resume processing in single desc error case */ if (error && !reset_ch && status == error) return; - spin_lock_irqsave(&priv->tail_lock[ch], flags); - tail = priv->tail[ch]; + spin_lock_irqsave(&priv->chan[ch].tail_lock, flags); + tail = priv->chan[ch].tail; } - spin_unlock_irqrestore(&priv->tail_lock[ch], flags); + spin_unlock_irqrestore(&priv->chan[ch].tail_lock, flags); } /* @@ -397,20 +413,20 @@ static void talitos_done(unsigned long data) static struct talitos_desc *current_desc(struct device *dev, int ch) { struct talitos_private *priv = dev_get_drvdata(dev); - int tail = priv->tail[ch]; + int tail = priv->chan[ch].tail; dma_addr_t cur_desc; cur_desc = in_be32(priv->reg + TALITOS_CDPR_LO(ch)); - while (priv->fifo[ch][tail].dma_desc != cur_desc) { + while (priv->chan[ch].fifo[tail].dma_desc != cur_desc) { tail = (tail + 1) & (priv->fifo_len - 1); - if (tail == priv->tail[ch]) { + if (tail == priv->chan[ch].tail) { dev_err(dev, "couldn't locate current descriptor\n"); return NULL; } } - return priv->fifo[ch][tail].desc; + return priv->chan[ch].fifo[tail].desc; } /* @@ -929,7 +945,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count, int n_sg = sg_count; while (n_sg--) { - link_tbl_ptr->ptr = cpu_to_be32(sg_dma_address(sg)); + to_talitos_ptr(link_tbl_ptr, sg_dma_address(sg)); link_tbl_ptr->len = cpu_to_be16(sg_dma_len(sg)); link_tbl_ptr->j_extent = 0; link_tbl_ptr++; @@ -970,7 +986,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, struct talitos_desc *desc = &edesc->desc; unsigned int cryptlen = areq->cryptlen; unsigned int authsize = ctx->authsize; - unsigned int ivsize; + unsigned int ivsize = crypto_aead_ivsize(aead); int sg_count, ret; int sg_link_tbl_len; @@ -978,11 +994,9 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key, 0, DMA_TO_DEVICE); /* hmac data */ - map_single_talitos_ptr(dev, &desc->ptr[1], sg_virt(areq->src) - - sg_virt(areq->assoc), sg_virt(areq->assoc), 0, - DMA_TO_DEVICE); + map_single_talitos_ptr(dev, &desc->ptr[1], areq->assoclen + ivsize, + sg_virt(areq->assoc), 0, DMA_TO_DEVICE); /* cipher iv */ - ivsize = crypto_aead_ivsize(aead); map_single_talitos_ptr(dev, &desc->ptr[2], ivsize, giv ?: areq->iv, 0, DMA_TO_DEVICE); @@ -1006,7 +1020,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, edesc->src_is_chained); if (sg_count == 1) { - desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src)); + to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src)); } else { sg_link_tbl_len = cryptlen; @@ -1017,14 +1031,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, &edesc->link_tbl[0]); if (sg_count > 1) { desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP; - desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl); + to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl); dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); } else { /* Only one segment now, so no link tbl needed */ - desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq-> - src)); + to_talitos_ptr(&desc->ptr[4], + sg_dma_address(areq->src)); } } @@ -1039,14 +1053,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, edesc->dst_is_chained); if (sg_count == 1) { - desc->ptr[5].ptr = cpu_to_be32(sg_dma_address(areq->dst)); + to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst)); } else { struct talitos_ptr *link_tbl_ptr = &edesc->link_tbl[edesc->src_nents + 1]; - desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *) - edesc->dma_link_tbl + - edesc->src_nents + 1); + to_talitos_ptr(&desc->ptr[5], edesc->dma_link_tbl + + (edesc->src_nents + 1) * + sizeof(struct talitos_ptr)); sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen, link_tbl_ptr); @@ -1059,11 +1073,9 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, link_tbl_ptr->len = cpu_to_be16(authsize); /* icv data follows link tables */ - link_tbl_ptr->ptr = cpu_to_be32((struct talitos_ptr *) - edesc->dma_link_tbl + - edesc->src_nents + - edesc->dst_nents + 2); - + to_talitos_ptr(link_tbl_ptr, edesc->dma_link_tbl + + (edesc->src_nents + edesc->dst_nents + 2) * + sizeof(struct talitos_ptr)); desc->ptr[5].j_extent |= DESC_PTR_LNKTBL_JUMP; dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); @@ -1338,7 +1350,7 @@ static int common_nonsnoop(struct talitos_edesc *edesc, /* first DWORD empty */ desc->ptr[0].len = 0; - desc->ptr[0].ptr = 0; + to_talitos_ptr(&desc->ptr[0], 0); desc->ptr[0].j_extent = 0; /* cipher iv */ @@ -1362,20 +1374,20 @@ static int common_nonsnoop(struct talitos_edesc *edesc, edesc->src_is_chained); if (sg_count == 1) { - desc->ptr[3].ptr = cpu_to_be32(sg_dma_address(areq->src)); + to_talitos_ptr(&desc->ptr[3], sg_dma_address(areq->src)); } else { sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen, &edesc->link_tbl[0]); if (sg_count > 1) { + to_talitos_ptr(&desc->ptr[3], edesc->dma_link_tbl); desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP; - desc->ptr[3].ptr = cpu_to_be32(edesc->dma_link_tbl); dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); } else { /* Only one segment now, so no link tbl needed */ - desc->ptr[3].ptr = cpu_to_be32(sg_dma_address(areq-> - src)); + to_talitos_ptr(&desc->ptr[3], + sg_dma_address(areq->src)); } } @@ -1390,15 +1402,15 @@ static int common_nonsnoop(struct talitos_edesc *edesc, edesc->dst_is_chained); if (sg_count == 1) { - desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->dst)); + to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->dst)); } else { struct talitos_ptr *link_tbl_ptr = &edesc->link_tbl[edesc->src_nents + 1]; + to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl + + (edesc->src_nents + 1) * + sizeof(struct talitos_ptr)); desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP; - desc->ptr[4].ptr = cpu_to_be32((struct talitos_ptr *) - edesc->dma_link_tbl + - edesc->src_nents + 1); sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen, link_tbl_ptr); dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl, @@ -1411,7 +1423,7 @@ static int common_nonsnoop(struct talitos_edesc *edesc, /* last DWORD empty */ desc->ptr[6].len = 0; - desc->ptr[6].ptr = 0; + to_talitos_ptr(&desc->ptr[6], 0); desc->ptr[6].j_extent = 0; ret = talitos_submit(dev, desc, callback, areq); @@ -1742,17 +1754,11 @@ static int talitos_remove(struct of_device *ofdev) if (hw_supports(dev, DESC_HDR_SEL0_RNG)) talitos_unregister_rng(dev); - kfree(priv->submit_count); - kfree(priv->tail); - kfree(priv->head); - - if (priv->fifo) - for (i = 0; i < priv->num_channels; i++) - kfree(priv->fifo[i]); + for (i = 0; i < priv->num_channels; i++) + if (priv->chan[i].fifo) + kfree(priv->chan[i].fifo); - kfree(priv->fifo); - kfree(priv->head_lock); - kfree(priv->tail_lock); + kfree(priv->chan); if (priv->irq != NO_IRQ) { free_irq(priv->irq, dev); @@ -1872,58 +1878,36 @@ static int talitos_probe(struct of_device *ofdev, if (of_device_is_compatible(np, "fsl,sec2.1")) priv->features |= TALITOS_FTR_HW_AUTH_CHECK; - priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels, - GFP_KERNEL); - priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels, - GFP_KERNEL); - if (!priv->head_lock || !priv->tail_lock) { - dev_err(dev, "failed to allocate fifo locks\n"); + priv->chan = kzalloc(sizeof(struct talitos_channel) * + priv->num_channels, GFP_KERNEL); + if (!priv->chan) { + dev_err(dev, "failed to allocate channel management space\n"); err = -ENOMEM; goto err_out; } for (i = 0; i < priv->num_channels; i++) { - spin_lock_init(&priv->head_lock[i]); - spin_lock_init(&priv->tail_lock[i]); - } - - priv->fifo = kmalloc(sizeof(struct talitos_request *) * - priv->num_channels, GFP_KERNEL); - if (!priv->fifo) { - dev_err(dev, "failed to allocate request fifo\n"); - err = -ENOMEM; - goto err_out; + spin_lock_init(&priv->chan[i].head_lock); + spin_lock_init(&priv->chan[i].tail_lock); } priv->fifo_len = roundup_pow_of_two(priv->chfifo_len); for (i = 0; i < priv->num_channels; i++) { - priv->fifo[i] = kzalloc(sizeof(struct talitos_request) * - priv->fifo_len, GFP_KERNEL); - if (!priv->fifo[i]) { + priv->chan[i].fifo = kzalloc(sizeof(struct talitos_request) * + priv->fifo_len, GFP_KERNEL); + if (!priv->chan[i].fifo) { dev_err(dev, "failed to allocate request fifo %d\n", i); err = -ENOMEM; goto err_out; } } - priv->submit_count = kmalloc(sizeof(atomic_t) * priv->num_channels, - GFP_KERNEL); - if (!priv->submit_count) { - dev_err(dev, "failed to allocate fifo submit count space\n"); - err = -ENOMEM; - goto err_out; - } for (i = 0; i < priv->num_channels; i++) - atomic_set(&priv->submit_count[i], -(priv->chfifo_len - 1)); + atomic_set(&priv->chan[i].submit_count, + -(priv->chfifo_len - 1)); - priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL); - priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL); - if (!priv->head || !priv->tail) { - dev_err(dev, "failed to allocate request index space\n"); - err = -ENOMEM; - goto err_out; - } + dma_set_mask(dev, DMA_BIT_MASK(36)); /* reset and initialize the h/w */ err = init_device(dev); diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 575981f0cfd..ff5a1450e14 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -57,6 +57,7 @@ #define TALITOS_CCCR_RESET 0x1 /* channel reset */ #define TALITOS_CCCR_LO(ch) (ch * TALITOS_CH_STRIDE + 0x110c) #define TALITOS_CCCR_LO_IWSE 0x80 /* chan. ICCR writeback enab. */ +#define TALITOS_CCCR_LO_EAE 0x20 /* extended address enable */ #define TALITOS_CCCR_LO_CDWE 0x10 /* chan. done writeback enab. */ #define TALITOS_CCCR_LO_NT 0x4 /* notification type */ #define TALITOS_CCCR_LO_CDIE 0x2 /* channel done IRQ enable */ diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 110e731f557..1c0b504a42f 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -196,7 +196,7 @@ static int manage_bandwidth(struct fw_card *card, int irm_id, int generation, switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, - data, sizeof(data))) { + data, 8)) { case RCODE_GENERATION: /* A generation change frees all bandwidth. */ return allocate ? -EAGAIN : bandwidth; @@ -233,7 +233,7 @@ static int manage_channel(struct fw_card *card, int irm_id, int generation, data[1] = old ^ c; switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, - offset, data, sizeof(data))) { + offset, data, 8)) { case RCODE_GENERATION: /* A generation change frees all channels. */ return allocate ? -EAGAIN : i; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index ecddd11b797..76b321bb73f 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -34,6 +34,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/pci.h> +#include <linux/pci_ids.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -2372,6 +2373,9 @@ static void ohci_pmac_off(struct pci_dev *dev) #define ohci_pmac_off(dev) #endif /* CONFIG_PPC_PMAC */ +#define PCI_VENDOR_ID_AGERE PCI_VENDOR_ID_ATT +#define PCI_DEVICE_ID_AGERE_FW643 0x5901 + static int __devinit pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { @@ -2422,6 +2426,16 @@ static int __devinit pci_probe(struct pci_dev *dev, version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; ohci->use_dualbuffer = version >= OHCI_VERSION_1_1; + /* dual-buffer mode is broken if more than one IR context is active */ + if (dev->vendor == PCI_VENDOR_ID_AGERE && + dev->device == PCI_DEVICE_ID_AGERE_FW643) + ohci->use_dualbuffer = false; + + /* dual-buffer mode is broken */ + if (dev->vendor == PCI_VENDOR_ID_RICOH && + dev->device == PCI_DEVICE_ID_RICOH_R5C832) + ohci->use_dualbuffer = false; + /* x86-32 currently doesn't use highmem for dma_alloc_coherent */ #if !defined(CONFIG_X86_32) /* dual-buffer mode is broken with descriptor addresses above 2G */ diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 8d51568ee14..e5df822a813 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -456,12 +456,12 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request, } spin_unlock_irqrestore(&card->lock, flags); - if (&orb->link != &lu->orb_list) + if (&orb->link != &lu->orb_list) { orb->callback(orb, &status); - else + kref_put(&orb->kref, free_orb); + } else { fw_error("status write for unknown orb\n"); - - kref_put(&orb->kref, free_orb); + } fw_send_response(card, request, RCODE_COMPLETE); } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0c07a755b3a..80e5ba490dc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2267,8 +2267,6 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) fence_list) { old_obj = old_obj_priv->obj; - reg = &dev_priv->fence_regs[old_obj_priv->fence_reg]; - if (old_obj_priv->pin_count) continue; @@ -2290,8 +2288,11 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) */ i915_gem_object_flush_gpu_write_domain(old_obj); ret = i915_gem_object_wait_rendering(old_obj); - if (ret != 0) + if (ret != 0) { + drm_gem_object_unreference(old_obj); return ret; + } + break; } @@ -2299,10 +2300,14 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) * Zap this virtual mapping so we can set up a fence again * for this object next time we need it. */ - i915_gem_release_mmap(reg->obj); + i915_gem_release_mmap(old_obj); + i = old_obj_priv->fence_reg; + reg = &dev_priv->fence_regs[i]; + old_obj_priv->fence_reg = I915_FENCE_REG_NONE; list_del_init(&old_obj_priv->fence_list); + drm_gem_object_unreference(old_obj); } @@ -4227,15 +4232,11 @@ int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - int ret; - if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - ret = i915_gem_idle(dev); drm_irq_uninstall(dev); - - return ret; + return i915_gem_idle(dev); } void diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3fadb535885..748ed50c55c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2005,7 +2005,21 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock, return; } -const static int latency_ns = 3000; /* default for non-igd platforms */ +/* + * Latency for FIFO fetches is dependent on several factors: + * - memory configuration (speed, channels) + * - chipset + * - current MCH state + * It can be fairly high in some situations, so here we assume a fairly + * pessimal value. It's a tradeoff between extra memory fetches (if we + * set this value too high, the FIFO will fetch frequently to stay full) + * and power consumption (set it too low to save power and we might see + * FIFO underruns and display "flicker"). + * + * A value of 5us seems to be a good balance; safe for very low end + * platforms but not overly aggressive on lower latency configs. + */ +const static int latency_ns = 5000; static int intel_get_fifo_size(struct drm_device *dev, int plane) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f2afc4af4bc..2b914d73207 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1263,7 +1263,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) if (IS_eDP(intel_output)) { intel_output->crtc_mask = (1 << 1); - intel_output->clone_mask = (1 << INTEL_OUTPUT_EDP); + intel_output->clone_mask = (1 << INTEL_EDP_CLONE_BIT); } else intel_output->crtc_mask = (1 << 0) | (1 << 1); connector->interlace_allowed = true; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25aa6facc12..26a6227c15f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -74,6 +74,7 @@ #define INTEL_LVDS_CLONE_BIT 14 #define INTEL_DVO_TMDS_CLONE_BIT 15 #define INTEL_DVO_LVDS_CLONE_BIT 16 +#define INTEL_EDP_CLONE_BIT 17 #define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_LVDS 1 diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 2fbe13a0de8..5b1c9e9fdba 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1730,6 +1730,7 @@ intel_tv_init(struct drm_device *dev) drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); tv_priv = (struct intel_tv_priv *)(intel_output + 1); intel_output->type = INTEL_OUTPUT_TVOUT; + intel_output->crtc_mask = (1 << 0) | (1 << 1); intel_output->clone_mask = (1 << INTEL_TV_CLONE_BIT); intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1)); intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT); diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 053f4ec397f..051bca6e3a4 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -995,7 +995,7 @@ static const unsigned r300_reg_safe_bm[159] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFF80FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0003FC01, 0xFFFFFFF8, 0xFE800B19, + 0x0003FC01, 0xFFFFFCF8, 0xFF800B19, }; static int r300_packet0_check(struct radeon_cs_parser *p, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7ca6c13569b..93d8f888930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -266,6 +266,7 @@ static struct radeon_asic rs400_asic = { /* * rs600. */ +int rs600_init(struct radeon_device *dev); void rs600_errata(struct radeon_device *rdev); void rs600_vram_info(struct radeon_device *rdev); int rs600_mc_init(struct radeon_device *rdev); @@ -281,7 +282,7 @@ uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs600_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs600_asic = { - .init = &r300_init, + .init = &rs600_init, .errata = &rs600_errata, .vram_info = &rs600_vram_info, .gpu_reset = &r300_gpu_reset, @@ -316,7 +317,6 @@ static struct radeon_asic rs600_asic = { /* * rs690,rs740 */ -int rs690_init(struct radeon_device *rdev); void rs690_errata(struct radeon_device *rdev); void rs690_vram_info(struct radeon_device *rdev); int rs690_mc_init(struct radeon_device *rdev); @@ -325,7 +325,7 @@ uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs690_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs690_asic = { - .init = &rs690_init, + .init = &rs600_init, .errata = &rs690_errata, .vram_info = &rs690_vram_info, .gpu_reset = &r300_gpu_reset, diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 7e8ce983a90..02fd11aad6a 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -409,3 +409,68 @@ void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) ((reg) & RS600_MC_ADDR_MASK)); WREG32(RS600_MC_DATA, v); } + +static const unsigned rs600_reg_safe_bm[219] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFCE, 0xF00EBFFF, 0x007C0000, + 0xF0000078, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFC78, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0x38FF8F50, 0xFFF88082, 0xF000000C, 0xFAE009FF, + 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x0000C100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFF80FFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003FC01, 0xFFFFFCF8, 0xFF800B19, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +int rs600_init(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = rs600_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rs600_reg_safe_bm); + return 0; +} diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index bc6b7c5339b..879882533e4 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -653,67 +653,3 @@ void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) WREG32(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); } -static const unsigned rs690_reg_safe_bm[219] = { - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0x17FF1FFF,0xFFFFFFFC,0xFFFFFFFF,0xFF30FFBF, - 0xFFFFFFF8,0xC3E6FFFF,0xFFFFF6DF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFF03F, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFEFCE,0xF00EBFFF,0x007C0000, - 0xF0000078,0xFF000009,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFF7FF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFC78,0xFFFFFFFF,0xFFFFFFFE,0xFFFFFFFF, - 0x38FF8F50,0xFFF88082,0xF000000C,0xFAE009FF, - 0x0000FFFF,0xFFFFFFFF,0xFFFFFFFF,0x00000000, - 0x00000000,0x0000C100,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0xFFFF0000,0xFFFFFFFF,0xFF80FFFF, - 0x00000000,0x00000000,0x00000000,0x00000000, - 0x0003FC01,0xFFFFFFF8,0xFE800B19,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, - 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, -}; - -int rs690_init(struct radeon_device *rdev) -{ - rdev->config.r300.reg_safe_bm = rs690_reg_safe_bm; - rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rs690_reg_safe_bm); - return 0; -} diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 31a7f668ae5..0566fb67e46 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -508,7 +508,7 @@ static const unsigned r500_reg_safe_bm[219] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF80FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0003FC01, 0x3FFFFCF8, 0xFE800B19, 0xFFFFFFFF, + 0x0003FC01, 0x3FFFFCF8, 0xFF800B19, 0xFFDFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c index 527908ff298..063b933d864 100644 --- a/drivers/ide/ide-cs.c +++ b/drivers/ide/ide-cs.c @@ -408,6 +408,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591), PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("CNF ", "CD-ROM", 0x46d7db81, 0x66536591), PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591), PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4), PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde), diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 8f9509e1ebf..55d093a36ae 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -362,6 +362,7 @@ static void destroy_cm_id(struct iw_cm_id *cm_id) * In either case, must tell the provider to reject. */ cm_id_priv->state = IW_CM_STATE_DESTROYING; + cm_id->device->iwcm->reject(cm_id, NULL, 0); break; case IW_CM_STATE_CONN_SENT: case IW_CM_STATE_DESTROYING: diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index de922a04ca2..7522008fda8 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2,6 +2,7 @@ * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved. * Copyright (c) 2005 Intel Corporation. All rights reserved. * Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2009 HNR Consulting. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -45,14 +46,21 @@ MODULE_DESCRIPTION("kernel IB MAD API"); MODULE_AUTHOR("Hal Rosenstock"); MODULE_AUTHOR("Sean Hefty"); +int mad_sendq_size = IB_MAD_QP_SEND_SIZE; +int mad_recvq_size = IB_MAD_QP_RECV_SIZE; + +module_param_named(send_queue_size, mad_sendq_size, int, 0444); +MODULE_PARM_DESC(send_queue_size, "Size of send queue in number of work requests"); +module_param_named(recv_queue_size, mad_recvq_size, int, 0444); +MODULE_PARM_DESC(recv_queue_size, "Size of receive queue in number of work requests"); + static struct kmem_cache *ib_mad_cache; static struct list_head ib_mad_port_list; static u32 ib_mad_client_id = 0; /* Port list lock */ -static spinlock_t ib_mad_port_list_lock; - +static DEFINE_SPINLOCK(ib_mad_port_list_lock); /* Forward declarations */ static int method_in_use(struct ib_mad_mgmt_method_table **method, @@ -1974,7 +1982,7 @@ static void adjust_timeout(struct ib_mad_agent_private *mad_agent_priv) unsigned long delay; if (list_empty(&mad_agent_priv->wait_list)) { - cancel_delayed_work(&mad_agent_priv->timed_work); + __cancel_delayed_work(&mad_agent_priv->timed_work); } else { mad_send_wr = list_entry(mad_agent_priv->wait_list.next, struct ib_mad_send_wr_private, @@ -1983,7 +1991,7 @@ static void adjust_timeout(struct ib_mad_agent_private *mad_agent_priv) if (time_after(mad_agent_priv->timeout, mad_send_wr->timeout)) { mad_agent_priv->timeout = mad_send_wr->timeout; - cancel_delayed_work(&mad_agent_priv->timed_work); + __cancel_delayed_work(&mad_agent_priv->timed_work); delay = mad_send_wr->timeout - jiffies; if ((long)delay <= 0) delay = 1; @@ -2023,7 +2031,7 @@ static void wait_for_response(struct ib_mad_send_wr_private *mad_send_wr) /* Reschedule a work item if we have a shorter timeout */ if (mad_agent_priv->wait_list.next == &mad_send_wr->agent_list) { - cancel_delayed_work(&mad_agent_priv->timed_work); + __cancel_delayed_work(&mad_agent_priv->timed_work); queue_delayed_work(mad_agent_priv->qp_info->port_priv->wq, &mad_agent_priv->timed_work, delay); } @@ -2736,8 +2744,8 @@ static int create_mad_qp(struct ib_mad_qp_info *qp_info, qp_init_attr.send_cq = qp_info->port_priv->cq; qp_init_attr.recv_cq = qp_info->port_priv->cq; qp_init_attr.sq_sig_type = IB_SIGNAL_ALL_WR; - qp_init_attr.cap.max_send_wr = IB_MAD_QP_SEND_SIZE; - qp_init_attr.cap.max_recv_wr = IB_MAD_QP_RECV_SIZE; + qp_init_attr.cap.max_send_wr = mad_sendq_size; + qp_init_attr.cap.max_recv_wr = mad_recvq_size; qp_init_attr.cap.max_send_sge = IB_MAD_SEND_REQ_MAX_SG; qp_init_attr.cap.max_recv_sge = IB_MAD_RECV_REQ_MAX_SG; qp_init_attr.qp_type = qp_type; @@ -2752,8 +2760,8 @@ static int create_mad_qp(struct ib_mad_qp_info *qp_info, goto error; } /* Use minimum queue sizes unless the CQ is resized */ - qp_info->send_queue.max_active = IB_MAD_QP_SEND_SIZE; - qp_info->recv_queue.max_active = IB_MAD_QP_RECV_SIZE; + qp_info->send_queue.max_active = mad_sendq_size; + qp_info->recv_queue.max_active = mad_recvq_size; return 0; error: @@ -2792,7 +2800,7 @@ static int ib_mad_port_open(struct ib_device *device, init_mad_qp(port_priv, &port_priv->qp_info[0]); init_mad_qp(port_priv, &port_priv->qp_info[1]); - cq_size = (IB_MAD_QP_SEND_SIZE + IB_MAD_QP_RECV_SIZE) * 2; + cq_size = (mad_sendq_size + mad_recvq_size) * 2; port_priv->cq = ib_create_cq(port_priv->device, ib_mad_thread_completion_handler, NULL, port_priv, cq_size, 0); @@ -2984,7 +2992,11 @@ static int __init ib_mad_init_module(void) { int ret; - spin_lock_init(&ib_mad_port_list_lock); + mad_recvq_size = min(mad_recvq_size, IB_MAD_QP_MAX_SIZE); + mad_recvq_size = max(mad_recvq_size, IB_MAD_QP_MIN_SIZE); + + mad_sendq_size = min(mad_sendq_size, IB_MAD_QP_MAX_SIZE); + mad_sendq_size = max(mad_sendq_size, IB_MAD_QP_MIN_SIZE); ib_mad_cache = kmem_cache_create("ib_mad", sizeof(struct ib_mad_private), @@ -3021,4 +3033,3 @@ static void __exit ib_mad_cleanup_module(void) module_init(ib_mad_init_module); module_exit(ib_mad_cleanup_module); - diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h index 05ce331733b..9430ab4969c 100644 --- a/drivers/infiniband/core/mad_priv.h +++ b/drivers/infiniband/core/mad_priv.h @@ -2,6 +2,7 @@ * Copyright (c) 2004, 2005, Voltaire, Inc. All rights reserved. * Copyright (c) 2005 Intel Corporation. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2009 HNR Consulting. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -49,6 +50,8 @@ /* QP and CQ parameters */ #define IB_MAD_QP_SEND_SIZE 128 #define IB_MAD_QP_RECV_SIZE 512 +#define IB_MAD_QP_MIN_SIZE 64 +#define IB_MAD_QP_MAX_SIZE 8192 #define IB_MAD_SEND_REQ_MAX_SG 2 #define IB_MAD_RECV_REQ_MAX_SG 1 diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index 107f170c57c..8d82ba17135 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -106,6 +106,8 @@ struct mcast_group { struct ib_sa_query *query; int query_id; u16 pkey_index; + u8 leave_state; + int retries; }; struct mcast_member { @@ -350,6 +352,7 @@ static int send_leave(struct mcast_group *group, u8 leave_state) rec = group->rec; rec.join_state = leave_state; + group->leave_state = leave_state; ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device, port->port_num, IB_SA_METHOD_DELETE, &rec, @@ -542,7 +545,11 @@ static void leave_handler(int status, struct ib_sa_mcmember_rec *rec, { struct mcast_group *group = context; - mcast_work_handler(&group->work); + if (status && group->retries > 0 && + !send_leave(group, group->leave_state)) + group->retries--; + else + mcast_work_handler(&group->work); } static struct mcast_group *acquire_group(struct mcast_port *port, @@ -565,6 +572,7 @@ static struct mcast_group *acquire_group(struct mcast_port *port, if (!group) return NULL; + group->retries = 3; group->port = port; group->rec.mgid = *mgid; group->pkey_index = MCAST_INVALID_PKEY_INDEX; diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 1865049e80f..82543716d59 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -109,10 +109,10 @@ static struct ib_client sa_client = { .remove = ib_sa_remove_one }; -static spinlock_t idr_lock; +static DEFINE_SPINLOCK(idr_lock); static DEFINE_IDR(query_idr); -static spinlock_t tid_lock; +static DEFINE_SPINLOCK(tid_lock); static u32 tid; #define PATH_REC_FIELD(field) \ @@ -1077,9 +1077,6 @@ static int __init ib_sa_init(void) { int ret; - spin_lock_init(&idr_lock); - spin_lock_init(&tid_lock); - get_random_bytes(&tid, sizeof tid); ret = ib_register_client(&sa_client); diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c index 87236753bce..5855e4405d9 100644 --- a/drivers/infiniband/core/smi.c +++ b/drivers/infiniband/core/smi.c @@ -52,6 +52,10 @@ enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp, hop_cnt = smp->hop_cnt; /* See section 14.2.2.2, Vol 1 IB spec */ + /* C14-6 -- valid hop_cnt values are from 0 to 63 */ + if (hop_cnt >= IB_SMP_MAX_PATH_HOPS) + return IB_SMI_DISCARD; + if (!ib_get_smp_direction(smp)) { /* C14-9:1 */ if (hop_cnt && hop_ptr == 0) { @@ -133,6 +137,10 @@ enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type, hop_cnt = smp->hop_cnt; /* See section 14.2.2.2, Vol 1 IB spec */ + /* C14-6 -- valid hop_cnt values are from 0 to 63 */ + if (hop_cnt >= IB_SMP_MAX_PATH_HOPS) + return IB_SMI_DISCARD; + if (!ib_get_smp_direction(smp)) { /* C14-9:1 -- sender should have incremented hop_ptr */ if (hop_cnt && hop_ptr == 0) diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index eb36a81dd09..d3fff9e008a 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -73,7 +73,7 @@ DEFINE_IDR(ib_uverbs_cq_idr); DEFINE_IDR(ib_uverbs_qp_idr); DEFINE_IDR(ib_uverbs_srq_idr); -static spinlock_t map_lock; +static DEFINE_SPINLOCK(map_lock); static struct ib_uverbs_device *dev_table[IB_UVERBS_MAX_DEVICES]; static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES); @@ -584,14 +584,16 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, if (hdr.command < 0 || hdr.command >= ARRAY_SIZE(uverbs_cmd_table) || - !uverbs_cmd_table[hdr.command] || - !(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command))) + !uverbs_cmd_table[hdr.command]) return -EINVAL; if (!file->ucontext && hdr.command != IB_USER_VERBS_CMD_GET_CONTEXT) return -EINVAL; + if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command))) + return -ENOSYS; + return uverbs_cmd_table[hdr.command](file, buf + sizeof hdr, hdr.in_words * 4, hdr.out_words * 4); } @@ -836,8 +838,6 @@ static int __init ib_uverbs_init(void) { int ret; - spin_lock_init(&map_lock); - ret = register_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES, "infiniband_verbs"); if (ret) { diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 0cfbb6d2f76..8250740c94b 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -86,11 +86,7 @@ MODULE_DEVICE_TABLE(pci, c2_pci_table); static void c2_print_macaddr(struct net_device *netdev) { - pr_debug("%s: MAC %02X:%02X:%02X:%02X:%02X:%02X, " - "IRQ %u\n", netdev->name, - netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], - netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5], - netdev->irq); + pr_debug("%s: MAC %pM, IRQ %u\n", netdev->name, netdev->dev_addr, netdev->irq); } static void c2_set_rxbufsize(struct c2_port *c2_port) diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index f1948fad85d..ad723bd8bf4 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -780,11 +780,11 @@ int c2_register_device(struct c2_dev *dev) /* Register pseudo network device */ dev->pseudo_netdev = c2_pseudo_netdev_init(dev); if (!dev->pseudo_netdev) - goto out3; + goto out; ret = register_netdev(dev->pseudo_netdev); if (ret) - goto out2; + goto out_free_netdev; pr_debug("%s:%u\n", __func__, __LINE__); strlcpy(dev->ibdev.name, "amso%d", IB_DEVICE_NAME_MAX); @@ -851,6 +851,10 @@ int c2_register_device(struct c2_dev *dev) dev->ibdev.post_recv = c2_post_receive; dev->ibdev.iwcm = kmalloc(sizeof(*dev->ibdev.iwcm), GFP_KERNEL); + if (dev->ibdev.iwcm == NULL) { + ret = -ENOMEM; + goto out_unregister_netdev; + } dev->ibdev.iwcm->add_ref = c2_add_ref; dev->ibdev.iwcm->rem_ref = c2_rem_ref; dev->ibdev.iwcm->get_qp = c2_get_qp; @@ -862,23 +866,25 @@ int c2_register_device(struct c2_dev *dev) ret = ib_register_device(&dev->ibdev); if (ret) - goto out1; + goto out_free_iwcm; for (i = 0; i < ARRAY_SIZE(c2_dev_attributes); ++i) { ret = device_create_file(&dev->ibdev.dev, c2_dev_attributes[i]); if (ret) - goto out0; + goto out_unregister_ibdev; } - goto out3; + goto out; -out0: +out_unregister_ibdev: ib_unregister_device(&dev->ibdev); -out1: +out_free_iwcm: + kfree(dev->ibdev.iwcm); +out_unregister_netdev: unregister_netdev(dev->pseudo_netdev); -out2: +out_free_netdev: free_netdev(dev->pseudo_netdev); -out3: +out: pr_debug("%s:%u ret=%d\n", __func__, __LINE__, ret); return ret; } diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 62f9cf2f94e..72ed3396b72 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -852,7 +852,9 @@ int cxio_rdma_init(struct cxio_rdev *rdev_p, struct t3_rdma_init_attr *attr) wqe->qpcaps = attr->qpcaps; wqe->ulpdu_size = cpu_to_be16(attr->tcp_emss); wqe->rqe_count = cpu_to_be16(attr->rqe_count); - wqe->flags_rtr_type = cpu_to_be16(attr->flags|V_RTR_TYPE(attr->rtr_type)); + wqe->flags_rtr_type = cpu_to_be16(attr->flags | + V_RTR_TYPE(attr->rtr_type) | + V_CHAN(attr->chan)); wqe->ord = cpu_to_be32(attr->ord); wqe->ird = cpu_to_be32(attr->ird); wqe->qp_dma_addr = cpu_to_be64(attr->qp_dma_addr); @@ -1032,6 +1034,7 @@ err3: err2: cxio_hal_destroy_ctrl_qp(rdev_p); err1: + rdev_p->t3cdev_p->ulp = NULL; list_del(&rdev_p->entry); return err; } diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h index 32e3b1461d8..a197a5b7ac7 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_wr.h +++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h @@ -327,6 +327,11 @@ enum rdma_init_rtr_types { #define V_RTR_TYPE(x) ((x) << S_RTR_TYPE) #define G_RTR_TYPE(x) ((((x) >> S_RTR_TYPE)) & M_RTR_TYPE) +#define S_CHAN 4 +#define M_CHAN 0x3 +#define V_CHAN(x) ((x) << S_CHAN) +#define G_CHAN(x) ((((x) >> S_CHAN)) & M_CHAN) + struct t3_rdma_init_attr { u32 tid; u32 qpid; @@ -346,6 +351,7 @@ struct t3_rdma_init_attr { u16 flags; u16 rqe_count; u32 irs; + u32 chan; }; struct t3_rdma_init_wr { diff --git a/drivers/infiniband/hw/cxgb3/iwch.c b/drivers/infiniband/hw/cxgb3/iwch.c index 26fc0a4eaa7..b0ea0105ddf 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.c +++ b/drivers/infiniband/hw/cxgb3/iwch.c @@ -51,7 +51,7 @@ cxgb3_cpl_handler_func t3c_handlers[NUM_CPL_CMDS]; static void open_rnic_dev(struct t3cdev *); static void close_rnic_dev(struct t3cdev *); -static void iwch_err_handler(struct t3cdev *, u32, u32); +static void iwch_event_handler(struct t3cdev *, u32, u32); struct cxgb3_client t3c_client = { .name = "iw_cxgb3", @@ -59,7 +59,7 @@ struct cxgb3_client t3c_client = { .remove = close_rnic_dev, .handlers = t3c_handlers, .redirect = iwch_ep_redirect, - .err_handler = iwch_err_handler + .event_handler = iwch_event_handler }; static LIST_HEAD(dev_list); @@ -105,11 +105,9 @@ static void rnic_init(struct iwch_dev *rnicp) static void open_rnic_dev(struct t3cdev *tdev) { struct iwch_dev *rnicp; - static int vers_printed; PDBG("%s t3cdev %p\n", __func__, tdev); - if (!vers_printed++) - printk(KERN_INFO MOD "Chelsio T3 RDMA Driver - version %s\n", + printk_once(KERN_INFO MOD "Chelsio T3 RDMA Driver - version %s\n", DRV_VERSION); rnicp = (struct iwch_dev *)ib_alloc_device(sizeof(*rnicp)); if (!rnicp) { @@ -162,21 +160,36 @@ static void close_rnic_dev(struct t3cdev *tdev) mutex_unlock(&dev_mutex); } -static void iwch_err_handler(struct t3cdev *tdev, u32 status, u32 error) +static void iwch_event_handler(struct t3cdev *tdev, u32 evt, u32 port_id) { struct cxio_rdev *rdev = tdev->ulp; - struct iwch_dev *rnicp = rdev_to_iwch_dev(rdev); + struct iwch_dev *rnicp; struct ib_event event; + u32 portnum = port_id + 1; - if (status == OFFLOAD_STATUS_DOWN) { + if (!rdev) + return; + rnicp = rdev_to_iwch_dev(rdev); + switch (evt) { + case OFFLOAD_STATUS_DOWN: { rdev->flags = CXIO_ERROR_FATAL; - - event.device = &rnicp->ibdev; event.event = IB_EVENT_DEVICE_FATAL; - event.element.port_num = 0; - ib_dispatch_event(&event); + break; + } + case OFFLOAD_PORT_DOWN: { + event.event = IB_EVENT_PORT_ERR; + break; + } + case OFFLOAD_PORT_UP: { + event.event = IB_EVENT_PORT_ACTIVE; + break; + } } + event.device = &rnicp->ibdev; + event.element.port_num = portnum; + ib_dispatch_event(&event); + return; } diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 52d7bb0c2a1..66b41351910 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -286,7 +286,7 @@ void __free_ep(struct kref *kref) ep = container_of(container_of(kref, struct iwch_ep_common, kref), struct iwch_ep, com); PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]); - if (ep->com.flags & RELEASE_RESOURCES) { + if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid); dst_release(ep->dst); l2t_release(L2DATA(ep->com.tdev), ep->l2t); @@ -297,7 +297,7 @@ void __free_ep(struct kref *kref) static void release_ep_resources(struct iwch_ep *ep) { PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid); - ep->com.flags |= RELEASE_RESOURCES; + set_bit(RELEASE_RESOURCES, &ep->com.flags); put_ep(&ep->com); } @@ -786,10 +786,12 @@ static void connect_request_upcall(struct iwch_ep *ep) event.private_data_len = ep->plen; event.private_data = ep->mpa_pkt + sizeof(struct mpa_message); event.provider_data = ep; - if (state_read(&ep->parent_ep->com) != DEAD) + if (state_read(&ep->parent_ep->com) != DEAD) { + get_ep(&ep->com); ep->parent_ep->com.cm_id->event_handler( ep->parent_ep->com.cm_id, &event); + } put_ep(&ep->parent_ep->com); ep->parent_ep = NULL; } @@ -1156,8 +1158,7 @@ static int abort_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) * We get 2 abort replies from the HW. The first one must * be ignored except for scribbling that we need one more. */ - if (!(ep->com.flags & ABORT_REQ_IN_PROGRESS)) { - ep->com.flags |= ABORT_REQ_IN_PROGRESS; + if (!test_and_set_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags)) { return CPL_RET_BUF_DONE; } @@ -1477,10 +1478,14 @@ static int peer_close(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) /* * We're gonna mark this puppy DEAD, but keep * the reference on it until the ULP accepts or - * rejects the CR. + * rejects the CR. Also wake up anyone waiting + * in rdma connection migration (see iwch_accept_cr()). */ __state_set(&ep->com, CLOSING); - get_ep(&ep->com); + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p\n", ep); + wake_up(&ep->com.waitq); break; case MPA_REP_SENT: __state_set(&ep->com, CLOSING); @@ -1561,8 +1566,7 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) * We get 2 peer aborts from the HW. The first one must * be ignored except for scribbling that we need one more. */ - if (!(ep->com.flags & PEER_ABORT_IN_PROGRESS)) { - ep->com.flags |= PEER_ABORT_IN_PROGRESS; + if (!test_and_set_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags)) { return CPL_RET_BUF_DONE; } @@ -1589,9 +1593,13 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) /* * We're gonna mark this puppy DEAD, but keep * the reference on it until the ULP accepts or - * rejects the CR. + * rejects the CR. Also wake up anyone waiting + * in rdma connection migration (see iwch_accept_cr()). */ - get_ep(&ep->com); + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p\n", ep); + wake_up(&ep->com.waitq); break; case MORIBUND: case CLOSING: @@ -1797,6 +1805,7 @@ int iwch_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) err = send_mpa_reject(ep, pdata, pdata_len); err = iwch_ep_disconnect(ep, 0, GFP_KERNEL); } + put_ep(&ep->com); return 0; } @@ -1810,8 +1819,10 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct iwch_qp *qp = get_qhp(h, conn_param->qpn); PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); - if (state_read(&ep->com) == DEAD) - return -ECONNRESET; + if (state_read(&ep->com) == DEAD) { + err = -ECONNRESET; + goto err; + } BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD); BUG_ON(!qp); @@ -1819,15 +1830,14 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) if ((conn_param->ord > qp->rhp->attr.max_rdma_read_qp_depth) || (conn_param->ird > qp->rhp->attr.max_rdma_reads_per_qp)) { abort_connection(ep, NULL, GFP_KERNEL); - return -EINVAL; + err = -EINVAL; + goto err; } cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; ep->com.qp = qp; - ep->com.rpl_done = 0; - ep->com.rpl_err = 0; ep->ird = conn_param->ird; ep->ord = conn_param->ord; @@ -1836,8 +1846,6 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord); - get_ep(&ep->com); - /* bind QP to EP and move to RTS */ attrs.mpa_attr = ep->mpa_attr; attrs.max_ird = ep->ird; @@ -1855,30 +1863,31 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) err = iwch_modify_qp(ep->com.qp->rhp, ep->com.qp, mask, &attrs, 1); if (err) - goto err; + goto err1; /* if needed, wait for wr_ack */ if (iwch_rqes_posted(qp)) { wait_event(ep->com.waitq, ep->com.rpl_done); err = ep->com.rpl_err; if (err) - goto err; + goto err1; } err = send_mpa_reply(ep, conn_param->private_data, conn_param->private_data_len); if (err) - goto err; + goto err1; state_set(&ep->com, FPDU_MODE); established_upcall(ep); put_ep(&ep->com); return 0; -err: +err1: ep->com.cm_id = NULL; ep->com.qp = NULL; cm_id->rem_ref(cm_id); +err: put_ep(&ep->com); return err; } @@ -2097,14 +2106,17 @@ int iwch_ep_disconnect(struct iwch_ep *ep, int abrupt, gfp_t gfp) ep->com.state = CLOSING; start_ep_timer(ep); } + set_bit(CLOSE_SENT, &ep->com.flags); break; case CLOSING: - close = 1; - if (abrupt) { - stop_ep_timer(ep); - ep->com.state = ABORTING; - } else - ep->com.state = MORIBUND; + if (!test_and_set_bit(CLOSE_SENT, &ep->com.flags)) { + close = 1; + if (abrupt) { + stop_ep_timer(ep); + ep->com.state = ABORTING; + } else + ep->com.state = MORIBUND; + } break; case MORIBUND: case ABORTING: diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.h b/drivers/infiniband/hw/cxgb3/iwch_cm.h index 43c0aea7ead..b9efadfffb4 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.h +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.h @@ -145,9 +145,10 @@ enum iwch_ep_state { }; enum iwch_ep_flags { - PEER_ABORT_IN_PROGRESS = (1 << 0), - ABORT_REQ_IN_PROGRESS = (1 << 1), - RELEASE_RESOURCES = (1 << 2), + PEER_ABORT_IN_PROGRESS = 0, + ABORT_REQ_IN_PROGRESS = 1, + RELEASE_RESOURCES = 2, + CLOSE_SENT = 3, }; struct iwch_ep_common { @@ -162,7 +163,7 @@ struct iwch_ep_common { wait_queue_head_t waitq; int rpl_done; int rpl_err; - u32 flags; + unsigned long flags; }; struct iwch_listen_ep { diff --git a/drivers/infiniband/hw/cxgb3/iwch_mem.c b/drivers/infiniband/hw/cxgb3/iwch_mem.c index ec49a5cbdeb..e1ec65ebb01 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_mem.c +++ b/drivers/infiniband/hw/cxgb3/iwch_mem.c @@ -39,7 +39,7 @@ #include "iwch.h" #include "iwch_provider.h" -static void iwch_finish_mem_reg(struct iwch_mr *mhp, u32 stag) +static int iwch_finish_mem_reg(struct iwch_mr *mhp, u32 stag) { u32 mmid; @@ -47,14 +47,15 @@ static void iwch_finish_mem_reg(struct iwch_mr *mhp, u32 stag) mhp->attr.stag = stag; mmid = stag >> 8; mhp->ibmr.rkey = mhp->ibmr.lkey = stag; - insert_handle(mhp->rhp, &mhp->rhp->mmidr, mhp, mmid); PDBG("%s mmid 0x%x mhp %p\n", __func__, mmid, mhp); + return insert_handle(mhp->rhp, &mhp->rhp->mmidr, mhp, mmid); } int iwch_register_mem(struct iwch_dev *rhp, struct iwch_pd *php, struct iwch_mr *mhp, int shift) { u32 stag; + int ret; if (cxio_register_phys_mem(&rhp->rdev, &stag, mhp->attr.pdid, @@ -66,9 +67,11 @@ int iwch_register_mem(struct iwch_dev *rhp, struct iwch_pd *php, mhp->attr.pbl_size, mhp->attr.pbl_addr)) return -ENOMEM; - iwch_finish_mem_reg(mhp, stag); - - return 0; + ret = iwch_finish_mem_reg(mhp, stag); + if (ret) + cxio_dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); + return ret; } int iwch_reregister_mem(struct iwch_dev *rhp, struct iwch_pd *php, @@ -77,6 +80,7 @@ int iwch_reregister_mem(struct iwch_dev *rhp, struct iwch_pd *php, int npages) { u32 stag; + int ret; /* We could support this... */ if (npages > mhp->attr.pbl_size) @@ -93,9 +97,12 @@ int iwch_reregister_mem(struct iwch_dev *rhp, struct iwch_pd *php, mhp->attr.pbl_size, mhp->attr.pbl_addr)) return -ENOMEM; - iwch_finish_mem_reg(mhp, stag); + ret = iwch_finish_mem_reg(mhp, stag); + if (ret) + cxio_dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); - return 0; + return ret; } int iwch_alloc_pbl(struct iwch_mr *mhp, int npages) diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index e2a63214008..6895523779d 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -195,7 +195,11 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve spin_lock_init(&chp->lock); atomic_set(&chp->refcnt, 1); init_waitqueue_head(&chp->wait); - insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid); + if (insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid)) { + cxio_destroy_cq(&chp->rhp->rdev, &chp->cq); + kfree(chp); + return ERR_PTR(-ENOMEM); + } if (ucontext) { struct iwch_mm_entry *mm; @@ -750,7 +754,11 @@ static struct ib_mw *iwch_alloc_mw(struct ib_pd *pd) mhp->attr.stag = stag; mmid = (stag) >> 8; mhp->ibmw.rkey = stag; - insert_handle(rhp, &rhp->mmidr, mhp, mmid); + if (insert_handle(rhp, &rhp->mmidr, mhp, mmid)) { + cxio_deallocate_window(&rhp->rdev, mhp->attr.stag); + kfree(mhp); + return ERR_PTR(-ENOMEM); + } PDBG("%s mmid 0x%x mhp %p stag 0x%x\n", __func__, mmid, mhp, stag); return &(mhp->ibmw); } @@ -778,37 +786,43 @@ static struct ib_mr *iwch_alloc_fast_reg_mr(struct ib_pd *pd, int pbl_depth) struct iwch_mr *mhp; u32 mmid; u32 stag = 0; - int ret; + int ret = 0; php = to_iwch_pd(pd); rhp = php->rhp; mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); if (!mhp) - return ERR_PTR(-ENOMEM); + goto err; mhp->rhp = rhp; ret = iwch_alloc_pbl(mhp, pbl_depth); - if (ret) { - kfree(mhp); - return ERR_PTR(ret); - } + if (ret) + goto err1; mhp->attr.pbl_size = pbl_depth; ret = cxio_allocate_stag(&rhp->rdev, &stag, php->pdid, mhp->attr.pbl_size, mhp->attr.pbl_addr); - if (ret) { - iwch_free_pbl(mhp); - kfree(mhp); - return ERR_PTR(ret); - } + if (ret) + goto err2; mhp->attr.pdid = php->pdid; mhp->attr.type = TPT_NON_SHARED_MR; mhp->attr.stag = stag; mhp->attr.state = 1; mmid = (stag) >> 8; mhp->ibmr.rkey = mhp->ibmr.lkey = stag; - insert_handle(rhp, &rhp->mmidr, mhp, mmid); + if (insert_handle(rhp, &rhp->mmidr, mhp, mmid)) + goto err3; + PDBG("%s mmid 0x%x mhp %p stag 0x%x\n", __func__, mmid, mhp, stag); return &(mhp->ibmr); +err3: + cxio_dereg_mem(&rhp->rdev, stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); +err2: + iwch_free_pbl(mhp); +err1: + kfree(mhp); +err: + return ERR_PTR(ret); } static struct ib_fast_reg_page_list *iwch_alloc_fastreg_pbl( @@ -961,7 +975,13 @@ static struct ib_qp *iwch_create_qp(struct ib_pd *pd, spin_lock_init(&qhp->lock); init_waitqueue_head(&qhp->wait); atomic_set(&qhp->refcnt, 1); - insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.qpid); + + if (insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.qpid)) { + cxio_destroy_qp(&rhp->rdev, &qhp->wq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); + kfree(qhp); + return ERR_PTR(-ENOMEM); + } if (udata) { @@ -1418,6 +1438,7 @@ int iwch_register_device(struct iwch_dev *dev) bail2: ib_unregister_device(&dev->ibdev); bail1: + kfree(dev->ibdev.iwcm); return ret; } @@ -1430,5 +1451,6 @@ void iwch_unregister_device(struct iwch_dev *dev) device_remove_file(&dev->ibdev.dev, iwch_class_attributes[i]); ib_unregister_device(&dev->ibdev); + kfree(dev->ibdev.iwcm); return; } diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index 27bbdc8e773..6e865347194 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -889,6 +889,7 @@ static int rdma_init(struct iwch_dev *rhp, struct iwch_qp *qhp, init_attr.qp_dma_size = (1UL << qhp->wq.size_log2); init_attr.rqe_count = iwch_rqes_posted(qhp); init_attr.flags = qhp->attr.mpa_attr.initiator ? MPA_INITIATOR : 0; + init_attr.chan = qhp->ep->l2t->smt_idx; if (peer2peer) { init_attr.rtr_type = RTR_READ; if (init_attr.ord == 0 && qhp->attr.mpa_attr.initiator) diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index fab18a2c74a..5b635aa5947 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -52,7 +52,7 @@ #include "ehca_tools.h" #include "hcp_if.h" -#define HCAD_VERSION "0028" +#define HCAD_VERSION "0029" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>"); @@ -64,7 +64,7 @@ static int ehca_hw_level = 0; static int ehca_poll_all_eqs = 1; int ehca_debug_level = 0; -int ehca_nr_ports = 2; +int ehca_nr_ports = -1; int ehca_use_hp_mr = 0; int ehca_port_act_time = 30; int ehca_static_rate = -1; @@ -95,8 +95,8 @@ MODULE_PARM_DESC(hw_level, "Hardware level (0: autosensing (default), " "0x10..0x14: eHCA, 0x20..0x23: eHCA2)"); MODULE_PARM_DESC(nr_ports, - "number of connected ports (-1: autodetect, 1: port one only, " - "2: two ports (default)"); + "number of connected ports (-1: autodetect (default), " + "1: port one only, 2: two ports)"); MODULE_PARM_DESC(use_hp_mr, "Use high performance MRs (default: no)"); MODULE_PARM_DESC(port_act_time, diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c index 5a3d96f84c7..8fd88cd828f 100644 --- a/drivers/infiniband/hw/ehca/ehca_reqs.c +++ b/drivers/infiniband/hw/ehca/ehca_reqs.c @@ -786,7 +786,11 @@ repoll: wc->slid = cqe->rlid; wc->dlid_path_bits = cqe->dlid; wc->src_qp = cqe->remote_qp_number; - wc->wc_flags = cqe->w_completion_flags; + /* + * HW has "Immed data present" and "GRH present" in bits 6 and 5. + * SW defines those in bits 1 and 0, so we can just shift and mask. + */ + wc->wc_flags = (cqe->w_completion_flags >> 5) & 3; wc->ex.imm_data = cpu_to_be32(cqe->immediate_data); wc->sl = cqe->service_level; diff --git a/drivers/infiniband/hw/ehca/ehca_sqp.c b/drivers/infiniband/hw/ehca/ehca_sqp.c index c568b28f4e2..8c1213f8916 100644 --- a/drivers/infiniband/hw/ehca/ehca_sqp.c +++ b/drivers/infiniband/hw/ehca/ehca_sqp.c @@ -125,14 +125,30 @@ struct ib_perf { u8 data[192]; } __attribute__ ((packed)); +/* TC/SL/FL packed into 32 bits, as in ClassPortInfo */ +struct tcslfl { + u32 tc:8; + u32 sl:4; + u32 fl:20; +} __attribute__ ((packed)); + +/* IP Version/TC/FL packed into 32 bits, as in GRH */ +struct vertcfl { + u32 ver:4; + u32 tc:8; + u32 fl:20; +} __attribute__ ((packed)); static int ehca_process_perf(struct ib_device *ibdev, u8 port_num, + struct ib_wc *in_wc, struct ib_grh *in_grh, struct ib_mad *in_mad, struct ib_mad *out_mad) { struct ib_perf *in_perf = (struct ib_perf *)in_mad; struct ib_perf *out_perf = (struct ib_perf *)out_mad; struct ib_class_port_info *poi = (struct ib_class_port_info *)out_perf->data; + struct tcslfl *tcslfl = + (struct tcslfl *)&poi->redirect_tcslfl; struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); struct ehca_sport *sport = &shca->sport[port_num - 1]; @@ -158,10 +174,29 @@ static int ehca_process_perf(struct ib_device *ibdev, u8 port_num, poi->base_version = 1; poi->class_version = 1; poi->resp_time_value = 18; - poi->redirect_lid = sport->saved_attr.lid; - poi->redirect_qp = sport->pma_qp_nr; + + /* copy local routing information from WC where applicable */ + tcslfl->sl = in_wc->sl; + poi->redirect_lid = + sport->saved_attr.lid | in_wc->dlid_path_bits; + poi->redirect_qp = sport->pma_qp_nr; poi->redirect_qkey = IB_QP1_QKEY; - poi->redirect_pkey = IB_DEFAULT_PKEY_FULL; + + ehca_query_pkey(ibdev, port_num, in_wc->pkey_index, + &poi->redirect_pkey); + + /* if request was globally routed, copy route info */ + if (in_grh) { + struct vertcfl *vertcfl = + (struct vertcfl *)&in_grh->version_tclass_flow; + memcpy(poi->redirect_gid, in_grh->dgid.raw, + sizeof(poi->redirect_gid)); + tcslfl->tc = vertcfl->tc; + tcslfl->fl = vertcfl->fl; + } else + /* else only fill in default GID */ + ehca_query_gid(ibdev, port_num, 0, + (union ib_gid *)&poi->redirect_gid); ehca_dbg(ibdev, "ehca_pma_lid=%x ehca_pma_qp=%x", sport->saved_attr.lid, sport->pma_qp_nr); @@ -183,8 +218,7 @@ perf_reply: int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, struct ib_wc *in_wc, struct ib_grh *in_grh, - struct ib_mad *in_mad, - struct ib_mad *out_mad) + struct ib_mad *in_mad, struct ib_mad *out_mad) { int ret; @@ -196,7 +230,8 @@ int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_SUCCESS; ehca_dbg(ibdev, "port_num=%x src_qp=%x", port_num, in_wc->src_qp); - ret = ehca_process_perf(ibdev, port_num, in_mad, out_mad); + ret = ehca_process_perf(ibdev, port_num, in_wc, in_grh, + in_mad, out_mad); return ret; } diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 23173982b32..38a28700661 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1616,7 +1616,7 @@ static int try_alloc_port(struct ipath_devdata *dd, int port, pd->port_cnt = 1; port_fp(fp) = pd; pd->port_pid = get_pid(task_pid(current)); - strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm)); + strlcpy(pd->port_comm, current->comm, sizeof(pd->port_comm)); ipath_stats.sps_ports++; ret = 0; } else diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c index 16a702d4601..ceb98ee7866 100644 --- a/drivers/infiniband/hw/ipath/ipath_mad.c +++ b/drivers/infiniband/hw/ipath/ipath_mad.c @@ -60,7 +60,7 @@ static int recv_subn_get_nodedescription(struct ib_smp *smp, if (smp->attr_mod) smp->status |= IB_SMP_INVALID_FIELD; - strncpy(smp->data, ibdev->node_desc, sizeof(smp->data)); + memcpy(smp->data, ibdev->node_desc, sizeof(smp->data)); return reply(smp); } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index ae3d7590346..3cb3f47a10b 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -342,6 +342,9 @@ static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev, struct mlx4_ib_alloc_ucontext_resp resp; int err; + if (!dev->ib_active) + return ERR_PTR(-EAGAIN); + resp.qp_tab_size = dev->dev->caps.num_qps; resp.bf_reg_size = dev->dev->caps.bf_reg_size; resp.bf_regs_per_page = dev->dev->caps.bf_regs_per_page; @@ -540,15 +543,11 @@ static struct device_attribute *mlx4_class_attributes[] = { static void *mlx4_ib_add(struct mlx4_dev *dev) { - static int mlx4_ib_version_printed; struct mlx4_ib_dev *ibdev; int num_ports = 0; int i; - if (!mlx4_ib_version_printed) { - printk(KERN_INFO "%s", mlx4_ib_version); - ++mlx4_ib_version_printed; - } + printk_once(KERN_INFO "%s", mlx4_ib_version); mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) num_ports++; @@ -673,6 +672,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) goto err_reg; } + ibdev->ib_active = true; + return ibdev; err_reg: @@ -729,6 +730,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, break; case MLX4_DEV_EVENT_CATASTROPHIC_ERROR: + ibdev->ib_active = false; ibev.event = IB_EVENT_DEVICE_FATAL; break; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 8a7dd6795fa..3486d7675e5 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -175,6 +175,7 @@ struct mlx4_ib_dev { spinlock_t sm_lock; struct mutex cap_mask_mutex; + bool ib_active; }; static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index c4a02648c8a..219b10397b4 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -615,10 +615,12 @@ static enum mlx4_qp_state to_mlx4_state(enum ib_qp_state state) } static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq) + __acquires(&send_cq->lock) __acquires(&recv_cq->lock) { - if (send_cq == recv_cq) + if (send_cq == recv_cq) { spin_lock_irq(&send_cq->lock); - else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { + __acquire(&recv_cq->lock); + } else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { spin_lock_irq(&send_cq->lock); spin_lock_nested(&recv_cq->lock, SINGLE_DEPTH_NESTING); } else { @@ -628,10 +630,12 @@ static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv } static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq) + __releases(&send_cq->lock) __releases(&recv_cq->lock) { - if (send_cq == recv_cq) + if (send_cq == recv_cq) { + __release(&recv_cq->lock); spin_unlock_irq(&send_cq->lock); - else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { + } else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { spin_unlock(&recv_cq->lock); spin_unlock_irq(&send_cq->lock); } else { diff --git a/drivers/infiniband/hw/mthca/mthca_catas.c b/drivers/infiniband/hw/mthca/mthca_catas.c index 65ad359fdf1..056b2a4c697 100644 --- a/drivers/infiniband/hw/mthca/mthca_catas.c +++ b/drivers/infiniband/hw/mthca/mthca_catas.c @@ -88,6 +88,7 @@ static void handle_catas(struct mthca_dev *dev) event.device = &dev->ib_dev; event.event = IB_EVENT_DEVICE_FATAL; event.element.port_num = 0; + dev->active = false; ib_dispatch_event(&event); diff --git a/drivers/infiniband/hw/mthca/mthca_config_reg.h b/drivers/infiniband/hw/mthca/mthca_config_reg.h index 75671f75cac..155bc66395b 100644 --- a/drivers/infiniband/hw/mthca/mthca_config_reg.h +++ b/drivers/infiniband/hw/mthca/mthca_config_reg.h @@ -34,8 +34,6 @@ #ifndef MTHCA_CONFIG_REG_H #define MTHCA_CONFIG_REG_H -#include <asm/page.h> - #define MTHCA_HCR_BASE 0x80680 #define MTHCA_HCR_SIZE 0x0001c #define MTHCA_ECR_BASE 0x80700 diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h index 9ef611f6dd3..7e6a6d64ad4 100644 --- a/drivers/infiniband/hw/mthca/mthca_dev.h +++ b/drivers/infiniband/hw/mthca/mthca_dev.h @@ -357,6 +357,7 @@ struct mthca_dev { struct ib_ah *sm_ah[MTHCA_MAX_PORTS]; spinlock_t sm_lock; u8 rate[MTHCA_MAX_PORTS]; + bool active; }; #ifdef CONFIG_INFINIBAND_MTHCA_DEBUG diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 90e4e450a12..8c31fa36e95 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -829,27 +829,34 @@ int mthca_init_eq_table(struct mthca_dev *dev) if (dev->mthca_flags & MTHCA_FLAG_MSI_X) { static const char *eq_name[] = { - [MTHCA_EQ_COMP] = DRV_NAME " (comp)", - [MTHCA_EQ_ASYNC] = DRV_NAME " (async)", - [MTHCA_EQ_CMD] = DRV_NAME " (cmd)" + [MTHCA_EQ_COMP] = DRV_NAME "-comp", + [MTHCA_EQ_ASYNC] = DRV_NAME "-async", + [MTHCA_EQ_CMD] = DRV_NAME "-cmd" }; for (i = 0; i < MTHCA_NUM_EQ; ++i) { + snprintf(dev->eq_table.eq[i].irq_name, + IB_DEVICE_NAME_MAX, + "%s@pci:%s", eq_name[i], + pci_name(dev->pdev)); err = request_irq(dev->eq_table.eq[i].msi_x_vector, mthca_is_memfree(dev) ? mthca_arbel_msi_x_interrupt : mthca_tavor_msi_x_interrupt, - 0, eq_name[i], dev->eq_table.eq + i); + 0, dev->eq_table.eq[i].irq_name, + dev->eq_table.eq + i); if (err) goto err_out_cmd; dev->eq_table.eq[i].have_irq = 1; } } else { + snprintf(dev->eq_table.eq[0].irq_name, IB_DEVICE_NAME_MAX, + DRV_NAME "@pci:%s", pci_name(dev->pdev)); err = request_irq(dev->pdev->irq, mthca_is_memfree(dev) ? mthca_arbel_interrupt : mthca_tavor_interrupt, - IRQF_SHARED, DRV_NAME, dev); + IRQF_SHARED, dev->eq_table.eq[0].irq_name, dev); if (err) goto err_out_cmd; dev->eq_table.have_irq = 1; diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index 13da9f1d24c..b01b2898787 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -1116,6 +1116,8 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) pci_set_drvdata(pdev, mdev); mdev->hca_type = hca_type; + mdev->active = true; + return 0; err_unregister: @@ -1215,15 +1217,11 @@ int __mthca_restart_one(struct pci_dev *pdev) static int __devinit mthca_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { - static int mthca_version_printed = 0; int ret; mutex_lock(&mthca_device_mutex); - if (!mthca_version_printed) { - printk(KERN_INFO "%s", mthca_version); - ++mthca_version_printed; - } + printk_once(KERN_INFO "%s", mthca_version); if (id->driver_data >= ARRAY_SIZE(mthca_hca_table)) { printk(KERN_ERR PFX "%s has invalid driver data %lx\n", diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 87ad889e367..bcf7a401482 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -334,6 +334,9 @@ static struct ib_ucontext *mthca_alloc_ucontext(struct ib_device *ibdev, struct mthca_ucontext *context; int err; + if (!(to_mdev(ibdev)->active)) + return ERR_PTR(-EAGAIN); + memset(&uresp, 0, sizeof uresp); uresp.qp_tab_size = to_mdev(ibdev)->limits.num_qps; diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index c621f8794b8..90f4c4d2e98 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h @@ -113,6 +113,7 @@ struct mthca_eq { int nent; struct mthca_buf_list *page_list; struct mthca_mr mr; + char irq_name[IB_DEVICE_NAME_MAX]; }; struct mthca_av; diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index f5081bfde6d..c10576fa60c 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -1319,10 +1319,12 @@ int mthca_alloc_qp(struct mthca_dev *dev, } static void mthca_lock_cqs(struct mthca_cq *send_cq, struct mthca_cq *recv_cq) + __acquires(&send_cq->lock) __acquires(&recv_cq->lock) { - if (send_cq == recv_cq) + if (send_cq == recv_cq) { spin_lock_irq(&send_cq->lock); - else if (send_cq->cqn < recv_cq->cqn) { + __acquire(&recv_cq->lock); + } else if (send_cq->cqn < recv_cq->cqn) { spin_lock_irq(&send_cq->lock); spin_lock_nested(&recv_cq->lock, SINGLE_DEPTH_NESTING); } else { @@ -1332,10 +1334,12 @@ static void mthca_lock_cqs(struct mthca_cq *send_cq, struct mthca_cq *recv_cq) } static void mthca_unlock_cqs(struct mthca_cq *send_cq, struct mthca_cq *recv_cq) + __releases(&send_cq->lock) __releases(&recv_cq->lock) { - if (send_cq == recv_cq) + if (send_cq == recv_cq) { + __release(&recv_cq->lock); spin_unlock_irq(&send_cq->lock); - else if (send_cq->cqn < recv_cq->cqn) { + } else if (send_cq->cqn < recv_cq->cqn) { spin_unlock(&recv_cq->lock); spin_unlock_irq(&send_cq->lock); } else { diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c index acb6817f606..2a13a163d33 100644 --- a/drivers/infiniband/hw/mthca/mthca_reset.c +++ b/drivers/infiniband/hw/mthca/mthca_reset.c @@ -30,7 +30,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/errno.h> #include <linux/pci.h> #include <linux/delay.h> diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index bf1720f7f35..bcc6abc4faf 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h @@ -523,7 +523,7 @@ int nes_cm_disconn(struct nes_qp *); void nes_cm_disconn_worker(void *); /* nes_verbs.c */ -int nes_hw_modify_qp(struct nes_device *, struct nes_qp *, u32, u32); +int nes_hw_modify_qp(struct nes_device *, struct nes_qp *, u32, u32, u32); int nes_modify_qp(struct ib_qp *, struct ib_qp_attr *, int, struct ib_udata *); struct nes_ib_device *nes_init_ofa_device(struct net_device *); void nes_destroy_ofa_device(struct nes_ib_device *); diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 114b802771a..73473db1986 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -2450,19 +2450,16 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod */ int nes_cm_disconn(struct nes_qp *nesqp) { - unsigned long flags; - - spin_lock_irqsave(&nesqp->lock, flags); - if (nesqp->disconn_pending == 0) { - nesqp->disconn_pending++; - spin_unlock_irqrestore(&nesqp->lock, flags); - /* init our disconnect work element, to */ - INIT_WORK(&nesqp->disconn_work, nes_disconnect_worker); + struct disconn_work *work; - queue_work(g_cm_core->disconn_wq, &nesqp->disconn_work); - } else - spin_unlock_irqrestore(&nesqp->lock, flags); + work = kzalloc(sizeof *work, GFP_ATOMIC); + if (!work) + return -ENOMEM; /* Timer will clean up */ + nes_add_ref(&nesqp->ibqp); + work->nesqp = nesqp; + INIT_WORK(&work->work, nes_disconnect_worker); + queue_work(g_cm_core->disconn_wq, &work->work); return 0; } @@ -2472,11 +2469,14 @@ int nes_cm_disconn(struct nes_qp *nesqp) */ static void nes_disconnect_worker(struct work_struct *work) { - struct nes_qp *nesqp = container_of(work, struct nes_qp, disconn_work); + struct disconn_work *dwork = container_of(work, struct disconn_work, work); + struct nes_qp *nesqp = dwork->nesqp; + kfree(dwork); nes_debug(NES_DBG_CM, "processing AEQE id 0x%04X for QP%u.\n", nesqp->last_aeq, nesqp->hwqp.qp_id); nes_cm_disconn_true(nesqp); + nes_rem_ref(&nesqp->ibqp); } @@ -2493,7 +2493,12 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) u16 last_ae; u8 original_hw_tcp_state; u8 original_ibqp_state; - u8 issued_disconnect_reset = 0; + enum iw_cm_event_type disconn_status = IW_CM_EVENT_STATUS_OK; + int issue_disconn = 0; + int issue_close = 0; + int issue_flush = 0; + u32 flush_q = NES_CQP_FLUSH_RQ; + struct ib_event ibevent; if (!nesqp) { nes_debug(NES_DBG_CM, "disconnect_worker nesqp is NULL\n"); @@ -2517,24 +2522,55 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) original_ibqp_state = nesqp->ibqp_state; last_ae = nesqp->last_aeq; + if (nesqp->term_flags) { + issue_disconn = 1; + issue_close = 1; + nesqp->cm_id = NULL; + if (nesqp->flush_issued == 0) { + nesqp->flush_issued = 1; + issue_flush = 1; + } + } else if ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) || + ((original_ibqp_state == IB_QPS_RTS) && + (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { + issue_disconn = 1; + if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) + disconn_status = IW_CM_EVENT_STATUS_RESET; + } + + if (((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) || + (original_hw_tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT) || + (last_ae == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) || + (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { + issue_close = 1; + nesqp->cm_id = NULL; + if (nesqp->flush_issued == 0) { + nesqp->flush_issued = 1; + issue_flush = 1; + } + } + + spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_CM, "set ibqp_state=%u\n", nesqp->ibqp_state); + if ((issue_flush) && (nesqp->destroyed == 0)) { + /* Flush the queue(s) */ + if (nesqp->hw_iwarp_state >= NES_AEQE_IWARP_STATE_TERMINATE) + flush_q |= NES_CQP_FLUSH_SQ; + flush_wqes(nesvnic->nesdev, nesqp, flush_q, 1); - if ((nesqp->cm_id) && (cm_id->event_handler)) { - if ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) || - ((original_ibqp_state == IB_QPS_RTS) && - (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { + if (nesqp->term_flags) { + ibevent.device = nesqp->ibqp.device; + ibevent.event = nesqp->terminate_eventtype; + ibevent.element.qp = &nesqp->ibqp; + nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); + } + } + + if ((cm_id) && (cm_id->event_handler)) { + if (issue_disconn) { atomic_inc(&cm_disconnects); cm_event.event = IW_CM_EVENT_DISCONNECT; - if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) { - cm_event.status = IW_CM_EVENT_STATUS_RESET; - nes_debug(NES_DBG_CM, "Generating a CM " - "Disconnect Event (status reset) for " - "QP%u, cm_id = %p. \n", - nesqp->hwqp.qp_id, cm_id); - } else - cm_event.status = IW_CM_EVENT_STATUS_OK; - + cm_event.status = disconn_status; cm_event.local_addr = cm_id->local_addr; cm_event.remote_addr = cm_id->remote_addr; cm_event.private_data = NULL; @@ -2547,29 +2583,14 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) nesqp->hwqp.sq_tail, cm_id, atomic_read(&nesqp->refcount)); - spin_unlock_irqrestore(&nesqp->lock, flags); ret = cm_id->event_handler(cm_id, &cm_event); if (ret) nes_debug(NES_DBG_CM, "OFA CM event_handler " "returned, ret=%d\n", ret); - spin_lock_irqsave(&nesqp->lock, flags); } - nesqp->disconn_pending = 0; - /* There might have been another AE while the lock was released */ - original_hw_tcp_state = nesqp->hw_tcp_state; - original_ibqp_state = nesqp->ibqp_state; - last_ae = nesqp->last_aeq; - - if ((issued_disconnect_reset == 0) && (nesqp->cm_id) && - ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) || - (original_hw_tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT) || - (last_ae == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) || - (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { + if (issue_close) { atomic_inc(&cm_closes); - nesqp->cm_id = NULL; - nesqp->in_disconnect = 0; - spin_unlock_irqrestore(&nesqp->lock, flags); nes_disconnect(nesqp, 1); cm_id->provider_data = nesqp; @@ -2588,28 +2609,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) } cm_id->rem_ref(cm_id); - - spin_lock_irqsave(&nesqp->lock, flags); - if (nesqp->flush_issued == 0) { - nesqp->flush_issued = 1; - spin_unlock_irqrestore(&nesqp->lock, flags); - flush_wqes(nesvnic->nesdev, nesqp, - NES_CQP_FLUSH_RQ, 1); - } else - spin_unlock_irqrestore(&nesqp->lock, flags); - } else { - cm_id = nesqp->cm_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - /* check to see if the inbound reset beat the outbound reset */ - if ((!cm_id) && (last_ae==NES_AEQE_AEID_RESET_SENT)) { - nes_debug(NES_DBG_CM, "QP%u: Decing refcount " - "due to inbound reset beating the " - "outbound reset.\n", nesqp->hwqp.qp_id); - } } - } else { - nesqp->disconn_pending = 0; - spin_unlock_irqrestore(&nesqp->lock, flags); } return 0; diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h index 8b7e7c0e496..90e8e4d8a5c 100644 --- a/drivers/infiniband/hw/nes/nes_cm.h +++ b/drivers/infiniband/hw/nes/nes_cm.h @@ -410,8 +410,6 @@ struct nes_cm_ops { int schedule_nes_timer(struct nes_cm_node *, struct sk_buff *, enum nes_timer_type, int, int); -int nes_cm_disconn(struct nes_qp *); - int nes_accept(struct iw_cm_id *, struct iw_cm_conn_param *); int nes_reject(struct iw_cm_id *, const void *, u8); int nes_connect(struct iw_cm_id *, struct iw_cm_conn_param *); diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 4a84d02ece0..63a1a8e1e8a 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -74,6 +74,8 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, static void process_critical_error(struct nes_device *nesdev); static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number); static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_Mode); +static void nes_terminate_timeout(unsigned long context); +static void nes_terminate_start_timer(struct nes_qp *nesqp); #ifdef CONFIG_INFINIBAND_NES_DEBUG static unsigned char *nes_iwarp_state_str[] = { @@ -2903,6 +2905,417 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq) } +static u8 *locate_mpa(u8 *pkt, u32 aeq_info) +{ + u16 pkt_len; + + if (aeq_info & NES_AEQE_Q2_DATA_ETHERNET) { + /* skip over ethernet header */ + pkt_len = be16_to_cpu(*(u16 *)(pkt + ETH_HLEN - 2)); + pkt += ETH_HLEN; + + /* Skip over IP and TCP headers */ + pkt += 4 * (pkt[0] & 0x0f); + pkt += 4 * ((pkt[12] >> 4) & 0x0f); + } + return pkt; +} + +/* Determine if incoming error pkt is rdma layer */ +static u32 iwarp_opcode(struct nes_qp *nesqp, u32 aeq_info) +{ + u8 *pkt; + u16 *mpa; + u32 opcode = 0xffffffff; + + if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) { + pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET; + mpa = (u16 *)locate_mpa(pkt, aeq_info); + opcode = be16_to_cpu(mpa[1]) & 0xf; + } + + return opcode; +} + +/* Build iWARP terminate header */ +static int nes_bld_terminate_hdr(struct nes_qp *nesqp, u16 async_event_id, u32 aeq_info) +{ + u8 *pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET; + u16 ddp_seg_len; + int copy_len = 0; + u8 is_tagged = 0; + u8 flush_code = 0; + struct nes_terminate_hdr *termhdr; + + termhdr = (struct nes_terminate_hdr *)nesqp->hwqp.q2_vbase; + memset(termhdr, 0, 64); + + if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) { + + /* Use data from offending packet to fill in ddp & rdma hdrs */ + pkt = locate_mpa(pkt, aeq_info); + ddp_seg_len = be16_to_cpu(*(u16 *)pkt); + if (ddp_seg_len) { + copy_len = 2; + termhdr->hdrct = DDP_LEN_FLAG; + if (pkt[2] & 0x80) { + is_tagged = 1; + if (ddp_seg_len >= TERM_DDP_LEN_TAGGED) { + copy_len += TERM_DDP_LEN_TAGGED; + termhdr->hdrct |= DDP_HDR_FLAG; + } + } else { + if (ddp_seg_len >= TERM_DDP_LEN_UNTAGGED) { + copy_len += TERM_DDP_LEN_UNTAGGED; + termhdr->hdrct |= DDP_HDR_FLAG; + } + + if (ddp_seg_len >= (TERM_DDP_LEN_UNTAGGED + TERM_RDMA_LEN)) { + if ((pkt[3] & RDMA_OPCODE_MASK) == RDMA_READ_REQ_OPCODE) { + copy_len += TERM_RDMA_LEN; + termhdr->hdrct |= RDMA_HDR_FLAG; + } + } + } + } + } + + switch (async_event_id) { + case NES_AEQE_AEID_AMP_UNALLOCATED_STAG: + switch (iwarp_opcode(nesqp, aeq_info)) { + case IWARP_OPCODE_WRITE: + flush_code = IB_WC_LOC_PROT_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER; + termhdr->error_code = DDP_TAGGED_INV_STAG; + break; + default: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_INV_STAG; + } + break; + case NES_AEQE_AEID_AMP_INVALID_STAG: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_INV_STAG; + break; + case NES_AEQE_AEID_AMP_BAD_QP: + flush_code = IB_WC_LOC_QP_OP_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_QN; + break; + case NES_AEQE_AEID_AMP_BAD_STAG_KEY: + case NES_AEQE_AEID_AMP_BAD_STAG_INDEX: + switch (iwarp_opcode(nesqp, aeq_info)) { + case IWARP_OPCODE_SEND_INV: + case IWARP_OPCODE_SEND_SE_INV: + flush_code = IB_WC_REM_OP_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP; + termhdr->error_code = RDMAP_CANT_INV_STAG; + break; + default: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_INV_STAG; + } + break; + case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION: + if (aeq_info & (NES_AEQE_Q2_DATA_ETHERNET | NES_AEQE_Q2_DATA_MPA)) { + flush_code = IB_WC_LOC_PROT_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER; + termhdr->error_code = DDP_TAGGED_BOUNDS; + } else { + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_INV_BOUNDS; + } + break; + case NES_AEQE_AEID_AMP_RIGHTS_VIOLATION: + case NES_AEQE_AEID_AMP_INVALIDATE_NO_REMOTE_ACCESS_RIGHTS: + case NES_AEQE_AEID_PRIV_OPERATION_DENIED: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_ACCESS; + break; + case NES_AEQE_AEID_AMP_TO_WRAP: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_TO_WRAP; + break; + case NES_AEQE_AEID_AMP_BAD_PD: + switch (iwarp_opcode(nesqp, aeq_info)) { + case IWARP_OPCODE_WRITE: + flush_code = IB_WC_LOC_PROT_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER; + termhdr->error_code = DDP_TAGGED_UNASSOC_STAG; + break; + case IWARP_OPCODE_SEND_INV: + case IWARP_OPCODE_SEND_SE_INV: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_CANT_INV_STAG; + break; + default: + flush_code = IB_WC_REM_ACCESS_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT; + termhdr->error_code = RDMAP_UNASSOC_STAG; + } + break; + case NES_AEQE_AEID_LLP_RECEIVED_MARKER_AND_LENGTH_FIELDS_DONT_MATCH: + flush_code = IB_WC_LOC_LEN_ERR; + termhdr->layer_etype = (LAYER_MPA << 4) | DDP_LLP; + termhdr->error_code = MPA_MARKER; + break; + case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR: + flush_code = IB_WC_GENERAL_ERR; + termhdr->layer_etype = (LAYER_MPA << 4) | DDP_LLP; + termhdr->error_code = MPA_CRC; + break; + case NES_AEQE_AEID_LLP_SEGMENT_TOO_LARGE: + case NES_AEQE_AEID_LLP_SEGMENT_TOO_SMALL: + flush_code = IB_WC_LOC_LEN_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_CATASTROPHIC; + termhdr->error_code = DDP_CATASTROPHIC_LOCAL; + break; + case NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC: + case NES_AEQE_AEID_DDP_NO_L_BIT: + flush_code = IB_WC_FATAL_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_CATASTROPHIC; + termhdr->error_code = DDP_CATASTROPHIC_LOCAL; + break; + case NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN: + case NES_AEQE_AEID_DDP_INVALID_MSN_RANGE_IS_NOT_VALID: + flush_code = IB_WC_GENERAL_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_MSN_RANGE; + break; + case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER: + flush_code = IB_WC_LOC_LEN_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_TOO_LONG; + break; + case NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION: + flush_code = IB_WC_GENERAL_ERR; + if (is_tagged) { + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER; + termhdr->error_code = DDP_TAGGED_INV_DDP_VER; + } else { + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_DDP_VER; + } + break; + case NES_AEQE_AEID_DDP_UBE_INVALID_MO: + flush_code = IB_WC_GENERAL_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_MO; + break; + case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE: + flush_code = IB_WC_REM_OP_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_MSN_NO_BUF; + break; + case NES_AEQE_AEID_DDP_UBE_INVALID_QN: + flush_code = IB_WC_GENERAL_ERR; + termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER; + termhdr->error_code = DDP_UNTAGGED_INV_QN; + break; + case NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION: + flush_code = IB_WC_GENERAL_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP; + termhdr->error_code = RDMAP_INV_RDMAP_VER; + break; + case NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE: + flush_code = IB_WC_LOC_QP_OP_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP; + termhdr->error_code = RDMAP_UNEXPECTED_OP; + break; + default: + flush_code = IB_WC_FATAL_ERR; + termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP; + termhdr->error_code = RDMAP_UNSPECIFIED; + break; + } + + if (copy_len) + memcpy(termhdr + 1, pkt, copy_len); + + if ((flush_code) && ((NES_AEQE_INBOUND_RDMA & aeq_info) == 0)) { + if (aeq_info & NES_AEQE_SQ) + nesqp->term_sq_flush_code = flush_code; + else + nesqp->term_rq_flush_code = flush_code; + } + + return sizeof(struct nes_terminate_hdr) + copy_len; +} + +static void nes_terminate_connection(struct nes_device *nesdev, struct nes_qp *nesqp, + struct nes_hw_aeqe *aeqe, enum ib_event_type eventtype) +{ + u64 context; + unsigned long flags; + u32 aeq_info; + u16 async_event_id; + u8 tcp_state; + u8 iwarp_state; + u32 termlen = 0; + u32 mod_qp_flags = NES_CQP_QP_IWARP_STATE_TERMINATE | + NES_CQP_QP_TERM_DONT_SEND_FIN; + struct nes_adapter *nesadapter = nesdev->nesadapter; + + if (nesqp->term_flags & NES_TERM_SENT) + return; /* Sanity check */ + + aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]); + tcp_state = (aeq_info & NES_AEQE_TCP_STATE_MASK) >> NES_AEQE_TCP_STATE_SHIFT; + iwarp_state = (aeq_info & NES_AEQE_IWARP_STATE_MASK) >> NES_AEQE_IWARP_STATE_SHIFT; + async_event_id = (u16)aeq_info; + + context = (unsigned long)nesadapter->qp_table[le32_to_cpu( + aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]) - NES_FIRST_QPN]; + if (!context) { + WARN_ON(!context); + return; + } + + nesqp = (struct nes_qp *)(unsigned long)context; + spin_lock_irqsave(&nesqp->lock, flags); + nesqp->hw_iwarp_state = iwarp_state; + nesqp->hw_tcp_state = tcp_state; + nesqp->last_aeq = async_event_id; + nesqp->terminate_eventtype = eventtype; + spin_unlock_irqrestore(&nesqp->lock, flags); + + if (nesadapter->send_term_ok) + termlen = nes_bld_terminate_hdr(nesqp, async_event_id, aeq_info); + else + mod_qp_flags |= NES_CQP_QP_TERM_DONT_SEND_TERM_MSG; + + nes_terminate_start_timer(nesqp); + nesqp->term_flags |= NES_TERM_SENT; + nes_hw_modify_qp(nesdev, nesqp, mod_qp_flags, termlen, 0); +} + +static void nes_terminate_send_fin(struct nes_device *nesdev, + struct nes_qp *nesqp, struct nes_hw_aeqe *aeqe) +{ + u32 aeq_info; + u16 async_event_id; + u8 tcp_state; + u8 iwarp_state; + unsigned long flags; + + aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]); + tcp_state = (aeq_info & NES_AEQE_TCP_STATE_MASK) >> NES_AEQE_TCP_STATE_SHIFT; + iwarp_state = (aeq_info & NES_AEQE_IWARP_STATE_MASK) >> NES_AEQE_IWARP_STATE_SHIFT; + async_event_id = (u16)aeq_info; + + spin_lock_irqsave(&nesqp->lock, flags); + nesqp->hw_iwarp_state = iwarp_state; + nesqp->hw_tcp_state = tcp_state; + nesqp->last_aeq = async_event_id; + spin_unlock_irqrestore(&nesqp->lock, flags); + + /* Send the fin only */ + nes_hw_modify_qp(nesdev, nesqp, NES_CQP_QP_IWARP_STATE_TERMINATE | + NES_CQP_QP_TERM_DONT_SEND_TERM_MSG, 0, 0); +} + +/* Cleanup after a terminate sent or received */ +static void nes_terminate_done(struct nes_qp *nesqp, int timeout_occurred) +{ + u32 next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR; + unsigned long flags; + struct nes_vnic *nesvnic = to_nesvnic(nesqp->ibqp.device); + struct nes_device *nesdev = nesvnic->nesdev; + u8 first_time = 0; + + spin_lock_irqsave(&nesqp->lock, flags); + if (nesqp->hte_added) { + nesqp->hte_added = 0; + next_iwarp_state |= NES_CQP_QP_DEL_HTE; + } + + first_time = (nesqp->term_flags & NES_TERM_DONE) == 0; + nesqp->term_flags |= NES_TERM_DONE; + spin_unlock_irqrestore(&nesqp->lock, flags); + + /* Make sure we go through this only once */ + if (first_time) { + if (timeout_occurred == 0) + del_timer(&nesqp->terminate_timer); + else + next_iwarp_state |= NES_CQP_QP_RESET; + + nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 0); + nes_cm_disconn(nesqp); + } +} + +static void nes_terminate_received(struct nes_device *nesdev, + struct nes_qp *nesqp, struct nes_hw_aeqe *aeqe) +{ + u32 aeq_info; + u8 *pkt; + u32 *mpa; + u8 ddp_ctl; + u8 rdma_ctl; + u16 aeq_id = 0; + + aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]); + if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) { + /* Terminate is not a performance path so the silicon */ + /* did not validate the frame - do it now */ + pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET; + mpa = (u32 *)locate_mpa(pkt, aeq_info); + ddp_ctl = (be32_to_cpu(mpa[0]) >> 8) & 0xff; + rdma_ctl = be32_to_cpu(mpa[0]) & 0xff; + if ((ddp_ctl & 0xc0) != 0x40) + aeq_id = NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC; + else if ((ddp_ctl & 0x03) != 1) + aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION; + else if (be32_to_cpu(mpa[2]) != 2) + aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_QN; + else if (be32_to_cpu(mpa[3]) != 1) + aeq_id = NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN; + else if (be32_to_cpu(mpa[4]) != 0) + aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_MO; + else if ((rdma_ctl & 0xc0) != 0x40) + aeq_id = NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION; + + if (aeq_id) { + /* Bad terminate recvd - send back a terminate */ + aeq_info = (aeq_info & 0xffff0000) | aeq_id; + aeqe->aeqe_words[NES_AEQE_MISC_IDX] = cpu_to_le32(aeq_info); + nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL); + return; + } + } + + nesqp->term_flags |= NES_TERM_RCVD; + nesqp->terminate_eventtype = IB_EVENT_QP_FATAL; + nes_terminate_start_timer(nesqp); + nes_terminate_send_fin(nesdev, nesqp, aeqe); +} + +/* Timeout routine in case terminate fails to complete */ +static void nes_terminate_timeout(unsigned long context) +{ + struct nes_qp *nesqp = (struct nes_qp *)(unsigned long)context; + + nes_terminate_done(nesqp, 1); +} + +/* Set a timer in case hw cannot complete the terminate sequence */ +static void nes_terminate_start_timer(struct nes_qp *nesqp) +{ + init_timer(&nesqp->terminate_timer); + nesqp->terminate_timer.function = nes_terminate_timeout; + nesqp->terminate_timer.expires = jiffies + HZ; + nesqp->terminate_timer.data = (unsigned long)nesqp; + add_timer(&nesqp->terminate_timer); +} + /** * nes_process_iwarp_aeqe */ @@ -2910,28 +3323,27 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, struct nes_hw_aeqe *aeqe) { u64 context; - u64 aeqe_context = 0; unsigned long flags; struct nes_qp *nesqp; + struct nes_hw_cq *hw_cq; + struct nes_cq *nescq; int resource_allocated; - /* struct iw_cm_id *cm_id; */ struct nes_adapter *nesadapter = nesdev->nesadapter; - struct ib_event ibevent; - /* struct iw_cm_event cm_event; */ u32 aeq_info; u32 next_iwarp_state = 0; u16 async_event_id; u8 tcp_state; u8 iwarp_state; + int must_disconn = 1; + int must_terminate = 0; + struct ib_event ibevent; nes_debug(NES_DBG_AEQ, "\n"); aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]); - if ((NES_AEQE_INBOUND_RDMA&aeq_info) || (!(NES_AEQE_QP&aeq_info))) { + if ((NES_AEQE_INBOUND_RDMA & aeq_info) || (!(NES_AEQE_QP & aeq_info))) { context = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]); context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32; } else { - aeqe_context = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]); - aeqe_context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32; context = (unsigned long)nesadapter->qp_table[le32_to_cpu( aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]) - NES_FIRST_QPN]; BUG_ON(!context); @@ -2948,7 +3360,11 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, switch (async_event_id) { case NES_AEQE_AEID_LLP_FIN_RECEIVED: - nesqp = *((struct nes_qp **)&context); + nesqp = (struct nes_qp *)(unsigned long)context; + + if (nesqp->term_flags) + return; /* Ignore it, wait for close complete */ + if (atomic_inc_return(&nesqp->close_timer_started) == 1) { nesqp->cm_id->add_ref(nesqp->cm_id); schedule_nes_timer(nesqp->cm_node, (struct sk_buff *)nesqp, @@ -2959,18 +3375,24 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount), async_event_id, nesqp->last_aeq, tcp_state); } + if ((tcp_state != NES_AEQE_TCP_STATE_CLOSE_WAIT) || (nesqp->ibqp_state != IB_QPS_RTS)) { /* FIN Received but tcp state or IB state moved on, should expect a close complete */ return; } + case NES_AEQE_AEID_LLP_CLOSE_COMPLETE: + nesqp = (struct nes_qp *)(unsigned long)context; + if (nesqp->term_flags) { + nes_terminate_done(nesqp, 0); + return; + } + case NES_AEQE_AEID_LLP_CONNECTION_RESET: - case NES_AEQE_AEID_TERMINATE_SENT: - case NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE: case NES_AEQE_AEID_RESET_SENT: - nesqp = *((struct nes_qp **)&context); + nesqp = (struct nes_qp *)(unsigned long)context; if (async_event_id == NES_AEQE_AEID_RESET_SENT) { tcp_state = NES_AEQE_TCP_STATE_CLOSED; } @@ -2982,12 +3404,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, if ((tcp_state == NES_AEQE_TCP_STATE_CLOSED) || (tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT)) { nesqp->hte_added = 0; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u to remove hte\n", - nesqp->hwqp.qp_id); - nes_hw_modify_qp(nesdev, nesqp, - NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_DEL_HTE, 0); - spin_lock_irqsave(&nesqp->lock, flags); + next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_DEL_HTE; } if ((nesqp->ibqp_state == IB_QPS_RTS) && @@ -2999,151 +3416,106 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING; break; case NES_AEQE_IWARP_STATE_TERMINATE: - next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE; - nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_TERMINATE; - if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) { - next_iwarp_state |= 0x02000000; - nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; - } + must_disconn = 0; /* terminate path takes care of disconn */ + if (nesqp->term_flags == 0) + must_terminate = 1; break; - default: - next_iwarp_state = 0; - } - spin_unlock_irqrestore(&nesqp->lock, flags); - if (next_iwarp_state) { - nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X," - " also added another reference\n", - nesqp->hwqp.qp_id, next_iwarp_state); - nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0); } - nes_cm_disconn(nesqp); } else { if (async_event_id == NES_AEQE_AEID_LLP_FIN_RECEIVED) { /* FIN Received but ib state not RTS, close complete will be on its way */ - spin_unlock_irqrestore(&nesqp->lock, flags); - return; - } - spin_unlock_irqrestore(&nesqp->lock, flags); - if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) { - next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000; - nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; - nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X," - " also added another reference\n", - nesqp->hwqp.qp_id, next_iwarp_state); - nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0); + must_disconn = 0; } - nes_cm_disconn(nesqp); } - break; - case NES_AEQE_AEID_LLP_TERMINATE_RECEIVED: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TERMINATE_RECEIVED" - " event on QP%u \n Q2 Data:\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_FATAL; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } - if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) || - ((nesqp->ibqp_state == IB_QPS_RTS)&& - (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { + + if (must_terminate) + nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL); + else if (must_disconn) { + if (next_iwarp_state) { + nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X\n", + nesqp->hwqp.qp_id, next_iwarp_state); + nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 0); + } nes_cm_disconn(nesqp); - } else { - nesqp->in_disconnect = 0; - wake_up(&nesqp->kick_waitq); } break; - case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR; - nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; - nesqp->last_aeq = async_event_id; - if (nesqp->cm_id) { - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES" - " event on QP%u, remote IP = 0x%08X \n", - nesqp->hwqp.qp_id, - ntohl(nesqp->cm_id->remote_addr.sin_addr.s_addr)); - } else { - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES" - " event on QP%u \n", - nesqp->hwqp.qp_id); - } - spin_unlock_irqrestore(&nesqp->lock, flags); - next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_RESET; - nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_FATAL; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } + + case NES_AEQE_AEID_TERMINATE_SENT: + nesqp = (struct nes_qp *)(unsigned long)context; + nes_terminate_send_fin(nesdev, nesqp, aeqe); break; - case NES_AEQE_AEID_AMP_BAD_STAG_INDEX: - if (NES_AEQE_INBOUND_RDMA&aeq_info) { - nesqp = nesadapter->qp_table[le32_to_cpu( - aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN]; - } else { - /* TODO: get the actual WQE and mask off wqe index */ - context &= ~((u64)511); - nesqp = *((struct nes_qp **)&context); - } - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_BAD_STAG_INDEX event on QP%u\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_ACCESS_ERR; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } + + case NES_AEQE_AEID_LLP_TERMINATE_RECEIVED: + nesqp = (struct nes_qp *)(unsigned long)context; + nes_terminate_received(nesdev, nesqp, aeqe); break; + + case NES_AEQE_AEID_AMP_BAD_STAG_KEY: + case NES_AEQE_AEID_AMP_BAD_STAG_INDEX: case NES_AEQE_AEID_AMP_UNALLOCATED_STAG: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_UNALLOCATED_STAG event on QP%u\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_ACCESS_ERR; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } - break; + case NES_AEQE_AEID_AMP_INVALID_STAG: + case NES_AEQE_AEID_AMP_RIGHTS_VIOLATION: + case NES_AEQE_AEID_AMP_INVALIDATE_NO_REMOTE_ACCESS_RIGHTS: case NES_AEQE_AEID_PRIV_OPERATION_DENIED: - nesqp = nesadapter->qp_table[le32_to_cpu(aeqe->aeqe_words - [NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN]; - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_PRIV_OPERATION_DENIED event on QP%u," - " nesqp = %p, AE reported %p\n", - nesqp->hwqp.qp_id, nesqp, *((struct nes_qp **)&context)); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_ACCESS_ERR; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); + case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER: + case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION: + case NES_AEQE_AEID_AMP_TO_WRAP: + nesqp = (struct nes_qp *)(unsigned long)context; + nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_ACCESS_ERR); + break; + + case NES_AEQE_AEID_LLP_SEGMENT_TOO_LARGE: + case NES_AEQE_AEID_LLP_SEGMENT_TOO_SMALL: + case NES_AEQE_AEID_DDP_UBE_INVALID_MO: + case NES_AEQE_AEID_DDP_UBE_INVALID_QN: + nesqp = (struct nes_qp *)(unsigned long)context; + if (iwarp_opcode(nesqp, aeq_info) > IWARP_OPCODE_TERM) { + aeq_info &= 0xffff0000; + aeq_info |= NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE; + aeqe->aeqe_words[NES_AEQE_MISC_IDX] = cpu_to_le32(aeq_info); } + + case NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE: + case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES: + case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE: + case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR: + case NES_AEQE_AEID_AMP_BAD_QP: + case NES_AEQE_AEID_LLP_RECEIVED_MARKER_AND_LENGTH_FIELDS_DONT_MATCH: + case NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC: + case NES_AEQE_AEID_DDP_NO_L_BIT: + case NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN: + case NES_AEQE_AEID_DDP_INVALID_MSN_RANGE_IS_NOT_VALID: + case NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION: + case NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION: + case NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE: + case NES_AEQE_AEID_AMP_BAD_PD: + case NES_AEQE_AEID_AMP_FASTREG_SHARED: + case NES_AEQE_AEID_AMP_FASTREG_VALID_STAG: + case NES_AEQE_AEID_AMP_FASTREG_MW_STAG: + case NES_AEQE_AEID_AMP_FASTREG_INVALID_RIGHTS: + case NES_AEQE_AEID_AMP_FASTREG_PBL_TABLE_OVERFLOW: + case NES_AEQE_AEID_AMP_FASTREG_INVALID_LENGTH: + case NES_AEQE_AEID_AMP_INVALIDATE_SHARED: + case NES_AEQE_AEID_AMP_INVALIDATE_MR_WITH_BOUND_WINDOWS: + case NES_AEQE_AEID_AMP_MWBIND_VALID_STAG: + case NES_AEQE_AEID_AMP_MWBIND_OF_MR_STAG: + case NES_AEQE_AEID_AMP_MWBIND_TO_ZERO_BASED_STAG: + case NES_AEQE_AEID_AMP_MWBIND_TO_MW_STAG: + case NES_AEQE_AEID_AMP_MWBIND_INVALID_RIGHTS: + case NES_AEQE_AEID_AMP_MWBIND_INVALID_BOUNDS: + case NES_AEQE_AEID_AMP_MWBIND_TO_INVALID_PARENT: + case NES_AEQE_AEID_AMP_MWBIND_BIND_DISABLED: + case NES_AEQE_AEID_BAD_CLOSE: + case NES_AEQE_AEID_RDMA_READ_WHILE_ORD_ZERO: + case NES_AEQE_AEID_STAG_ZERO_INVALID: + case NES_AEQE_AEID_ROE_INVALID_RDMA_READ_REQUEST: + case NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP: + nesqp = (struct nes_qp *)(unsigned long)context; + nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL); break; + case NES_AEQE_AEID_CQ_OPERATION_ERROR: context <<= 1; nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_CQ_OPERATION_ERROR event on CQ%u, %p\n", @@ -3153,83 +3525,19 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, if (resource_allocated) { printk(KERN_ERR PFX "%s: Processing an NES_AEQE_AEID_CQ_OPERATION_ERROR event on CQ%u\n", __func__, le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])); + hw_cq = (struct nes_hw_cq *)(unsigned long)context; + if (hw_cq) { + nescq = container_of(hw_cq, struct nes_cq, hw_cq); + if (nescq->ibcq.event_handler) { + ibevent.device = nescq->ibcq.device; + ibevent.event = IB_EVENT_CQ_ERR; + ibevent.element.cq = &nescq->ibcq; + nescq->ibcq.event_handler(&ibevent, nescq->ibcq.cq_context); + } + } } break; - case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER: - nesqp = nesadapter->qp_table[le32_to_cpu( - aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN]; - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG" - "_FOR_AVAILABLE_BUFFER event on QP%u\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_ACCESS_ERR; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } - /* tell cm to disconnect, cm will queue work to thread */ - nes_cm_disconn(nesqp); - break; - case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_INVALID_MSN" - "_NO_BUFFER_AVAILABLE event on QP%u\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_FATAL; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } - /* tell cm to disconnect, cm will queue work to thread */ - nes_cm_disconn(nesqp); - break; - case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR" - " event on QP%u \n Q2 Data:\n", - nesqp->hwqp.qp_id); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_FATAL; - nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); - } - /* tell cm to disconnect, cm will queue work to thread */ - nes_cm_disconn(nesqp); - break; - /* TODO: additional AEs need to be here */ - case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION: - nesqp = *((struct nes_qp **)&context); - spin_lock_irqsave(&nesqp->lock, flags); - nesqp->hw_iwarp_state = iwarp_state; - nesqp->hw_tcp_state = tcp_state; - nesqp->last_aeq = async_event_id; - spin_unlock_irqrestore(&nesqp->lock, flags); - if (nesqp->ibqp.event_handler) { - ibevent.device = nesqp->ibqp.device; - ibevent.element.qp = &nesqp->ibqp; - ibevent.event = IB_EVENT_QP_ACCESS_ERR; - nesqp->ibqp.event_handler(&ibevent, - nesqp->ibqp.qp_context); - } - nes_cm_disconn(nesqp); - break; + default: nes_debug(NES_DBG_AEQ, "Processing an iWARP related AE for QP, misc = 0x%04X\n", async_event_id); @@ -3238,7 +3546,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, } - /** * nes_iwarp_ce_handler */ @@ -3373,6 +3680,8 @@ void flush_wqes(struct nes_device *nesdev, struct nes_qp *nesqp, { struct nes_cqp_request *cqp_request; struct nes_hw_cqp_wqe *cqp_wqe; + u32 sq_code = (NES_IWARP_CQE_MAJOR_FLUSH << 16) | NES_IWARP_CQE_MINOR_FLUSH; + u32 rq_code = (NES_IWARP_CQE_MAJOR_FLUSH << 16) | NES_IWARP_CQE_MINOR_FLUSH; int ret; cqp_request = nes_get_cqp_request(nesdev); @@ -3389,6 +3698,24 @@ void flush_wqes(struct nes_device *nesdev, struct nes_qp *nesqp, cqp_wqe = &cqp_request->cqp_wqe; nes_fill_init_cqp_wqe(cqp_wqe, nesdev); + /* If wqe in error was identified, set code to be put into cqe */ + if ((nesqp->term_sq_flush_code) && (which_wq & NES_CQP_FLUSH_SQ)) { + which_wq |= NES_CQP_FLUSH_MAJ_MIN; + sq_code = (CQE_MAJOR_DRV << 16) | nesqp->term_sq_flush_code; + nesqp->term_sq_flush_code = 0; + } + + if ((nesqp->term_rq_flush_code) && (which_wq & NES_CQP_FLUSH_RQ)) { + which_wq |= NES_CQP_FLUSH_MAJ_MIN; + rq_code = (CQE_MAJOR_DRV << 16) | nesqp->term_rq_flush_code; + nesqp->term_rq_flush_code = 0; + } + + if (which_wq & NES_CQP_FLUSH_MAJ_MIN) { + cqp_wqe->wqe_words[NES_CQP_QP_WQE_FLUSH_SQ_CODE] = cpu_to_le32(sq_code); + cqp_wqe->wqe_words[NES_CQP_QP_WQE_FLUSH_RQ_CODE] = cpu_to_le32(rq_code); + } + cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_FLUSH_WQES | which_wq); cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(nesqp->hwqp.qp_id); diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h index c3654c6383f..f28a41ba9fa 100644 --- a/drivers/infiniband/hw/nes/nes_hw.h +++ b/drivers/infiniband/hw/nes/nes_hw.h @@ -241,6 +241,7 @@ enum nes_cqp_stag_wqeword_idx { }; #define NES_CQP_OP_IWARP_STATE_SHIFT 28 +#define NES_CQP_OP_TERMLEN_SHIFT 28 enum nes_cqp_qp_bits { NES_CQP_QP_ARP_VALID = (1<<8), @@ -265,12 +266,16 @@ enum nes_cqp_qp_bits { NES_CQP_QP_IWARP_STATE_TERMINATE = (5<<NES_CQP_OP_IWARP_STATE_SHIFT), NES_CQP_QP_IWARP_STATE_ERROR = (6<<NES_CQP_OP_IWARP_STATE_SHIFT), NES_CQP_QP_IWARP_STATE_MASK = (7<<NES_CQP_OP_IWARP_STATE_SHIFT), + NES_CQP_QP_TERM_DONT_SEND_FIN = (1<<24), + NES_CQP_QP_TERM_DONT_SEND_TERM_MSG = (1<<25), NES_CQP_QP_RESET = (1<<31), }; enum nes_cqp_qp_wqe_word_idx { NES_CQP_QP_WQE_CONTEXT_LOW_IDX = 6, NES_CQP_QP_WQE_CONTEXT_HIGH_IDX = 7, + NES_CQP_QP_WQE_FLUSH_SQ_CODE = 8, + NES_CQP_QP_WQE_FLUSH_RQ_CODE = 9, NES_CQP_QP_WQE_NEW_MSS_IDX = 15, }; @@ -361,6 +366,7 @@ enum nes_cqp_arp_bits { enum nes_cqp_flush_bits { NES_CQP_FLUSH_SQ = (1<<30), NES_CQP_FLUSH_RQ = (1<<31), + NES_CQP_FLUSH_MAJ_MIN = (1<<28), }; enum nes_cqe_opcode_bits { @@ -633,11 +639,14 @@ enum nes_aeqe_bits { NES_AEQE_INBOUND_RDMA = (1<<19), NES_AEQE_IWARP_STATE_MASK = (7<<20), NES_AEQE_TCP_STATE_MASK = (0xf<<24), + NES_AEQE_Q2_DATA_WRITTEN = (0x3<<28), NES_AEQE_VALID = (1<<31), }; #define NES_AEQE_IWARP_STATE_SHIFT 20 #define NES_AEQE_TCP_STATE_SHIFT 24 +#define NES_AEQE_Q2_DATA_ETHERNET (1<<28) +#define NES_AEQE_Q2_DATA_MPA (1<<29) enum nes_aeqe_iwarp_state { NES_AEQE_IWARP_STATE_NON_EXISTANT = 0, @@ -751,6 +760,15 @@ enum nes_iwarp_sq_wqe_bits { NES_IWARP_SQ_OP_NOP = 12, }; +enum nes_iwarp_cqe_major_code { + NES_IWARP_CQE_MAJOR_FLUSH = 1, + NES_IWARP_CQE_MAJOR_DRV = 0x8000 +}; + +enum nes_iwarp_cqe_minor_code { + NES_IWARP_CQE_MINOR_FLUSH = 1 +}; + #define NES_EEPROM_READ_REQUEST (1<<16) #define NES_MAC_ADDR_VALID (1<<20) @@ -1119,6 +1137,7 @@ struct nes_adapter { u8 netdev_max; /* from host nic address count in EEPROM */ u8 port_count; u8 virtwq; + u8 send_term_ok; u8 et_use_adaptive_rx_coalesce; u8 adapter_fcn_count; u8 pft_mcast_map[NES_PFT_SIZE]; @@ -1217,6 +1236,90 @@ struct nes_ib_device { u32 num_pd; }; +enum nes_hdrct_flags { + DDP_LEN_FLAG = 0x80, + DDP_HDR_FLAG = 0x40, + RDMA_HDR_FLAG = 0x20 +}; + +enum nes_term_layers { + LAYER_RDMA = 0, + LAYER_DDP = 1, + LAYER_MPA = 2 +}; + +enum nes_term_error_types { + RDMAP_CATASTROPHIC = 0, + RDMAP_REMOTE_PROT = 1, + RDMAP_REMOTE_OP = 2, + DDP_CATASTROPHIC = 0, + DDP_TAGGED_BUFFER = 1, + DDP_UNTAGGED_BUFFER = 2, + DDP_LLP = 3 +}; + +enum nes_term_rdma_errors { + RDMAP_INV_STAG = 0x00, + RDMAP_INV_BOUNDS = 0x01, + RDMAP_ACCESS = 0x02, + RDMAP_UNASSOC_STAG = 0x03, + RDMAP_TO_WRAP = 0x04, + RDMAP_INV_RDMAP_VER = 0x05, + RDMAP_UNEXPECTED_OP = 0x06, + RDMAP_CATASTROPHIC_LOCAL = 0x07, + RDMAP_CATASTROPHIC_GLOBAL = 0x08, + RDMAP_CANT_INV_STAG = 0x09, + RDMAP_UNSPECIFIED = 0xff +}; + +enum nes_term_ddp_errors { + DDP_CATASTROPHIC_LOCAL = 0x00, + DDP_TAGGED_INV_STAG = 0x00, + DDP_TAGGED_BOUNDS = 0x01, + DDP_TAGGED_UNASSOC_STAG = 0x02, + DDP_TAGGED_TO_WRAP = 0x03, + DDP_TAGGED_INV_DDP_VER = 0x04, + DDP_UNTAGGED_INV_QN = 0x01, + DDP_UNTAGGED_INV_MSN_NO_BUF = 0x02, + DDP_UNTAGGED_INV_MSN_RANGE = 0x03, + DDP_UNTAGGED_INV_MO = 0x04, + DDP_UNTAGGED_INV_TOO_LONG = 0x05, + DDP_UNTAGGED_INV_DDP_VER = 0x06 +}; + +enum nes_term_mpa_errors { + MPA_CLOSED = 0x01, + MPA_CRC = 0x02, + MPA_MARKER = 0x03, + MPA_REQ_RSP = 0x04, +}; + +struct nes_terminate_hdr { + u8 layer_etype; + u8 error_code; + u8 hdrct; + u8 rsvd; +}; + +/* Used to determine how to fill in terminate error codes */ +#define IWARP_OPCODE_WRITE 0 +#define IWARP_OPCODE_READREQ 1 +#define IWARP_OPCODE_READRSP 2 +#define IWARP_OPCODE_SEND 3 +#define IWARP_OPCODE_SEND_INV 4 +#define IWARP_OPCODE_SEND_SE 5 +#define IWARP_OPCODE_SEND_SE_INV 6 +#define IWARP_OPCODE_TERM 7 + +/* These values are used only during terminate processing */ +#define TERM_DDP_LEN_TAGGED 14 +#define TERM_DDP_LEN_UNTAGGED 18 +#define TERM_RDMA_LEN 28 +#define RDMA_OPCODE_MASK 0x0f +#define RDMA_READ_REQ_OPCODE 1 +#define BAD_FRAME_OFFSET 64 +#define CQE_MAJOR_DRV 0x8000 + #define nes_vlan_rx vlan_hwaccel_receive_skb #define nes_netif_rx netif_receive_skb diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c index a282031d15c..9687c397ce1 100644 --- a/drivers/infiniband/hw/nes/nes_utils.c +++ b/drivers/infiniband/hw/nes/nes_utils.c @@ -183,6 +183,9 @@ int nes_read_eeprom_values(struct nes_device *nesdev, struct nes_adapter *nesada } else if (((major_ver == 2) && (minor_ver > 21)) || ((major_ver > 2) && (major_ver != 255))) { nesadapter->virtwq = 1; } + if (((major_ver == 3) && (minor_ver >= 16)) || (major_ver > 3)) + nesadapter->send_term_ok = 1; + nesadapter->firmware_version = (((u32)(u8)(eeprom_data>>8)) << 16) + (u32)((u8)eeprom_data); @@ -548,7 +551,7 @@ struct nes_cqp_request *nes_get_cqp_request(struct nes_device *nesdev) spin_unlock_irqrestore(&nesdev->cqp.lock, flags); } if (cqp_request == NULL) { - cqp_request = kzalloc(sizeof(struct nes_cqp_request), GFP_KERNEL); + cqp_request = kzalloc(sizeof(struct nes_cqp_request), GFP_ATOMIC); if (cqp_request) { cqp_request->dynamic = 1; INIT_LIST_HEAD(&cqp_request->list); diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 21e0fd336cf..a680c42d6e8 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -667,15 +667,32 @@ static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *prop */ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *props) { + struct nes_vnic *nesvnic = to_nesvnic(ibdev); + struct net_device *netdev = nesvnic->netdev; + memset(props, 0, sizeof(*props)); - props->max_mtu = IB_MTU_2048; - props->active_mtu = IB_MTU_2048; + props->max_mtu = IB_MTU_4096; + + if (netdev->mtu >= 4096) + props->active_mtu = IB_MTU_4096; + else if (netdev->mtu >= 2048) + props->active_mtu = IB_MTU_2048; + else if (netdev->mtu >= 1024) + props->active_mtu = IB_MTU_1024; + else if (netdev->mtu >= 512) + props->active_mtu = IB_MTU_512; + else + props->active_mtu = IB_MTU_256; + props->lid = 1; props->lmc = 0; props->sm_lid = 0; props->sm_sl = 0; - props->state = IB_PORT_ACTIVE; + if (nesvnic->linkup) + props->state = IB_PORT_ACTIVE; + else + props->state = IB_PORT_DOWN; props->phys_state = 0; props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_REINIT_SUP | IB_PORT_VENDOR_CLASS_SUP | IB_PORT_BOOT_MGMT_SUP; @@ -1506,12 +1523,45 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd, /** + * nes_clean_cq + */ +static void nes_clean_cq(struct nes_qp *nesqp, struct nes_cq *nescq) +{ + u32 cq_head; + u32 lo; + u32 hi; + u64 u64temp; + unsigned long flags = 0; + + spin_lock_irqsave(&nescq->lock, flags); + + cq_head = nescq->hw_cq.cq_head; + while (le32_to_cpu(nescq->hw_cq.cq_vbase[cq_head].cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_VALID) { + rmb(); + lo = le32_to_cpu(nescq->hw_cq.cq_vbase[cq_head].cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]); + hi = le32_to_cpu(nescq->hw_cq.cq_vbase[cq_head].cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]); + u64temp = (((u64)hi) << 32) | ((u64)lo); + u64temp &= ~(NES_SW_CONTEXT_ALIGN-1); + if (u64temp == (u64)(unsigned long)nesqp) { + /* Zero the context value so cqe will be ignored */ + nescq->hw_cq.cq_vbase[cq_head].cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX] = 0; + nescq->hw_cq.cq_vbase[cq_head].cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX] = 0; + } + + if (++cq_head >= nescq->hw_cq.cq_size) + cq_head = 0; + } + + spin_unlock_irqrestore(&nescq->lock, flags); +} + + +/** * nes_destroy_qp */ static int nes_destroy_qp(struct ib_qp *ibqp) { struct nes_qp *nesqp = to_nesqp(ibqp); - /* struct nes_vnic *nesvnic = to_nesvnic(ibqp->device); */ struct nes_ucontext *nes_ucontext; struct ib_qp_attr attr; struct iw_cm_id *cm_id; @@ -1548,7 +1598,6 @@ static int nes_destroy_qp(struct ib_qp *ibqp) nes_debug(NES_DBG_QP, "OFA CM event_handler returned, ret=%d\n", ret); } - if (nesqp->user_mode) { if ((ibqp->uobject)&&(ibqp->uobject->context)) { nes_ucontext = to_nesucontext(ibqp->uobject->context); @@ -1560,6 +1609,13 @@ static int nes_destroy_qp(struct ib_qp *ibqp) } if (nesqp->pbl_pbase) kunmap(nesqp->page); + } else { + /* Clean any pending completions from the cq(s) */ + if (nesqp->nesscq) + nes_clean_cq(nesqp, nesqp->nesscq); + + if ((nesqp->nesrcq) && (nesqp->nesrcq != nesqp->nesscq)) + nes_clean_cq(nesqp, nesqp->nesrcq); } nes_rem_ref(&nesqp->ibqp); @@ -2884,7 +2940,7 @@ static int nes_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, * nes_hw_modify_qp */ int nes_hw_modify_qp(struct nes_device *nesdev, struct nes_qp *nesqp, - u32 next_iwarp_state, u32 wait_completion) + u32 next_iwarp_state, u32 termlen, u32 wait_completion) { struct nes_hw_cqp_wqe *cqp_wqe; /* struct iw_cm_id *cm_id = nesqp->cm_id; */ @@ -2916,6 +2972,13 @@ int nes_hw_modify_qp(struct nes_device *nesdev, struct nes_qp *nesqp, set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id); set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, (u64)nesqp->nesqp_context_pbase); + /* If sending a terminate message, fill in the length (in words) */ + if (((next_iwarp_state & NES_CQP_QP_IWARP_STATE_MASK) == NES_CQP_QP_IWARP_STATE_TERMINATE) && + !(next_iwarp_state & NES_CQP_QP_TERM_DONT_SEND_TERM_MSG)) { + termlen = ((termlen + 3) >> 2) << NES_CQP_OP_TERMLEN_SHIFT; + set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_NEW_MSS_IDX, termlen); + } + atomic_set(&cqp_request->refcount, 2); nes_post_cqp_request(nesdev, cqp_request); @@ -3086,6 +3149,9 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, } nes_debug(NES_DBG_MOD_QP, "QP%u: new state = error\n", nesqp->hwqp.qp_id); + if (nesqp->term_flags) + del_timer(&nesqp->terminate_timer); + next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR; /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */ if (nesqp->hte_added) { @@ -3163,7 +3229,7 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (issue_modify_qp) { nes_debug(NES_DBG_MOD_QP, "call nes_hw_modify_qp\n"); - ret = nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 1); + ret = nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 1); if (ret) nes_debug(NES_DBG_MOD_QP, "nes_hw_modify_qp (next_iwarp_state = 0x%08X)" " failed for QP%u.\n", @@ -3328,6 +3394,12 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, head = nesqp->hwqp.sq_head; while (ib_wr) { + /* Check for QP error */ + if (nesqp->term_flags) { + err = -EINVAL; + break; + } + /* Check for SQ overflow */ if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) { err = -EINVAL; @@ -3484,6 +3556,12 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr, head = nesqp->hwqp.rq_head; while (ib_wr) { + /* Check for QP error */ + if (nesqp->term_flags) { + err = -EINVAL; + break; + } + if (ib_wr->num_sge > nesdev->nesadapter->max_sge) { err = -EINVAL; break; @@ -3547,7 +3625,6 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) { u64 u64temp; u64 wrid; - /* u64 u64temp; */ unsigned long flags = 0; struct nes_vnic *nesvnic = to_nesvnic(ibcq->device); struct nes_device *nesdev = nesvnic->nesdev; @@ -3555,12 +3632,13 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) struct nes_qp *nesqp; struct nes_hw_cqe cqe; u32 head; - u32 wq_tail; + u32 wq_tail = 0; u32 cq_size; u32 cqe_count = 0; u32 wqe_index; u32 u32temp; - /* u32 counter; */ + u32 move_cq_head = 1; + u32 err_code; nes_debug(NES_DBG_CQ, "\n"); @@ -3570,29 +3648,40 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) cq_size = nescq->hw_cq.cq_size; while (cqe_count < num_entries) { - if (le32_to_cpu(nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & - NES_CQE_VALID) { - /* - * Make sure we read CQ entry contents *after* - * we've checked the valid bit. - */ - rmb(); - - cqe = nescq->hw_cq.cq_vbase[head]; - nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX] = 0; - u32temp = le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]); - wqe_index = u32temp & - (nesdev->nesadapter->max_qp_wr - 1); - u32temp &= ~(NES_SW_CONTEXT_ALIGN-1); - /* parse CQE, get completion context from WQE (either rq or sq */ - u64temp = (((u64)(le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX])))<<32) | - ((u64)u32temp); - nesqp = *((struct nes_qp **)&u64temp); + if ((le32_to_cpu(nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & + NES_CQE_VALID) == 0) + break; + + /* + * Make sure we read CQ entry contents *after* + * we've checked the valid bit. + */ + rmb(); + + cqe = nescq->hw_cq.cq_vbase[head]; + u32temp = le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]); + wqe_index = u32temp & (nesdev->nesadapter->max_qp_wr - 1); + u32temp &= ~(NES_SW_CONTEXT_ALIGN-1); + /* parse CQE, get completion context from WQE (either rq or sq) */ + u64temp = (((u64)(le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX])))<<32) | + ((u64)u32temp); + + if (u64temp) { + nesqp = (struct nes_qp *)(unsigned long)u64temp; memset(entry, 0, sizeof *entry); if (cqe.cqe_words[NES_CQE_ERROR_CODE_IDX] == 0) { entry->status = IB_WC_SUCCESS; } else { - entry->status = IB_WC_WR_FLUSH_ERR; + err_code = le32_to_cpu(cqe.cqe_words[NES_CQE_ERROR_CODE_IDX]); + if (NES_IWARP_CQE_MAJOR_DRV == (err_code >> 16)) { + entry->status = err_code & 0x0000ffff; + + /* The rest of the cqe's will be marked as flushed */ + nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_ERROR_CODE_IDX] = + cpu_to_le32((NES_IWARP_CQE_MAJOR_FLUSH << 16) | + NES_IWARP_CQE_MINOR_FLUSH); + } else + entry->status = IB_WC_WR_FLUSH_ERR; } entry->qp = &nesqp->ibqp; @@ -3601,20 +3690,18 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) if (le32_to_cpu(cqe.cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_SQ) { if (nesqp->skip_lsmm) { nesqp->skip_lsmm = 0; - wq_tail = nesqp->hwqp.sq_tail++; + nesqp->hwqp.sq_tail++; } /* Working on a SQ Completion*/ - wq_tail = wqe_index; - nesqp->hwqp.sq_tail = (wqe_index+1)&(nesqp->hwqp.sq_size - 1); - wrid = (((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wq_tail]. + wrid = (((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wqe_index]. wqe_words[NES_IWARP_SQ_WQE_COMP_SCRATCH_HIGH_IDX]))) << 32) | - ((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wq_tail]. + ((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wqe_index]. wqe_words[NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX]))); - entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail]. + entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wqe_index]. wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX]); - switch (le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail]. + switch (le32_to_cpu(nesqp->hwqp.sq_vbase[wqe_index]. wqe_words[NES_IWARP_SQ_WQE_MISC_IDX]) & 0x3f) { case NES_IWARP_SQ_OP_RDMAW: nes_debug(NES_DBG_CQ, "Operation = RDMA WRITE.\n"); @@ -3623,7 +3710,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) case NES_IWARP_SQ_OP_RDMAR: nes_debug(NES_DBG_CQ, "Operation = RDMA READ.\n"); entry->opcode = IB_WC_RDMA_READ; - entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail]. + entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wqe_index]. wqe_words[NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX]); break; case NES_IWARP_SQ_OP_SENDINV: @@ -3634,33 +3721,54 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) entry->opcode = IB_WC_SEND; break; } + + nesqp->hwqp.sq_tail = (wqe_index+1)&(nesqp->hwqp.sq_size - 1); + if ((entry->status != IB_WC_SUCCESS) && (nesqp->hwqp.sq_tail != nesqp->hwqp.sq_head)) { + move_cq_head = 0; + wq_tail = nesqp->hwqp.sq_tail; + } } else { /* Working on a RQ Completion*/ - wq_tail = wqe_index; - nesqp->hwqp.rq_tail = (wqe_index+1)&(nesqp->hwqp.rq_size - 1); entry->byte_len = le32_to_cpu(cqe.cqe_words[NES_CQE_PAYLOAD_LENGTH_IDX]); - wrid = ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wq_tail].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_LOW_IDX]))) | - ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wq_tail].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_HIGH_IDX]))<<32); + wrid = ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wqe_index].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_LOW_IDX]))) | + ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wqe_index].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_HIGH_IDX]))<<32); entry->opcode = IB_WC_RECV; + + nesqp->hwqp.rq_tail = (wqe_index+1)&(nesqp->hwqp.rq_size - 1); + if ((entry->status != IB_WC_SUCCESS) && (nesqp->hwqp.rq_tail != nesqp->hwqp.rq_head)) { + move_cq_head = 0; + wq_tail = nesqp->hwqp.rq_tail; + } } + entry->wr_id = wrid; + entry++; + cqe_count++; + } + if (move_cq_head) { + nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX] = 0; if (++head >= cq_size) head = 0; - cqe_count++; nescq->polled_completions++; + if ((nescq->polled_completions > (cq_size / 2)) || (nescq->polled_completions == 255)) { nes_debug(NES_DBG_CQ, "CQ%u Issuing CQE Allocate since more than half of cqes" - " are pending %u of %u.\n", - nescq->hw_cq.cq_number, nescq->polled_completions, cq_size); + " are pending %u of %u.\n", + nescq->hw_cq.cq_number, nescq->polled_completions, cq_size); nes_write32(nesdev->regs+NES_CQE_ALLOC, - nescq->hw_cq.cq_number | (nescq->polled_completions << 16)); + nescq->hw_cq.cq_number | (nescq->polled_completions << 16)); nescq->polled_completions = 0; } - entry++; - } else - break; + } else { + /* Update the wqe index and set status to flush */ + wqe_index = le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]); + wqe_index = (wqe_index & (~(nesdev->nesadapter->max_qp_wr - 1))) | wq_tail; + nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX] = + cpu_to_le32(wqe_index); + move_cq_head = 1; /* ready for next pass */ + } } if (nescq->polled_completions) { diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h index 41c07f29f7c..89822d75f82 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.h +++ b/drivers/infiniband/hw/nes/nes_verbs.h @@ -40,6 +40,10 @@ struct nes_device; #define NES_MAX_USER_DB_REGIONS 4096 #define NES_MAX_USER_WQ_REGIONS 4096 +#define NES_TERM_SENT 0x01 +#define NES_TERM_RCVD 0x02 +#define NES_TERM_DONE 0x04 + struct nes_ucontext { struct ib_ucontext ibucontext; struct nes_device *nesdev; @@ -119,6 +123,11 @@ struct nes_wq { spinlock_t lock; }; +struct disconn_work { + struct work_struct work; + struct nes_qp *nesqp; +}; + struct iw_cm_id; struct ietf_mpa_frame; @@ -127,7 +136,6 @@ struct nes_qp { void *allocated_buffer; struct iw_cm_id *cm_id; struct workqueue_struct *wq; - struct work_struct disconn_work; struct nes_cq *nesscq; struct nes_cq *nesrcq; struct nes_pd *nespd; @@ -155,9 +163,13 @@ struct nes_qp { void *pbl_vbase; dma_addr_t pbl_pbase; struct page *page; + struct timer_list terminate_timer; + enum ib_event_type terminate_eventtype; wait_queue_head_t kick_waitq; u16 in_disconnect; u16 private_data_len; + u16 term_sq_flush_code; + u16 term_rq_flush_code; u8 active_conn; u8 skip_lsmm; u8 user_mode; @@ -165,7 +177,7 @@ struct nes_qp { u8 hw_iwarp_state; u8 flush_issued; u8 hw_tcp_state; - u8 disconn_pending; + u8 term_flags; u8 destroyed; }; #endif /* NES_VERBS_H */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 181b1f32325..8f4b4fca2a1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -31,7 +31,6 @@ */ #include <rdma/ib_cm.h> -#include <rdma/ib_cache.h> #include <net/dst.h> #include <net/icmp.h> #include <linux/icmpv6.h> diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index e7e5adf84e8..e35f4a0ea9d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -36,7 +36,6 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> -#include <rdma/ib_cache.h> #include <linux/ip.h> #include <linux/tcp.h> diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index e319d91f60a..2bf5116deec 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -604,8 +604,11 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) skb_queue_len(&neigh->queue)); goto err_drop; } - } else + } else { + spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha)); + return; + } } else { neigh->ah = NULL; @@ -688,7 +691,9 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, ipoib_dbg(priv, "Send unicast ARP to %04x\n", be16_to_cpu(path->pathrec.dlid)); + spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); + return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { /* put pseudoheader back on for next time */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index a0e97532e71..25874fc680c 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -720,7 +720,9 @@ out: } } + spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); + return; } unlock: @@ -758,6 +760,20 @@ void ipoib_mcast_dev_flush(struct net_device *dev) } } +static int ipoib_mcast_addr_is_valid(const u8 *addr, unsigned int addrlen, + const u8 *broadcast) +{ + if (addrlen != INFINIBAND_ALEN) + return 0; + /* reserved QPN, prefix, scope */ + if (memcmp(addr, broadcast, 6)) + return 0; + /* signature lower, pkey */ + if (memcmp(addr + 7, broadcast + 7, 3)) + return 0; + return 1; +} + void ipoib_mcast_restart_task(struct work_struct *work) { struct ipoib_dev_priv *priv = @@ -791,6 +807,11 @@ void ipoib_mcast_restart_task(struct work_struct *work) for (mclist = dev->mc_list; mclist; mclist = mclist->next) { union ib_gid mgid; + if (!ipoib_mcast_addr_is_valid(mclist->dmi_addr, + mclist->dmi_addrlen, + dev->broadcast)) + continue; + memcpy(mgid.raw, mclist->dmi_addr + 4, sizeof mgid); mcast = __ipoib_mcast_find(dev, &mgid); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 95fe0452dae..6c6a09b1c0f 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -880,6 +880,14 @@ static unsigned int atkbd_hp_zv6100_forced_release_keys[] = { }; /* + * Perform fixup for HP (Compaq) Presario R4000 R4100 R4200 that don't generate + * release for their volume buttons + */ +static unsigned int atkbd_hp_r4000_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; + +/* * Samsung NC10,NC20 with Fn+F? key release not working */ static unsigned int atkbd_samsung_forced_release_keys[] = { @@ -1537,6 +1545,33 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_hp_zv6100_forced_release_keys, }, { + .ident = "HP Presario R4000", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_r4000_forced_release_keys, + }, + { + .ident = "HP Presario R4100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_r4000_forced_release_keys, + }, + { + .ident = "HP Presario R4200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_r4000_forced_release_keys, + }, + { .ident = "Inventec Symphony", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index ae04d8a494e..ccbf23ece8e 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -382,6 +382,14 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, }, + { + .ident = "Acer Aspire 5536", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + }, + }, { } }; diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 3710ff88fc1..556acff3952 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -171,6 +171,14 @@ static int set_chunk_size(struct dm_exception_store *store, */ chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9); + return dm_exception_store_set_chunk_size(store, chunk_size_ulong, + error); +} + +int dm_exception_store_set_chunk_size(struct dm_exception_store *store, + unsigned long chunk_size_ulong, + char **error) +{ /* Check chunk_size is a power of 2 */ if (!is_power_of_2(chunk_size_ulong)) { *error = "Chunk size is not a power of 2"; @@ -183,6 +191,11 @@ static int set_chunk_size(struct dm_exception_store *store, return -EINVAL; } + if (chunk_size_ulong > INT_MAX >> SECTOR_SHIFT) { + *error = "Chunk size is too high"; + return -EINVAL; + } + store->chunk_size = chunk_size_ulong; store->chunk_mask = chunk_size_ulong - 1; store->chunk_shift = ffs(chunk_size_ulong) - 1; diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h index 2442c8c0789..812c71872ba 100644 --- a/drivers/md/dm-exception-store.h +++ b/drivers/md/dm-exception-store.h @@ -168,6 +168,10 @@ static inline chunk_t sector_to_chunk(struct dm_exception_store *store, int dm_exception_store_type_register(struct dm_exception_store_type *type); int dm_exception_store_type_unregister(struct dm_exception_store_type *type); +int dm_exception_store_set_chunk_size(struct dm_exception_store *store, + unsigned long chunk_size_ulong, + char **error); + int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, unsigned *args_used, struct dm_exception_store **store); diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c index e69b9656099..652bd33109e 100644 --- a/drivers/md/dm-log-userspace-base.c +++ b/drivers/md/dm-log-userspace-base.c @@ -21,6 +21,7 @@ struct log_c { struct dm_target *ti; uint32_t region_size; region_t region_count; + uint64_t luid; char uuid[DM_UUID_LEN]; char *usr_argv_str; @@ -63,7 +64,7 @@ static int userspace_do_request(struct log_c *lc, const char *uuid, * restored. */ retry: - r = dm_consult_userspace(uuid, request_type, data, + r = dm_consult_userspace(uuid, lc->luid, request_type, data, data_size, rdata, rdata_size); if (r != -ESRCH) @@ -74,14 +75,15 @@ retry: set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(2*HZ); DMWARN("Attempting to contact userspace log server..."); - r = dm_consult_userspace(uuid, DM_ULOG_CTR, lc->usr_argv_str, + r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_CTR, + lc->usr_argv_str, strlen(lc->usr_argv_str) + 1, NULL, NULL); if (!r) break; } DMINFO("Reconnected to userspace log server... DM_ULOG_CTR complete"); - r = dm_consult_userspace(uuid, DM_ULOG_RESUME, NULL, + r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_RESUME, NULL, 0, NULL, NULL); if (!r) goto retry; @@ -111,10 +113,9 @@ static int build_constructor_string(struct dm_target *ti, return -ENOMEM; } - for (i = 0, str_size = 0; i < argc; i++) - str_size += sprintf(str + str_size, "%s ", argv[i]); - str_size += sprintf(str + str_size, "%llu", - (unsigned long long)ti->len); + str_size = sprintf(str, "%llu", (unsigned long long)ti->len); + for (i = 0; i < argc; i++) + str_size += sprintf(str + str_size, " %s", argv[i]); *ctr_str = str; return str_size; @@ -154,6 +155,9 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, return -ENOMEM; } + /* The ptr value is sufficient for local unique id */ + lc->luid = (uint64_t)lc; + lc->ti = ti; if (strlen(argv[0]) > (DM_UUID_LEN - 1)) { @@ -173,7 +177,7 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, } /* Send table string */ - r = dm_consult_userspace(lc->uuid, DM_ULOG_CTR, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR, ctr_str, str_size, NULL, NULL); if (r == -ESRCH) { @@ -183,7 +187,7 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, /* Since the region size does not change, get it now */ rdata_size = sizeof(rdata); - r = dm_consult_userspace(lc->uuid, DM_ULOG_GET_REGION_SIZE, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_GET_REGION_SIZE, NULL, 0, (char *)&rdata, &rdata_size); if (r) { @@ -212,7 +216,7 @@ static void userspace_dtr(struct dm_dirty_log *log) int r; struct log_c *lc = log->context; - r = dm_consult_userspace(lc->uuid, DM_ULOG_DTR, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_DTR, NULL, 0, NULL, NULL); @@ -227,7 +231,7 @@ static int userspace_presuspend(struct dm_dirty_log *log) int r; struct log_c *lc = log->context; - r = dm_consult_userspace(lc->uuid, DM_ULOG_PRESUSPEND, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_PRESUSPEND, NULL, 0, NULL, NULL); @@ -239,7 +243,7 @@ static int userspace_postsuspend(struct dm_dirty_log *log) int r; struct log_c *lc = log->context; - r = dm_consult_userspace(lc->uuid, DM_ULOG_POSTSUSPEND, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_POSTSUSPEND, NULL, 0, NULL, NULL); @@ -252,7 +256,7 @@ static int userspace_resume(struct dm_dirty_log *log) struct log_c *lc = log->context; lc->in_sync_hint = 0; - r = dm_consult_userspace(lc->uuid, DM_ULOG_RESUME, + r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_RESUME, NULL, 0, NULL, NULL); @@ -561,6 +565,7 @@ static int userspace_status(struct dm_dirty_log *log, status_type_t status_type, char *result, unsigned maxlen) { int r = 0; + char *table_args; size_t sz = (size_t)maxlen; struct log_c *lc = log->context; @@ -577,8 +582,12 @@ static int userspace_status(struct dm_dirty_log *log, status_type_t status_type, break; case STATUSTYPE_TABLE: sz = 0; - DMEMIT("%s %u %s %s", log->type->name, lc->usr_argc + 1, - lc->uuid, lc->usr_argv_str); + table_args = strchr(lc->usr_argv_str, ' '); + BUG_ON(!table_args); /* There will always be a ' ' */ + table_args++; + + DMEMIT("%s %u %s %s ", log->type->name, lc->usr_argc, + lc->uuid, table_args); break; } return (r) ? 0 : (int)sz; diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 8ce74d95ae4..ba0edad2d04 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -147,7 +147,8 @@ static void cn_ulog_callback(void *data) /** * dm_consult_userspace - * @uuid: log's uuid (must be DM_UUID_LEN in size) + * @uuid: log's universal unique identifier (must be DM_UUID_LEN in size) + * @luid: log's local unique identifier * @request_type: found in include/linux/dm-log-userspace.h * @data: data to tx to the server * @data_size: size of data in bytes @@ -163,7 +164,7 @@ static void cn_ulog_callback(void *data) * * Returns: 0 on success, -EXXX on failure **/ -int dm_consult_userspace(const char *uuid, int request_type, +int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type, char *data, size_t data_size, char *rdata, size_t *rdata_size) { @@ -190,6 +191,7 @@ resend: memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - overhead_size); memcpy(tfr->uuid, uuid, DM_UUID_LEN); + tfr->luid = luid; tfr->seq = dm_ulog_seq++; /* diff --git a/drivers/md/dm-log-userspace-transfer.h b/drivers/md/dm-log-userspace-transfer.h index c26d8e4e271..04ee874f915 100644 --- a/drivers/md/dm-log-userspace-transfer.h +++ b/drivers/md/dm-log-userspace-transfer.h @@ -11,7 +11,7 @@ int dm_ulog_tfr_init(void); void dm_ulog_tfr_exit(void); -int dm_consult_userspace(const char *uuid, int request_type, +int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type, char *data, size_t data_size, char *rdata, size_t *rdata_size); diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 9726577cde4..33f179e66bf 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -648,7 +648,13 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes) */ dm_rh_inc_pending(ms->rh, &sync); dm_rh_inc_pending(ms->rh, &nosync); - ms->log_failure = dm_rh_flush(ms->rh) ? 1 : 0; + + /* + * If the flush fails on a previous call and succeeds here, + * we must not reset the log_failure variable. We need + * userspace interaction to do that. + */ + ms->log_failure = dm_rh_flush(ms->rh) ? 1 : ms->log_failure; /* * Dispatch io. diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 6e3fe4f1493..d5b2e08750d 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -106,6 +106,13 @@ struct pstore { void *zero_area; /* + * An area used for header. The header can be written + * concurrently with metadata (when invalidating the snapshot), + * so it needs a separate buffer. + */ + void *header_area; + + /* * Used to keep track of which metadata area the data in * 'chunk' refers to. */ @@ -148,16 +155,27 @@ static int alloc_area(struct pstore *ps) */ ps->area = vmalloc(len); if (!ps->area) - return r; + goto err_area; ps->zero_area = vmalloc(len); - if (!ps->zero_area) { - vfree(ps->area); - return r; - } + if (!ps->zero_area) + goto err_zero_area; memset(ps->zero_area, 0, len); + ps->header_area = vmalloc(len); + if (!ps->header_area) + goto err_header_area; + return 0; + +err_header_area: + vfree(ps->zero_area); + +err_zero_area: + vfree(ps->area); + +err_area: + return r; } static void free_area(struct pstore *ps) @@ -169,6 +187,10 @@ static void free_area(struct pstore *ps) if (ps->zero_area) vfree(ps->zero_area); ps->zero_area = NULL; + + if (ps->header_area) + vfree(ps->header_area); + ps->header_area = NULL; } struct mdata_req { @@ -188,7 +210,8 @@ static void do_metadata(struct work_struct *work) /* * Read or write a chunk aligned and sized block of data from a device. */ -static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) +static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw, + int metadata) { struct dm_io_region where = { .bdev = ps->store->cow->bdev, @@ -198,7 +221,7 @@ static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) struct dm_io_request io_req = { .bi_rw = rw, .mem.type = DM_IO_VMA, - .mem.ptr.vma = ps->area, + .mem.ptr.vma = area, .client = ps->io_client, .notify.fn = NULL, }; @@ -240,7 +263,7 @@ static int area_io(struct pstore *ps, int rw) chunk = area_location(ps, ps->current_area); - r = chunk_io(ps, chunk, rw, 0); + r = chunk_io(ps, ps->area, chunk, rw, 0); if (r) return r; @@ -254,20 +277,7 @@ static void zero_memory_area(struct pstore *ps) static int zero_disk_area(struct pstore *ps, chunk_t area) { - struct dm_io_region where = { - .bdev = ps->store->cow->bdev, - .sector = ps->store->chunk_size * area_location(ps, area), - .count = ps->store->chunk_size, - }; - struct dm_io_request io_req = { - .bi_rw = WRITE, - .mem.type = DM_IO_VMA, - .mem.ptr.vma = ps->zero_area, - .client = ps->io_client, - .notify.fn = NULL, - }; - - return dm_io(&io_req, 1, &where, NULL); + return chunk_io(ps, ps->zero_area, area_location(ps, area), WRITE, 0); } static int read_header(struct pstore *ps, int *new_snapshot) @@ -276,6 +286,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) struct disk_header *dh; chunk_t chunk_size; int chunk_size_supplied = 1; + char *chunk_err; /* * Use default chunk size (or hardsect_size, if larger) if none supplied @@ -297,11 +308,11 @@ static int read_header(struct pstore *ps, int *new_snapshot) if (r) return r; - r = chunk_io(ps, 0, READ, 1); + r = chunk_io(ps, ps->header_area, 0, READ, 1); if (r) goto bad; - dh = (struct disk_header *) ps->area; + dh = ps->header_area; if (le32_to_cpu(dh->magic) == 0) { *new_snapshot = 1; @@ -319,20 +330,25 @@ static int read_header(struct pstore *ps, int *new_snapshot) ps->version = le32_to_cpu(dh->version); chunk_size = le32_to_cpu(dh->chunk_size); - if (!chunk_size_supplied || ps->store->chunk_size == chunk_size) + if (ps->store->chunk_size == chunk_size) return 0; - DMWARN("chunk size %llu in device metadata overrides " - "table chunk size of %llu.", - (unsigned long long)chunk_size, - (unsigned long long)ps->store->chunk_size); + if (chunk_size_supplied) + DMWARN("chunk size %llu in device metadata overrides " + "table chunk size of %llu.", + (unsigned long long)chunk_size, + (unsigned long long)ps->store->chunk_size); /* We had a bogus chunk_size. Fix stuff up. */ free_area(ps); - ps->store->chunk_size = chunk_size; - ps->store->chunk_mask = chunk_size - 1; - ps->store->chunk_shift = ffs(chunk_size) - 1; + r = dm_exception_store_set_chunk_size(ps->store, chunk_size, + &chunk_err); + if (r) { + DMERR("invalid on-disk chunk size %llu: %s.", + (unsigned long long)chunk_size, chunk_err); + return r; + } r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), ps->io_client); @@ -351,15 +367,15 @@ static int write_header(struct pstore *ps) { struct disk_header *dh; - memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT); + memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT); - dh = (struct disk_header *) ps->area; + dh = ps->header_area; dh->magic = cpu_to_le32(SNAP_MAGIC); dh->valid = cpu_to_le32(ps->valid); dh->version = cpu_to_le32(ps->version); dh->chunk_size = cpu_to_le32(ps->store->chunk_size); - return chunk_io(ps, 0, WRITE, 1); + return chunk_io(ps, ps->header_area, 0, WRITE, 1); } /* @@ -679,6 +695,8 @@ static int persistent_ctr(struct dm_exception_store *store, ps->valid = 1; ps->version = SNAPSHOT_DISK_VERSION; ps->area = NULL; + ps->zero_area = NULL; + ps->header_area = NULL; ps->next_free = 2; /* skipping the header and first area */ ps->current_committed = 0; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index d573165cd2b..57f1bf7f3b7 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1176,6 +1176,15 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, return 0; } +static int snapshot_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data) +{ + struct dm_snapshot *snap = ti->private; + + return fn(ti, snap->origin, 0, ti->len, data); +} + + /*----------------------------------------------------------------- * Origin methods *---------------------------------------------------------------*/ @@ -1410,20 +1419,29 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result, return 0; } +static int origin_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data) +{ + struct dm_dev *dev = ti->private; + + return fn(ti, dev, 0, ti->len, data); +} + static struct target_type origin_target = { .name = "snapshot-origin", - .version = {1, 6, 0}, + .version = {1, 7, 0}, .module = THIS_MODULE, .ctr = origin_ctr, .dtr = origin_dtr, .map = origin_map, .resume = origin_resume, .status = origin_status, + .iterate_devices = origin_iterate_devices, }; static struct target_type snapshot_target = { .name = "snapshot", - .version = {1, 6, 0}, + .version = {1, 7, 0}, .module = THIS_MODULE, .ctr = snapshot_ctr, .dtr = snapshot_dtr, @@ -1431,6 +1449,7 @@ static struct target_type snapshot_target = { .end_io = snapshot_end_io, .resume = snapshot_resume, .status = snapshot_status, + .iterate_devices = snapshot_iterate_devices, }; static int __init dm_snapshot_init(void) diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 4e0e5937e42..3e563d25173 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -329,9 +329,19 @@ static int stripe_iterate_devices(struct dm_target *ti, return ret; } +static void stripe_io_hints(struct dm_target *ti, + struct queue_limits *limits) +{ + struct stripe_c *sc = ti->private; + unsigned chunk_size = (sc->chunk_mask + 1) << 9; + + blk_limits_io_min(limits, chunk_size); + limits->io_opt = chunk_size * sc->stripes; +} + static struct target_type stripe_target = { .name = "striped", - .version = {1, 2, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = stripe_ctr, .dtr = stripe_dtr, @@ -339,6 +349,7 @@ static struct target_type stripe_target = { .end_io = stripe_end_io, .status = stripe_status, .iterate_devices = stripe_iterate_devices, + .io_hints = stripe_io_hints, }; int __init dm_stripe_init(void) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index d952b344191..1a6cb3c7822 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -343,10 +343,10 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) } /* - * If possible, this checks an area of a destination device is valid. + * If possible, this checks an area of a destination device is invalid. */ -static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, - sector_t start, sector_t len, void *data) +static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, + sector_t start, sector_t len, void *data) { struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; @@ -357,36 +357,40 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, char b[BDEVNAME_SIZE]; if (!dev_size) - return 1; + return 0; if ((start >= dev_size) || (start + len > dev_size)) { - DMWARN("%s: %s too small for target", - dm_device_name(ti->table->md), bdevname(bdev, b)); - return 0; + DMWARN("%s: %s too small for target: " + "start=%llu, len=%llu, dev_size=%llu", + dm_device_name(ti->table->md), bdevname(bdev, b), + (unsigned long long)start, + (unsigned long long)len, + (unsigned long long)dev_size); + return 1; } if (logical_block_size_sectors <= 1) - return 1; + return 0; if (start & (logical_block_size_sectors - 1)) { DMWARN("%s: start=%llu not aligned to h/w " - "logical block size %hu of %s", + "logical block size %u of %s", dm_device_name(ti->table->md), (unsigned long long)start, limits->logical_block_size, bdevname(bdev, b)); - return 0; + return 1; } if (len & (logical_block_size_sectors - 1)) { DMWARN("%s: len=%llu not aligned to h/w " - "logical block size %hu of %s", + "logical block size %u of %s", dm_device_name(ti->table->md), (unsigned long long)len, limits->logical_block_size, bdevname(bdev, b)); - return 0; + return 1; } - return 1; + return 0; } /* @@ -496,8 +500,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, } if (blk_stack_limits(limits, &q->limits, start << 9) < 0) - DMWARN("%s: target device %s is misaligned", - dm_device_name(ti->table->md), bdevname(bdev, b)); + DMWARN("%s: target device %s is misaligned: " + "physical_block_size=%u, logical_block_size=%u, " + "alignment_offset=%u, start=%llu", + dm_device_name(ti->table->md), bdevname(bdev, b), + q->limits.physical_block_size, + q->limits.logical_block_size, + q->limits.alignment_offset, + (unsigned long long) start << 9); + /* * Check if merge fn is supported. @@ -698,7 +709,7 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table, if (remaining) { DMWARN("%s: table line %u (start sect %llu len %llu) " - "not aligned to h/w logical block size %hu", + "not aligned to h/w logical block size %u", dm_device_name(table->md), i, (unsigned long long) ti->begin, (unsigned long long) ti->len, @@ -996,12 +1007,16 @@ int dm_calculate_queue_limits(struct dm_table *table, ti->type->iterate_devices(ti, dm_set_device_limits, &ti_limits); + /* Set I/O hints portion of queue limits */ + if (ti->type->io_hints) + ti->type->io_hints(ti, &ti_limits); + /* * Check each device area is consistent with the target's * overall queue limits. */ - if (!ti->type->iterate_devices(ti, device_area_is_valid, - &ti_limits)) + if (ti->type->iterate_devices(ti, device_area_is_invalid, + &ti_limits)) return -EINVAL; combine_limits: diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 8a311ea0d44..b4845b14740 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -738,16 +738,22 @@ static void rq_completed(struct mapped_device *md, int run_queue) dm_put(md); } +static void free_rq_clone(struct request *clone) +{ + struct dm_rq_target_io *tio = clone->end_io_data; + + blk_rq_unprep_clone(clone); + free_rq_tio(tio); +} + static void dm_unprep_request(struct request *rq) { struct request *clone = rq->special; - struct dm_rq_target_io *tio = clone->end_io_data; rq->special = NULL; rq->cmd_flags &= ~REQ_DONTPREP; - blk_rq_unprep_clone(clone); - free_rq_tio(tio); + free_rq_clone(clone); } /* @@ -825,8 +831,7 @@ static void dm_end_request(struct request *clone, int error) rq->sense_len = clone->sense_len; } - BUG_ON(clone->bio); - free_rq_tio(tio); + free_rq_clone(clone); blk_end_request_all(rq, error); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ae5fe91867e..10ed195c0c1 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -736,7 +736,7 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->partitioned = 1; return add_mtd_partitions(&flash->mtd, parts, nr_parts); } - } else if (data->nr_parts) + } else if (data && data->nr_parts) dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", data->nr_parts, data->name); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index fb86cacd5bd..1002e188299 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -135,16 +135,17 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, size_t *retlen, uint8_t *buf) { + loff_t mask = mtd->writesize - 1; struct mtd_oob_ops ops; int res; ops.mode = MTD_OOB_PLACE; - ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooboffs = offs & mask; ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + res = mtd->read_oob(mtd, offs & ~mask, &ops); *retlen = ops.oobretlen; return res; } @@ -155,16 +156,17 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, size_t *retlen, uint8_t *buf) { + loff_t mask = mtd->writesize - 1; struct mtd_oob_ops ops; int res; ops.mode = MTD_OOB_PLACE; - ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooboffs = offs & mask; ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + res = mtd->write_oob(mtd, offs & ~mask, &ops); *retlen = ops.oobretlen; return res; } @@ -177,17 +179,18 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, size_t *retlen, uint8_t *buf, uint8_t *oob) { + loff_t mask = mtd->writesize - 1; struct mtd_oob_ops ops; int res; ops.mode = MTD_OOB_PLACE; - ops.ooboffs = offs; + ops.ooboffs = offs & mask; ops.ooblen = mtd->oobsize; ops.oobbuf = oob; ops.datbuf = buf; ops.len = len; - res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + res = mtd->write_oob(mtd, offs & ~mask, &ops); *retlen = ops.retlen; return res; } diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index fb5df5c6203..c97ab82ec74 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -1286,6 +1286,7 @@ static int cxgb_open(struct net_device *dev) if (!other_ports) schedule_chk_task(adapter); + cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_UP, pi->port_id); return 0; } @@ -1318,6 +1319,7 @@ static int cxgb_close(struct net_device *dev) if (!adapter->open_device_map) cxgb_down(adapter); + cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_DOWN, pi->port_id); return 0; } @@ -2717,7 +2719,7 @@ static int t3_adapter_error(struct adapter *adapter, int reset) if (is_offload(adapter) && test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { - cxgb3_err_notify(&adapter->tdev, OFFLOAD_STATUS_DOWN, 0); + cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_DOWN, 0); offload_close(&adapter->tdev); } @@ -2782,7 +2784,7 @@ static void t3_resume_ports(struct adapter *adapter) } if (is_offload(adapter) && !ofld_disable) - cxgb3_err_notify(&adapter->tdev, OFFLOAD_STATUS_UP, 0); + cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_UP, 0); } /* diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index f9f54b57b28..75064eea1d8 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -153,14 +153,14 @@ void cxgb3_remove_clients(struct t3cdev *tdev) mutex_unlock(&cxgb3_db_lock); } -void cxgb3_err_notify(struct t3cdev *tdev, u32 status, u32 error) +void cxgb3_event_notify(struct t3cdev *tdev, u32 event, u32 port) { struct cxgb3_client *client; mutex_lock(&cxgb3_db_lock); list_for_each_entry(client, &client_list, client_list) { - if (client->err_handler) - client->err_handler(tdev, status, error); + if (client->event_handler) + client->event_handler(tdev, event, port); } mutex_unlock(&cxgb3_db_lock); } diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/cxgb3/cxgb3_offload.h index 55945f422ae..670aa62042d 100644 --- a/drivers/net/cxgb3/cxgb3_offload.h +++ b/drivers/net/cxgb3/cxgb3_offload.h @@ -64,14 +64,16 @@ void cxgb3_register_client(struct cxgb3_client *client); void cxgb3_unregister_client(struct cxgb3_client *client); void cxgb3_add_clients(struct t3cdev *tdev); void cxgb3_remove_clients(struct t3cdev *tdev); -void cxgb3_err_notify(struct t3cdev *tdev, u32 status, u32 error); +void cxgb3_event_notify(struct t3cdev *tdev, u32 event, u32 port); typedef int (*cxgb3_cpl_handler_func)(struct t3cdev *dev, struct sk_buff *skb, void *ctx); enum { OFFLOAD_STATUS_UP, - OFFLOAD_STATUS_DOWN + OFFLOAD_STATUS_DOWN, + OFFLOAD_PORT_DOWN, + OFFLOAD_PORT_UP }; struct cxgb3_client { @@ -82,7 +84,7 @@ struct cxgb3_client { int (*redirect)(void *ctx, struct dst_entry *old, struct dst_entry *new, struct l2t_entry *l2t); struct list_head client_list; - void (*err_handler)(struct t3cdev *tdev, u32 status, u32 error); + void (*event_handler)(struct t3cdev *tdev, u32 event, u32 port); }; /* diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index e212f2c5448..a00ec639c38 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -491,6 +491,7 @@ static int gfar_remove(struct of_device *ofdev) dev_set_drvdata(&ofdev->dev, NULL); + unregister_netdev(priv->ndev); iounmap(priv->regs); free_netdev(priv->ndev); diff --git a/drivers/net/mlx4/cq.c b/drivers/net/mlx4/cq.c index ac57b6a42c6..ccfe276943f 100644 --- a/drivers/net/mlx4/cq.c +++ b/drivers/net/mlx4/cq.c @@ -34,7 +34,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/hardirq.h> #include <linux/mlx4/cmd.h> diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index b9ceddde46c..bffb7995cb7 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/dma-mapping.h> @@ -42,6 +41,10 @@ #include "fw.h" enum { + MLX4_IRQNAME_SIZE = 64 +}; + +enum { MLX4_NUM_ASYNC_EQE = 0x100, MLX4_NUM_SPARE_EQE = 0x80, MLX4_EQ_ENTRY_SIZE = 0x20 @@ -526,48 +529,6 @@ static void mlx4_unmap_clr_int(struct mlx4_dev *dev) iounmap(priv->clr_base); } -int mlx4_map_eq_icm(struct mlx4_dev *dev, u64 icm_virt) -{ - struct mlx4_priv *priv = mlx4_priv(dev); - int ret; - - /* - * We assume that mapping one page is enough for the whole EQ - * context table. This is fine with all current HCAs, because - * we only use 32 EQs and each EQ uses 64 bytes of context - * memory, or 1 KB total. - */ - priv->eq_table.icm_virt = icm_virt; - priv->eq_table.icm_page = alloc_page(GFP_HIGHUSER); - if (!priv->eq_table.icm_page) - return -ENOMEM; - priv->eq_table.icm_dma = pci_map_page(dev->pdev, priv->eq_table.icm_page, 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, priv->eq_table.icm_dma)) { - __free_page(priv->eq_table.icm_page); - return -ENOMEM; - } - - ret = mlx4_MAP_ICM_page(dev, priv->eq_table.icm_dma, icm_virt); - if (ret) { - pci_unmap_page(dev->pdev, priv->eq_table.icm_dma, PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); - __free_page(priv->eq_table.icm_page); - } - - return ret; -} - -void mlx4_unmap_eq_icm(struct mlx4_dev *dev) -{ - struct mlx4_priv *priv = mlx4_priv(dev); - - mlx4_UNMAP_ICM(dev, priv->eq_table.icm_virt, 1); - pci_unmap_page(dev->pdev, priv->eq_table.icm_dma, PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); - __free_page(priv->eq_table.icm_page); -} - int mlx4_alloc_eq_table(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -615,7 +576,9 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) priv->eq_table.clr_int = priv->clr_base + (priv->eq_table.inta_pin < 32 ? 4 : 0); - priv->eq_table.irq_names = kmalloc(16 * dev->caps.num_comp_vectors, GFP_KERNEL); + priv->eq_table.irq_names = + kmalloc(MLX4_IRQNAME_SIZE * (dev->caps.num_comp_vectors + 1), + GFP_KERNEL); if (!priv->eq_table.irq_names) { err = -ENOMEM; goto err_out_bitmap; @@ -638,17 +601,25 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) goto err_out_comp; if (dev->flags & MLX4_FLAG_MSI_X) { - static const char async_eq_name[] = "mlx4-async"; const char *eq_name; for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) { if (i < dev->caps.num_comp_vectors) { - snprintf(priv->eq_table.irq_names + i * 16, 16, - "mlx4-comp-%d", i); - eq_name = priv->eq_table.irq_names + i * 16; - } else - eq_name = async_eq_name; + snprintf(priv->eq_table.irq_names + + i * MLX4_IRQNAME_SIZE, + MLX4_IRQNAME_SIZE, + "mlx4-comp-%d@pci:%s", i, + pci_name(dev->pdev)); + } else { + snprintf(priv->eq_table.irq_names + + i * MLX4_IRQNAME_SIZE, + MLX4_IRQNAME_SIZE, + "mlx4-async@pci:%s", + pci_name(dev->pdev)); + } + eq_name = priv->eq_table.irq_names + + i * MLX4_IRQNAME_SIZE; err = request_irq(priv->eq_table.eq[i].irq, mlx4_msi_x_interrupt, 0, eq_name, priv->eq_table.eq + i); @@ -658,8 +629,12 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) priv->eq_table.eq[i].have_irq = 1; } } else { + snprintf(priv->eq_table.irq_names, + MLX4_IRQNAME_SIZE, + DRV_NAME "@pci:%s", + pci_name(dev->pdev)); err = request_irq(dev->pdev->irq, mlx4_interrupt, - IRQF_SHARED, DRV_NAME, dev); + IRQF_SHARED, priv->eq_table.irq_names, dev); if (err) goto err_out_async; diff --git a/drivers/net/mlx4/icm.c b/drivers/net/mlx4/icm.c index baf4bf66062..04b382fcb8c 100644 --- a/drivers/net/mlx4/icm.c +++ b/drivers/net/mlx4/icm.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/scatterlist.h> diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index dac621b1e9f..3dd481e77f9 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -525,7 +525,10 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap, goto err_unmap_aux; } - err = mlx4_map_eq_icm(dev, init_hca->eqc_base); + err = mlx4_init_icm_table(dev, &priv->eq_table.table, + init_hca->eqc_base, dev_cap->eqc_entry_sz, + dev->caps.num_eqs, dev->caps.num_eqs, + 0, 0); if (err) { mlx4_err(dev, "Failed to map EQ context memory, aborting.\n"); goto err_unmap_cmpt; @@ -668,7 +671,7 @@ err_unmap_mtt: mlx4_cleanup_icm_table(dev, &priv->mr_table.mtt_table); err_unmap_eq: - mlx4_unmap_eq_icm(dev); + mlx4_cleanup_icm_table(dev, &priv->eq_table.table); err_unmap_cmpt: mlx4_cleanup_icm_table(dev, &priv->eq_table.cmpt_table); @@ -698,11 +701,11 @@ static void mlx4_free_icms(struct mlx4_dev *dev) mlx4_cleanup_icm_table(dev, &priv->qp_table.qp_table); mlx4_cleanup_icm_table(dev, &priv->mr_table.dmpt_table); mlx4_cleanup_icm_table(dev, &priv->mr_table.mtt_table); + mlx4_cleanup_icm_table(dev, &priv->eq_table.table); mlx4_cleanup_icm_table(dev, &priv->eq_table.cmpt_table); mlx4_cleanup_icm_table(dev, &priv->cq_table.cmpt_table); mlx4_cleanup_icm_table(dev, &priv->srq_table.cmpt_table); mlx4_cleanup_icm_table(dev, &priv->qp_table.cmpt_table); - mlx4_unmap_eq_icm(dev); mlx4_UNMAP_ICM_AUX(dev); mlx4_free_icm(dev, priv->fw.aux_icm, 0); @@ -786,7 +789,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) return 0; err_close: - mlx4_close_hca(dev); + mlx4_CLOSE_HCA(dev, 0); err_free_icm: mlx4_free_icms(dev); @@ -1070,18 +1073,12 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) goto err_disable_pdev; } - err = pci_request_region(pdev, 0, DRV_NAME); + err = pci_request_regions(pdev, DRV_NAME); if (err) { - dev_err(&pdev->dev, "Cannot request control region, aborting.\n"); + dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n"); goto err_disable_pdev; } - err = pci_request_region(pdev, 2, DRV_NAME); - if (err) { - dev_err(&pdev->dev, "Cannot request UAR region, aborting.\n"); - goto err_release_bar0; - } - pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); @@ -1090,7 +1087,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n"); - goto err_release_bar2; + goto err_release_regions; } } err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); @@ -1101,7 +1098,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (err) { dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, " "aborting.\n"); - goto err_release_bar2; + goto err_release_regions; } } @@ -1110,7 +1107,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(&pdev->dev, "Device struct alloc failed, " "aborting.\n"); err = -ENOMEM; - goto err_release_bar2; + goto err_release_regions; } dev = &priv->dev; @@ -1205,11 +1202,8 @@ err_cmd: err_free_dev: kfree(priv); -err_release_bar2: - pci_release_region(pdev, 2); - -err_release_bar0: - pci_release_region(pdev, 0); +err_release_regions: + pci_release_regions(pdev); err_disable_pdev: pci_disable_device(pdev); @@ -1265,8 +1259,7 @@ static void mlx4_remove_one(struct pci_dev *pdev) pci_disable_msix(pdev); kfree(priv); - pci_release_region(pdev, 2); - pci_release_region(pdev, 0); + pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c index 6053c357a47..5ccbce9866f 100644 --- a/drivers/net/mlx4/mcg.c +++ b/drivers/net/mlx4/mcg.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/string.h> #include <linux/slab.h> diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index 5bd79c2b184..bc72d6e4919 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -205,9 +205,7 @@ struct mlx4_eq_table { void __iomem **uar_map; u32 clr_mask; struct mlx4_eq *eq; - u64 icm_virt; - struct page *icm_page; - dma_addr_t icm_dma; + struct mlx4_icm_table table; struct mlx4_icm_table cmpt_table; int have_irq; u8 inta_pin; @@ -373,9 +371,6 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap, struct mlx4_init_hca_param *init_hca); -int mlx4_map_eq_icm(struct mlx4_dev *dev, u64 icm_virt); -void mlx4_unmap_eq_icm(struct mlx4_dev *dev); - int mlx4_cmd_init(struct mlx4_dev *dev); void mlx4_cmd_cleanup(struct mlx4_dev *dev); void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param); diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index f96948be0a4..ca7ab8e7b4c 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -32,7 +32,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/errno.h> #include <linux/mlx4/cmd.h> diff --git a/drivers/net/mlx4/pd.c b/drivers/net/mlx4/pd.c index 26d1a7a9e37..c4988d6bd5b 100644 --- a/drivers/net/mlx4/pd.c +++ b/drivers/net/mlx4/pd.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/errno.h> #include <asm/page.h> diff --git a/drivers/net/mlx4/profile.c b/drivers/net/mlx4/profile.c index bd22df95adf..ca25b9dc837 100644 --- a/drivers/net/mlx4/profile.c +++ b/drivers/net/mlx4/profile.c @@ -32,8 +32,6 @@ * SOFTWARE. */ -#include <linux/init.h> - #include "mlx4.h" #include "fw.h" diff --git a/drivers/net/mlx4/qp.c b/drivers/net/mlx4/qp.c index 1c565ef8d17..42ab9fc01d3 100644 --- a/drivers/net/mlx4/qp.c +++ b/drivers/net/mlx4/qp.c @@ -33,8 +33,6 @@ * SOFTWARE. */ -#include <linux/init.h> - #include <linux/mlx4/cmd.h> #include <linux/mlx4/qp.h> diff --git a/drivers/net/mlx4/reset.c b/drivers/net/mlx4/reset.c index 3951b884c0f..e5741dab382 100644 --- a/drivers/net/mlx4/reset.c +++ b/drivers/net/mlx4/reset.c @@ -31,7 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> #include <linux/errno.h> #include <linux/pci.h> #include <linux/delay.h> diff --git a/drivers/net/mlx4/srq.c b/drivers/net/mlx4/srq.c index fe9f218691f..1377d0dc8f1 100644 --- a/drivers/net/mlx4/srq.c +++ b/drivers/net/mlx4/srq.c @@ -31,8 +31,6 @@ * SOFTWARE. */ -#include <linux/init.h> - #include <linux/mlx4/cmd.h> #include "mlx4.h" diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 42b6c6319bc..87214a257d2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk) static int tun_attach(struct tun_struct *tun, struct file *file) { struct tun_file *tfile = file->private_data; - const struct cred *cred = current_cred(); int err; ASSERT_RTNL(); - /* Check permissions */ - if (((tun->owner != -1 && cred->euid != tun->owner) || - (tun->group != -1 && !in_egroup_p(tun->group))) && - !capable(CAP_NET_ADMIN)) - return -EPERM; - netif_tx_lock_bh(tun->dev); err = -EINVAL; @@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) dev = __dev_get_by_name(net, ifr->ifr_name); if (dev) { + const struct cred *cred = current_cred(); + if (ifr->ifr_flags & IFF_TUN_EXCL) return -EBUSY; if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops) @@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else return -EINVAL; + if (((tun->owner != -1 && cred->euid != tun->owner) || + (tun->group != -1 && !in_egroup_p(tun->group))) && + !capable(CAP_NET_ADMIN)) + return -EPERM; + err = security_tun_dev_attach(tun->sk); + if (err < 0) + return err; + err = tun_attach(tun, file); if (err < 0) return err; @@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (!capable(CAP_NET_ADMIN)) return -EPERM; + err = security_tun_dev_create(); + if (err < 0) + return err; /* Set dev type */ if (ifr->ifr_flags & IFF_TUN) { @@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun->sk = sk; container_of(sk, struct tun_sock, sk)->tun = tun; + security_tun_dev_post_create(sk); + tun_net_init(dev); if (strchr(dev->name, '%')) { diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 6dcac73b4d2..f593fbbb4e5 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2874,45 +2874,27 @@ static int ipw_fw_dma_add_command_block(struct ipw_priv *priv, return 0; } -static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, - u32 src_phys, u32 dest_address, u32 length) +static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address, + int nr, u32 dest_address, u32 len) { - u32 bytes_left = length; - u32 src_offset = 0; - u32 dest_offset = 0; - int status = 0; + int ret, i; + u32 size; + IPW_DEBUG_FW(">> \n"); - IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n", - src_phys, dest_address, length); - while (bytes_left > CB_MAX_LENGTH) { - status = ipw_fw_dma_add_command_block(priv, - src_phys + src_offset, - dest_address + - dest_offset, - CB_MAX_LENGTH, 0, 0); - if (status) { + IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n", + nr, dest_address, len); + + for (i = 0; i < nr; i++) { + size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH); + ret = ipw_fw_dma_add_command_block(priv, src_address[i], + dest_address + + i * CB_MAX_LENGTH, size, + 0, 0); + if (ret) { IPW_DEBUG_FW_INFO(": Failed\n"); return -1; } else IPW_DEBUG_FW_INFO(": Added new cb\n"); - - src_offset += CB_MAX_LENGTH; - dest_offset += CB_MAX_LENGTH; - bytes_left -= CB_MAX_LENGTH; - } - - /* add the buffer tail */ - if (bytes_left > 0) { - status = - ipw_fw_dma_add_command_block(priv, src_phys + src_offset, - dest_address + dest_offset, - bytes_left, 0, 0); - if (status) { - IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n"); - return -1; - } else - IPW_DEBUG_FW_INFO - (": Adding new cb - the buffer tail\n"); } IPW_DEBUG_FW("<< \n"); @@ -3160,59 +3142,91 @@ static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) { - int rc = -1; + int ret = -1; int offset = 0; struct fw_chunk *chunk; - dma_addr_t shared_phys; - u8 *shared_virt; + int total_nr = 0; + int i; + struct pci_pool *pool; + u32 *virts[CB_NUMBER_OF_ELEMENTS_SMALL]; + dma_addr_t phys[CB_NUMBER_OF_ELEMENTS_SMALL]; IPW_DEBUG_TRACE("<< : \n"); - shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys); - if (!shared_virt) + pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0); + if (!pool) { + IPW_ERROR("pci_pool_create failed\n"); return -ENOMEM; - - memmove(shared_virt, data, len); + } /* Start the Dma */ - rc = ipw_fw_dma_enable(priv); + ret = ipw_fw_dma_enable(priv); /* the DMA is already ready this would be a bug. */ BUG_ON(priv->sram_desc.last_cb_index > 0); do { + u32 chunk_len; + u8 *start; + int size; + int nr = 0; + chunk = (struct fw_chunk *)(data + offset); offset += sizeof(struct fw_chunk); + chunk_len = le32_to_cpu(chunk->length); + start = data + offset; + + nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; + for (i = 0; i < nr; i++) { + virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL, + &phys[total_nr]); + if (!virts[total_nr]) { + ret = -ENOMEM; + goto out; + } + size = min_t(u32, chunk_len - i * CB_MAX_LENGTH, + CB_MAX_LENGTH); + memcpy(virts[total_nr], start, size); + start += size; + total_nr++; + /* We don't support fw chunk larger than 64*8K */ + BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL); + } + /* build DMA packet and queue up for sending */ /* dma to chunk->address, the chunk->length bytes from data + * offeset*/ /* Dma loading */ - rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset, - le32_to_cpu(chunk->address), - le32_to_cpu(chunk->length)); - if (rc) { + ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr], + nr, le32_to_cpu(chunk->address), + chunk_len); + if (ret) { IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); goto out; } - offset += le32_to_cpu(chunk->length); + offset += chunk_len; } while (offset < len); /* Run the DMA and wait for the answer */ - rc = ipw_fw_dma_kick(priv); - if (rc) { + ret = ipw_fw_dma_kick(priv); + if (ret) { IPW_ERROR("dmaKick Failed\n"); goto out; } - rc = ipw_fw_dma_wait(priv); - if (rc) { + ret = ipw_fw_dma_wait(priv); + if (ret) { IPW_ERROR("dmaWaitSync Failed\n"); goto out; } - out: - pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys); - return rc; + out: + for (i = 0; i < total_nr; i++) + pci_pool_free(pool, virts[i], phys[i]); + + pci_pool_destroy(pool); + + return ret; } /* stop nic */ diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 242257b1944..a7aae24f288 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -21,7 +21,6 @@ #include <linux/sched.h> #include <linux/oprofile.h> -#include <linux/vmalloc.h> #include <linux/errno.h> #include "event_buffer.h" @@ -407,6 +406,21 @@ int oprofile_add_data(struct op_entry *entry, unsigned long val) return op_cpu_buffer_add_data(entry, val); } +int oprofile_add_data64(struct op_entry *entry, u64 val) +{ + if (!entry->event) + return 0; + if (op_cpu_buffer_get_size(entry) < 2) + /* + * the function returns 0 to indicate a too small + * buffer, even if there is some space left + */ + return 0; + if (!op_cpu_buffer_add_data(entry, (u32)val)) + return 0; + return op_cpu_buffer_add_data(entry, (u32)(val >> 32)); +} + int oprofile_write_commit(struct op_entry *entry) { if (!entry->event) diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index 3cffce90f82..dc8a0428260 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -12,6 +12,8 @@ #include <linux/init.h> #include <linux/oprofile.h> #include <linux/moduleparam.h> +#include <linux/workqueue.h> +#include <linux/time.h> #include <asm/mutex.h> #include "oprof.h" @@ -87,6 +89,69 @@ out: return err; } +#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX + +static void switch_worker(struct work_struct *work); +static DECLARE_DELAYED_WORK(switch_work, switch_worker); + +static void start_switch_worker(void) +{ + if (oprofile_ops.switch_events) + schedule_delayed_work(&switch_work, oprofile_time_slice); +} + +static void stop_switch_worker(void) +{ + cancel_delayed_work_sync(&switch_work); +} + +static void switch_worker(struct work_struct *work) +{ + if (oprofile_ops.switch_events()) + return; + + atomic_inc(&oprofile_stats.multiplex_counter); + start_switch_worker(); +} + +/* User inputs in ms, converts to jiffies */ +int oprofile_set_timeout(unsigned long val_msec) +{ + int err = 0; + unsigned long time_slice; + + mutex_lock(&start_mutex); + + if (oprofile_started) { + err = -EBUSY; + goto out; + } + + if (!oprofile_ops.switch_events) { + err = -EINVAL; + goto out; + } + + time_slice = msecs_to_jiffies(val_msec); + if (time_slice == MAX_JIFFY_OFFSET) { + err = -EINVAL; + goto out; + } + + oprofile_time_slice = time_slice; + +out: + mutex_unlock(&start_mutex); + return err; + +} + +#else + +static inline void start_switch_worker(void) { } +static inline void stop_switch_worker(void) { } + +#endif /* Actually start profiling (echo 1>/dev/oprofile/enable) */ int oprofile_start(void) @@ -108,6 +173,8 @@ int oprofile_start(void) if ((err = oprofile_ops.start())) goto out; + start_switch_worker(); + oprofile_started = 1; out: mutex_unlock(&start_mutex); @@ -123,6 +190,9 @@ void oprofile_stop(void) goto out; oprofile_ops.stop(); oprofile_started = 0; + + stop_switch_worker(); + /* wake up the daemon to read what remains */ wake_up_buffer_waiter(); out: @@ -155,7 +225,6 @@ post_sync: mutex_unlock(&start_mutex); } - int oprofile_set_backtrace(unsigned long val) { int err = 0; diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index c288d3c24b5..cb92f5c98c1 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -24,6 +24,8 @@ struct oprofile_operations; extern unsigned long oprofile_buffer_size; extern unsigned long oprofile_cpu_buffer_size; extern unsigned long oprofile_buffer_watershed; +extern unsigned long oprofile_time_slice; + extern struct oprofile_operations oprofile_ops; extern unsigned long oprofile_started; extern unsigned long oprofile_backtrace_depth; @@ -35,5 +37,6 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root); void oprofile_timer_init(struct oprofile_operations *ops); int oprofile_set_backtrace(unsigned long depth); +int oprofile_set_timeout(unsigned long time); #endif /* OPROF_H */ diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c index 5d36ffc30dd..bbd7516e086 100644 --- a/drivers/oprofile/oprofile_files.c +++ b/drivers/oprofile/oprofile_files.c @@ -9,6 +9,7 @@ #include <linux/fs.h> #include <linux/oprofile.h> +#include <linux/jiffies.h> #include "event_buffer.h" #include "oprofile_stats.h" @@ -17,10 +18,51 @@ #define BUFFER_SIZE_DEFAULT 131072 #define CPU_BUFFER_SIZE_DEFAULT 8192 #define BUFFER_WATERSHED_DEFAULT 32768 /* FIXME: tune */ +#define TIME_SLICE_DEFAULT 1 unsigned long oprofile_buffer_size; unsigned long oprofile_cpu_buffer_size; unsigned long oprofile_buffer_watershed; +unsigned long oprofile_time_slice; + +#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX + +static ssize_t timeout_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + return oprofilefs_ulong_to_user(jiffies_to_msecs(oprofile_time_slice), + buf, count, offset); +} + + +static ssize_t timeout_write(struct file *file, char const __user *buf, + size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = oprofilefs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = oprofile_set_timeout(val); + + if (retval) + return retval; + return count; +} + + +static const struct file_operations timeout_fops = { + .read = timeout_read, + .write = timeout_write, +}; + +#endif + static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { @@ -129,6 +171,7 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root) oprofile_buffer_size = BUFFER_SIZE_DEFAULT; oprofile_cpu_buffer_size = CPU_BUFFER_SIZE_DEFAULT; oprofile_buffer_watershed = BUFFER_WATERSHED_DEFAULT; + oprofile_time_slice = msecs_to_jiffies(TIME_SLICE_DEFAULT); oprofilefs_create_file(sb, root, "enable", &enable_fops); oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666); @@ -139,6 +182,9 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root) oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops); oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops); oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops); +#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX + oprofilefs_create_file(sb, root, "time_slice", &timeout_fops); +#endif oprofile_create_stats_files(sb, root); if (oprofile_ops.create_files) oprofile_ops.create_files(sb, root); diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c index 3c2270a8300..61689e814d4 100644 --- a/drivers/oprofile/oprofile_stats.c +++ b/drivers/oprofile/oprofile_stats.c @@ -34,6 +34,7 @@ void oprofile_reset_stats(void) atomic_set(&oprofile_stats.sample_lost_no_mapping, 0); atomic_set(&oprofile_stats.event_lost_overflow, 0); atomic_set(&oprofile_stats.bt_lost_no_mapping, 0); + atomic_set(&oprofile_stats.multiplex_counter, 0); } @@ -76,4 +77,8 @@ void oprofile_create_stats_files(struct super_block *sb, struct dentry *root) &oprofile_stats.event_lost_overflow); oprofilefs_create_ro_atomic(sb, dir, "bt_lost_no_mapping", &oprofile_stats.bt_lost_no_mapping); +#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX + oprofilefs_create_ro_atomic(sb, dir, "multiplex_counter", + &oprofile_stats.multiplex_counter); +#endif } diff --git a/drivers/oprofile/oprofile_stats.h b/drivers/oprofile/oprofile_stats.h index 3da0d08dc1f..0b54e46c3c1 100644 --- a/drivers/oprofile/oprofile_stats.h +++ b/drivers/oprofile/oprofile_stats.h @@ -17,6 +17,7 @@ struct oprofile_stat_struct { atomic_t sample_lost_no_mapping; atomic_t bt_lost_no_mapping; atomic_t event_lost_overflow; + atomic_t multiplex_counter; }; extern struct oprofile_stat_struct oprofile_stats; diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 4f5b8712931..44803644ca0 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -55,15 +55,12 @@ static struct irq_2_iommu *irq_2_iommu(unsigned int irq) return desc->irq_2_iommu; } -static struct irq_2_iommu *irq_2_iommu_alloc_node(unsigned int irq, int node) +static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq) { struct irq_desc *desc; struct irq_2_iommu *irq_iommu; - /* - * alloc irq desc if not allocated already. - */ - desc = irq_to_desc_alloc_node(irq, node); + desc = irq_to_desc(irq); if (!desc) { printk(KERN_INFO "can not get irq_desc for %d\n", irq); return NULL; @@ -72,16 +69,11 @@ static struct irq_2_iommu *irq_2_iommu_alloc_node(unsigned int irq, int node) irq_iommu = desc->irq_2_iommu; if (!irq_iommu) - desc->irq_2_iommu = get_one_free_irq_2_iommu(node); + desc->irq_2_iommu = get_one_free_irq_2_iommu(irq_node(irq)); return desc->irq_2_iommu; } -static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq) -{ - return irq_2_iommu_alloc_node(irq, cpu_to_node(boot_cpu_id)); -} - #else /* !CONFIG_SPARSE_IRQ */ static struct irq_2_iommu irq_2_iommuX[NR_IRQS]; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index e3a87210e94..e03fe98f061 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -598,6 +598,29 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno, } /** + * pci_sriov_resource_alignment - get resource alignment for VF BAR + * @dev: the PCI device + * @resno: the resource number + * + * Returns the alignment of the VF BAR found in the SR-IOV capability. + * This is not the same as the resource size which is defined as + * the VF BAR size multiplied by the number of VFs. The alignment + * is just the VF BAR size. + */ +int pci_sriov_resource_alignment(struct pci_dev *dev, int resno) +{ + struct resource tmp; + enum pci_bar_type type; + int reg = pci_iov_resource_bar(dev, resno, &type); + + if (!reg) + return 0; + + __pci_read_base(dev, type, &tmp, reg); + return resource_alignment(&tmp); +} + +/** * pci_restore_iov_state - restore the state of the IOV capability * @dev: the PCI device */ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f73bcbedf37..5ff4d25bf0e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -243,6 +243,7 @@ extern int pci_iov_init(struct pci_dev *dev); extern void pci_iov_release(struct pci_dev *dev); extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); +extern int pci_sriov_resource_alignment(struct pci_dev *dev, int resno); extern void pci_restore_iov_state(struct pci_dev *dev); extern int pci_iov_bus_range(struct pci_bus *bus); @@ -298,4 +299,16 @@ static inline int pci_ats_enabled(struct pci_dev *dev) } #endif /* CONFIG_PCI_IOV */ +static inline int pci_resource_alignment(struct pci_dev *dev, + struct resource *res) +{ +#ifdef CONFIG_PCI_IOV + int resno = res - dev->resource; + + if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + return pci_sriov_resource_alignment(dev, resno); +#endif + return resource_alignment(res); +} + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b636e245445..7c443b4583a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -25,7 +25,7 @@ #include <linux/ioport.h> #include <linux/cache.h> #include <linux/slab.h> - +#include "pci.h" static void pbus_assign_resources_sorted(const struct pci_bus *bus) { @@ -384,7 +384,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long continue; r_size = resource_size(r); /* For bridges size != alignment */ - align = resource_alignment(r); + align = pci_resource_alignment(dev, r); order = __ffs(align) - 20; if (order > 11) { dev_warn(&dev->dev, "BAR %d bad alignment %llx: " diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 1898c7b4790..88cdd1a937d 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -144,7 +144,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, size = resource_size(res); min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -178,7 +178,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) struct pci_bus *bus; int ret; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " "alignment) %pR flags %#lx\n", @@ -259,7 +259,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) if (!(r->flags) || r->parent) continue; - r_align = resource_alignment(r); + r_align = pci_resource_alignment(dev, r); if (!r_align) { dev_warn(&dev->dev, "BAR %d: bogus alignment " "%pR flags %#lx\n", @@ -271,7 +271,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) struct resource_list *ln = list->next; if (ln) - align = resource_alignment(ln->res); + align = pci_resource_alignment(ln->dev, ln->res); if (r_align > align) { tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 3f62dd50bbb..e109da4583a 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -669,14 +669,14 @@ static void dasd_profile_end(struct dasd_block *block, * memory and 2) dasd_smalloc_request uses the static ccw memory * that gets allocated for each device. */ -struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, +struct dasd_ccw_req *dasd_kmalloc_request(int magic, int cplength, int datasize, struct dasd_device *device) { struct dasd_ccw_req *cqr; /* Sanity checks */ - BUG_ON( magic == NULL || datasize > PAGE_SIZE || + BUG_ON(datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE); cqr = kzalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC); @@ -700,14 +700,13 @@ struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, return ERR_PTR(-ENOMEM); } } - strncpy((char *) &cqr->magic, magic, 4); - ASCEBC((char *) &cqr->magic, 4); + cqr->magic = magic; set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); dasd_get_device(device); return cqr; } -struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, +struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize, struct dasd_device *device) { @@ -717,7 +716,7 @@ struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, int size; /* Sanity checks */ - BUG_ON( magic == NULL || datasize > PAGE_SIZE || + BUG_ON(datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE); size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; @@ -744,8 +743,7 @@ struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, cqr->data = data; memset(cqr->data, 0, datasize); } - strncpy((char *) &cqr->magic, magic, 4); - ASCEBC((char *) &cqr->magic, 4); + cqr->magic = magic; set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); dasd_get_device(device); return cqr; @@ -899,9 +897,6 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) switch (rc) { case 0: cqr->status = DASD_CQR_IN_IO; - DBF_DEV_EVENT(DBF_DEBUG, device, - "start_IO: request %p started successful", - cqr); break; case -EBUSY: DBF_DEV_EVENT(DBF_DEBUG, device, "%s", @@ -1699,8 +1694,11 @@ static void __dasd_process_request_queue(struct dasd_block *block) * for that. State DASD_STATE_ONLINE is normal block device * operation. */ - if (basedev->state < DASD_STATE_READY) + if (basedev->state < DASD_STATE_READY) { + while ((req = blk_fetch_request(block->request_queue))) + __blk_end_request_all(req, -EIO); return; + } /* Now we try to fetch requests from the request queue */ while (!blk_queue_plugged(queue) && (req = blk_peek_request(queue))) { if (basedev->features & DASD_FEATURE_READONLY && @@ -2530,7 +2528,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_restore_device); static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, void *rdc_buffer, int rdc_buffer_size, - char *magic) + int magic) { struct dasd_ccw_req *cqr; struct ccw1 *ccw; @@ -2561,7 +2559,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, } -int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, +int dasd_generic_read_dev_chars(struct dasd_device *device, int magic, void *rdc_buffer, int rdc_buffer_size) { int ret; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 27991b69205..e8ff7b0c961 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -7,7 +7,7 @@ * */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-eckd" #include <linux/timer.h> #include <linux/slab.h> diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 5b7bbc87593..70a008c0052 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -5,7 +5,7 @@ * Author(s): Stefan Weinhuber <wein@de.ibm.com> */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-eckd" #include <linux/list.h> #include <asm/ebcdic.h> @@ -379,8 +379,7 @@ static int read_unit_address_configuration(struct dasd_device *device, int rc; unsigned long flags; - cqr = dasd_kmalloc_request("ECKD", - 1 /* PSF */ + 1 /* RSSD */ , + cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data)), device); if (IS_ERR(cqr)) diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 644086ba2ed..4e49b4a6c88 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -8,7 +8,7 @@ * */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-diag" #include <linux/stddef.h> #include <linux/kernel.h> @@ -523,8 +523,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, /* Build the request */ datasize = sizeof(struct dasd_diag_req) + count*sizeof(struct dasd_diag_bio); - cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, - datasize, memdev); + cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, datasize, memdev); if (IS_ERR(cqr)) return cqr; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index c11770f5b36..a1ce573648a 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -10,7 +10,7 @@ * Author.........: Nigel Hislop <hislop_nigel@emc.com> */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-eckd" #include <linux/stddef.h> #include <linux/kernel.h> @@ -730,7 +730,8 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, struct dasd_ccw_req *cqr; struct ccw1 *ccw; - cqr = dasd_smalloc_request("ECKD", 1 /* RCD */, ciw->count, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count, + device); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", @@ -934,8 +935,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) struct dasd_eckd_private *private; private = (struct dasd_eckd_private *) device->private; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 1 /* PSF */ + 1 /* RSSD */ , + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_features)), device); @@ -998,7 +998,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, struct dasd_psf_ssc_data *psf_ssc_data; struct ccw1 *ccw; - cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , sizeof(struct dasd_psf_ssc_data), device); @@ -1149,8 +1149,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) goto out_err3; /* Read Device Characteristics */ - rc = dasd_generic_read_dev_chars(device, "ECKD", &private->rdc_data, - 64); + rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, + &private->rdc_data, 64); if (rc) { DBF_EVENT(DBF_WARNING, "Read device characteristics failed, rc=%d for " @@ -1217,8 +1217,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cplength = 8; datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data); - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -1499,8 +1498,7 @@ dasd_eckd_format_device(struct dasd_device * device, return ERR_PTR(-EINVAL); } /* Allocate the format ccw request. */ - fcp = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, device); + fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); if (IS_ERR(fcp)) return fcp; @@ -1783,8 +1781,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( datasize += count*sizeof(struct LO_eckd_data); } /* Allocate the ccw request. */ - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, startdev); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, + startdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -1948,8 +1946,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cidaw * sizeof(unsigned long long); /* Allocate the ccw request. */ - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, startdev); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, + startdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -2249,8 +2247,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( /* Allocate the ccw request. */ itcw_size = itcw_calc_size(0, ctidaw, 0); - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 0, itcw_size, startdev); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); if (IS_ERR(cqr)) return cqr; @@ -2557,8 +2554,7 @@ dasd_eckd_release(struct dasd_device *device) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -2600,8 +2596,7 @@ dasd_eckd_reserve(struct dasd_device *device) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -2642,8 +2637,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -2681,8 +2675,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) struct ccw1 *ccw; int rc; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - 1 /* PSF */ + 1 /* RSSD */ , + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_perf_stats_t)), device); @@ -2828,7 +2821,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) } /* setup CCWs for PSF + RSSD */ - cqr = dasd_smalloc_request("ECKD", 2 , 0, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -3254,7 +3247,7 @@ int dasd_eckd_restore_device(struct dasd_device *device) /* Read Device Characteristics */ memset(&private->rdc_data, 0, sizeof(private->rdc_data)); - rc = dasd_generic_read_dev_chars(device, "ECKD", + rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, &private->rdc_data, 64); if (rc) { DBF_EVENT(DBF_WARNING, diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index c24c8c30380..d96039eae59 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -6,7 +6,7 @@ * Author(s): Stefan Weinhuber <wein@de.ibm.com> */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-eckd" #include <linux/init.h> #include <linux/fs.h> @@ -464,7 +464,7 @@ int dasd_eer_enable(struct dasd_device *device) if (!device->discipline || strcmp(device->discipline->name, "ECKD")) return -EPERM; /* FIXME: -EMEDIUMTYPE ? */ - cqr = dasd_kmalloc_request("ECKD", 1 /* SNSS */, + cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */, SNSS_DATA_SIZE, device); if (IS_ERR(cqr)) return -ENOMEM; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index cb8f9cef742..7656384a811 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -99,8 +99,8 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr) cqr->lpm = LPM_ANYPATH; cqr->status = DASD_CQR_FILLED; } else { - dev_err(&device->cdev->dev, - "default ERP has run out of retries and failed\n"); + pr_err("%s: default ERP has run out of retries and failed\n", + dev_name(&device->cdev->dev)); cqr->status = DASD_CQR_FAILED; cqr->stopclk = get_clock(); } diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 31849ad5e59..f245377e8e2 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -5,7 +5,7 @@ * Copyright IBM Corp. 1999, 2009 */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-fba" #include <linux/stddef.h> #include <linux/kernel.h> @@ -152,8 +152,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) block->base = device; /* Read Device Characteristics */ - rc = dasd_generic_read_dev_chars(device, "FBA ", &private->rdc_data, - 32); + rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, + &private->rdc_data, 32); if (rc) { DBF_EVENT(DBF_WARNING, "Read device characteristics returned " "error %d for device: %s", @@ -305,8 +305,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, datasize += (count - 1)*sizeof(struct LO_fba_data); } /* Allocate the ccw request. */ - cqr = dasd_smalloc_request(dasd_fba_discipline.name, - cplength, datasize, memdev); + cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index b699ca356ac..5e47a1ee52b 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -59,6 +59,11 @@ #include <asm/dasd.h> #include <asm/idals.h> +/* DASD discipline magic */ +#define DASD_ECKD_MAGIC 0xC5C3D2C4 +#define DASD_DIAG_MAGIC 0xC4C9C1C7 +#define DASD_FBA_MAGIC 0xC6C2C140 + /* * SECTION: Type definitions */ @@ -540,9 +545,9 @@ extern struct block_device_operations dasd_device_operations; extern struct kmem_cache *dasd_page_cache; struct dasd_ccw_req * -dasd_kmalloc_request(char *, int, int, struct dasd_device *); +dasd_kmalloc_request(int , int, int, struct dasd_device *); struct dasd_ccw_req * -dasd_smalloc_request(char *, int, int, struct dasd_device *); +dasd_smalloc_request(int , int, int, struct dasd_device *); void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *); @@ -587,7 +592,7 @@ void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_pm_freeze(struct ccw_device *); int dasd_generic_restore_device(struct ccw_device *); -int dasd_generic_read_dev_chars(struct dasd_device *, char *, void *, int); +int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); char *dasd_get_sense(struct irb *); /* externals in dasd_devmap.c */ diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index df918ef2796..f756a1b0c57 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -98,8 +98,8 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) if (!capable (CAP_SYS_ADMIN)) return -EACCES; - dev_info(&base->cdev->dev, "The DASD has been put in the quiesce " - "state\n"); + pr_info("%s: The DASD has been put in the quiesce " + "state\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); base->stopped |= DASD_STOPPED_QUIESCE; spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); @@ -119,8 +119,8 @@ static int dasd_ioctl_resume(struct dasd_block *block) if (!capable (CAP_SYS_ADMIN)) return -EACCES; - dev_info(&base->cdev->dev, "I/O operations have been resumed " - "on the DASD\n"); + pr_info("%s: I/O operations have been resumed " + "on the DASD\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); base->stopped &= ~DASD_STOPPED_QUIESCE; spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); @@ -146,8 +146,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) return -EPERM; if (base->state != DASD_STATE_BASIC) { - dev_warn(&base->cdev->dev, - "The DASD cannot be formatted while it is enabled\n"); + pr_warning("%s: The DASD cannot be formatted while it is " + "enabled\n", dev_name(&base->cdev->dev)); return -EBUSY; } @@ -175,9 +175,9 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) dasd_sfree_request(cqr, cqr->memdev); if (rc) { if (rc != -ERESTARTSYS) - dev_err(&base->cdev->dev, - "Formatting unit %d failed with " - "rc=%d\n", fdata->start_unit, rc); + pr_err("%s: Formatting unit %d failed with " + "rc=%d\n", dev_name(&base->cdev->dev), + fdata->start_unit, rc); return rc; } fdata->start_unit++; @@ -204,9 +204,9 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) return -EFAULT; if (bdev != bdev->bd_contains) { - dev_warn(&block->base->cdev->dev, - "The specified DASD is a partition and cannot be " - "formatted\n"); + pr_warning("%s: The specified DASD is a partition and cannot " + "be formatted\n", + dev_name(&block->base->cdev->dev)); return -EINVAL; } return dasd_format(block, &fdata); diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index db442cd6621..ee604e92a5f 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -42,7 +42,6 @@ #include <linux/suspend.h> #include <linux/platform_device.h> #include <asm/uaccess.h> -#include <asm/checksum.h> #define XPRAM_NAME "xpram" #define XPRAM_DEVS 1 /* one partition */ @@ -51,7 +50,6 @@ typedef struct { unsigned int size; /* size of xpram segment in pages */ unsigned int offset; /* start page of xpram segment */ - unsigned int csum; /* partition checksum for suspend */ } xpram_device_t; static xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; @@ -387,58 +385,6 @@ out: } /* - * Save checksums for all partitions. - */ -static int xpram_save_checksums(void) -{ - unsigned long mem_page; - int rc, i; - - rc = 0; - mem_page = (unsigned long) __get_free_page(GFP_KERNEL); - if (!mem_page) - return -ENOMEM; - for (i = 0; i < xpram_devs; i++) { - rc = xpram_page_in(mem_page, xpram_devices[i].offset); - if (rc) - goto fail; - xpram_devices[i].csum = csum_partial((const void *) mem_page, - PAGE_SIZE, 0); - } -fail: - free_page(mem_page); - return rc ? -ENXIO : 0; -} - -/* - * Verify checksums for all partitions. - */ -static int xpram_validate_checksums(void) -{ - unsigned long mem_page; - unsigned int csum; - int rc, i; - - rc = 0; - mem_page = (unsigned long) __get_free_page(GFP_KERNEL); - if (!mem_page) - return -ENOMEM; - for (i = 0; i < xpram_devs; i++) { - rc = xpram_page_in(mem_page, xpram_devices[i].offset); - if (rc) - goto fail; - csum = csum_partial((const void *) mem_page, PAGE_SIZE, 0); - if (xpram_devices[i].csum != csum) { - rc = -EINVAL; - goto fail; - } - } -fail: - free_page(mem_page); - return rc ? -ENXIO : 0; -} - -/* * Resume failed: Print error message and call panic. */ static void xpram_resume_error(const char *message) @@ -458,21 +404,10 @@ static int xpram_restore(struct device *dev) xpram_resume_error("xpram disappeared"); if (xpram_pages != xpram_highest_page_index() + 1) xpram_resume_error("Size of xpram changed"); - if (xpram_validate_checksums()) - xpram_resume_error("Data of xpram changed"); return 0; } -/* - * Save necessary state in suspend. - */ -static int xpram_freeze(struct device *dev) -{ - return xpram_save_checksums(); -} - static struct dev_pm_ops xpram_pm_ops = { - .freeze = xpram_freeze, .restore = xpram_restore, }; diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 0769ced52db..4e34d3686c2 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -82,6 +82,16 @@ config SCLP_CPI You should only select this option if you know what you are doing, need this feature and intend to run your kernel in LPAR. +config SCLP_ASYNC + tristate "Support for Call Home via Asynchronous SCLP Records" + depends on S390 + help + This option enables the call home function, which is able to inform + the service element and connected organisations about a kernel panic. + You should only select this option if you know what you are doing, + want for inform other people about your kernel panics, + need this feature and intend to run your kernel in LPAR. + config S390_TAPE tristate "S/390 tape device support" depends on CCW diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 7e73e39a174..efb500ab66c 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SCLP_TTY) += sclp_tty.o obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o +obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 3234e90bd7f..89ece1c235a 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -581,7 +581,7 @@ static int __init mon_init(void) monreader_device->release = (void (*)(struct device *))kfree; rc = device_register(monreader_device); if (rc) { - kfree(monreader_device); + put_device(monreader_device); goto out_driver; } diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 60e7cb07095..6bb5a6bdfab 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -27,6 +27,7 @@ #define EVTYP_VT220MSG 0x1A #define EVTYP_CONFMGMDATA 0x04 #define EVTYP_SDIAS 0x1C +#define EVTYP_ASYNC 0x0A #define EVTYP_OPCMD_MASK 0x80000000 #define EVTYP_MSG_MASK 0x40000000 @@ -38,6 +39,7 @@ #define EVTYP_VT220MSG_MASK 0x00000040 #define EVTYP_CONFMGMDATA_MASK 0x10000000 #define EVTYP_SDIAS_MASK 0x00000010 +#define EVTYP_ASYNC_MASK 0x00400000 #define GNRLMSGFLGS_DOM 0x8000 #define GNRLMSGFLGS_SNDALRM 0x4000 @@ -85,12 +87,12 @@ struct sccb_header { } __attribute__((packed)); extern u64 sclp_facilities; - #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) #define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) #define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) + struct gds_subvector { u8 length; u8 key; diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c new file mode 100644 index 00000000000..daaec185ed3 --- /dev/null +++ b/drivers/s390/char/sclp_async.c @@ -0,0 +1,224 @@ +/* + * Enable Asynchronous Notification via SCLP. + * + * Copyright IBM Corp. 2009 + * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com> + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kmod.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/proc_fs.h> +#include <linux/sysctl.h> +#include <linux/utsname.h> +#include "sclp.h" + +static int callhome_enabled; +static struct sclp_req *request; +static struct sclp_async_sccb *sccb; +static int sclp_async_send_wait(char *message); +static struct ctl_table_header *callhome_sysctl_header; +static DEFINE_SPINLOCK(sclp_async_lock); +static char nodename[64]; +#define SCLP_NORMAL_WRITE 0x00 + +struct async_evbuf { + struct evbuf_header header; + u64 reserved; + u8 rflags; + u8 empty; + u8 rtype; + u8 otype; + char comp_id[12]; + char data[3000]; /* there is still some space left */ +} __attribute__((packed)); + +struct sclp_async_sccb { + struct sccb_header header; + struct async_evbuf evbuf; +} __attribute__((packed)); + +static struct sclp_register sclp_async_register = { + .send_mask = EVTYP_ASYNC_MASK, +}; + +static int call_home_on_panic(struct notifier_block *self, + unsigned long event, void *data) +{ + strncat(data, nodename, strlen(nodename)); + sclp_async_send_wait(data); + return NOTIFY_DONE; +} + +static struct notifier_block call_home_panic_nb = { + .notifier_call = call_home_on_panic, + .priority = INT_MAX, +}; + +static int proc_handler_callhome(ctl_table *ctl, int write, struct file *filp, + void __user *buffer, size_t *count, + loff_t *ppos) +{ + unsigned long val; + int len, rc; + char buf[2]; + + if (!*count | (*ppos && !write)) { + *count = 0; + return 0; + } + if (!write) { + len = sprintf(buf, "%d\n", callhome_enabled); + buf[len] = '\0'; + rc = copy_to_user(buffer, buf, sizeof(buf)); + if (rc != 0) + return -EFAULT; + } else { + len = *count; + rc = copy_from_user(buf, buffer, sizeof(buf)); + if (rc != 0) + return -EFAULT; + if (strict_strtoul(buf, 0, &val) != 0) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + callhome_enabled = val; + } + *count = len; + *ppos += len; + return 0; +} + +static struct ctl_table callhome_table[] = { + { + .procname = "callhome", + .mode = 0644, + .proc_handler = &proc_handler_callhome, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table kern_dir_table[] = { + { + .ctl_name = CTL_KERN, + .procname = "kernel", + .maxlen = 0, + .mode = 0555, + .child = callhome_table, + }, + { .ctl_name = 0 } +}; + +/* + * Function used to transfer asynchronous notification + * records which waits for send completion + */ +static int sclp_async_send_wait(char *message) +{ + struct async_evbuf *evb; + int rc; + unsigned long flags; + + if (!callhome_enabled) + return 0; + sccb->evbuf.header.type = EVTYP_ASYNC; + sccb->evbuf.rtype = 0xA5; + sccb->evbuf.otype = 0x00; + evb = &sccb->evbuf; + request->command = SCLP_CMDW_WRITE_EVENT_DATA; + request->sccb = sccb; + request->status = SCLP_REQ_FILLED; + strncpy(sccb->evbuf.data, message, sizeof(sccb->evbuf.data)); + /* + * Retain Queue + * e.g. 5639CC140 500 Red Hat RHEL5 Linux for zSeries (RHEL AS) + */ + strncpy(sccb->evbuf.comp_id, "000000000", sizeof(sccb->evbuf.comp_id)); + sccb->evbuf.header.length = sizeof(sccb->evbuf); + sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header); + sccb->header.function_code = SCLP_NORMAL_WRITE; + rc = sclp_add_request(request); + if (rc) + return rc; + spin_lock_irqsave(&sclp_async_lock, flags); + while (request->status != SCLP_REQ_DONE && + request->status != SCLP_REQ_FAILED) { + sclp_sync_wait(); + } + spin_unlock_irqrestore(&sclp_async_lock, flags); + if (request->status != SCLP_REQ_DONE) + return -EIO; + rc = ((struct sclp_async_sccb *) + request->sccb)->header.response_code; + if (rc != 0x0020) + return -EIO; + if (evb->header.flags != 0x80) + return -EIO; + return rc; +} + +static int __init sclp_async_init(void) +{ + int rc; + + rc = sclp_register(&sclp_async_register); + if (rc) + return rc; + callhome_sysctl_header = register_sysctl_table(kern_dir_table); + if (!callhome_sysctl_header) { + rc = -ENOMEM; + goto out_sclp; + } + if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) { + rc = -EOPNOTSUPP; + goto out_sclp; + } + rc = -ENOMEM; + request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); + if (!request) + goto out_sys; + sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + goto out_mem; + rc = atomic_notifier_chain_register(&panic_notifier_list, + &call_home_panic_nb); + if (rc) + goto out_mem; + + strncpy(nodename, init_utsname()->nodename, 64); + return 0; + +out_mem: + kfree(request); + free_page((unsigned long) sccb); +out_sys: + unregister_sysctl_table(callhome_sysctl_header); +out_sclp: + sclp_unregister(&sclp_async_register); + return rc; + +} +module_init(sclp_async_init); + +static void __exit sclp_async_exit(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &call_home_panic_nb); + unregister_sysctl_table(callhome_sysctl_header); + sclp_unregister(&sclp_async_register); + free_page((unsigned long) sccb); + kfree(request); +} +module_exit(sclp_async_exit); + +MODULE_AUTHOR("Copyright IBM Corp. 2009"); +MODULE_AUTHOR("Hans-Joachim Picht <hans@linux.vnet.ibm.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCLP Asynchronous Notification Records"); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 5a519fac37b..2fe45ff77b7 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -8,7 +8,7 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> */ -#define KMSG_COMPONENT "tape" +#define KMSG_COMPONENT "tape_34xx" #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 418f72dd39b..e4cc3aae916 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -8,7 +8,7 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> */ -#define KMSG_COMPONENT "tape" +#define KMSG_COMPONENT "tape_3590" #include <linux/module.h> #include <linux/init.h> @@ -39,8 +39,6 @@ EXPORT_SYMBOL(TAPE_DBF_AREA); * - Read Alternate: implemented *******************************************************************/ -#define KMSG_COMPONENT "tape" - static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = { [0x00] = "", [0x10] = "Lost Sense", diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 47ff695255e..4cb9e70507a 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -302,8 +302,6 @@ tapeblock_revalidate_disk(struct gendisk *disk) if (!device->blk_data.medium_changed) return 0; - dev_info(&device->cdev->dev, "Determining the size of the recorded " - "area...\n"); rc = tape_mtop(device, MTFSFM, 1); if (rc) return rc; @@ -312,6 +310,8 @@ tapeblock_revalidate_disk(struct gendisk *disk) if (rc < 0) return rc; + pr_info("%s: Determining the size of the recorded area...\n", + dev_name(&device->cdev->dev)); DBF_LH(3, "Image file ends at %d\n", rc); nr_of_blks = rc; @@ -330,8 +330,8 @@ tapeblock_revalidate_disk(struct gendisk *disk) device->bof = rc; nr_of_blks -= rc; - dev_info(&device->cdev->dev, "The size of the recorded area is %i " - "blocks\n", nr_of_blks); + pr_info("%s: The size of the recorded area is %i blocks\n", + dev_name(&device->cdev->dev), nr_of_blks); set_capacity(device->blk_data.disk, nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512)); @@ -366,8 +366,8 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) if (device->required_tapemarks) { DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); - dev_warn(&device->cdev->dev, "Opening the tape failed because" - " of missing end-of-file marks\n"); + pr_warning("%s: Opening the tape failed because of missing " + "end-of-file marks\n", dev_name(&device->cdev->dev)); rc = -EPERM; goto put_device; } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 1d420d94759..5cd31e07164 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -214,13 +214,15 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) switch(newstate){ case MS_UNLOADED: device->tape_generic_status |= GMT_DR_OPEN(~0); - dev_info(&device->cdev->dev, "The tape cartridge has been " - "successfully unloaded\n"); + if (device->medium_state == MS_LOADED) + pr_info("%s: The tape cartridge has been successfully " + "unloaded\n", dev_name(&device->cdev->dev)); break; case MS_LOADED: device->tape_generic_status &= ~GMT_DR_OPEN(~0); - dev_info(&device->cdev->dev, "A tape cartridge has been " - "mounted\n"); + if (device->medium_state == MS_UNLOADED) + pr_info("%s: A tape cartridge has been mounted\n", + dev_name(&device->cdev->dev)); break; default: // print nothing @@ -358,11 +360,11 @@ tape_generic_online(struct tape_device *device, out_char: tapechar_cleanup_device(device); +out_minor: + tape_remove_minor(device); out_discipline: device->discipline->cleanup_device(device); device->discipline = NULL; -out_minor: - tape_remove_minor(device); out: module_put(discipline->owner); return rc; @@ -654,8 +656,8 @@ tape_generic_remove(struct ccw_device *cdev) */ DBF_EVENT(3, "(%08x): Drive in use vanished!\n", device->cdev_id); - dev_warn(&device->cdev->dev, "A tape unit was detached" - " while in use\n"); + pr_warning("%s: A tape unit was detached while in " + "use\n", dev_name(&device->cdev->dev)); tape_state_set(device, TS_NOT_OPER); __tape_discard_requests(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 1a9420ba518..750354ad16e 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -68,7 +68,7 @@ tape_std_assign(struct tape_device *device) * to another host (actually this shouldn't happen but it does). * So we set up a timeout for this call. */ - init_timer(&timeout); + init_timer_on_stack(&timeout); timeout.function = tape_std_assign_timeout; timeout.data = (unsigned long) request; timeout.expires = jiffies + 2 * HZ; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index c20a4fe6da5..d1a142fa3eb 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -765,8 +765,10 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) } else return -ENOMEM; ret = device_register(dev); - if (ret) + if (ret) { + put_device(dev); return ret; + } ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group); if (ret) { diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 31b902e94f7..77571b68539 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -1026,9 +1026,15 @@ static int __init ur_init(void) debug_set_level(vmur_dbf, 6); + vmur_class = class_create(THIS_MODULE, "vmur"); + if (IS_ERR(vmur_class)) { + rc = PTR_ERR(vmur_class); + goto fail_free_dbf; + } + rc = ccw_driver_register(&ur_driver); if (rc) - goto fail_free_dbf; + goto fail_class_destroy; rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur"); if (rc) { @@ -1038,18 +1044,13 @@ static int __init ur_init(void) } ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0); - vmur_class = class_create(THIS_MODULE, "vmur"); - if (IS_ERR(vmur_class)) { - rc = PTR_ERR(vmur_class); - goto fail_unregister_region; - } pr_info("%s loaded.\n", ur_banner); return 0; -fail_unregister_region: - unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); fail_unregister_driver: ccw_driver_unregister(&ur_driver); +fail_class_destroy: + class_destroy(vmur_class); fail_free_dbf: debug_unregister(vmur_dbf); return rc; @@ -1057,9 +1058,9 @@ fail_free_dbf: static void __exit ur_exit(void) { - class_destroy(vmur_class); unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); ccw_driver_unregister(&ur_driver); + class_destroy(vmur_class); debug_unregister(vmur_dbf); pr_info("%s unloaded.\n", ur_banner); } diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 1bbae433fbd..c431198bdbc 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -275,7 +275,7 @@ struct zcore_header { u32 num_pages; u32 pad1; u64 tod; - cpuid_t cpu_id; + struct cpuid cpu_id; u32 arch_id; u32 volnr; u32 build_arch; diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index adb3dd30152..fa4c9662f65 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \ +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ fcx.o itcw.o crw.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 3e5f304ad88..40002830d48 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -417,7 +417,8 @@ int chp_new(struct chp_id chpid) if (ret) { CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n", chpid.cssid, chpid.id, ret); - goto out_free; + put_device(&chp->dev); + goto out; } ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); if (ret) { diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 425e8f89a6c..37aa611d4ac 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -37,29 +37,6 @@ struct channel_path_desc { struct channel_path; -struct css_general_char { - u64 : 12; - u32 dynio : 1; /* bit 12 */ - u32 : 28; - u32 aif : 1; /* bit 41 */ - u32 : 3; - u32 mcss : 1; /* bit 45 */ - u32 fcs : 1; /* bit 46 */ - u32 : 1; - u32 ext_mb : 1; /* bit 48 */ - u32 : 7; - u32 aif_tdd : 1; /* bit 56 */ - u32 : 1; - u32 qebsm : 1; /* bit 58 */ - u32 : 8; - u32 aif_osa : 1; /* bit 67 */ - u32 : 14; - u32 cib : 1; /* bit 82 */ - u32 : 5; - u32 fcx : 1; /* bit 88 */ - u32 : 7; -}__attribute__((packed)); - struct css_chsc_char { u64 res; u64 : 20; @@ -72,7 +49,6 @@ struct css_chsc_char { u32 : 19; }__attribute__((packed)); -extern struct css_general_char css_general_characteristics; extern struct css_chsc_char css_chsc_characteristics; struct chsc_ssd_info { diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 5ec7789bd9d..138124fcfca 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -139,12 +139,11 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ __u8 lpm, /* logical path mask */ __u8 key) /* storage key */ { - char dbf_txt[15]; int ccode; union orb *orb; - CIO_TRACE_EVENT(4, "stIO"); - CIO_TRACE_EVENT(4, dev_name(&sch->dev)); + CIO_TRACE_EVENT(5, "stIO"); + CIO_TRACE_EVENT(5, dev_name(&sch->dev)); orb = &to_io_private(sch)->orb; memset(orb, 0, sizeof(union orb)); @@ -169,8 +168,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ ccode = ssch(sch->schid, orb); /* process condition code */ - sprintf(dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT(4, dbf_txt); + CIO_HEX_EVENT(5, &ccode, sizeof(ccode)); switch (ccode) { case 0: @@ -201,16 +199,14 @@ cio_start (struct subchannel *sch, struct ccw1 *cpa, __u8 lpm) int cio_resume (struct subchannel *sch) { - char dbf_txt[15]; int ccode; - CIO_TRACE_EVENT (4, "resIO"); + CIO_TRACE_EVENT(4, "resIO"); CIO_TRACE_EVENT(4, dev_name(&sch->dev)); ccode = rsch (sch->schid); - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (4, dbf_txt); + CIO_HEX_EVENT(4, &ccode, sizeof(ccode)); switch (ccode) { case 0: @@ -235,13 +231,12 @@ cio_resume (struct subchannel *sch) int cio_halt(struct subchannel *sch) { - char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; - CIO_TRACE_EVENT (2, "haltIO"); + CIO_TRACE_EVENT(2, "haltIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); /* @@ -249,8 +244,7 @@ cio_halt(struct subchannel *sch) */ ccode = hsch (sch->schid); - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (2, dbf_txt); + CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: @@ -270,13 +264,12 @@ cio_halt(struct subchannel *sch) int cio_clear(struct subchannel *sch) { - char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; - CIO_TRACE_EVENT (2, "clearIO"); + CIO_TRACE_EVENT(2, "clearIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); /* @@ -284,8 +277,7 @@ cio_clear(struct subchannel *sch) */ ccode = csch (sch->schid); - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (2, dbf_txt); + CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: @@ -306,19 +298,17 @@ cio_clear(struct subchannel *sch) int cio_cancel (struct subchannel *sch) { - char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; - CIO_TRACE_EVENT (2, "cancelIO"); + CIO_TRACE_EVENT(2, "cancelIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); ccode = xsch (sch->schid); - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (2, dbf_txt); + CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: /* success */ @@ -429,11 +419,10 @@ EXPORT_SYMBOL_GPL(cio_update_schib); */ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { - char dbf_txt[15]; int retry; int ret; - CIO_TRACE_EVENT (2, "ensch"); + CIO_TRACE_EVENT(2, "ensch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) @@ -460,8 +449,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) } else break; } - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (2, dbf_txt); + CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; } EXPORT_SYMBOL_GPL(cio_enable_subchannel); @@ -472,11 +460,10 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel); */ int cio_disable_subchannel(struct subchannel *sch) { - char dbf_txt[15]; int retry; int ret; - CIO_TRACE_EVENT (2, "dissch"); + CIO_TRACE_EVENT(2, "dissch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) @@ -495,8 +482,7 @@ int cio_disable_subchannel(struct subchannel *sch) } else break; } - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (2, dbf_txt); + CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; } EXPORT_SYMBOL_GPL(cio_disable_subchannel); @@ -578,11 +564,6 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) goto out; } mutex_init(&sch->reg_mutex); - /* Set a name for the subchannel */ - if (cio_is_console(schid)) - sch->dev.init_name = cio_get_console_sch_name(schid); - else - dev_set_name(&sch->dev, "0.%x.%04x", schid.ssid, schid.sch_no); /* * The first subchannel that is not-operational (ccode==3) @@ -686,7 +667,6 @@ void __irq_entry do_IRQ(struct pt_regs *regs) #ifdef CONFIG_CCW_CONSOLE static struct subchannel console_subchannel; -static char console_sch_name[10] = "0.x.xxxx"; static struct io_subchannel_private console_priv; static int console_subchannel_in_use; @@ -873,12 +853,6 @@ cio_get_console_subchannel(void) return &console_subchannel; } -const char *cio_get_console_sch_name(struct subchannel_id schid) -{ - snprintf(console_sch_name, 10, "0.%x.%04x", schid.ssid, schid.sch_no); - return (const char *)console_sch_name; -} - #endif static int __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 5150fba742a..2e43558c704 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -133,15 +133,11 @@ extern int cio_is_console(struct subchannel_id); extern struct subchannel *cio_get_console_subchannel(void); extern spinlock_t * cio_get_console_lock(void); extern void *cio_get_console_priv(void); -extern const char *cio_get_console_sch_name(struct subchannel_id schid); -extern const char *cio_get_console_cdev_name(struct subchannel *sch); #else #define cio_is_console(schid) 0 #define cio_get_console_subchannel() NULL #define cio_get_console_lock() NULL #define cio_get_console_priv() NULL -#define cio_get_console_sch_name(schid) NULL -#define cio_get_console_cdev_name(sch) NULL #endif #endif diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 85d43c6bcb6..e995123fd80 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -152,24 +152,15 @@ css_alloc_subchannel(struct subchannel_id schid) } static void -css_free_subchannel(struct subchannel *sch) -{ - if (sch) { - /* Reset intparm to zeroes. */ - sch->config.intparm = 0; - cio_commit_config(sch); - kfree(sch->lock); - kfree(sch); - } -} - -static void css_subchannel_release(struct device *dev) { struct subchannel *sch; sch = to_subchannel(dev); if (!cio_is_console(sch->schid)) { + /* Reset intparm to zeroes. */ + sch->config.intparm = 0; + cio_commit_config(sch); kfree(sch->lock); kfree(sch); } @@ -180,6 +171,8 @@ static int css_sch_device_register(struct subchannel *sch) int ret; mutex_lock(&sch->reg_mutex); + dev_set_name(&sch->dev, "0.%x.%04x", sch->schid.ssid, + sch->schid.sch_no); ret = device_register(&sch->dev); mutex_unlock(&sch->reg_mutex); return ret; @@ -327,7 +320,7 @@ int css_probe_device(struct subchannel_id schid) return PTR_ERR(sch); ret = css_register_subchannel(sch); if (ret) - css_free_subchannel(sch); + put_device(&sch->dev); return ret; } @@ -644,7 +637,10 @@ __init_channel_subsystem(struct subchannel_id schid, void *data) * not working) so we do it now. This is true e.g. for the * console subchannel. */ - css_register_subchannel(sch); + if (css_register_subchannel(sch)) { + if (!cio_is_console(schid)) + put_device(&sch->dev); + } return 0; } @@ -661,8 +657,8 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) css->global_pgid.pgid_high.cpu_addr = 0; #endif } - css->global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident; - css->global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine; + css->global_pgid.cpu_id = S390_lowcore.cpu_id.ident; + css->global_pgid.cpu_model = S390_lowcore.cpu_id.machine; css->global_pgid.tod_high = tod_high; } @@ -920,8 +916,10 @@ init_channel_subsystem (void) goto out_device; } ret = device_register(&css->pseudo_subchannel->dev); - if (ret) + if (ret) { + put_device(&css->pseudo_subchannel->dev); goto out_file; + } } ret = register_reboot_notifier(&css_reboot_notifier); if (ret) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index d593bc76afe..0f95405c2c5 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -307,8 +307,11 @@ int ccw_device_is_orphan(struct ccw_device *cdev) static void ccw_device_unregister(struct ccw_device *cdev) { - if (test_and_clear_bit(1, &cdev->private->registered)) + if (test_and_clear_bit(1, &cdev->private->registered)) { device_del(&cdev->dev); + /* Release reference from device_initialize(). */ + put_device(&cdev->dev); + } } static void ccw_device_remove_orphan_cb(struct work_struct *work) @@ -319,7 +322,6 @@ static void ccw_device_remove_orphan_cb(struct work_struct *work) priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; ccw_device_unregister(cdev); - put_device(&cdev->dev); /* Release cdev reference for workqueue processing. */ put_device(&cdev->dev); } @@ -333,15 +335,15 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) * Forced offline in disconnected state means * 'throw away device'. */ - /* Get cdev reference for workqueue processing. */ - if (!get_device(&cdev->dev)) - return; if (ccw_device_is_orphan(cdev)) { /* * Deregister ccw device. * Unfortunately, we cannot do this directly from the * attribute method. */ + /* Get cdev reference for workqueue processing. */ + if (!get_device(&cdev->dev)) + return; spin_lock_irqsave(cdev->ccwlock, flags); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); @@ -380,30 +382,34 @@ int ccw_device_set_offline(struct ccw_device *cdev) } cdev->online = 0; spin_lock_irq(cdev->ccwlock); - ret = ccw_device_offline(cdev); - if (ret == -ENODEV) { - if (cdev->private->state != DEV_STATE_NOT_OPER) { - cdev->private->state = DEV_STATE_OFFLINE; - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - } + /* Wait until a final state or DISCONNECTED is reached */ + while (!dev_fsm_final_state(cdev) && + cdev->private->state != DEV_STATE_DISCONNECTED) { spin_unlock_irq(cdev->ccwlock); - /* Give up reference from ccw_device_set_online(). */ - put_device(&cdev->dev); - return ret; + wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || + cdev->private->state == DEV_STATE_DISCONNECTED)); + spin_lock_irq(cdev->ccwlock); } + ret = ccw_device_offline(cdev); + if (ret) + goto error; spin_unlock_irq(cdev->ccwlock); - if (ret == 0) { - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); - /* Give up reference from ccw_device_set_online(). */ - put_device(&cdev->dev); - } else { - CIO_MSG_EVENT(0, "ccw_device_offline returned %d, " - "device 0.%x.%04x\n", - ret, cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - cdev->online = 1; - } - return ret; + wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || + cdev->private->state == DEV_STATE_DISCONNECTED)); + /* Give up reference from ccw_device_set_online(). */ + put_device(&cdev->dev); + return 0; + +error: + CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + cdev->private->state = DEV_STATE_OFFLINE; + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); + spin_unlock_irq(cdev->ccwlock); + /* Give up reference from ccw_device_set_online(). */ + put_device(&cdev->dev); + return -ENODEV; } /** @@ -421,6 +427,7 @@ int ccw_device_set_offline(struct ccw_device *cdev) int ccw_device_set_online(struct ccw_device *cdev) { int ret; + int ret2; if (!cdev) return -ENODEV; @@ -444,28 +451,53 @@ int ccw_device_set_online(struct ccw_device *cdev) put_device(&cdev->dev); return ret; } - if (cdev->private->state != DEV_STATE_ONLINE) { + spin_lock_irq(cdev->ccwlock); + /* Check if online processing was successful */ + if ((cdev->private->state != DEV_STATE_ONLINE) && + (cdev->private->state != DEV_STATE_W4SENSE)) { + spin_unlock_irq(cdev->ccwlock); /* Give up online reference since onlining failed. */ put_device(&cdev->dev); return -ENODEV; } - if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) { - cdev->online = 1; - return 0; - } + spin_unlock_irq(cdev->ccwlock); + if (cdev->drv->set_online) + ret = cdev->drv->set_online(cdev); + if (ret) + goto rollback; + cdev->online = 1; + return 0; + +rollback: spin_lock_irq(cdev->ccwlock); - ret = ccw_device_offline(cdev); + /* Wait until a final state or DISCONNECTED is reached */ + while (!dev_fsm_final_state(cdev) && + cdev->private->state != DEV_STATE_DISCONNECTED) { + spin_unlock_irq(cdev->ccwlock); + wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || + cdev->private->state == DEV_STATE_DISCONNECTED)); + spin_lock_irq(cdev->ccwlock); + } + ret2 = ccw_device_offline(cdev); + if (ret2) + goto error; spin_unlock_irq(cdev->ccwlock); - if (ret == 0) - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); - else - CIO_MSG_EVENT(0, "ccw_device_offline returned %d, " - "device 0.%x.%04x\n", - ret, cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); + wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || + cdev->private->state == DEV_STATE_DISCONNECTED)); /* Give up online reference since onlining failed. */ put_device(&cdev->dev); - return (ret == 0) ? -ENODEV : ret; + return ret; + +error: + CIO_MSG_EVENT(0, "rollback ccw_device_offline returned %d, " + "device 0.%x.%04x\n", + ret2, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + cdev->private->state = DEV_STATE_OFFLINE; + spin_unlock_irq(cdev->ccwlock); + /* Give up online reference since onlining failed. */ + put_device(&cdev->dev); + return ret; } static int online_store_handle_offline(struct ccw_device *cdev) @@ -637,8 +669,12 @@ static int ccw_device_register(struct ccw_device *cdev) int ret; dev->bus = &ccw_bus_type; - - if ((ret = device_add(dev))) + ret = dev_set_name(&cdev->dev, "0.%x.%04x", cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + if (ret) + return ret; + ret = device_add(dev); + if (ret) return ret; set_bit(1, &cdev->private->registered); @@ -1024,9 +1060,6 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) return; sch = to_subchannel(cdev->dev.parent); css_sch_device_unregister(sch); - /* Reset intparm to zeroes. */ - sch->config.intparm = 0; - cio_commit_config(sch); /* Release cdev reference for workqueue processing.*/ put_device(&cdev->dev); /* Release subchannel reference for local processing. */ @@ -1035,6 +1068,9 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) { + /* Get cdev reference for workqueue processing. */ + if (!get_device(&cdev->dev)) + return; PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister); queue_work(slow_path_wq, &cdev->private->kick_work); @@ -1055,9 +1091,6 @@ io_subchannel_recog_done(struct ccw_device *cdev) /* Device did not respond in time. */ case DEV_STATE_NOT_OPER: cdev->private->flags.recog_done = 1; - /* Remove device found not operational. */ - if (!get_device(&cdev->dev)) - break; ccw_device_schedule_sch_unregister(cdev); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); @@ -1095,13 +1128,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) init_waitqueue_head(&priv->wait_q); init_timer(&priv->timer); - /* Set an initial name for the device. */ - if (cio_is_console(sch->schid)) - cdev->dev.init_name = cio_get_console_cdev_name(sch); - else - dev_set_name(&cdev->dev, "0.%x.%04x", - sch->schid.ssid, sch->schib.pmcw.dev); - /* Increase counter of devices currently in recognition. */ atomic_inc(&ccw_device_init_count); @@ -1171,8 +1197,8 @@ static void io_subchannel_irq(struct subchannel *sch) cdev = sch_get_cdev(sch); - CIO_TRACE_EVENT(3, "IRQ"); - CIO_TRACE_EVENT(3, dev_name(&sch->dev)); + CIO_TRACE_EVENT(6, "IRQ"); + CIO_TRACE_EVENT(6, dev_name(&sch->dev)); if (cdev) dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); } @@ -1210,9 +1236,6 @@ static void io_subchannel_do_unreg(struct work_struct *work) sch = container_of(work, struct subchannel, work); css_sch_device_unregister(sch); - /* Reset intparm to zeroes. */ - sch->config.intparm = 0; - cio_commit_config(sch); put_device(&sch->dev); } @@ -1334,7 +1357,6 @@ io_subchannel_remove (struct subchannel *sch) cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); - put_device(&cdev->dev); kfree(sch->private); sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return 0; @@ -1571,8 +1593,6 @@ static int purge_fn(struct device *dev, void *data) spin_unlock_irq(cdev->ccwlock); if (!unreg) goto out; - if (!get_device(&cdev->dev)) - goto out; CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, priv->dev_id.devno); ccw_device_schedule_sch_unregister(cdev); @@ -1688,10 +1708,6 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow) spin_unlock_irqrestore(sch->lock, flags); css_sch_device_unregister(sch); spin_lock_irqsave(sch->lock, flags); - - /* Reset intparm to zeroes. */ - sch->config.intparm = 0; - cio_commit_config(sch); break; case REPROBE: ccw_device_trigger_reprobe(cdev); @@ -1712,7 +1728,6 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow) #ifdef CONFIG_CCW_CONSOLE static struct ccw_device console_cdev; -static char console_cdev_name[10] = "0.x.xxxx"; static struct ccw_device_private console_private; static int console_cdev_in_use; @@ -1796,13 +1811,6 @@ int ccw_device_force_console(void) return ccw_device_pm_restore(&console_cdev.dev); } EXPORT_SYMBOL_GPL(ccw_device_force_console); - -const char *cio_get_console_cdev_name(struct subchannel *sch) -{ - snprintf(console_cdev_name, 10, "0.%x.%04x", - sch->schid.ssid, sch->schib.pmcw.dev); - return (const char *)console_cdev_name; -} #endif /* @@ -2020,7 +2028,9 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) spin_unlock_irq(sch->lock); if (ret) { CIO_MSG_EVENT(0, "Couldn't start recognition for device " - "%s (ret=%d)\n", dev_name(&cdev->dev), ret); + "0.%x.%04x (ret=%d)\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); spin_lock_irq(sch->lock); cdev->private->state = DEV_STATE_DISCONNECTED; spin_unlock_irq(sch->lock); @@ -2083,8 +2093,9 @@ static int ccw_device_pm_restore(struct device *dev) } /* check if the device id has changed */ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { - CIO_MSG_EVENT(0, "resume: sch %s: failed (devno changed from " - "%04x to %04x)\n", dev_name(&sch->dev), + CIO_MSG_EVENT(0, "resume: sch 0.%x.%04x: failed (devno " + "changed from %04x to %04x)\n", + sch->schid.ssid, sch->schid.sch_no, cdev->private->dev_id.devno, sch->schib.pmcw.dev); goto out_unreg_unlock; @@ -2117,8 +2128,9 @@ static int ccw_device_pm_restore(struct device *dev) if (cm_enabled) { ret = ccw_set_cmf(cdev, 1); if (ret) { - CIO_MSG_EVENT(2, "resume: cdev %s: cmf failed " - "(rc=%d)\n", dev_name(&cdev->dev), ret); + CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed " + "(rc=%d)\n", cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); ret = 0; } } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 3db88c52d28..e728ce447f6 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -394,6 +394,13 @@ ccw_device_done(struct ccw_device *cdev, int state) ccw_device_schedule_sch_unregister(cdev); cdev->private->flags.donotify = 0; } + if (state == DEV_STATE_NOT_OPER) { + CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n", + cdev->private->dev_id.devno, sch->schid.sch_no); + if (!ccw_device_notify(cdev, CIO_GONE)) + ccw_device_schedule_sch_unregister(cdev); + cdev->private->flags.donotify = 0; + } if (cdev->private->flags.donotify) { cdev->private->flags.donotify = 0; @@ -731,6 +738,17 @@ static void ccw_device_generic_notoper(struct ccw_device *cdev, } /* + * Handle path verification event in offline state. + */ +static void ccw_device_offline_verify(struct ccw_device *cdev, + enum dev_event dev_event) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + css_schedule_eval(sch->schid); +} + +/* * Handle path verification event. */ static void @@ -887,6 +905,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) } call_handler: cdev->private->state = DEV_STATE_ONLINE; + /* In case sensing interfered with setting the device online */ + wake_up(&cdev->private->wait_q); /* Call the handler. */ if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify) /* Start delayed path verification. */ @@ -1149,7 +1169,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_offline_verify, }, [DEV_STATE_VERIFY] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index b1241f8fae8..ff7748a9199 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -1,7 +1,7 @@ /* * linux/drivers/s390/cio/qdio.h * - * Copyright 2000,2008 IBM Corp. + * Copyright 2000,2009 IBM Corp. * Author(s): Utz Bacher <utz.bacher@de.ibm.com> * Jan Glauber <jang@linux.vnet.ibm.com> */ @@ -246,6 +246,7 @@ struct qdio_q { atomic_t nr_buf_used; struct qdio_irq *irq_ptr; + struct dentry *debugfs_q; struct tasklet_struct tasklet; /* error condition during a data transfer */ @@ -267,6 +268,7 @@ struct qdio_irq { struct qib qib; u32 *dsci; /* address of device state change indicator */ struct ccw_device *cdev; + struct dentry *debugfs_dev; unsigned long int_parm; struct subchannel_id schid; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index b8626d4df11..1b78f639ead 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -1,14 +1,12 @@ /* * drivers/s390/cio/qdio_debug.c * - * Copyright IBM Corp. 2008 + * Copyright IBM Corp. 2008,2009 * * Author: Jan Glauber (jang@linux.vnet.ibm.com) */ -#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/debugfs.h> -#include <asm/qdio.h> #include <asm/debug.h> #include "qdio_debug.h" #include "qdio.h" @@ -17,10 +15,7 @@ debug_info_t *qdio_dbf_setup; debug_info_t *qdio_dbf_error; static struct dentry *debugfs_root; -#define MAX_DEBUGFS_QUEUES 32 -static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL }; -static DEFINE_MUTEX(debugfs_mutex); -#define QDIO_DEBUGFS_NAME_LEN 40 +#define QDIO_DEBUGFS_NAME_LEN 10 void qdio_allocate_dbf(struct qdio_initialize *init_data, struct qdio_irq *irq_ptr) @@ -130,20 +125,6 @@ static int qstat_seq_open(struct inode *inode, struct file *filp) filp->f_path.dentry->d_inode->i_private); } -static void remove_debugfs_entry(struct qdio_q *q) -{ - int i; - - for (i = 0; i < MAX_DEBUGFS_QUEUES; i++) { - if (!debugfs_queues[i]) - continue; - if (debugfs_queues[i]->d_inode->i_private == q) { - debugfs_remove(debugfs_queues[i]); - debugfs_queues[i] = NULL; - } - } -} - static struct file_operations debugfs_fops = { .owner = THIS_MODULE, .open = qstat_seq_open, @@ -155,22 +136,15 @@ static struct file_operations debugfs_fops = { static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev) { - int i = 0; char name[QDIO_DEBUGFS_NAME_LEN]; - while (debugfs_queues[i] != NULL) { - i++; - if (i >= MAX_DEBUGFS_QUEUES) - return; - } - snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%s_%d", - dev_name(&cdev->dev), + snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%d", q->is_input_q ? "input" : "output", q->nr); - debugfs_queues[i] = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, - debugfs_root, q, &debugfs_fops); - if (IS_ERR(debugfs_queues[i])) - debugfs_queues[i] = NULL; + q->debugfs_q = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, + q->irq_ptr->debugfs_dev, q, &debugfs_fops); + if (IS_ERR(q->debugfs_q)) + q->debugfs_q = NULL; } void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) @@ -178,12 +152,14 @@ void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) struct qdio_q *q; int i; - mutex_lock(&debugfs_mutex); + irq_ptr->debugfs_dev = debugfs_create_dir(dev_name(&cdev->dev), + debugfs_root); + if (IS_ERR(irq_ptr->debugfs_dev)) + irq_ptr->debugfs_dev = NULL; for_each_input_queue(irq_ptr, q, i) setup_debugfs_entry(q, cdev); for_each_output_queue(irq_ptr, q, i) setup_debugfs_entry(q, cdev); - mutex_unlock(&debugfs_mutex); } void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) @@ -191,17 +167,16 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd struct qdio_q *q; int i; - mutex_lock(&debugfs_mutex); for_each_input_queue(irq_ptr, q, i) - remove_debugfs_entry(q); + debugfs_remove(q->debugfs_q); for_each_output_queue(irq_ptr, q, i) - remove_debugfs_entry(q); - mutex_unlock(&debugfs_mutex); + debugfs_remove(q->debugfs_q); + debugfs_remove(irq_ptr->debugfs_dev); } int __init qdio_debug_init(void) { - debugfs_root = debugfs_create_dir("qdio_queues", NULL); + debugfs_root = debugfs_create_dir("qdio", NULL); qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16); debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 0038750ad94..9aef402a5f1 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -798,8 +798,10 @@ static void __tiqdio_inbound_processing(struct qdio_q *q) if (!qdio_inbound_q_done(q)) { qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); - if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) + if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) { tasklet_schedule(&q->tasklet); + return; + } } qdio_stop_polling(q); diff --git a/drivers/s390/cio/scsw.c b/drivers/s390/cio/scsw.c deleted file mode 100644 index f8da25ab576..00000000000 --- a/drivers/s390/cio/scsw.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Helper functions for scsw access. - * - * Copyright IBM Corp. 2008 - * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> - */ - -#include <linux/types.h> -#include <linux/module.h> -#include <asm/cio.h> -#include "css.h" -#include "chsc.h" - -/** - * scsw_is_tm - check for transport mode scsw - * @scsw: pointer to scsw - * - * Return non-zero if the specified scsw is a transport mode scsw, zero - * otherwise. - */ -int scsw_is_tm(union scsw *scsw) -{ - return css_general_characteristics.fcx && (scsw->tm.x == 1); -} -EXPORT_SYMBOL(scsw_is_tm); - -/** - * scsw_key - return scsw key field - * @scsw: pointer to scsw - * - * Return the value of the key field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_key(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.key; - else - return scsw->cmd.key; -} -EXPORT_SYMBOL(scsw_key); - -/** - * scsw_eswf - return scsw eswf field - * @scsw: pointer to scsw - * - * Return the value of the eswf field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_eswf(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.eswf; - else - return scsw->cmd.eswf; -} -EXPORT_SYMBOL(scsw_eswf); - -/** - * scsw_cc - return scsw cc field - * @scsw: pointer to scsw - * - * Return the value of the cc field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_cc(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.cc; - else - return scsw->cmd.cc; -} -EXPORT_SYMBOL(scsw_cc); - -/** - * scsw_ectl - return scsw ectl field - * @scsw: pointer to scsw - * - * Return the value of the ectl field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_ectl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.ectl; - else - return scsw->cmd.ectl; -} -EXPORT_SYMBOL(scsw_ectl); - -/** - * scsw_pno - return scsw pno field - * @scsw: pointer to scsw - * - * Return the value of the pno field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_pno(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.pno; - else - return scsw->cmd.pno; -} -EXPORT_SYMBOL(scsw_pno); - -/** - * scsw_fctl - return scsw fctl field - * @scsw: pointer to scsw - * - * Return the value of the fctl field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_fctl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.fctl; - else - return scsw->cmd.fctl; -} -EXPORT_SYMBOL(scsw_fctl); - -/** - * scsw_actl - return scsw actl field - * @scsw: pointer to scsw - * - * Return the value of the actl field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_actl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.actl; - else - return scsw->cmd.actl; -} -EXPORT_SYMBOL(scsw_actl); - -/** - * scsw_stctl - return scsw stctl field - * @scsw: pointer to scsw - * - * Return the value of the stctl field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_stctl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.stctl; - else - return scsw->cmd.stctl; -} -EXPORT_SYMBOL(scsw_stctl); - -/** - * scsw_dstat - return scsw dstat field - * @scsw: pointer to scsw - * - * Return the value of the dstat field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_dstat(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.dstat; - else - return scsw->cmd.dstat; -} -EXPORT_SYMBOL(scsw_dstat); - -/** - * scsw_cstat - return scsw cstat field - * @scsw: pointer to scsw - * - * Return the value of the cstat field of the specified scsw, regardless of - * whether it is a transport mode or command mode scsw. - */ -u32 scsw_cstat(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw->tm.cstat; - else - return scsw->cmd.cstat; -} -EXPORT_SYMBOL(scsw_cstat); - -/** - * scsw_cmd_is_valid_key - check key field validity - * @scsw: pointer to scsw - * - * Return non-zero if the key field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_key(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_key); - -/** - * scsw_cmd_is_valid_sctl - check fctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fctl field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_sctl(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_sctl); - -/** - * scsw_cmd_is_valid_eswf - check eswf field validity - * @scsw: pointer to scsw - * - * Return non-zero if the eswf field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_eswf(union scsw *scsw) -{ - return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_eswf); - -/** - * scsw_cmd_is_valid_cc - check cc field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cc field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_cc(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && - (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_cc); - -/** - * scsw_cmd_is_valid_fmt - check fmt field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fmt field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_fmt(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_fmt); - -/** - * scsw_cmd_is_valid_pfch - check pfch field validity - * @scsw: pointer to scsw - * - * Return non-zero if the pfch field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_pfch(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_pfch); - -/** - * scsw_cmd_is_valid_isic - check isic field validity - * @scsw: pointer to scsw - * - * Return non-zero if the isic field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_isic(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_isic); - -/** - * scsw_cmd_is_valid_alcc - check alcc field validity - * @scsw: pointer to scsw - * - * Return non-zero if the alcc field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_alcc(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_alcc); - -/** - * scsw_cmd_is_valid_ssi - check ssi field validity - * @scsw: pointer to scsw - * - * Return non-zero if the ssi field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_ssi(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_ssi); - -/** - * scsw_cmd_is_valid_zcc - check zcc field validity - * @scsw: pointer to scsw - * - * Return non-zero if the zcc field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_zcc(union scsw *scsw) -{ - return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && - (scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_zcc); - -/** - * scsw_cmd_is_valid_ectl - check ectl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the ectl field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_ectl(union scsw *scsw) -{ - return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && - !(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) && - (scsw->cmd.stctl & SCSW_STCTL_ALERT_STATUS); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_ectl); - -/** - * scsw_cmd_is_valid_pno - check pno field validity - * @scsw: pointer to scsw - * - * Return non-zero if the pno field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_pno(union scsw *scsw) -{ - return (scsw->cmd.fctl != 0) && - (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && - (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) || - ((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) && - (scsw->cmd.actl & SCSW_ACTL_SUSPENDED))); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_pno); - -/** - * scsw_cmd_is_valid_fctl - check fctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fctl field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_fctl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_cmd_is_valid_fctl); - -/** - * scsw_cmd_is_valid_actl - check actl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the actl field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_actl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_cmd_is_valid_actl); - -/** - * scsw_cmd_is_valid_stctl - check stctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the stctl field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_stctl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_cmd_is_valid_stctl); - -/** - * scsw_cmd_is_valid_dstat - check dstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the dstat field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_dstat(union scsw *scsw) -{ - return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && - (scsw->cmd.cc != 3); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_dstat); - -/** - * scsw_cmd_is_valid_cstat - check cstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cstat field of the specified command mode scsw is - * valid, zero otherwise. - */ -int scsw_cmd_is_valid_cstat(union scsw *scsw) -{ - return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && - (scsw->cmd.cc != 3); -} -EXPORT_SYMBOL(scsw_cmd_is_valid_cstat); - -/** - * scsw_tm_is_valid_key - check key field validity - * @scsw: pointer to scsw - * - * Return non-zero if the key field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_key(union scsw *scsw) -{ - return (scsw->tm.fctl & SCSW_FCTL_START_FUNC); -} -EXPORT_SYMBOL(scsw_tm_is_valid_key); - -/** - * scsw_tm_is_valid_eswf - check eswf field validity - * @scsw: pointer to scsw - * - * Return non-zero if the eswf field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_eswf(union scsw *scsw) -{ - return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND); -} -EXPORT_SYMBOL(scsw_tm_is_valid_eswf); - -/** - * scsw_tm_is_valid_cc - check cc field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cc field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_cc(union scsw *scsw) -{ - return (scsw->tm.fctl & SCSW_FCTL_START_FUNC) && - (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND); -} -EXPORT_SYMBOL(scsw_tm_is_valid_cc); - -/** - * scsw_tm_is_valid_fmt - check fmt field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fmt field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_fmt(union scsw *scsw) -{ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_fmt); - -/** - * scsw_tm_is_valid_x - check x field validity - * @scsw: pointer to scsw - * - * Return non-zero if the x field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_x(union scsw *scsw) -{ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_x); - -/** - * scsw_tm_is_valid_q - check q field validity - * @scsw: pointer to scsw - * - * Return non-zero if the q field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_q(union scsw *scsw) -{ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_q); - -/** - * scsw_tm_is_valid_ectl - check ectl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the ectl field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_ectl(union scsw *scsw) -{ - return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && - !(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) && - (scsw->tm.stctl & SCSW_STCTL_ALERT_STATUS); -} -EXPORT_SYMBOL(scsw_tm_is_valid_ectl); - -/** - * scsw_tm_is_valid_pno - check pno field validity - * @scsw: pointer to scsw - * - * Return non-zero if the pno field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_pno(union scsw *scsw) -{ - return (scsw->tm.fctl != 0) && - (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && - (!(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) || - ((scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) && - (scsw->tm.actl & SCSW_ACTL_SUSPENDED))); -} -EXPORT_SYMBOL(scsw_tm_is_valid_pno); - -/** - * scsw_tm_is_valid_fctl - check fctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fctl field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_fctl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_fctl); - -/** - * scsw_tm_is_valid_actl - check actl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the actl field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_actl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_actl); - -/** - * scsw_tm_is_valid_stctl - check stctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the stctl field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_stctl(union scsw *scsw) -{ - /* Only valid if pmcw.dnv == 1*/ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_stctl); - -/** - * scsw_tm_is_valid_dstat - check dstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the dstat field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_dstat(union scsw *scsw) -{ - return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && - (scsw->tm.cc != 3); -} -EXPORT_SYMBOL(scsw_tm_is_valid_dstat); - -/** - * scsw_tm_is_valid_cstat - check cstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cstat field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_cstat(union scsw *scsw) -{ - return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && - (scsw->tm.cc != 3); -} -EXPORT_SYMBOL(scsw_tm_is_valid_cstat); - -/** - * scsw_tm_is_valid_fcxs - check fcxs field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fcxs field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_fcxs(union scsw *scsw) -{ - return 1; -} -EXPORT_SYMBOL(scsw_tm_is_valid_fcxs); - -/** - * scsw_tm_is_valid_schxs - check schxs field validity - * @scsw: pointer to scsw - * - * Return non-zero if the schxs field of the specified transport mode scsw is - * valid, zero otherwise. - */ -int scsw_tm_is_valid_schxs(union scsw *scsw) -{ - return (scsw->tm.cstat & (SCHN_STAT_PROG_CHECK | - SCHN_STAT_INTF_CTRL_CHK | - SCHN_STAT_PROT_CHECK | - SCHN_STAT_CHN_DATA_CHK)); -} -EXPORT_SYMBOL(scsw_tm_is_valid_schxs); - -/** - * scsw_is_valid_actl - check actl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the actl field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_actl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_actl(scsw); - else - return scsw_cmd_is_valid_actl(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_actl); - -/** - * scsw_is_valid_cc - check cc field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cc field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_cc(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_cc(scsw); - else - return scsw_cmd_is_valid_cc(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_cc); - -/** - * scsw_is_valid_cstat - check cstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the cstat field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_cstat(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_cstat(scsw); - else - return scsw_cmd_is_valid_cstat(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_cstat); - -/** - * scsw_is_valid_dstat - check dstat field validity - * @scsw: pointer to scsw - * - * Return non-zero if the dstat field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_dstat(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_dstat(scsw); - else - return scsw_cmd_is_valid_dstat(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_dstat); - -/** - * scsw_is_valid_ectl - check ectl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the ectl field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_ectl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_ectl(scsw); - else - return scsw_cmd_is_valid_ectl(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_ectl); - -/** - * scsw_is_valid_eswf - check eswf field validity - * @scsw: pointer to scsw - * - * Return non-zero if the eswf field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_eswf(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_eswf(scsw); - else - return scsw_cmd_is_valid_eswf(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_eswf); - -/** - * scsw_is_valid_fctl - check fctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the fctl field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_fctl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_fctl(scsw); - else - return scsw_cmd_is_valid_fctl(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_fctl); - -/** - * scsw_is_valid_key - check key field validity - * @scsw: pointer to scsw - * - * Return non-zero if the key field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_key(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_key(scsw); - else - return scsw_cmd_is_valid_key(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_key); - -/** - * scsw_is_valid_pno - check pno field validity - * @scsw: pointer to scsw - * - * Return non-zero if the pno field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_pno(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_pno(scsw); - else - return scsw_cmd_is_valid_pno(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_pno); - -/** - * scsw_is_valid_stctl - check stctl field validity - * @scsw: pointer to scsw - * - * Return non-zero if the stctl field of the specified scsw is valid, - * regardless of whether it is a transport mode or command mode scsw. - * Return zero if the field does not contain a valid value. - */ -int scsw_is_valid_stctl(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_valid_stctl(scsw); - else - return scsw_cmd_is_valid_stctl(scsw); -} -EXPORT_SYMBOL(scsw_is_valid_stctl); - -/** - * scsw_cmd_is_solicited - check for solicited scsw - * @scsw: pointer to scsw - * - * Return non-zero if the command mode scsw indicates that the associated - * status condition is solicited, zero if it is unsolicited. - */ -int scsw_cmd_is_solicited(union scsw *scsw) -{ - return (scsw->cmd.cc != 0) || (scsw->cmd.stctl != - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)); -} -EXPORT_SYMBOL(scsw_cmd_is_solicited); - -/** - * scsw_tm_is_solicited - check for solicited scsw - * @scsw: pointer to scsw - * - * Return non-zero if the transport mode scsw indicates that the associated - * status condition is solicited, zero if it is unsolicited. - */ -int scsw_tm_is_solicited(union scsw *scsw) -{ - return (scsw->tm.cc != 0) || (scsw->tm.stctl != - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)); -} -EXPORT_SYMBOL(scsw_tm_is_solicited); - -/** - * scsw_is_solicited - check for solicited scsw - * @scsw: pointer to scsw - * - * Return non-zero if the transport or command mode scsw indicates that the - * associated status condition is solicited, zero if it is unsolicited. - */ -int scsw_is_solicited(union scsw *scsw) -{ - if (scsw_is_tm(scsw)) - return scsw_tm_is_solicited(scsw); - else - return scsw_cmd_is_solicited(scsw); -} -EXPORT_SYMBOL(scsw_is_solicited); diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index ed3dcdea7fe..090b32a339c 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -648,7 +648,9 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) /* Poll on the device until all requests are finished. */ do { flags = 0; + spin_lock_bh(&ap_dev->lock); __ap_poll_device(ap_dev, &flags); + spin_unlock_bh(&ap_dev->lock); } while ((flags & 1) || (flags & 2)); ap_device_remove(dev); @@ -1109,12 +1111,15 @@ static void ap_scan_bus(struct work_struct *unused) ap_dev->device.bus = &ap_bus_type; ap_dev->device.parent = ap_root_device; - dev_set_name(&ap_dev->device, "card%02x", - AP_QID_DEVICE(ap_dev->qid)); + if (dev_set_name(&ap_dev->device, "card%02x", + AP_QID_DEVICE(ap_dev->qid))) { + kfree(ap_dev); + continue; + } ap_dev->device.release = ap_device_release; rc = device_register(&ap_dev->device); if (rc) { - kfree(ap_dev); + put_device(&ap_dev->device); continue; } /* Add device attributes. */ @@ -1407,14 +1412,12 @@ static void ap_reset(struct ap_device *ap_dev) static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags) { - spin_lock(&ap_dev->lock); if (!ap_dev->unregistered) { if (ap_poll_queue(ap_dev, flags)) ap_dev->unregistered = 1; if (ap_dev->reset == AP_RESET_DO) ap_reset(ap_dev); } - spin_unlock(&ap_dev->lock); return 0; } @@ -1441,7 +1444,9 @@ static void ap_poll_all(unsigned long dummy) flags = 0; spin_lock(&ap_device_list_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { + spin_lock(&ap_dev->lock); __ap_poll_device(ap_dev, &flags); + spin_unlock(&ap_dev->lock); } spin_unlock(&ap_device_list_lock); } while (flags & 1); @@ -1487,7 +1492,9 @@ static int ap_poll_thread(void *data) flags = 0; spin_lock_bh(&ap_device_list_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { + spin_lock(&ap_dev->lock); __ap_poll_device(ap_dev, &flags); + spin_unlock(&ap_dev->lock); } spin_unlock_bh(&ap_device_list_lock); } diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index e38e5d306fa..2930fc763ac 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -403,10 +403,14 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) return len; } -void __init s390_virtio_console_init(void) +static int __init s390_virtio_console_init(void) { - virtio_cons_early_init(early_put_chars); + if (!MACHINE_IS_KVM) + return -ENODEV; + return virtio_cons_early_init(early_put_chars); } +console_initcall(s390_virtio_console_init); + /* * We do this after core stuff, but before the drivers. diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 8c36eafcfbf..87dff11061b 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1839,9 +1839,10 @@ static int netiucv_register_device(struct net_device *ndev) return -ENOMEM; ret = device_register(dev); - - if (ret) + if (ret) { + put_device(dev); return ret; + } ret = netiucv_add_files(dev); if (ret) goto out_unreg; @@ -2226,8 +2227,10 @@ static int __init netiucv_init(void) netiucv_dev->release = (void (*)(struct device *))kfree; netiucv_dev->driver = &netiucv_driver; rc = device_register(netiucv_dev); - if (rc) + if (rc) { + put_device(netiucv_dev); goto out_driver; + } netiucv_banner(); return rc; diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index e76a320d373..102000d1af6 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -219,13 +219,13 @@ static int __init smsg_init(void) smsg_dev->driver = &smsg_driver; rc = device_register(smsg_dev); if (rc) - goto out_free_dev; + goto out_put; cpcmd("SET SMSG IUCV", NULL, 0, NULL); return 0; -out_free_dev: - kfree(smsg_dev); +out_put: + put_device(smsg_dev); out_free_path: iucv_path_free(smsg_path); smsg_path = NULL; diff --git a/drivers/scsi/cxgb3i/cxgb3i_init.c b/drivers/scsi/cxgb3i/cxgb3i_init.c index 042d9bce991..d0ab23a5835 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_init.c +++ b/drivers/scsi/cxgb3i/cxgb3i_init.c @@ -26,7 +26,7 @@ MODULE_VERSION(DRV_MODULE_VERSION); static void open_s3_dev(struct t3cdev *); static void close_s3_dev(struct t3cdev *); -static void s3_err_handler(struct t3cdev *tdev, u32 status, u32 error); +static void s3_event_handler(struct t3cdev *tdev, u32 event, u32 port); static cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS]; static struct cxgb3_client t3c_client = { @@ -34,7 +34,7 @@ static struct cxgb3_client t3c_client = { .handlers = cxgb3i_cpl_handlers, .add = open_s3_dev, .remove = close_s3_dev, - .err_handler = s3_err_handler, + .event_handler = s3_event_handler, }; /** @@ -66,16 +66,16 @@ static void close_s3_dev(struct t3cdev *t3dev) cxgb3i_ddp_cleanup(t3dev); } -static void s3_err_handler(struct t3cdev *tdev, u32 status, u32 error) +static void s3_event_handler(struct t3cdev *tdev, u32 event, u32 port) { struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(tdev); - cxgb3i_log_info("snic 0x%p, tdev 0x%p, status 0x%x, err 0x%x.\n", - snic, tdev, status, error); + cxgb3i_log_info("snic 0x%p, tdev 0x%p, event 0x%x, port 0x%x.\n", + snic, tdev, event, port); if (!snic) return; - switch (status) { + switch (event) { case OFFLOAD_STATUS_DOWN: snic->flags |= CXGB3I_ADAPTER_FLAG_RESET; break; diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 9d7c99394ec..640f65c6ef8 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1752,12 +1752,12 @@ static int comedi_open(struct inode *inode, struct file *file) mutex_lock(&dev->mutex); if (dev->attached) goto ok; - if (!capable(CAP_SYS_MODULE) && dev->in_request_module) { + if (!capable(CAP_NET_ADMIN) && dev->in_request_module) { DPRINTK("in request module\n"); mutex_unlock(&dev->mutex); return -ENODEV; } - if (capable(CAP_SYS_MODULE) && dev->in_request_module) + if (capable(CAP_NET_ADMIN) && dev->in_request_module) goto ok; dev->in_request_module = 1; @@ -1770,8 +1770,8 @@ static int comedi_open(struct inode *inode, struct file *file) dev->in_request_module = 0; - if (!dev->attached && !capable(CAP_SYS_MODULE)) { - DPRINTK("not attached and not CAP_SYS_MODULE\n"); + if (!dev->attached && !capable(CAP_NET_ADMIN)) { + DPRINTK("not attached and not CAP_NET_ADMIN\n"); mutex_unlock(&dev->mutex); return -ENODEV; } diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index 7b605795b77..e63c9bea6c5 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -1950,14 +1950,7 @@ static int pohmelfs_get_sb(struct file_system_type *fs_type, */ static void pohmelfs_kill_super(struct super_block *sb) { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .range_start = 0, - .range_end = LLONG_MAX, - .nr_to_write = LONG_MAX, - }; - generic_sync_sb_inodes(sb, &wbc); - + sync_inodes_sb(sb); kill_anon_super(sb); } |