From 431bca73f7bc96cf9d51ae9bfd783d3c5bafd7cb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 2 May 2007 09:31:35 +0200 Subject: [MTD] Add Kyungmin Park to MAINTAINERS for OneNAND Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 77bff8ce1a4..036e31f1d89 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2550,6 +2550,12 @@ M: corbet@lwn.net L: video4linux-list@redhat.com S: Maintained +ONENAND FLASH DRIVER +P: Kyungmin Park +M: kyungmin.park@samsung.com +L: linux-mtd@lists.infradead.org +S: Maintained + ONSTREAM SCSI TAPE DRIVER P: Willem Riede M: osst@riede.org -- cgit v1.2.3 From 7c96b7a146eb81d13be738709c36752c20abe4fc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 2 May 2007 08:36:21 +0100 Subject: [JFFS2] Remove dead file histo_mips.h Its contents were subsumed into compr_rubin.c in a previous commit, but I forgot to git-rm it. Signed-off-by: David Woodhouse --- fs/jffs2/histo_mips.h | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 fs/jffs2/histo_mips.h diff --git a/fs/jffs2/histo_mips.h b/fs/jffs2/histo_mips.h deleted file mode 100644 index fa3dac19a10..00000000000 --- a/fs/jffs2/histo_mips.h +++ /dev/null @@ -1,2 +0,0 @@ -#define BIT_DIVIDER_MIPS 1043 -static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */ -- cgit v1.2.3 From d7e5a5462f68270ed66efff22b1981be57a28c19 Mon Sep 17 00:00:00 2001 From: Segher Boessenkool Date: Wed, 2 May 2007 12:18:41 +0200 Subject: [RSLIB] Support non-canonical GF representations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the CAFÉ NAND controller, we need to support non-canonical representations of the Galois field. Allow the caller to provide its own function for generating the field, and CAFÉ can use rslib instead of its own implementation. Signed-off-by: Segher Boessenkool Signed-off-by: David Woodhouse --- include/linux/rslib.h | 4 ++ lib/reed_solomon/reed_solomon.c | 84 +++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/include/linux/rslib.h b/include/linux/rslib.h index ace25acfdc9..746580c1939 100644 --- a/include/linux/rslib.h +++ b/include/linux/rslib.h @@ -34,6 +34,7 @@ * @prim: Primitive element, index form * @iprim: prim-th root of 1, index form * @gfpoly: The primitive generator polynominal + * @gffunc: Function to generate the field, if non-canonical representation * @users: Users of this structure * @list: List entry for the rs control list */ @@ -48,6 +49,7 @@ struct rs_control { int prim; int iprim; int gfpoly; + int (*gffunc)(int); int users; struct list_head list; }; @@ -77,6 +79,8 @@ int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len, /* Create or get a matching rs control structure */ struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, int nroots); +struct rs_control *init_rs_non_canonical(int symsize, int (*func)(int), + int fcr, int prim, int nroots); /* Release a rs control structure */ void free_rs(struct rs_control *rs); diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c index a4b730a2180..5b0d8522b7c 100644 --- a/lib/reed_solomon/reed_solomon.c +++ b/lib/reed_solomon/reed_solomon.c @@ -56,6 +56,7 @@ static DEFINE_MUTEX(rslistlock); * rs_init - Initialize a Reed-Solomon codec * @symsize: symbol size, bits (1-8) * @gfpoly: Field generator polynomial coefficients + * @gffunc: Field generator function * @fcr: first root of RS code generator polynomial, index form * @prim: primitive element to generate polynomial roots * @nroots: RS code generator polynomial degree (number of roots) @@ -63,8 +64,8 @@ static DEFINE_MUTEX(rslistlock); * Allocate a control structure and the polynom arrays for faster * en/decoding. Fill the arrays according to the given parameters. */ -static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, - int prim, int nroots) +static struct rs_control *rs_init(int symsize, int gfpoly, int (*gffunc)(int), + int fcr, int prim, int nroots) { struct rs_control *rs; int i, j, sr, root, iprim; @@ -82,6 +83,7 @@ static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, rs->prim = prim; rs->nroots = nroots; rs->gfpoly = gfpoly; + rs->gffunc = gffunc; /* Allocate the arrays */ rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL); @@ -99,17 +101,26 @@ static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, /* Generate Galois field lookup tables */ rs->index_of[0] = rs->nn; /* log(zero) = -inf */ rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */ - sr = 1; - for (i = 0; i < rs->nn; i++) { - rs->index_of[sr] = i; - rs->alpha_to[i] = sr; - sr <<= 1; - if (sr & (1 << symsize)) - sr ^= gfpoly; - sr &= rs->nn; + if (gfpoly) { + sr = 1; + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if (sr & (1 << symsize)) + sr ^= gfpoly; + sr &= rs->nn; + } + } else { + sr = gffunc(0); + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr = gffunc(sr); + } } /* If it's not primitive, exit */ - if(sr != 1) + if(sr != rs->alpha_to[0]) goto errpol; /* Find prim-th root of 1, used in decoding */ @@ -173,18 +184,22 @@ void free_rs(struct rs_control *rs) } /** - * init_rs - Find a matching or allocate a new rs control structure + * init_rs_internal - Find a matching or allocate a new rs control structure * @symsize: the symbol size (number of bits) * @gfpoly: the extended Galois field generator polynomial coefficients, * with the 0th coefficient in the low order bit. The polynomial * must be primitive; + * @gffunc: pointer to function to generate the next field element, + * or the multiplicative identity element if given 0. Used + * instead of gfpoly if gfpoly is 0 * @fcr: the first consecutive root of the rs code generator polynomial * in index form * @prim: primitive element to generate polynomial roots * @nroots: RS code generator polynomial degree (number of roots) */ -struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, - int nroots) +static struct rs_control *init_rs_internal(int symsize, int gfpoly, + int (*gffunc)(int), int fcr, + int prim, int nroots) { struct list_head *tmp; struct rs_control *rs; @@ -208,6 +223,8 @@ struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, continue; if (gfpoly != rs->gfpoly) continue; + if (gffunc != rs->gffunc) + continue; if (fcr != rs->fcr) continue; if (prim != rs->prim) @@ -220,7 +237,7 @@ struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, } /* Create a new one */ - rs = rs_init(symsize, gfpoly, fcr, prim, nroots); + rs = rs_init(symsize, gfpoly, gffunc, fcr, prim, nroots); if (rs) { rs->users = 1; list_add(&rs->list, &rslist); @@ -230,6 +247,42 @@ out: return rs; } +/** + * init_rs - Find a matching or allocate a new rs control structure + * @symsize: the symbol size (number of bits) + * @gfpoly: the extended Galois field generator polynomial coefficients, + * with the 0th coefficient in the low order bit. The polynomial + * must be primitive; + * @fcr: the first consecutive root of the rs code generator polynomial + * in index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + */ +struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, + int nroots) +{ + return init_rs_internal(symsize, gfpoly, NULL, fcr, prim, nroots); +} + +/** + * init_rs_non_canonical - Find a matching or allocate a new rs control + * structure, for fields with non-canonical + * representation + * @symsize: the symbol size (number of bits) + * @gffunc: pointer to function to generate the next field element, + * or the multiplicative identity element if given 0. Used + * instead of gfpoly if gfpoly is 0 + * @fcr: the first consecutive root of the rs code generator polynomial + * in index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + */ +struct rs_control *init_rs_non_canonical(int symsize, int (*gffunc)(int), + int fcr, int prim, int nroots) +{ + return init_rs_internal(symsize, 0, gffunc, fcr, prim, nroots); +} + #ifdef CONFIG_REED_SOLOMON_ENC8 /** * encode_rs8 - Calculate the parity for data values (8bit data width) @@ -321,6 +374,7 @@ EXPORT_SYMBOL_GPL(decode_rs16); #endif EXPORT_SYMBOL_GPL(init_rs); +EXPORT_SYMBOL_GPL(init_rs_non_canonical); EXPORT_SYMBOL_GPL(free_rs); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8c61b7a7f4d4e46be61cf1777361749b2d300c14 Mon Sep 17 00:00:00 2001 From: Segher Boessenkool Date: Wed, 2 May 2007 12:18:49 +0200 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Use=20rslib=20for=20CAF=C3=89=20ECC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Segher Boessenkool Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 12 +- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/cafe.c | 110 +++- drivers/mtd/nand/cafe_ecc.c | 1381 ------------------------------------------- 4 files changed, 106 insertions(+), 1399 deletions(-) delete mode 100644 drivers/mtd/nand/cafe_ecc.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index d05873b8c15..03ed2545f09 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,11 +232,13 @@ config MTD_NAND_BASLER_EXCITE will be named "excite_nandflash.ko". config MTD_NAND_CAFE - tristate "NAND support for OLPC CAFÉ chip" - depends on PCI - help - Use NAND flash attached to the CAFÉ chip designed for the $100 - laptop. + tristate "NAND support for OLPC CAFÉ chip" + depends on PCI + select REED_SOLOMON + select REED_SOLOMON_DEC16 + help + Use NAND flash attached to the CAFÉ chip designed for the $100 + laptop. config MTD_NAND_CS553X tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6872031a3fb..79e9e5da1c0 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -28,4 +28,4 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o -cafe_nand-objs := cafe.o cafe_ecc.o +cafe_nand-objs := cafe.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index c328a751451..05f6ec9f5aa 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -11,6 +11,7 @@ #undef DEBUG #include #include +#include #include #include #include @@ -46,13 +47,11 @@ #define CAFE_GLOBAL_IRQ_MASK 0x300c #define CAFE_NAND_RESET 0x3034 -int cafe_correct_ecc(unsigned char *buf, - unsigned short *chk_syndrome_list); - struct cafe_priv { struct nand_chip nand; struct pci_dev *pdev; void __iomem *mmio; + struct rs_control *rs; uint32_t ctl1; uint32_t ctl2; int datalen; @@ -374,28 +373,66 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { - unsigned short syn[8]; - int i; + unsigned short syn[8], pat[4]; + int pos[4]; + u8 *oob = chip->oob_poi; + int i, n; for (i=0; i<8; i+=2) { uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); - syn[i] = tmp & 0xfff; - syn[i+1] = (tmp >> 16) & 0xfff; + syn[i] = cafe->rs->index_of[tmp & 0xfff]; + syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; + } + + n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, + pat); + + for (i = 0; i < n; i++) { + int p = pos[i]; + + /* The 12-bit symbols are mapped to bytes here */ + + if (p > 1374) { + /* out of range */ + n = -1374; + } else if (p == 0) { + /* high four bits do not correspond to data */ + if (pat[i] > 0xff) + n = -2048; + else + buf[0] ^= pat[i]; + } else if (p == 1365) { + buf[2047] ^= pat[i] >> 4; + oob[0] ^= pat[i] << 4; + } else if (p > 1365) { + if ((p & 1) == 1) { + oob[3*p/2 - 2048] ^= pat[i] >> 4; + oob[3*p/2 - 2047] ^= pat[i] << 4; + } else { + oob[3*p/2 - 2049] ^= pat[i] >> 8; + oob[3*p/2 - 2048] ^= pat[i]; + } + } else if ((p & 1) == 1) { + buf[3*p/2] ^= pat[i] >> 4; + buf[3*p/2 + 1] ^= pat[i] << 4; + } else { + buf[3*p/2 - 1] ^= pat[i] >> 8; + buf[3*p/2] ^= pat[i]; + } } - if ((i = cafe_correct_ecc(buf, syn)) < 0) { + if (n < 0) { dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", cafe_readl(cafe, NAND_ADDR2) * 2048); - for (i=0; i< 0x5c; i+=4) + for (i = 0; i < 0x5c; i += 4) printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); mtd->ecc_stats.failed++; } else { - dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i); - mtd->ecc_stats.corrected += i; + dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); + mtd->ecc_stats.corrected += n; } } - return 0; } @@ -525,6 +562,48 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) return 0; } +/* F_2[X]/(X**6+X+1) */ +static unsigned short __devinit gf64_mul(u8 a, u8 b) +{ + u8 c; + unsigned int i; + + c = 0; + for (i = 0; i < 6; i++) { + if (a & 1) + c ^= b; + a >>= 1; + b <<= 1; + if ((b & 0x40) != 0) + b ^= 0x43; + } + + return c; +} + +/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ +static u16 __devinit gf4096_mul(u16 a, u16 b) +{ + u8 ah, al, bh, bl, ch, cl; + + ah = a >> 6; + al = a & 0x3f; + bh = b >> 6; + bl = b & 0x3f; + + ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); + cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); + + return (ch << 6) ^ cl; +} + +static int __devinit cafe_mul(int x) +{ + if (x == 0) + return 1; + return gf4096_mul(x, 0xe01); +} + static int __devinit cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -564,6 +643,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, } cafe->nand.buffers = (void *)cafe->dmabuf + 2112; + cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); + if (!cafe->rs) { + err = -ENOMEM; + goto out_ior; + } + cafe->nand.cmdfunc = cafe_nand_cmdfunc; cafe->nand.dev_ready = cafe_device_ready; cafe->nand.read_byte = cafe_read_byte; @@ -713,6 +798,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); nand_release(mtd); + free_rs(cafe->rs); pci_iounmap(pdev, cafe->mmio); dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); kfree(mtd); diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c deleted file mode 100644 index ea5c8491d2c..00000000000 --- a/drivers/mtd/nand/cafe_ecc.c +++ /dev/null @@ -1,1381 +0,0 @@ -/* Error correction for CAFÉ NAND controller - * - * © 2006 Marvell, Inc. - * Author: Tom Chiou - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -static unsigned short gf4096_mul(unsigned short, unsigned short); -static unsigned short gf64_mul(unsigned short, unsigned short); -static unsigned short gf4096_inv(unsigned short); -static unsigned short err_pos(unsigned short); -static void find_4bit_err_coefs(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short *); -static void zero_4x5_col3(unsigned short[4][5]); -static void zero_4x5_col2(unsigned short[4][5]); -static void zero_4x5_col1(unsigned short[4][5]); -static void swap_4x5_rows(unsigned short[4][5], int, int, int); -static void swap_2x3_rows(unsigned short m[2][3]); -static void solve_4x5(unsigned short m[4][5], unsigned short *, int *); -static void sort_coefs(int *, unsigned short *, int); -static void find_4bit_err_pats(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short *); -static void find_3bit_err_coefs(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short, - unsigned short *); -static void zero_3x4_col2(unsigned short[3][4]); -static void zero_3x4_col1(unsigned short[3][4]); -static void swap_3x4_rows(unsigned short[3][4], int, int, int); -static void solve_3x4(unsigned short[3][4], unsigned short *, int *); -static void find_3bit_err_pats(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short, - unsigned short *); - -static void find_2bit_err_pats(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short *); -static void find_2x2_soln(unsigned short, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned short, - unsigned short *); -static void solve_2x3(unsigned short[2][3], unsigned short *); -static int chk_no_err_only(unsigned short *, unsigned short *); -static int chk_1_err_only(unsigned short *, unsigned short *); -static int chk_2_err_only(unsigned short *, unsigned short *); -static int chk_3_err_only(unsigned short *, unsigned short *); -static int chk_4_err_only(unsigned short *, unsigned short *); - -static unsigned short gf64_mul(unsigned short a, unsigned short b) -{ - unsigned short tmp1, tmp2, tmp3, tmp4, tmp5; - unsigned short c_bit0, c_bit1, c_bit2, c_bit3, c_bit4, c_bit5, c; - - tmp1 = ((a) ^ (a >> 5)); - tmp2 = ((a >> 4) ^ (a >> 5)); - tmp3 = ((a >> 3) ^ (a >> 4)); - tmp4 = ((a >> 2) ^ (a >> 3)); - tmp5 = ((a >> 1) ^ (a >> 2)); - - c_bit0 = ((a & b) ^ ((a >> 5) & (b >> 1)) ^ ((a >> 4) & (b >> 2)) ^ - ((a >> 3) & (b >> 3)) ^ ((a >> 2) & (b >> 4)) ^ ((a >> 1) & (b >> 5))) & 0x1; - - c_bit1 = (((a >> 1) & b) ^ (tmp1 & (b >> 1)) ^ (tmp2 & (b >> 2)) ^ - (tmp3 & (b >> 3)) ^ (tmp4 & (b >> 4)) ^ (tmp5 & (b >> 5))) & 0x1; - - c_bit2 = (((a >> 2) & b) ^ ((a >> 1) & (b >> 1)) ^ (tmp1 & (b >> 2)) ^ - (tmp2 & (b >> 3)) ^ (tmp3 & (b >> 4)) ^ (tmp4 & (b >> 5))) & 0x1; - - c_bit3 = (((a >> 3) & b) ^ ((a >> 2) & (b >> 1)) ^ ((a >> 1) & (b >> 2)) ^ - (tmp1 & (b >> 3)) ^ (tmp2 & (b >> 4)) ^ (tmp3 & (b >> 5))) & 0x1; - - c_bit4 = (((a >> 4) & b) ^ ((a >> 3) & (b >> 1)) ^ ((a >> 2) & (b >> 2)) ^ - ((a >> 1) & (b >> 3)) ^ (tmp1 & (b >> 4)) ^ (tmp2 & (b >> 5))) & 0x1; - - c_bit5 = (((a >> 5) & b) ^ ((a >> 4) & (b >> 1)) ^ ((a >> 3) & (b >> 2)) ^ - ((a >> 2) & (b >> 3)) ^ ((a >> 1) & (b >> 4)) ^ (tmp1 & (b >> 5))) & 0x1; - - c = c_bit0 | (c_bit1 << 1) | (c_bit2 << 2) | (c_bit3 << 3) | (c_bit4 << 4) | (c_bit5 << 5); - - return c; -} - -static unsigned short gf4096_mul(unsigned short a, unsigned short b) -{ - unsigned short ah, al, bh, bl, alxah, blxbh, ablh, albl, ahbh, ahbhB, c; - - ah = (a >> 6) & 0x3f; - al = a & 0x3f; - bh = (b >> 6) & 0x3f; - bl = b & 0x3f; - alxah = al ^ ah; - blxbh = bl ^ bh; - - ablh = gf64_mul(alxah, blxbh); - albl = gf64_mul(al, bl); - ahbh = gf64_mul(ah, bh); - - ahbhB = ((ahbh & 0x1) << 5) | - ((ahbh & 0x20) >> 1) | - ((ahbh & 0x10) >> 1) | ((ahbh & 0x8) >> 1) | ((ahbh & 0x4) >> 1) | (((ahbh >> 1) ^ ahbh) & 0x1); - - c = ((ablh ^ albl) << 6) | (ahbhB ^ albl); - return c; -} - -static void find_2bit_err_pats(unsigned short s0, unsigned short s1, unsigned short r0, unsigned short r1, unsigned short *pats) -{ - find_2x2_soln(0x1, 0x1, r0, r1, s0, s1, pats); -} - -static void find_3bit_err_coefs(unsigned short s0, unsigned short s1, - unsigned short s2, unsigned short s3, unsigned short s4, unsigned short s5, unsigned short *coefs) -{ - unsigned short m[3][4]; - int row_order[3]; - - row_order[0] = 0; - row_order[1] = 1; - row_order[2] = 2; - m[0][0] = s2; - m[0][1] = s1; - m[0][2] = s0; - m[0][3] = s3; - m[1][0] = s3; - m[1][1] = s2; - m[1][2] = s1; - m[1][3] = s4; - m[2][0] = s4; - m[2][1] = s3; - m[2][2] = s2; - m[2][3] = s5; - - if (m[0][2] != 0x0) { - zero_3x4_col2(m); - } else if (m[1][2] != 0x0) { - swap_3x4_rows(m, 0, 1, 4); - zero_3x4_col2(m); - } else if (m[2][2] != 0x0) { - swap_3x4_rows(m, 0, 2, 4); - zero_3x4_col2(m); - } else { - printk(KERN_ERR "Error: find_3bit_err_coefs, s0,s1,s2 all zeros!\n"); - } - - if (m[1][1] != 0x0) { - zero_3x4_col1(m); - } else if (m[2][1] != 0x0) { - swap_3x4_rows(m, 1, 2, 4); - zero_3x4_col1(m); - } else { - printk(KERN_ERR "Error: find_3bit_err_coefs, cannot resolve col 1!\n"); - } - - /* solve coefs */ - solve_3x4(m, coefs, row_order); -} - -static void zero_3x4_col2(unsigned short m[3][4]) -{ - unsigned short minv1, minv2; - - minv1 = gf4096_mul(m[1][2], gf4096_inv(m[0][2])); - minv2 = gf4096_mul(m[2][2], gf4096_inv(m[0][2])); - m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1); - m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1); - m[1][3] = m[1][3] ^ gf4096_mul(m[0][3], minv1); - m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2); - m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2); - m[2][3] = m[2][3] ^ gf4096_mul(m[0][3], minv2); -} - -static void zero_3x4_col1(unsigned short m[3][4]) -{ - unsigned short minv; - minv = gf4096_mul(m[2][1], gf4096_inv(m[1][1])); - m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv); - m[2][3] = m[2][3] ^ gf4096_mul(m[1][3], minv); -} - -static void swap_3x4_rows(unsigned short m[3][4], int i, int j, int col_width) -{ - unsigned short tmp0; - int cnt; - for (cnt = 0; cnt < col_width; cnt++) { - tmp0 = m[i][cnt]; - m[i][cnt] = m[j][cnt]; - m[j][cnt] = tmp0; - } -} - -static void solve_3x4(unsigned short m[3][4], unsigned short *coefs, int *row_order) -{ - unsigned short tmp[3]; - tmp[0] = gf4096_mul(m[2][3], gf4096_inv(m[2][0])); - tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ m[1][3]), gf4096_inv(m[1][1])); - tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ gf4096_mul(tmp[1], m[0][1]) ^ m[0][3]), gf4096_inv(m[0][2])); - sort_coefs(row_order, tmp, 3); - coefs[0] = tmp[0]; - coefs[1] = tmp[1]; - coefs[2] = tmp[2]; -} - -static void find_3bit_err_pats(unsigned short s0, unsigned short s1, - unsigned short s2, unsigned short r0, - unsigned short r1, unsigned short r2, - unsigned short *pats) -{ - find_2x2_soln(r0 ^ r2, r1 ^ r2, - gf4096_mul(r0, r0 ^ r2), gf4096_mul(r1, r1 ^ r2), - gf4096_mul(s0, r2) ^ s1, gf4096_mul(s1, r2) ^ s2, pats); - pats[2] = s0 ^ pats[0] ^ pats[1]; -} - -static void find_4bit_err_coefs(unsigned short s0, unsigned short s1, - unsigned short s2, unsigned short s3, - unsigned short s4, unsigned short s5, - unsigned short s6, unsigned short s7, - unsigned short *coefs) -{ - unsigned short m[4][5]; - int row_order[4]; - - row_order[0] = 0; - row_order[1] = 1; - row_order[2] = 2; - row_order[3] = 3; - - m[0][0] = s3; - m[0][1] = s2; - m[0][2] = s1; - m[0][3] = s0; - m[0][4] = s4; - m[1][0] = s4; - m[1][1] = s3; - m[1][2] = s2; - m[1][3] = s1; - m[1][4] = s5; - m[2][0] = s5; - m[2][1] = s4; - m[2][2] = s3; - m[2][3] = s2; - m[2][4] = s6; - m[3][0] = s6; - m[3][1] = s5; - m[3][2] = s4; - m[3][3] = s3; - m[3][4] = s7; - - if (m[0][3] != 0x0) { - zero_4x5_col3(m); - } else if (m[1][3] != 0x0) { - swap_4x5_rows(m, 0, 1, 5); - zero_4x5_col3(m); - } else if (m[2][3] != 0x0) { - swap_4x5_rows(m, 0, 2, 5); - zero_4x5_col3(m); - } else if (m[3][3] != 0x0) { - swap_4x5_rows(m, 0, 3, 5); - zero_4x5_col3(m); - } else { - printk(KERN_ERR "Error: find_4bit_err_coefs, s0,s1,s2,s3 all zeros!\n"); - } - - if (m[1][2] != 0x0) { - zero_4x5_col2(m); - } else if (m[2][2] != 0x0) { - swap_4x5_rows(m, 1, 2, 5); - zero_4x5_col2(m); - } else if (m[3][2] != 0x0) { - swap_4x5_rows(m, 1, 3, 5); - zero_4x5_col2(m); - } else { - printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 2!\n"); - } - - if (m[2][1] != 0x0) { - zero_4x5_col1(m); - } else if (m[3][1] != 0x0) { - swap_4x5_rows(m, 2, 3, 5); - zero_4x5_col1(m); - } else { - printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 1!\n"); - } - - solve_4x5(m, coefs, row_order); -} - -static void zero_4x5_col3(unsigned short m[4][5]) -{ - unsigned short minv1, minv2, minv3; - - minv1 = gf4096_mul(m[1][3], gf4096_inv(m[0][3])); - minv2 = gf4096_mul(m[2][3], gf4096_inv(m[0][3])); - minv3 = gf4096_mul(m[3][3], gf4096_inv(m[0][3])); - - m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1); - m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1); - m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv1); - m[1][4] = m[1][4] ^ gf4096_mul(m[0][4], minv1); - m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2); - m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2); - m[2][2] = m[2][2] ^ gf4096_mul(m[0][2], minv2); - m[2][4] = m[2][4] ^ gf4096_mul(m[0][4], minv2); - m[3][0] = m[3][0] ^ gf4096_mul(m[0][0], minv3); - m[3][1] = m[3][1] ^ gf4096_mul(m[0][1], minv3); - m[3][2] = m[3][2] ^ gf4096_mul(m[0][2], minv3); - m[3][4] = m[3][4] ^ gf4096_mul(m[0][4], minv3); -} - -static void zero_4x5_col2(unsigned short m[4][5]) -{ - unsigned short minv2, minv3; - - minv2 = gf4096_mul(m[2][2], gf4096_inv(m[1][2])); - minv3 = gf4096_mul(m[3][2], gf4096_inv(m[1][2])); - - m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv2); - m[2][1] = m[2][1] ^ gf4096_mul(m[1][1], minv2); - m[2][4] = m[2][4] ^ gf4096_mul(m[1][4], minv2); - m[3][0] = m[3][0] ^ gf4096_mul(m[1][0], minv3); - m[3][1] = m[3][1] ^ gf4096_mul(m[1][1], minv3); - m[3][4] = m[3][4] ^ gf4096_mul(m[1][4], minv3); -} - -static void zero_4x5_col1(unsigned short m[4][5]) -{ - unsigned short minv; - - minv = gf4096_mul(m[3][1], gf4096_inv(m[2][1])); - - m[3][0] = m[3][0] ^ gf4096_mul(m[2][0], minv); - m[3][4] = m[3][4] ^ gf4096_mul(m[2][4], minv); -} - -static void swap_4x5_rows(unsigned short m[4][5], int i, int j, int col_width) -{ - unsigned short tmp0; - int cnt; - - for (cnt = 0; cnt < col_width; cnt++) { - tmp0 = m[i][cnt]; - m[i][cnt] = m[j][cnt]; - m[j][cnt] = tmp0; - } -} - -static void solve_4x5(unsigned short m[4][5], unsigned short *coefs, int *row_order) -{ - unsigned short tmp[4]; - - tmp[0] = gf4096_mul(m[3][4], gf4096_inv(m[3][0])); - tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[2][0]) ^ m[2][4]), gf4096_inv(m[2][1])); - tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ gf4096_mul(tmp[1], m[1][1]) ^ m[1][4]), gf4096_inv(m[1][2])); - tmp[3] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ - gf4096_mul(tmp[1], m[0][1]) ^ gf4096_mul(tmp[2], m[0][2]) ^ m[0][4]), gf4096_inv(m[0][3])); - sort_coefs(row_order, tmp, 4); - coefs[0] = tmp[0]; - coefs[1] = tmp[1]; - coefs[2] = tmp[2]; - coefs[3] = tmp[3]; -} - -static void sort_coefs(int *order, unsigned short *soln, int len) -{ - int cnt, start_cnt, least_ord, least_cnt; - unsigned short tmp0; - for (start_cnt = 0; start_cnt < len; start_cnt++) { - for (cnt = start_cnt; cnt < len; cnt++) { - if (cnt == start_cnt) { - least_ord = order[cnt]; - least_cnt = start_cnt; - } else { - if (least_ord > order[cnt]) { - least_ord = order[cnt]; - least_cnt = cnt; - } - } - } - if (least_cnt != start_cnt) { - tmp0 = order[least_cnt]; - order[least_cnt] = order[start_cnt]; - order[start_cnt] = tmp0; - tmp0 = soln[least_cnt]; - soln[least_cnt] = soln[start_cnt]; - soln[start_cnt] = tmp0; - } - } -} - -static void find_4bit_err_pats(unsigned short s0, unsigned short s1, - unsigned short s2, unsigned short s3, - unsigned short z1, unsigned short z2, - unsigned short z3, unsigned short z4, - unsigned short *pats) -{ - unsigned short z4_z1, z3z4_z3z3, z4_z2, s0z4_s1, z1z4_z1z1, - z4_z3, z2z4_z2z2, s1z4_s2, z3z3z4_z3z3z3, z1z1z4_z1z1z1, z2z2z4_z2z2z2, s2z4_s3; - unsigned short tmp0, tmp1, tmp2, tmp3; - - z4_z1 = z4 ^ z1; - z3z4_z3z3 = gf4096_mul(z3, z4) ^ gf4096_mul(z3, z3); - z4_z2 = z4 ^ z2; - s0z4_s1 = gf4096_mul(s0, z4) ^ s1; - z1z4_z1z1 = gf4096_mul(z1, z4) ^ gf4096_mul(z1, z1); - z4_z3 = z4 ^ z3; - z2z4_z2z2 = gf4096_mul(z2, z4) ^ gf4096_mul(z2, z2); - s1z4_s2 = gf4096_mul(s1, z4) ^ s2; - z3z3z4_z3z3z3 = gf4096_mul(gf4096_mul(z3, z3), z4) ^ gf4096_mul(gf4096_mul(z3, z3), z3); - z1z1z4_z1z1z1 = gf4096_mul(gf4096_mul(z1, z1), z4) ^ gf4096_mul(gf4096_mul(z1, z1), z1); - z2z2z4_z2z2z2 = gf4096_mul(gf4096_mul(z2, z2), z4) ^ gf4096_mul(gf4096_mul(z2, z2), z2); - s2z4_s3 = gf4096_mul(s2, z4) ^ s3; - - //find err pat 0,1 - find_2x2_soln(gf4096_mul(z4_z1, z3z4_z3z3) ^ - gf4096_mul(z1z4_z1z1, z4_z3), gf4096_mul(z4_z2, - z3z4_z3z3) ^ - gf4096_mul(z2z4_z2z2, z4_z3), gf4096_mul(z1z4_z1z1, - z3z3z4_z3z3z3) ^ - gf4096_mul(z1z1z4_z1z1z1, z3z4_z3z3), - gf4096_mul(z2z4_z2z2, - z3z3z4_z3z3z3) ^ gf4096_mul(z2z2z4_z2z2z2, - z3z4_z3z3), - gf4096_mul(s0z4_s1, z3z4_z3z3) ^ gf4096_mul(s1z4_s2, - z4_z3), - gf4096_mul(s1z4_s2, z3z3z4_z3z3z3) ^ gf4096_mul(s2z4_s3, z3z4_z3z3), pats); - tmp0 = pats[0]; - tmp1 = pats[1]; - tmp2 = pats[0] ^ pats[1] ^ s0; - tmp3 = gf4096_mul(pats[0], z1) ^ gf4096_mul(pats[1], z2) ^ s1; - - //find err pat 2,3 - find_2x2_soln(0x1, 0x1, z3, z4, tmp2, tmp3, pats); - pats[2] = pats[0]; - pats[3] = pats[1]; - pats[0] = tmp0; - pats[1] = tmp1; -} - -static void find_2x2_soln(unsigned short c00, unsigned short c01, - unsigned short c10, unsigned short c11, - unsigned short lval0, unsigned short lval1, - unsigned short *soln) -{ - unsigned short m[2][3]; - m[0][0] = c00; - m[0][1] = c01; - m[0][2] = lval0; - m[1][0] = c10; - m[1][1] = c11; - m[1][2] = lval1; - - if (m[0][1] != 0x0) { - /* */ - } else if (m[1][1] != 0x0) { - swap_2x3_rows(m); - } else { - printk(KERN_ERR "Warning: find_2bit_err_coefs, s0,s1 all zeros!\n"); - } - - solve_2x3(m, soln); -} - -static void swap_2x3_rows(unsigned short m[2][3]) -{ - unsigned short tmp0; - int cnt; - - for (cnt = 0; cnt < 3; cnt++) { - tmp0 = m[0][cnt]; - m[0][cnt] = m[1][cnt]; - m[1][cnt] = tmp0; - } -} - -static void solve_2x3(unsigned short m[2][3], unsigned short *coefs) -{ - unsigned short minv; - - minv = gf4096_mul(m[1][1], gf4096_inv(m[0][1])); - m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv); - m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv); - coefs[0] = gf4096_mul(m[1][2], gf4096_inv(m[1][0])); - coefs[1] = gf4096_mul((gf4096_mul(coefs[0], m[0][0]) ^ m[0][2]), gf4096_inv(m[0][1])); -} - -static unsigned char gf64_inv[64] = { - 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, - 61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45, 6, - 63, 2, 27, 21, 56, 9, 50, 19, 13, 47, 48, 5, 7, 30, 12, 41, - 42, 4, 38, 18, 10, 29, 17, 60, 36, 8, 59, 58, 55, 16, 3, 32 -}; - -static unsigned short gf4096_inv(unsigned short din) -{ - unsigned short alahxal, ah2B, deno, inv, bl, bh; - unsigned short ah, al, ahxal; - unsigned short dout; - - ah = (din >> 6) & 0x3f; - al = din & 0x3f; - ahxal = ah ^ al; - ah2B = (((ah ^ (ah >> 3)) & 0x1) << 5) | - ((ah >> 1) & 0x10) | - ((((ah >> 5) ^ (ah >> 2)) & 0x1) << 3) | - ((ah >> 2) & 0x4) | ((((ah >> 4) ^ (ah >> 1)) & 0x1) << 1) | (ah & 0x1); - alahxal = gf64_mul(ahxal, al); - deno = alahxal ^ ah2B; - inv = gf64_inv[deno]; - bl = gf64_mul(inv, ahxal); - bh = gf64_mul(inv, ah); - dout = ((bh & 0x3f) << 6) | (bl & 0x3f); - return (((bh & 0x3f) << 6) | (bl & 0x3f)); -} - -static unsigned short err_pos_lut[4096] = { - 0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041, - 0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff, - 0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff, - 0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7, - 0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff, - 0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff, - 0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1, - 0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff, - 0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7, - 0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff, - 0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff, - 0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d, - 0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f, - 0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8, - 0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe, - 0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff, - 0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff, - 0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1, - 0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff, - 0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff, - 0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff, - 0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff, - 0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff, - 0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff, - 0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff, - 0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8, - 0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e, - 0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff, - 0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff, - 0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff, - 0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535, - 0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d, - 0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff, - 0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5, - 0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff, - 0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294, - 0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff, - 0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4, - 0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f, - 0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b, - 0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff, - 0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff, - 0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff, - 0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff, - 0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff, - 0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158, - 0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a, - 0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff, - 0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff, - 0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017, - 0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a, - 0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351, - 0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff, - 0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3, - 0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff, - 0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074, - 0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da, - 0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433, - 0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285, - 0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff, - 0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d, - 0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff, - 0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff, - 0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331, - 0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff, - 0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6, - 0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3, - 0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff, - 0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff, - 0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf, - 0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff, - 0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc, - 0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381, - 0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff, - 0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f, - 0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff, - 0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488, - 0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442, - 0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6, - 0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1, - 0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae, - 0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff, - 0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff, - 0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff, - 0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2, - 0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec, - 0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134, - 0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b, - 0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff, - 0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff, - 0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff, - 0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff, - 0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5, - 0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff, - 0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e, - 0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff, - 0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff, - 0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff, - 0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2, - 0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff, - 0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff, - 0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff, - 0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff, - 0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff, - 0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff, - 0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff, - 0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff, - 0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8, - 0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb, - 0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff, - 0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff, - 0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd, - 0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff, - 0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8, - 0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424, - 0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9, - 0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f, - 0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7, - 0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff, - 0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d, - 0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff, - 0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178, - 0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff, - 0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba, - 0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff, - 0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e, - 0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff, - 0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6, - 0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff, - 0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff, - 0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff, - 0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc, - 0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6, - 0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5, - 0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff, - 0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff, - 0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff, - 0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc, - 0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff, - 0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff, - 0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3, - 0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff, - 0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6, - 0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff, - 0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a, - 0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff, - 0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff, - 0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb, - 0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff, - 0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff, - 0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7, - 0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff, - 0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff, - 0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af, - 0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff, - 0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff, - 0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2, - 0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff, - 0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff, - 0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff, - 0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339, - 0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff, - 0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff, - 0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402, - 0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff, - 0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff, - 0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368, - 0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff, - 0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b, - 0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f, - 0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff, - 0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1, - 0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff, - 0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff, - 0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb, - 0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc, - 0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097, - 0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431, - 0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1, - 0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4, - 0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff, - 0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff, - 0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6, - 0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd, - 0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff, - 0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb, - 0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff, - 0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd, - 0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff, - 0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff, - 0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519, - 0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff, - 0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084, - 0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0, - 0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c, - 0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff, - 0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f, - 0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a, - 0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211, - 0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff, - 0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff, - 0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff, - 0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff, - 0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff, - 0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff, - 0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180, - 0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0, - 0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004, - 0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302, - 0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff, - 0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff, - 0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072, - 0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272, - 0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff, - 0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344, - 0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff, - 0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff, - 0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e, - 0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d, - 0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff, - 0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff, - 0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330, - 0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042, - 0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364, - 0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff, - 0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff, - 0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f, - 0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb, - 0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e, - 0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c, - 0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194, - 0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff, - 0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff, - 0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff, - 0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff, - 0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff, - 0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6, - 0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff, - 0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290, - 0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff, - 0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9, - 0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d, - 0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff, - 0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9, - 0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167, - 0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020, - 0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347, - 0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff, - 0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff, - 0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff, - 0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff, - 0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff, - 0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff, - 0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039, - 0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096, - 0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff, - 0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff, - 0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff, - 0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a, - 0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8, - 0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff, - 0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd, - 0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e, - 0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f, - 0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff, - 0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff, - 0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7, - 0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb, - 0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff, - 0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff, - 0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078, - 0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300, - 0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200, - 0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f, - 0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff, - 0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216, - 0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d, - 0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0, - 0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff, - 0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff, - 0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff, - 0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff, - 0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff, - 0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff, - 0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086, - 0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff, - 0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff, - 0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444, - 0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff, - 0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff, - 0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff, - 0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110, - 0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099, - 0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff, - 0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da, - 0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff, - 0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff, - 0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff, - 0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff, - 0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff, - 0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff, - 0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283, - 0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff, - 0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff, - 0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff, - 0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff, - 0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff, - 0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195, - 0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff, - 0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff, - 0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff, - 0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff, - 0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff, - 0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245, - 0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff, - 0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6, - 0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1, - 0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da, - 0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff, - 0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff, - 0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5, - 0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223, - 0xfff, 0x3ff, 0xfff, 0x058, 0x174, 0x3ef, 0xfff, 0x002 -}; - -static unsigned short err_pos(unsigned short din) -{ - BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); - return err_pos_lut[din]; -} -static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -{ - if ((chk_syndrome_list[0] | chk_syndrome_list[1] | - chk_syndrome_list[2] | chk_syndrome_list[3] | - chk_syndrome_list[4] | chk_syndrome_list[5] | - chk_syndrome_list[6] | chk_syndrome_list[7]) != 0x0) { - return -EINVAL; - } else { - err_info[0] = 0x0; - return 0; - } -} -static int chk_1_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -{ - unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; - tmp0 = gf4096_mul(chk_syndrome_list[1], gf4096_inv(chk_syndrome_list[0])); - tmp1 = gf4096_mul(chk_syndrome_list[2], gf4096_inv(chk_syndrome_list[1])); - tmp2 = gf4096_mul(chk_syndrome_list[3], gf4096_inv(chk_syndrome_list[2])); - tmp3 = gf4096_mul(chk_syndrome_list[4], gf4096_inv(chk_syndrome_list[3])); - tmp4 = gf4096_mul(chk_syndrome_list[5], gf4096_inv(chk_syndrome_list[4])); - tmp5 = gf4096_mul(chk_syndrome_list[6], gf4096_inv(chk_syndrome_list[5])); - tmp6 = gf4096_mul(chk_syndrome_list[7], gf4096_inv(chk_syndrome_list[6])); - if ((tmp0 == tmp1) & (tmp1 == tmp2) & (tmp2 == tmp3) & (tmp3 == tmp4) & (tmp4 == tmp5) & (tmp5 == tmp6)) { - err_info[0] = 0x1; // encode 1-symbol error as 0x1 - err_info[1] = err_pos(tmp0); - err_info[1] = (unsigned short)(0x55e - err_info[1]); - err_info[5] = chk_syndrome_list[0]; - return 0; - } else - return -EINVAL; -} -static int chk_2_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -{ - unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - unsigned short coefs[4]; - unsigned short err_pats[4]; - int found_num_root = 0; - unsigned short bit2_root0, bit2_root1; - unsigned short bit2_root0_inv, bit2_root1_inv; - unsigned short err_loc_eqn, test_root; - unsigned short bit2_loc0, bit2_loc1; - unsigned short bit2_pat0, bit2_pat1; - - find_2x2_soln(chk_syndrome_list[1], - chk_syndrome_list[0], - chk_syndrome_list[2], chk_syndrome_list[1], chk_syndrome_list[2], chk_syndrome_list[3], coefs); - for (test_root = 0x1; test_root < 0xfff; test_root++) { - err_loc_eqn = - gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) ^ 0x1; - if (err_loc_eqn == 0x0) { - if (found_num_root == 0) { - bit2_root0 = test_root; - found_num_root = 1; - } else if (found_num_root == 1) { - bit2_root1 = test_root; - found_num_root = 2; - break; - } - } - } - if (found_num_root != 2) - return -EINVAL; - else { - bit2_root0_inv = gf4096_inv(bit2_root0); - bit2_root1_inv = gf4096_inv(bit2_root1); - find_2bit_err_pats(chk_syndrome_list[0], - chk_syndrome_list[1], bit2_root0_inv, bit2_root1_inv, err_pats); - bit2_pat0 = err_pats[0]; - bit2_pat1 = err_pats[1]; - //for(x+1) - tmp0 = gf4096_mul(gf4096_mul(bit2_root0_inv, bit2_root0_inv), gf4096_mul(bit2_root0_inv, bit2_root0_inv)); //rinv0^4 - tmp1 = gf4096_mul(bit2_root0_inv, tmp0); //rinv0^5 - tmp2 = gf4096_mul(bit2_root0_inv, tmp1); //rinv0^6 - tmp3 = gf4096_mul(bit2_root0_inv, tmp2); //rinv0^7 - tmp4 = gf4096_mul(gf4096_mul(bit2_root1_inv, bit2_root1_inv), gf4096_mul(bit2_root1_inv, bit2_root1_inv)); //rinv1^4 - tmp5 = gf4096_mul(bit2_root1_inv, tmp4); //rinv1^5 - tmp6 = gf4096_mul(bit2_root1_inv, tmp5); //rinv1^6 - tmp7 = gf4096_mul(bit2_root1_inv, tmp6); //rinv1^7 - //check if only 2-bit error - if ((chk_syndrome_list[4] == - (gf4096_mul(bit2_pat0, tmp0) ^ - gf4096_mul(bit2_pat1, - tmp4))) & (chk_syndrome_list[5] == - (gf4096_mul(bit2_pat0, tmp1) ^ - gf4096_mul(bit2_pat1, - tmp5))) & - (chk_syndrome_list[6] == - (gf4096_mul(bit2_pat0, tmp2) ^ - gf4096_mul(bit2_pat1, - tmp6))) & (chk_syndrome_list[7] == - (gf4096_mul(bit2_pat0, tmp3) ^ gf4096_mul(bit2_pat1, tmp7)))) { - if ((err_pos(bit2_root0_inv) == 0xfff) | (err_pos(bit2_root1_inv) == 0xfff)) { - return -EINVAL; - } else { - bit2_loc0 = 0x55e - err_pos(bit2_root0_inv); - bit2_loc1 = 0x55e - err_pos(bit2_root1_inv); - err_info[0] = 0x2; // encode 2-symbol error as 0x2 - err_info[1] = bit2_loc0; - err_info[2] = bit2_loc1; - err_info[5] = bit2_pat0; - err_info[6] = bit2_pat1; - return 0; - } - } else - return -EINVAL; - } -} -static int chk_3_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -{ - unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - unsigned short coefs[4]; - unsigned short err_pats[4]; - int found_num_root = 0; - unsigned short bit3_root0, bit3_root1, bit3_root2; - unsigned short bit3_root0_inv, bit3_root1_inv, bit3_root2_inv; - unsigned short err_loc_eqn, test_root; - - find_3bit_err_coefs(chk_syndrome_list[0], chk_syndrome_list[1], - chk_syndrome_list[2], chk_syndrome_list[3], - chk_syndrome_list[4], chk_syndrome_list[5], coefs); - - for (test_root = 0x1; test_root < 0xfff; test_root++) { - err_loc_eqn = gf4096_mul(coefs[2], - gf4096_mul(gf4096_mul(test_root, test_root), - test_root)) ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) - ^ gf4096_mul(coefs[0], test_root) ^ 0x1; - - if (err_loc_eqn == 0x0) { - if (found_num_root == 0) { - bit3_root0 = test_root; - found_num_root = 1; - } else if (found_num_root == 1) { - bit3_root1 = test_root; - found_num_root = 2; - } else if (found_num_root == 2) { - bit3_root2 = test_root; - found_num_root = 3; - break; - } - } - } - if (found_num_root != 3) - return -EINVAL; - else { - bit3_root0_inv = gf4096_inv(bit3_root0); - bit3_root1_inv = gf4096_inv(bit3_root1); - bit3_root2_inv = gf4096_inv(bit3_root2); - - find_3bit_err_pats(chk_syndrome_list[0], chk_syndrome_list[1], - chk_syndrome_list[2], bit3_root0_inv, - bit3_root1_inv, bit3_root2_inv, err_pats); - - //check if only 3-bit error - tmp0 = gf4096_mul(bit3_root0_inv, bit3_root0_inv); - tmp0 = gf4096_mul(tmp0, tmp0); - tmp0 = gf4096_mul(tmp0, bit3_root0_inv); - tmp0 = gf4096_mul(tmp0, bit3_root0_inv); //rinv0^6 - tmp1 = gf4096_mul(tmp0, bit3_root0_inv); //rinv0^7 - tmp2 = gf4096_mul(bit3_root1_inv, bit3_root1_inv); - tmp2 = gf4096_mul(tmp2, tmp2); - tmp2 = gf4096_mul(tmp2, bit3_root1_inv); - tmp2 = gf4096_mul(tmp2, bit3_root1_inv); //rinv1^6 - tmp3 = gf4096_mul(tmp2, bit3_root1_inv); //rinv1^7 - tmp4 = gf4096_mul(bit3_root2_inv, bit3_root2_inv); - tmp4 = gf4096_mul(tmp4, tmp4); - tmp4 = gf4096_mul(tmp4, bit3_root2_inv); - tmp4 = gf4096_mul(tmp4, bit3_root2_inv); //rinv2^6 - tmp5 = gf4096_mul(tmp4, bit3_root2_inv); //rinv2^7 - - //check if only 3 errors - if ((chk_syndrome_list[6] == (gf4096_mul(err_pats[0], tmp0) ^ - gf4096_mul(err_pats[1], tmp2) ^ - gf4096_mul(err_pats[2], tmp4))) & - (chk_syndrome_list[7] == (gf4096_mul(err_pats[0], tmp1) ^ - gf4096_mul(err_pats[1], tmp3) ^ gf4096_mul(err_pats[2], tmp5)))) { - if ((err_pos(bit3_root0_inv) == 0xfff) | - (err_pos(bit3_root1_inv) == 0xfff) | (err_pos(bit3_root2_inv) == 0xfff)) { - return -EINVAL; - } else { - err_info[0] = 0x3; - err_info[1] = (0x55e - err_pos(bit3_root0_inv)); - err_info[2] = (0x55e - err_pos(bit3_root1_inv)); - err_info[3] = (0x55e - err_pos(bit3_root2_inv)); - err_info[5] = err_pats[0]; - err_info[6] = err_pats[1]; - err_info[7] = err_pats[2]; - return 0; - } - } else - return -EINVAL; - } -} -static int chk_4_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -{ - unsigned short coefs[4]; - unsigned short err_pats[4]; - int found_num_root = 0; - unsigned short bit4_root0, bit4_root1, bit4_root2, bit4_root3; - unsigned short bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv; - unsigned short err_loc_eqn, test_root; - - find_4bit_err_coefs(chk_syndrome_list[0], - chk_syndrome_list[1], - chk_syndrome_list[2], - chk_syndrome_list[3], - chk_syndrome_list[4], - chk_syndrome_list[5], chk_syndrome_list[6], chk_syndrome_list[7], coefs); - - for (test_root = 0x1; test_root < 0xfff; test_root++) { - err_loc_eqn = - gf4096_mul(coefs[3], - gf4096_mul(gf4096_mul - (gf4096_mul(test_root, test_root), - test_root), - test_root)) ^ gf4096_mul(coefs[2], - gf4096_mul - (gf4096_mul(test_root, test_root), test_root)) - ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) - ^ 0x1; - if (err_loc_eqn == 0x0) { - if (found_num_root == 0) { - bit4_root0 = test_root; - found_num_root = 1; - } else if (found_num_root == 1) { - bit4_root1 = test_root; - found_num_root = 2; - } else if (found_num_root == 2) { - bit4_root2 = test_root; - found_num_root = 3; - } else { - found_num_root = 4; - bit4_root3 = test_root; - break; - } - } - } - if (found_num_root != 4) { - return -EINVAL; - } else { - bit4_root0_inv = gf4096_inv(bit4_root0); - bit4_root1_inv = gf4096_inv(bit4_root1); - bit4_root2_inv = gf4096_inv(bit4_root2); - bit4_root3_inv = gf4096_inv(bit4_root3); - find_4bit_err_pats(chk_syndrome_list[0], - chk_syndrome_list[1], - chk_syndrome_list[2], - chk_syndrome_list[3], - bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv, err_pats); - err_info[0] = 0x4; - err_info[1] = (0x55e - err_pos(bit4_root0_inv)); - err_info[2] = (0x55e - err_pos(bit4_root1_inv)); - err_info[3] = (0x55e - err_pos(bit4_root2_inv)); - err_info[4] = (0x55e - err_pos(bit4_root3_inv)); - err_info[5] = err_pats[0]; - err_info[6] = err_pats[1]; - err_info[7] = err_pats[2]; - err_info[8] = err_pats[3]; - return 0; - } -} - -void correct_12bit_symbol(unsigned char *buf, unsigned short sym, - unsigned short val) -{ - if (unlikely(sym > 1366)) { - printk(KERN_ERR "Error: symbol %d out of range; cannot correct\n", sym); - } else if (sym == 0) { - buf[0] ^= val; - } else if (sym & 1) { - buf[1+(3*(sym-1))/2] ^= (val >> 4); - buf[2+(3*(sym-1))/2] ^= ((val & 0xf) << 4); - } else { - buf[2+(3*(sym-2))/2] ^= (val >> 8); - buf[3+(3*(sym-2))/2] ^= (val & 0xff); - } -} - -static int debugecc = 0; -module_param(debugecc, int, 0644); - -int cafe_correct_ecc(unsigned char *buf, - unsigned short *chk_syndrome_list) -{ - unsigned short err_info[9]; - int i; - - if (debugecc) { - printk(KERN_WARNING "cafe_correct_ecc invoked. Syndromes %x %x %x %x %x %x %x %x\n", - chk_syndrome_list[0], chk_syndrome_list[1], - chk_syndrome_list[2], chk_syndrome_list[3], - chk_syndrome_list[4], chk_syndrome_list[5], - chk_syndrome_list[6], chk_syndrome_list[7]); - for (i=0; i < 2048; i+=16) { - printk(KERN_WARNING "D %04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - i, - buf[i], buf[i+1], buf[i+2], buf[i+3], - buf[i+4], buf[i+5], buf[i+6], buf[i+7], - buf[i+8], buf[i+9], buf[i+10], buf[i+11], - buf[i+12], buf[i+13], buf[i+14], buf[i+15]); - } - for ( ; i < 2112; i+=16) { - printk(KERN_WARNING "O %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - i - 2048, - buf[i], buf[i+1], buf[i+2], buf[i+3], - buf[i+4], buf[i+5], buf[i+6], buf[i+7], - buf[i+8], buf[i+9], buf[i+10], buf[i+11], - buf[i+12], buf[i+13], buf[i+14], buf[i+15]); - } - } - - - - if (chk_no_err_only(chk_syndrome_list, err_info) && - chk_1_err_only(chk_syndrome_list, err_info) && - chk_2_err_only(chk_syndrome_list, err_info) && - chk_3_err_only(chk_syndrome_list, err_info) && - chk_4_err_only(chk_syndrome_list, err_info)) { - return -EIO; - } - - for (i=0; i < err_info[0]; i++) { - if (debugecc) - printk(KERN_WARNING "Correct symbol %d with 0x%03x\n", - err_info[1+i], err_info[5+i]); - - correct_12bit_symbol(buf, err_info[1+i], err_info[5+i]); - } - - return err_info[0]; -} - -- cgit v1.2.3 From 14448005abd10887a2d361e20e04760dc3d8482f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 2 May 2007 12:04:57 +0100 Subject: [MTD] [NAND] Rename cafe.c to cafe_nand.c and remove the multi-obj magic Signed-off-by: David Woodhouse --- drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/cafe.c | 838 ------------------------------------------- drivers/mtd/nand/cafe_nand.c | 838 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 838 insertions(+), 839 deletions(-) delete mode 100644 drivers/mtd/nand/cafe.c create mode 100644 drivers/mtd/nand/cafe_nand.c diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 79e9e5da1c0..de1424b06b9 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -28,4 +28,3 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o -cafe_nand-objs := cafe.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c deleted file mode 100644 index 05f6ec9f5aa..00000000000 --- a/drivers/mtd/nand/cafe.c +++ /dev/null @@ -1,838 +0,0 @@ -/* - * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 - * - * Copyright © 2006 Red Hat, Inc. - * Copyright © 2006 David Woodhouse - */ - -#define DEBUG - -#include -#undef DEBUG -#include -#include -#include -#include -#include -#include -#include -#include - -#define CAFE_NAND_CTRL1 0x00 -#define CAFE_NAND_CTRL2 0x04 -#define CAFE_NAND_CTRL3 0x08 -#define CAFE_NAND_STATUS 0x0c -#define CAFE_NAND_IRQ 0x10 -#define CAFE_NAND_IRQ_MASK 0x14 -#define CAFE_NAND_DATA_LEN 0x18 -#define CAFE_NAND_ADDR1 0x1c -#define CAFE_NAND_ADDR2 0x20 -#define CAFE_NAND_TIMING1 0x24 -#define CAFE_NAND_TIMING2 0x28 -#define CAFE_NAND_TIMING3 0x2c -#define CAFE_NAND_NONMEM 0x30 -#define CAFE_NAND_ECC_RESULT 0x3C -#define CAFE_NAND_DMA_CTRL 0x40 -#define CAFE_NAND_DMA_ADDR0 0x44 -#define CAFE_NAND_DMA_ADDR1 0x48 -#define CAFE_NAND_ECC_SYN01 0x50 -#define CAFE_NAND_ECC_SYN23 0x54 -#define CAFE_NAND_ECC_SYN45 0x58 -#define CAFE_NAND_ECC_SYN67 0x5c -#define CAFE_NAND_READ_DATA 0x1000 -#define CAFE_NAND_WRITE_DATA 0x2000 - -#define CAFE_GLOBAL_CTRL 0x3004 -#define CAFE_GLOBAL_IRQ 0x3008 -#define CAFE_GLOBAL_IRQ_MASK 0x300c -#define CAFE_NAND_RESET 0x3034 - -struct cafe_priv { - struct nand_chip nand; - struct pci_dev *pdev; - void __iomem *mmio; - struct rs_control *rs; - uint32_t ctl1; - uint32_t ctl2; - int datalen; - int nr_data; - int data_pos; - int page_addr; - dma_addr_t dmaaddr; - unsigned char *dmabuf; -}; - -static int usedma = 1; -module_param(usedma, int, 0644); - -static int skipbbt = 0; -module_param(skipbbt, int, 0644); - -static int debug = 0; -module_param(debug, int, 0644); - -static int regdebug = 0; -module_param(regdebug, int, 0644); - -static int checkecc = 1; -module_param(checkecc, int, 0644); - -static int numtimings; -static int timing[3]; -module_param_array(timing, int, &numtimings, 0644); - -/* Hrm. Why isn't this already conditional on something in the struct device? */ -#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) - -/* Make it easier to switch to PIO if we need to */ -#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) -#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) - -static int cafe_device_ready(struct mtd_info *mtd) -{ - struct cafe_priv *cafe = mtd->priv; - int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000); - uint32_t irqs = cafe_readl(cafe, NAND_IRQ); - - cafe_writel(cafe, irqs, NAND_IRQ); - - cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), - cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); - - return result; -} - - -static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct cafe_priv *cafe = mtd->priv; - - if (usedma) - memcpy(cafe->dmabuf + cafe->datalen, buf, len); - else - memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); - - cafe->datalen += len; - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", - len, cafe->datalen); -} - -static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct cafe_priv *cafe = mtd->priv; - - if (usedma) - memcpy(buf, cafe->dmabuf + cafe->datalen, len); - else - memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", - len, cafe->datalen); - cafe->datalen += len; -} - -static uint8_t cafe_read_byte(struct mtd_info *mtd) -{ - struct cafe_priv *cafe = mtd->priv; - uint8_t d; - - cafe_read_buf(mtd, &d, 1); - cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); - - return d; -} - -static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct cafe_priv *cafe = mtd->priv; - int adrbytes = 0; - uint32_t ctl1; - uint32_t doneint = 0x80000000; - - cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", - command, column, page_addr); - - if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { - /* Second half of a command we already calculated */ - cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); - ctl1 = cafe->ctl1; - cafe->ctl2 &= ~(1<<30); - cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", - cafe->ctl1, cafe->nr_data); - goto do_command; - } - /* Reset ECC engine */ - cafe_writel(cafe, 0, NAND_CTRL2); - - /* Emulate NAND_CMD_READOOB on large-page chips */ - if (mtd->writesize > 512 && - command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* FIXME: Do we need to send read command before sending data - for small-page chips, to position the buffer correctly? */ - - if (column != -1) { - cafe_writel(cafe, column, NAND_ADDR1); - adrbytes = 2; - if (page_addr != -1) - goto write_adr2; - } else if (page_addr != -1) { - cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); - page_addr >>= 16; - write_adr2: - cafe_writel(cafe, page_addr, NAND_ADDR2); - adrbytes += 2; - if (mtd->size > mtd->writesize << 16) - adrbytes++; - } - - cafe->data_pos = cafe->datalen = 0; - - /* Set command valid bit */ - ctl1 = 0x80000000 | command; - - /* Set RD or WR bits as appropriate */ - if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { - ctl1 |= (1<<26); /* rd */ - /* Always 5 bytes, for now */ - cafe->datalen = 4; - /* And one address cycle -- even for STATUS, since the controller doesn't work without */ - adrbytes = 1; - } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { - ctl1 |= 1<<26; /* rd */ - /* For now, assume just read to end of page */ - cafe->datalen = mtd->writesize + mtd->oobsize - column; - } else if (command == NAND_CMD_SEQIN) - ctl1 |= 1<<25; /* wr */ - - /* Set number of address bytes */ - if (adrbytes) - ctl1 |= ((adrbytes-1)|8) << 27; - - if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { - /* Ignore the first command of a pair; the hardware - deals with them both at once, later */ - cafe->ctl1 = ctl1; - cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", - cafe->ctl1, cafe->datalen); - return; - } - /* RNDOUT and READ0 commands need a following byte */ - if (command == NAND_CMD_RNDOUT) - cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); - else if (command == NAND_CMD_READ0 && mtd->writesize > 512) - cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); - - do_command: - cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", - cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); - - /* NB: The datasheet lies -- we really should be subtracting 1 here */ - cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); - cafe_writel(cafe, 0x90000000, NAND_IRQ); - if (usedma && (ctl1 & (3<<25))) { - uint32_t dmactl = 0xc0000000 + cafe->datalen; - /* If WR or RD bits set, set up DMA */ - if (ctl1 & (1<<26)) { - /* It's a read */ - dmactl |= (1<<29); - /* ... so it's done when the DMA is done, not just - the command. */ - doneint = 0x10000000; - } - cafe_writel(cafe, dmactl, NAND_DMA_CTRL); - } - cafe->datalen = 0; - - if (unlikely(regdebug)) { - int i; - printk("About to write command %08x to register 0\n", ctl1); - for (i=4; i< 0x5c; i+=4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); - } - - cafe_writel(cafe, ctl1, NAND_CTRL1); - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - if (1) { - int c; - uint32_t irqs; - - for (c = 500000; c != 0; c--) { - irqs = cafe_readl(cafe, NAND_IRQ); - if (irqs & doneint) - break; - udelay(1); - if (!(c % 100000)) - cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); - cpu_relax(); - } - cafe_writel(cafe, doneint, NAND_IRQ); - cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", - command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); - } - - WARN_ON(cafe->ctl2 & (1<<30)); - - switch (command) { - - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - case NAND_CMD_RNDOUT: - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); - return; - } - nand_wait_ready(mtd); - cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); -} - -static void cafe_select_chip(struct mtd_info *mtd, int chipnr) -{ - //struct cafe_priv *cafe = mtd->priv; - // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); -} - -static int cafe_nand_interrupt(int irq, void *id) -{ - struct mtd_info *mtd = id; - struct cafe_priv *cafe = mtd->priv; - uint32_t irqs = cafe_readl(cafe, NAND_IRQ); - cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); - if (!irqs) - return IRQ_NONE; - - cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); - return IRQ_HANDLED; -} - -static void cafe_nand_bug(struct mtd_info *mtd) -{ - BUG(); -} - -static int cafe_nand_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - int status = 0; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/* Don't use -- use nand_read_oob_std for now */ -static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) -{ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; -} -/** - * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. - */ -static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) -{ - struct cafe_priv *cafe = mtd->priv; - - cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", - cafe_readl(cafe, NAND_ECC_RESULT), - cafe_readl(cafe, NAND_ECC_SYN01)); - - chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { - unsigned short syn[8], pat[4]; - int pos[4]; - u8 *oob = chip->oob_poi; - int i, n; - - for (i=0; i<8; i+=2) { - uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); - syn[i] = cafe->rs->index_of[tmp & 0xfff]; - syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; - } - - n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, - pat); - - for (i = 0; i < n; i++) { - int p = pos[i]; - - /* The 12-bit symbols are mapped to bytes here */ - - if (p > 1374) { - /* out of range */ - n = -1374; - } else if (p == 0) { - /* high four bits do not correspond to data */ - if (pat[i] > 0xff) - n = -2048; - else - buf[0] ^= pat[i]; - } else if (p == 1365) { - buf[2047] ^= pat[i] >> 4; - oob[0] ^= pat[i] << 4; - } else if (p > 1365) { - if ((p & 1) == 1) { - oob[3*p/2 - 2048] ^= pat[i] >> 4; - oob[3*p/2 - 2047] ^= pat[i] << 4; - } else { - oob[3*p/2 - 2049] ^= pat[i] >> 8; - oob[3*p/2 - 2048] ^= pat[i]; - } - } else if ((p & 1) == 1) { - buf[3*p/2] ^= pat[i] >> 4; - buf[3*p/2 + 1] ^= pat[i] << 4; - } else { - buf[3*p/2 - 1] ^= pat[i] >> 8; - buf[3*p/2] ^= pat[i]; - } - } - - if (n < 0) { - dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", - cafe_readl(cafe, NAND_ADDR2) * 2048); - for (i = 0; i < 0x5c; i += 4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); - mtd->ecc_stats.failed++; - } else { - dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); - mtd->ecc_stats.corrected += n; - } - } - - return 0; -} - -static struct nand_ecclayout cafe_oobinfo_2048 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 50}} -}; - -/* Ick. The BBT code really ought to be able to work this bit out - for itself from the above, at least for the 2KiB case */ -static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; -static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; - -static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; -static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; - - -static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_bbt_pattern_2048 -}; - -static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_mirror_pattern_2048 -}; - -static struct nand_ecclayout cafe_oobinfo_512 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 2}} -}; - -static struct nand_bbt_descr cafe_bbt_main_descr_512 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 1, - .veroffs = 15, - .maxblocks = 4, - .pattern = cafe_bbt_pattern_512 -}; - -static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 1, - .veroffs = 15, - .maxblocks = 4, - .pattern = cafe_mirror_pattern_512 -}; - - -static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) -{ - struct cafe_priv *cafe = mtd->priv; - - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Set up ECC autogeneration */ - cafe->ctl2 |= (1<<30); -} - -static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) -{ - int status; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); - else - chip->ecc.write_page(mtd, chip, buf); - - /* - * Cached progamming disabled for now, Not sure if its worth the - * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) - */ - cached = 0; - - if (!cached || !(chip->options & NAND_CACHEPRG)) { - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - /* - * See if operation failed and additional status checks are - * available - */ - if ((status & NAND_STATUS_FAIL) && (chip->errstat)) - status = chip->errstat(mtd, chip, FL_WRITING, status, - page); - - if (status & NAND_STATUS_FAIL) - return -EIO; - } else { - chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - } - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - if (chip->verify_buf(mtd, buf, mtd->writesize)) - return -EIO; -#endif - return 0; -} - -static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) -{ - return 0; -} - -/* F_2[X]/(X**6+X+1) */ -static unsigned short __devinit gf64_mul(u8 a, u8 b) -{ - u8 c; - unsigned int i; - - c = 0; - for (i = 0; i < 6; i++) { - if (a & 1) - c ^= b; - a >>= 1; - b <<= 1; - if ((b & 0x40) != 0) - b ^= 0x43; - } - - return c; -} - -/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ -static u16 __devinit gf4096_mul(u16 a, u16 b) -{ - u8 ah, al, bh, bl, ch, cl; - - ah = a >> 6; - al = a & 0x3f; - bh = b >> 6; - bl = b & 0x3f; - - ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); - cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); - - return (ch << 6) ^ cl; -} - -static int __devinit cafe_mul(int x) -{ - if (x == 0) - return 1; - return gf4096_mul(x, 0xe01); -} - -static int __devinit cafe_nand_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mtd_info *mtd; - struct cafe_priv *cafe; - uint32_t ctrl; - int err = 0; - - err = pci_enable_device(pdev); - if (err) - return err; - - pci_set_master(pdev); - - mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); - if (!mtd) { - dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); - return -ENOMEM; - } - cafe = (void *)(&mtd[1]); - - mtd->priv = cafe; - mtd->owner = THIS_MODULE; - - cafe->pdev = pdev; - cafe->mmio = pci_iomap(pdev, 0, 0); - if (!cafe->mmio) { - dev_warn(&pdev->dev, "failed to iomap\n"); - err = -ENOMEM; - goto out_free_mtd; - } - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_ior; - } - cafe->nand.buffers = (void *)cafe->dmabuf + 2112; - - cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); - if (!cafe->rs) { - err = -ENOMEM; - goto out_ior; - } - - cafe->nand.cmdfunc = cafe_nand_cmdfunc; - cafe->nand.dev_ready = cafe_device_ready; - cafe->nand.read_byte = cafe_read_byte; - cafe->nand.read_buf = cafe_read_buf; - cafe->nand.write_buf = cafe_write_buf; - cafe->nand.select_chip = cafe_select_chip; - - cafe->nand.chip_delay = 0; - - /* Enable the following for a flash based bad block table */ - cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; - - if (skipbbt) { - cafe->nand.options |= NAND_SKIP_BBTSCAN; - cafe->nand.block_bad = cafe_nand_block_bad; - } - - if (numtimings && numtimings != 3) { - dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); - } - - if (numtimings == 3) { - cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", - timing[0], timing[1], timing[2]); - } else { - timing[0] = cafe_readl(cafe, NAND_TIMING1); - timing[1] = cafe_readl(cafe, NAND_TIMING2); - timing[2] = cafe_readl(cafe, NAND_TIMING3); - - if (timing[0] | timing[1] | timing[2]) { - cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", - timing[0], timing[1], timing[2]); - } else { - dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); - timing[0] = timing[1] = timing[2] = 0xffffffff; - } - } - - /* Start off by resetting the NAND controller completely */ - cafe_writel(cafe, 1, NAND_RESET); - cafe_writel(cafe, 0, NAND_RESET); - - cafe_writel(cafe, timing[0], NAND_TIMING1); - cafe_writel(cafe, timing[1], NAND_TIMING2); - cafe_writel(cafe, timing[2], NAND_TIMING3); - - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); - err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, - "CAFE NAND", mtd); - if (err) { - dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; - } - - /* Disable master reset, enable NAND clock */ - ctrl = cafe_readl(cafe, GLOBAL_CTRL); - ctrl &= 0xffffeff0; - ctrl |= 0x00007000; - cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); - cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); - cafe_writel(cafe, 0, NAND_DMA_CTRL); - - cafe_writel(cafe, 0x7006, GLOBAL_CTRL); - cafe_writel(cafe, 0x700a, GLOBAL_CTRL); - - /* Set up DMA address */ - cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); - if (sizeof(cafe->dmaaddr) > 4) - /* Shift in two parts to shut the compiler up */ - cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); - else - cafe_writel(cafe, 0, NAND_DMA_ADDR1); - - cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); - - /* Enable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); - cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); - - /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 1)) { - err = -ENXIO; - goto out_irq; - } - - cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ - if (mtd->writesize == 2048) - cafe->ctl2 |= 1<<29; /* 2KiB page size */ - - /* Set up ECC according to the type of chip we found */ - if (mtd->writesize == 2048) { - cafe->nand.ecc.layout = &cafe_oobinfo_2048; - cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; - } else if (mtd->writesize == 512) { - cafe->nand.ecc.layout = &cafe_oobinfo_512; - cafe->nand.bbt_td = &cafe_bbt_main_descr_512; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; - } else { - printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", - mtd->writesize); - goto out_irq; - } - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - cafe->nand.ecc.size = mtd->writesize; - cafe->nand.ecc.bytes = 14; - cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; - cafe->nand.ecc.calculate = (void *)cafe_nand_bug; - cafe->nand.ecc.correct = (void *)cafe_nand_bug; - cafe->nand.write_page = cafe_nand_write_page; - cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; - cafe->nand.ecc.write_oob = cafe_nand_write_oob; - cafe->nand.ecc.read_page = cafe_nand_read_page; - cafe->nand.ecc.read_oob = cafe_nand_read_oob; - - err = nand_scan_tail(mtd); - if (err) - goto out_irq; - - pci_set_drvdata(pdev, mtd); - add_mtd_device(mtd); - goto out; - - out_irq: - /* Disable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); - free_irq(pdev->irq, mtd); - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - out_ior: - pci_iounmap(pdev, cafe->mmio); - out_free_mtd: - kfree(mtd); - out: - return err; -} - -static void __devexit cafe_nand_remove(struct pci_dev *pdev) -{ - struct mtd_info *mtd = pci_get_drvdata(pdev); - struct cafe_priv *cafe = mtd->priv; - - del_mtd_device(mtd); - /* Disable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); - free_irq(pdev->irq, mtd); - nand_release(mtd); - free_rs(cafe->rs); - pci_iounmap(pdev, cafe->mmio); - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - kfree(mtd); -} - -static struct pci_device_id cafe_nand_tbl[] = { - { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } -}; - -MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); - -static struct pci_driver cafe_nand_pci_driver = { - .name = "CAFÉ NAND", - .id_table = cafe_nand_tbl, - .probe = cafe_nand_probe, - .remove = __devexit_p(cafe_nand_remove), -#ifdef CONFIG_PMx - .suspend = cafe_nand_suspend, - .resume = cafe_nand_resume, -#endif -}; - -static int cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c new file mode 100644 index 00000000000..05f6ec9f5aa --- /dev/null +++ b/drivers/mtd/nand/cafe_nand.c @@ -0,0 +1,838 @@ +/* + * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 + * + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + */ + +#define DEBUG + +#include +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAFE_NAND_CTRL1 0x00 +#define CAFE_NAND_CTRL2 0x04 +#define CAFE_NAND_CTRL3 0x08 +#define CAFE_NAND_STATUS 0x0c +#define CAFE_NAND_IRQ 0x10 +#define CAFE_NAND_IRQ_MASK 0x14 +#define CAFE_NAND_DATA_LEN 0x18 +#define CAFE_NAND_ADDR1 0x1c +#define CAFE_NAND_ADDR2 0x20 +#define CAFE_NAND_TIMING1 0x24 +#define CAFE_NAND_TIMING2 0x28 +#define CAFE_NAND_TIMING3 0x2c +#define CAFE_NAND_NONMEM 0x30 +#define CAFE_NAND_ECC_RESULT 0x3C +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 +#define CAFE_NAND_ECC_SYN01 0x50 +#define CAFE_NAND_ECC_SYN23 0x54 +#define CAFE_NAND_ECC_SYN45 0x58 +#define CAFE_NAND_ECC_SYN67 0x5c +#define CAFE_NAND_READ_DATA 0x1000 +#define CAFE_NAND_WRITE_DATA 0x2000 + +#define CAFE_GLOBAL_CTRL 0x3004 +#define CAFE_GLOBAL_IRQ 0x3008 +#define CAFE_GLOBAL_IRQ_MASK 0x300c +#define CAFE_NAND_RESET 0x3034 + +struct cafe_priv { + struct nand_chip nand; + struct pci_dev *pdev; + void __iomem *mmio; + struct rs_control *rs; + uint32_t ctl1; + uint32_t ctl2; + int datalen; + int nr_data; + int data_pos; + int page_addr; + dma_addr_t dmaaddr; + unsigned char *dmabuf; +}; + +static int usedma = 1; +module_param(usedma, int, 0644); + +static int skipbbt = 0; +module_param(skipbbt, int, 0644); + +static int debug = 0; +module_param(debug, int, 0644); + +static int regdebug = 0; +module_param(regdebug, int, 0644); + +static int checkecc = 1; +module_param(checkecc, int, 0644); + +static int numtimings; +static int timing[3]; +module_param_array(timing, int, &numtimings, 0644); + +/* Hrm. Why isn't this already conditional on something in the struct device? */ +#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) + +/* Make it easier to switch to PIO if we need to */ +#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) +#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) + +static int cafe_device_ready(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000); + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); + + cafe_writel(cafe, irqs, NAND_IRQ); + + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), + cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + return result; +} + + +static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(cafe->dmabuf + cafe->datalen, buf, len); + else + memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + + cafe->datalen += len; + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + len, cafe->datalen); +} + +static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(buf, cafe->dmabuf + cafe->datalen, len); + else + memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + len, cafe->datalen); + cafe->datalen += len; +} + +static uint8_t cafe_read_byte(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + uint8_t d; + + cafe_read_buf(mtd, &d, 1); + cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + + return d; +} + +static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct cafe_priv *cafe = mtd->priv; + int adrbytes = 0; + uint32_t ctl1; + uint32_t doneint = 0x80000000; + + cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + command, column, page_addr); + + if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { + /* Second half of a command we already calculated */ + cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); + ctl1 = cafe->ctl1; + cafe->ctl2 &= ~(1<<30); + cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe->ctl1, cafe->nr_data); + goto do_command; + } + /* Reset ECC engine */ + cafe_writel(cafe, 0, NAND_CTRL2); + + /* Emulate NAND_CMD_READOOB on large-page chips */ + if (mtd->writesize > 512 && + command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* FIXME: Do we need to send read command before sending data + for small-page chips, to position the buffer correctly? */ + + if (column != -1) { + cafe_writel(cafe, column, NAND_ADDR1); + adrbytes = 2; + if (page_addr != -1) + goto write_adr2; + } else if (page_addr != -1) { + cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); + page_addr >>= 16; + write_adr2: + cafe_writel(cafe, page_addr, NAND_ADDR2); + adrbytes += 2; + if (mtd->size > mtd->writesize << 16) + adrbytes++; + } + + cafe->data_pos = cafe->datalen = 0; + + /* Set command valid bit */ + ctl1 = 0x80000000 | command; + + /* Set RD or WR bits as appropriate */ + if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { + ctl1 |= (1<<26); /* rd */ + /* Always 5 bytes, for now */ + cafe->datalen = 4; + /* And one address cycle -- even for STATUS, since the controller doesn't work without */ + adrbytes = 1; + } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { + ctl1 |= 1<<26; /* rd */ + /* For now, assume just read to end of page */ + cafe->datalen = mtd->writesize + mtd->oobsize - column; + } else if (command == NAND_CMD_SEQIN) + ctl1 |= 1<<25; /* wr */ + + /* Set number of address bytes */ + if (adrbytes) + ctl1 |= ((adrbytes-1)|8) << 27; + + if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { + /* Ignore the first command of a pair; the hardware + deals with them both at once, later */ + cafe->ctl1 = ctl1; + cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe->ctl1, cafe->datalen); + return; + } + /* RNDOUT and READ0 commands need a following byte */ + if (command == NAND_CMD_RNDOUT) + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); + else if (command == NAND_CMD_READ0 && mtd->writesize > 512) + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); + + do_command: + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); + + /* NB: The datasheet lies -- we really should be subtracting 1 here */ + cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); + cafe_writel(cafe, 0x90000000, NAND_IRQ); + if (usedma && (ctl1 & (3<<25))) { + uint32_t dmactl = 0xc0000000 + cafe->datalen; + /* If WR or RD bits set, set up DMA */ + if (ctl1 & (1<<26)) { + /* It's a read */ + dmactl |= (1<<29); + /* ... so it's done when the DMA is done, not just + the command. */ + doneint = 0x10000000; + } + cafe_writel(cafe, dmactl, NAND_DMA_CTRL); + } + cafe->datalen = 0; + + if (unlikely(regdebug)) { + int i; + printk("About to write command %08x to register 0\n", ctl1); + for (i=4; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + } + + cafe_writel(cafe, ctl1, NAND_CTRL1); + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if (1) { + int c; + uint32_t irqs; + + for (c = 500000; c != 0; c--) { + irqs = cafe_readl(cafe, NAND_IRQ); + if (irqs & doneint) + break; + udelay(1); + if (!(c % 100000)) + cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + cpu_relax(); + } + cafe_writel(cafe, doneint, NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", + command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); + } + + WARN_ON(cafe->ctl2 & (1<<30)); + + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + case NAND_CMD_RNDOUT: + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); + return; + } + nand_wait_ready(mtd); + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); +} + +static void cafe_select_chip(struct mtd_info *mtd, int chipnr) +{ + //struct cafe_priv *cafe = mtd->priv; + // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); +} + +static int cafe_nand_interrupt(int irq, void *id) +{ + struct mtd_info *mtd = id; + struct cafe_priv *cafe = mtd->priv; + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); + cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); + if (!irqs) + return IRQ_NONE; + + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); + return IRQ_HANDLED; +} + +static void cafe_nand_bug(struct mtd_info *mtd) +{ + BUG(); +} + +static int cafe_nand_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int status = 0; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* Don't use -- use nand_read_oob_std for now */ +static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 1; +} +/** + * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", + cafe_readl(cafe, NAND_ECC_RESULT), + cafe_readl(cafe, NAND_ECC_SYN01)); + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { + unsigned short syn[8], pat[4]; + int pos[4]; + u8 *oob = chip->oob_poi; + int i, n; + + for (i=0; i<8; i+=2) { + uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); + syn[i] = cafe->rs->index_of[tmp & 0xfff]; + syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; + } + + n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, + pat); + + for (i = 0; i < n; i++) { + int p = pos[i]; + + /* The 12-bit symbols are mapped to bytes here */ + + if (p > 1374) { + /* out of range */ + n = -1374; + } else if (p == 0) { + /* high four bits do not correspond to data */ + if (pat[i] > 0xff) + n = -2048; + else + buf[0] ^= pat[i]; + } else if (p == 1365) { + buf[2047] ^= pat[i] >> 4; + oob[0] ^= pat[i] << 4; + } else if (p > 1365) { + if ((p & 1) == 1) { + oob[3*p/2 - 2048] ^= pat[i] >> 4; + oob[3*p/2 - 2047] ^= pat[i] << 4; + } else { + oob[3*p/2 - 2049] ^= pat[i] >> 8; + oob[3*p/2 - 2048] ^= pat[i]; + } + } else if ((p & 1) == 1) { + buf[3*p/2] ^= pat[i] >> 4; + buf[3*p/2 + 1] ^= pat[i] << 4; + } else { + buf[3*p/2 - 1] ^= pat[i] >> 8; + buf[3*p/2] ^= pat[i]; + } + } + + if (n < 0) { + dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", + cafe_readl(cafe, NAND_ADDR2) * 2048); + for (i = 0; i < 0x5c; i += 4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + mtd->ecc_stats.failed++; + } else { + dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); + mtd->ecc_stats.corrected += n; + } + } + + return 0; +} + +static struct nand_ecclayout cafe_oobinfo_2048 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 50}} +}; + +/* Ick. The BBT code really ought to be able to work this bit out + for itself from the above, at least for the 2KiB case */ +static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; + +static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; +static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; + + +static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_bbt_pattern_2048 +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_mirror_pattern_2048 +}; + +static struct nand_ecclayout cafe_oobinfo_512 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 2}} +}; + +static struct nand_bbt_descr cafe_bbt_main_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_bbt_pattern_512 +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_mirror_pattern_512 +}; + + +static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Set up ECC autogeneration */ + cafe->ctl2 |= (1<<30); +} + +static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + +static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/* F_2[X]/(X**6+X+1) */ +static unsigned short __devinit gf64_mul(u8 a, u8 b) +{ + u8 c; + unsigned int i; + + c = 0; + for (i = 0; i < 6; i++) { + if (a & 1) + c ^= b; + a >>= 1; + b <<= 1; + if ((b & 0x40) != 0) + b ^= 0x43; + } + + return c; +} + +/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ +static u16 __devinit gf4096_mul(u16 a, u16 b) +{ + u8 ah, al, bh, bl, ch, cl; + + ah = a >> 6; + al = a & 0x3f; + bh = b >> 6; + bl = b & 0x3f; + + ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); + cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); + + return (ch << 6) ^ cl; +} + +static int __devinit cafe_mul(int x) +{ + if (x == 0) + return 1; + return gf4096_mul(x, 0xe01); +} + +static int __devinit cafe_nand_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mtd_info *mtd; + struct cafe_priv *cafe; + uint32_t ctrl; + int err = 0; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); + if (!mtd) { + dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); + return -ENOMEM; + } + cafe = (void *)(&mtd[1]); + + mtd->priv = cafe; + mtd->owner = THIS_MODULE; + + cafe->pdev = pdev; + cafe->mmio = pci_iomap(pdev, 0, 0); + if (!cafe->mmio) { + dev_warn(&pdev->dev, "failed to iomap\n"); + err = -ENOMEM; + goto out_free_mtd; + } + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_ior; + } + cafe->nand.buffers = (void *)cafe->dmabuf + 2112; + + cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); + if (!cafe->rs) { + err = -ENOMEM; + goto out_ior; + } + + cafe->nand.cmdfunc = cafe_nand_cmdfunc; + cafe->nand.dev_ready = cafe_device_ready; + cafe->nand.read_byte = cafe_read_byte; + cafe->nand.read_buf = cafe_read_buf; + cafe->nand.write_buf = cafe_write_buf; + cafe->nand.select_chip = cafe_select_chip; + + cafe->nand.chip_delay = 0; + + /* Enable the following for a flash based bad block table */ + cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + if (skipbbt) { + cafe->nand.options |= NAND_SKIP_BBTSCAN; + cafe->nand.block_bad = cafe_nand_block_bad; + } + + if (numtimings && numtimings != 3) { + dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); + } + + if (numtimings == 3) { + cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + timing[0] = cafe_readl(cafe, NAND_TIMING1); + timing[1] = cafe_readl(cafe, NAND_TIMING2); + timing[2] = cafe_readl(cafe, NAND_TIMING3); + + if (timing[0] | timing[1] | timing[2]) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); + timing[0] = timing[1] = timing[2] = 0xffffffff; + } + } + + /* Start off by resetting the NAND controller completely */ + cafe_writel(cafe, 1, NAND_RESET); + cafe_writel(cafe, 0, NAND_RESET); + + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); + + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, + "CAFE NAND", mtd); + if (err) { + dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); + goto out_free_dma; + } + + /* Disable master reset, enable NAND clock */ + ctrl = cafe_readl(cafe, GLOBAL_CTRL); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); + cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); + cafe_writel(cafe, 0, NAND_DMA_CTRL); + + cafe_writel(cafe, 0x7006, GLOBAL_CTRL); + cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + + /* Set up DMA address */ + cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); + if (sizeof(cafe->dmaaddr) > 4) + /* Shift in two parts to shut the compiler up */ + cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); + else + cafe_writel(cafe, 0, NAND_DMA_ADDR1); + + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); + + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + /* Scan to find existence of the device */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto out_irq; + } + + cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= 1<<29; /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + if (mtd->writesize == 2048) { + cafe->nand.ecc.layout = &cafe_oobinfo_2048; + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; + } else if (mtd->writesize == 512) { + cafe->nand.ecc.layout = &cafe_oobinfo_512; + cafe->nand.bbt_td = &cafe_bbt_main_descr_512; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; + } else { + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", + mtd->writesize); + goto out_irq; + } + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.write_page = cafe_nand_write_page; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + err = nand_scan_tail(mtd); + if (err) + goto out_irq; + + pci_set_drvdata(pdev, mtd); + add_mtd_device(mtd); + goto out; + + out_irq: + /* Disable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); + free_irq(pdev->irq, mtd); + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + out_ior: + pci_iounmap(pdev, cafe->mmio); + out_free_mtd: + kfree(mtd); + out: + return err; +} + +static void __devexit cafe_nand_remove(struct pci_dev *pdev) +{ + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct cafe_priv *cafe = mtd->priv; + + del_mtd_device(mtd); + /* Disable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); + free_irq(pdev->irq, mtd); + nand_release(mtd); + free_rs(cafe->rs); + pci_iounmap(pdev, cafe->mmio); + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + kfree(mtd); +} + +static struct pci_device_id cafe_nand_tbl[] = { + { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } +}; + +MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); + +static struct pci_driver cafe_nand_pci_driver = { + .name = "CAFÉ NAND", + .id_table = cafe_nand_tbl, + .probe = cafe_nand_probe, + .remove = __devexit_p(cafe_nand_remove), +#ifdef CONFIG_PMx + .suspend = cafe_nand_suspend, + .resume = cafe_nand_resume, +#endif +}; + +static int cafe_nand_init(void) +{ + return pci_register_driver(&cafe_nand_pci_driver); +} + +static void cafe_nand_exit(void) +{ + pci_unregister_driver(&cafe_nand_pci_driver); +} +module_init(cafe_nand_init); +module_exit(cafe_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); -- cgit v1.2.3 From 048c37b4907ecf0e55100365e20758667f0bd27d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 2 May 2007 12:26:37 +0100 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Support=20multiple=20chips=20in=20CAF?= =?UTF-8?q?=C3=89=20driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CAFÉ can handle two chip on separate chipselect lines. Hook up the undocumented chipselect bits in the driver and probe both. In the case of OLPC, it's not actually two separate devices -- it's a single '1GiB' package with two 512MiB dies internally. So clear the NAND_BBT_PERCHIP flag to treat it as a single chip for BBT purposes, and make life easier for the firmware. Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 05f6ec9f5aa..cff969d05d4 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -47,6 +47,9 @@ #define CAFE_GLOBAL_IRQ_MASK 0x300c #define CAFE_NAND_RESET 0x3034 +/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */ +#define CTRL1_CHIPSELECT (1<<19) + struct cafe_priv { struct nand_chip nand; struct pci_dev *pdev; @@ -194,8 +197,8 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cafe->data_pos = cafe->datalen = 0; - /* Set command valid bit */ - ctl1 = 0x80000000 | command; + /* Set command valid bit, mask in the chip select bit */ + ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT); /* Set RD or WR bits as appropriate */ if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { @@ -308,8 +311,16 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, static void cafe_select_chip(struct mtd_info *mtd, int chipnr) { - //struct cafe_priv *cafe = mtd->priv; - // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); + struct cafe_priv *cafe = mtd->priv; + + cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); + + /* Mask the appropriate bit into the stored value of ctl1 + which will be used by cafe_nand_cmdfunc() */ + if (chipnr) + cafe->ctl1 |= CTRL1_CHIPSELECT; + else + cafe->ctl1 &= ~CTRL1_CHIPSELECT; } static int cafe_nand_interrupt(int irq, void *id) @@ -453,7 +464,7 @@ static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 4, .veroffs = 18, @@ -463,7 +474,7 @@ static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 4, .veroffs = 18, @@ -479,7 +490,7 @@ static struct nand_ecclayout cafe_oobinfo_512 = { static struct nand_bbt_descr cafe_bbt_main_descr_512 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 1, .veroffs = 15, @@ -489,7 +500,7 @@ static struct nand_bbt_descr cafe_bbt_main_descr_512 = { static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 1, .veroffs = 15, @@ -731,7 +742,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 2)) { err = -ENXIO; goto out_irq; } -- cgit v1.2.3 From 4839f0481d8b985aabd6653ba42cf09e2abcc2bd Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 2 May 2007 12:33:17 +0100 Subject: [MTD] block2mtd_paramline[] mustn't be __initdata block2mtd_paramline[] is used in the non-__init block2mtd_setup() Signed-off-by: Adrian Bunk Acked-by: Joern Engel Cc: Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/devices/block2mtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index ce47544dc12..4070720561f 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -375,7 +375,7 @@ static inline void kill_final_newline(char *str) #ifndef MODULE static int block2mtd_init_called = 0; -static __initdata char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ +static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ #endif -- cgit v1.2.3 From 1a12f46af1af1a4b7b9c7ae7ab2c8ded3481a4ba Mon Sep 17 00:00:00 2001 From: Thomas Knobloch Date: Thu, 3 May 2007 07:39:37 +0100 Subject: [MTD] [NAND] Tidy up handling of page number in nand_block_bad() Further to the previous patch fixing the calculation of page number, both branches are using the same result. Clean up the function accordingly, calculating it (and also masking with pagemask) only in one place. Signed-off-by: Thomas Knobloch Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 04de315e493..7e68203fe1b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -303,28 +303,27 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + if (getchip) { - page = (int)(ofs >> chip->page_shift); chipnr = (int)(ofs >> chip->chip_shift); nand_get_device(chip, mtd, FL_READING); /* Select the NAND device */ chip->select_chip(mtd, chipnr); - } else - page = (int)(ofs >> chip->page_shift); + } if (chip->options & NAND_BUSWIDTH_16) { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, - page & chip->pagemask); + page); bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; if ((bad & 0xFF) != 0xff) res = 1; } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, - page & chip->pagemask); + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); if (chip->read_byte(mtd) != 0xff) res = 1; } -- cgit v1.2.3 From 693ef66d8914f50cb899b5268676ea508d1f3178 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Thu, 3 May 2007 08:16:44 +0200 Subject: [MTD] [NAND] at91_nand.c: CMDLINE_PARTS support This patch allows you to specify at91_nand partitions on the kernel command line using the mtdparts variable, if CONFIG_MTD_CMDLINE_PARTS is set. Signed-off-by: Frank Mandarino Signed-off-by: Andrew Victor Signed-off-by: David Woodhouse --- drivers/mtd/nand/at91_nand.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index 14b80cc90a7..512e999177f 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -82,6 +82,10 @@ static void at91_nand_disable(struct at91_nand_host *host) at91_set_gpio_value(host->board->enable_pin, 1); } +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + /* * Probe for the NAND device. */ @@ -151,6 +155,12 @@ static int __init at91_nand_probe(struct platform_device *pdev) #ifdef CONFIG_MTD_PARTITIONS if (host->board->partition_info) partitions = host->board->partition_info(mtd->size, &num_partitions); +#ifdef CONFIG_MTD_CMDLINE_PARTS + else { + mtd->name = "at91_nand"; + num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); + } +#endif if ((!partitions) || (num_partitions == 0)) { printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); -- cgit v1.2.3 From 3fddb6c985e3823c991399840d2d5ef5940e1b60 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 5 May 2007 09:52:49 +0100 Subject: [JFFS2] Don't advance c->wbuf_ofs to next eraseblock after wbuf flush After flushing the last page of an eraseblock, don't leave the wbuf 'offset' field pointing at the start of the next physical eraseblock. This was causing a BUG() on NOR-ECC (Sibley) flash, where we start writing a little further in, after the cleanmarker. Debugged by Alexander Belyakov Signed-off-by: David Woodhouse --- fs/jffs2/wbuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index c556e85a565..91d1d0f1c66 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -637,7 +637,10 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) memset(c->wbuf,0xff,c->wbuf_pagesize); /* adjust write buffer offset, else we get a non contiguous write bug */ - c->wbuf_ofs += c->wbuf_pagesize; + if (SECTOR_ADDR(c->wbuf_ofs) == SECTOR_ADDR(c->wbuf_ofs+c->wbuf_pagesize)) + c->wbuf_ofs += c->wbuf_pagesize; + else + c->wbuf_ofs = 0xffffffff; c->wbuf_len = 0; return 0; } -- cgit v1.2.3 From 1123e2a85941c7f506bd42c91c7e9ab74fc42d79 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 5 May 2007 16:29:34 +0100 Subject: [JFFS2] Remember to calculate overlap on nodes which replace older nodes This fixes a problem Artem found with the integck test tool -- we weren't correctly keeping track of the 'overlap' flag in some cases, which led to the nodes being played back in an incorrect order and file corruption. Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 6aff38930b5..b0645ac7769 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -276,10 +276,9 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, /* Who cares if the new one is good; keep it for now anyway. */ rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); /* Same overlapping from in front and behind */ - tn->overlapped = this->overlapped; jffs2_kill_tn(c, this); dbg_readinode("Like new node. Throw away old\n"); - return 0; + goto calc_overlaps; } } if (this->version < tn->version && @@ -293,7 +292,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, } /* ... and is good. Kill 'this'... */ rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); - tn->overlapped = this->overlapped; jffs2_kill_tn(c, this); /* ... and any subsequent nodes which are also overlapped */ this = tn_next(tn); @@ -309,7 +307,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, this = next; } dbg_readinode("Done inserting new\n"); - return 0; + goto calc_overlaps; } if (this->version > tn->version && this->fn->ofs <= tn->fn->ofs && @@ -321,6 +319,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, return 0; } /* ... but 'this' was bad. Replace it... */ + tn->overlapped = this->overlapped; rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); dbg_readinode("Bad CRC on old overlapping node. Kill it\n"); jffs2_kill_tn(c, this); @@ -359,6 +358,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, rb_link_node(&tn->rb, &insert_point->rb, link); rb_insert_color(&tn->rb, &rii->tn_root); } + + calc_overlaps: /* If there's anything behind that overlaps us, note it */ this = tn_prev(tn); if (this) { @@ -483,7 +484,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, vers_next = tn_prev(this); eat_last(&ver_root, &this->rb); if (check_tn_node(c, this)) { - dbg_readinode("node ver %x, 0x%x-0x%x failed CRC\n", + dbg_readinode("node ver %d, 0x%x-0x%x failed CRC\n", this->version, this->fn->ofs, this->fn->ofs+this->fn->size); jffs2_kill_tn(c, this); @@ -496,7 +497,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, high_ver = this->version; rii->latest_ref = this->fn->raw; } - dbg_readinode("Add %p (v %x, 0x%x-0x%x, ov %d) to fragtree\n", + dbg_readinode("Add %p (v %d, 0x%x-0x%x, ov %d) to fragtree\n", this, this->version, this->fn->ofs, this->fn->ofs+this->fn->size, this->overlapped); @@ -850,7 +851,7 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref return ret; } #ifdef JFFS2_DBG_READINODE_MESSAGES - dbg_readinode("After adding ver %d:\n", tn->version); + dbg_readinode("After adding ver %d:\n", je32_to_cpu(rd->version)); tn = tn_first(&rii->tn_root); while (tn) { dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n", -- cgit v1.2.3 From 96dd8d25d1ca8c233bd47752349d27a631c18719 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 6 May 2007 14:41:40 +0100 Subject: [JFFS2] Remove broken insert_point optimisation in jffs2_add_tn_to_tree() The original code would remember, during the first pass over the tree, a suitable place to start the insertion from when we eventually come to add a new node. The optimisation was broken, and we sometimes ended up inserting a new node in the wrong place because we started the insertion from the wrong point. Just ditch the optimisation and start the insertion from the root of the tree, for now. I'll try it again when I'm feeling cleverer. Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index b0645ac7769..f461604cf01 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -219,7 +219,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) { uint32_t fn_end = tn->fn->ofs + tn->fn->size; - struct jffs2_tmp_dnode_info *insert_point = NULL, *this; + struct jffs2_tmp_dnode_info *this; dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version); @@ -248,9 +248,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, return 0; } - /* If we add a new node it'll be somewhere under here. */ - insert_point = this; - /* If the node is coincident with another at a lower address, back up until the other node is found. It may be relevant */ while (tn->overlapped) @@ -325,24 +322,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, jffs2_kill_tn(c, this); return 0; } - /* We want to be inserted under the last node which is - either at a lower offset _or_ has a smaller range */ - if (this->fn->ofs < tn->fn->ofs || - (this->fn->ofs == tn->fn->ofs && - this->fn->size <= tn->fn->size)) - insert_point = this; this = tn_next(this); } - dbg_readinode("insert_point %p, ver %d, 0x%x-0x%x, ov %d\n", - insert_point, insert_point->version, insert_point->fn->ofs, - insert_point->fn->ofs+insert_point->fn->size, - insert_point->overlapped); + /* We neither completely obsoleted nor were completely - obsoleted by an earlier node. Insert under insert_point */ + obsoleted by an earlier node. Insert into the tree */ { - struct rb_node *parent = &insert_point->rb; - struct rb_node **link = &parent; + struct rb_node *parent; + struct rb_node **link = &rii->tn_root.rb_node; + struct jffs2_tmp_dnode_info *insert_point; while (*link) { parent = *link; @@ -458,7 +447,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, this = tn_last(&rii->tn_root); while (this) { dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs, - this->fn->ofs+this->fn->size, this->overlapped); + this->fn->ofs+this->fn->size, this->overlapped); this = tn_prev(this); } #endif -- cgit v1.2.3 From fcf3cafb3e7e7750f4876571492594c3a4854ee5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 7 May 2007 13:16:13 +0100 Subject: [JFFS2] Remove another bogus optimisation in jffs2_add_tn_to_tree() We attempted to insert new nodes into the tree by just using rb_replace_node to let them replace an earlier node which they completely overlapped. However, that could place the new node into the wrong place in the tree, since its start could be node only before the start of the victim, but before the node _before_ the victim in the tree (if that previous node actually ends _after_ the new node, thus isn't entirely overlapped and wasn't itself chosen to be the victim). Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index f461604cf01..bd31d4956c6 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -221,7 +221,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, uint32_t fn_end = tn->fn->ofs + tn->fn->size; struct jffs2_tmp_dnode_info *this; - dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version); + dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw)); /* If a node has zero dsize, we only have to keep if it if it might be the node with highest version -- i.e. the one which will end up as f->metadata. @@ -271,11 +271,11 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, return 0; } else { /* Who cares if the new one is good; keep it for now anyway. */ + dbg_readinode("Like new node. Throw away old\n"); rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); - /* Same overlapping from in front and behind */ jffs2_kill_tn(c, this); - dbg_readinode("Like new node. Throw away old\n"); - goto calc_overlaps; + /* Same overlapping from in front and behind */ + return 0; } } if (this->version < tn->version && @@ -287,11 +287,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, jffs2_kill_tn(c, tn); return 0; } - /* ... and is good. Kill 'this'... */ - rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); - jffs2_kill_tn(c, this); - /* ... and any subsequent nodes which are also overlapped */ - this = tn_next(tn); + /* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */ while (this && this->fn->ofs + this->fn->size < fn_end) { struct jffs2_tmp_dnode_info *next = tn_next(this); if (this->version < tn->version) { @@ -303,8 +299,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, } this = next; } - dbg_readinode("Done inserting new\n"); - goto calc_overlaps; + dbg_readinode("Done killing overlapped nodes\n"); + break; } if (this->version > tn->version && this->fn->ofs <= tn->fn->ofs && @@ -316,11 +312,10 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, return 0; } /* ... but 'this' was bad. Replace it... */ - tn->overlapped = this->overlapped; - rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); dbg_readinode("Bad CRC on old overlapping node. Kill it\n"); + tn_erase(this, &rii->tn_root); jffs2_kill_tn(c, this); - return 0; + break; } this = tn_next(this); -- cgit v1.2.3 From 0f7ac29e594808eaddb61f17dcd503ad36b3d769 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 7 May 2007 13:17:00 -0700 Subject: [IA64] Fix some section mismatch errors Section mismatch: reference to ... .init.text:prefill_possible_map from .text between 'setup_per_cpu_areas' and 'cpu_init' .init.text:iosapic_override_isa_irq from .text between 'iosapic_init' and 'iosapic_remove' Signed-off-by: Tony Luck --- arch/ia64/kernel/iosapic.c | 2 +- arch/ia64/kernel/setup.c | 2 +- include/asm-ia64/iosapic.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index dcfbf3e7a9e..e87dd93d4db 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -1013,7 +1013,7 @@ iosapic_register_platform_intr (u32 int_type, unsigned int gsi, /* * ACPI calls this when it finds an entry for a legacy ISA IRQ override. */ -void __init +void __devinit iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, unsigned long polarity, unsigned long trigger) diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 6e19da122ae..9df1efe7487 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -786,7 +786,7 @@ identify_cpu (struct cpuinfo_ia64 *c) c->unimpl_pa_mask = ~((1L<<63) | ((1L << phys_addr_size) - 1)); } -void +void __init setup_per_cpu_areas (void) { /* start_kernel() requires this... */ diff --git a/include/asm-ia64/iosapic.h b/include/asm-ia64/iosapic.h index 20f98f1751a..421cb6b62a7 100644 --- a/include/asm-ia64/iosapic.h +++ b/include/asm-ia64/iosapic.h @@ -83,7 +83,7 @@ extern int gsi_to_irq (unsigned int gsi); extern int iosapic_register_intr (unsigned int gsi, unsigned long polarity, unsigned long trigger); extern void iosapic_unregister_intr (unsigned int irq); -extern void __init iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, +extern void __devinit iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, unsigned long polarity, unsigned long trigger); extern int __init iosapic_register_platform_intr (u32 int_type, -- cgit v1.2.3 From 1c97964520503c60aa91680d4b525236c2885a36 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 8 May 2007 00:19:54 +0100 Subject: [JFFS2] Simplify and clean up jffs2_add_tn_to_tree() some more. Fixing at least a couple more bugs in the process. Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index bd31d4956c6..4884d5edfe6 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -240,20 +240,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, /* Find the earliest node which _may_ be relevant to this one */ this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs); - if (!this) { - /* First addition to empty tree. $DEITY how I love the easy cases */ - rb_link_node(&tn->rb, NULL, &rii->tn_root.rb_node); - rb_insert_color(&tn->rb, &rii->tn_root); - dbg_readinode("keep new frag\n"); - return 0; - } - - /* If the node is coincident with another at a lower address, - back up until the other node is found. It may be relevant */ - while (tn->overlapped) - tn = tn_prev(tn); + if (this) { + /* If the node is coincident with another at a lower address, + back up until the other node is found. It may be relevant */ + while (this->overlapped) + this = tn_prev(this); - dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole"); + /* First node should never be marked overlapped */ + BUG_ON(!this); + dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole"); + } while (this) { if (this->fn->ofs > fn_end) @@ -288,7 +284,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, return 0; } /* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */ - while (this && this->fn->ofs + this->fn->size < fn_end) { + while (this && this->fn->ofs + this->fn->size <= fn_end) { struct jffs2_tmp_dnode_info *next = tn_next(this); if (this->version < tn->version) { tn_erase(this, &rii->tn_root); @@ -300,7 +296,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, this = next; } dbg_readinode("Done killing overlapped nodes\n"); - break; + continue; } if (this->version > tn->version && this->fn->ofs <= tn->fn->ofs && @@ -326,7 +322,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, { struct rb_node *parent; struct rb_node **link = &rii->tn_root.rb_node; - struct jffs2_tmp_dnode_info *insert_point; + struct jffs2_tmp_dnode_info *insert_point = NULL; while (*link) { parent = *link; @@ -343,7 +339,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, rb_insert_color(&tn->rb, &rii->tn_root); } - calc_overlaps: /* If there's anything behind that overlaps us, note it */ this = tn_prev(tn); if (this) { -- cgit v1.2.3 From 972edcb79ec8c8512ed5b29ca6718065328d6992 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Sun, 6 May 2007 18:46:57 +0400 Subject: [MTD] [NAND] platform NAND driver: update header This patch extends nand.h in order to enable platform NAND driver. Signed-off-by: Vitaly Wool Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse --- include/linux/mtd/nand.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index cf197ad62da..d2365c8dcac 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -560,6 +560,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, * @chip_delay: R/B delay value in us * @options: Option flags, e.g. 16bit buswidth * @ecclayout: ecc layout info structure + * @part_probe_types: NULL-terminated array of probe types * @priv: hardware controller specific settings */ struct platform_nand_chip { @@ -570,6 +571,7 @@ struct platform_nand_chip { struct nand_ecclayout *ecclayout; int chip_delay; unsigned int options; + const char **part_probe_types; void *priv; }; @@ -578,6 +580,8 @@ struct platform_nand_chip { * @hwcontrol: platform specific hardware control structure * @dev_ready: platform specific function to read ready/busy pin * @select_chip: platform specific chip select function + * @cmd_ctrl: platform specific function for controlling + * ALE/CLE/nCE. Also used to write command and address * @priv: private data to transport driver specific settings * * All fields are optional and depend on the hardware driver requirements @@ -586,9 +590,21 @@ struct platform_nand_ctrl { void (*hwcontrol)(struct mtd_info *mtd, int cmd); int (*dev_ready)(struct mtd_info *mtd); void (*select_chip)(struct mtd_info *mtd, int chip); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, + unsigned int ctrl); void *priv; }; +/** + * struct platform_nand_data - container structure for platform-specific data + * @chip: chip level chip structure + * @ctrl: controller level device structure + */ +struct platform_nand_data { + struct platform_nand_chip chip; + struct platform_nand_ctrl ctrl; +}; + /* Some helpers to access the data structures */ static inline struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) -- cgit v1.2.3 From 711fdf627ce1374796632f16acec1ab63d11e38f Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Sun, 6 May 2007 19:31:18 +0400 Subject: [MTD] [NAND] platform NAND driver: add driver This patch adds support for generic platform NAND driver. Updated after tglx's review/discussion in IRC #mtd channel. Signed-off-by: Vitaly Wool Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 9 +++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/plat_nand.c | 150 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 drivers/mtd/nand/plat_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 03ed2545f09..f1d60b6f048 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -272,4 +272,13 @@ config MTD_NAND_NANDSIM The simulator may simulate various NAND flash chips for the MTD nand layer. +config MTD_NAND_PLATFORM + tristate "Support for generic platform NAND driver" + depends on MTD_NAND + help + This implements a generic NAND driver for on-SOC platform + devices. You will need to provide platform-specific functions + via platform_data. + + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index de1424b06b9..edba1db14bf 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -26,5 +26,6 @@ obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o +obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c new file mode 100644 index 00000000000..cd725fc5e81 --- /dev/null +++ b/drivers/mtd/nand/plat_nand.c @@ -0,0 +1,150 @@ +/* + * Generic NAND driver + * + * Author: Vitaly Wool + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct plat_nand_data { + struct nand_chip chip; + struct mtd_info mtd; + void __iomem *io_base; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; + struct mtd_partition *parts; +#endif +}; + +/* + * Probe for the NAND device. + */ +static int __init plat_nand_probe(struct platform_device *pdev) +{ + struct platform_nand_data *pdata = pdev->dev.platform_data; + struct plat_nand_data *data; + int res = 0; + + /* Allocate memory for the device structure (and zero it) */ + data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate device structure.\n"); + return -ENOMEM; + } + + data->io_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + if (data->io_base == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + kfree(data); + return -EIO; + } + + data->chip.priv = &data; + data->mtd.priv = &data->chip; + data->mtd.owner = THIS_MODULE; + + data->chip.IO_ADDR_R = data->io_base; + data->chip.IO_ADDR_W = data->io_base; + data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; + data->chip.dev_ready = pdata->ctrl.dev_ready; + data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.chip_delay = pdata->chip.chip_delay; + data->chip.options |= pdata->chip.options; + + data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; + data->chip.ecc.layout = pdata->chip.ecclayout; + data->chip.ecc.mode = NAND_ECC_SOFT; + + platform_set_drvdata(pdev, data); + + /* Scan to find existance of the device */ + if (nand_scan(&data->mtd, 1)) { + res = -ENXIO; + goto out; + } + +#ifdef CONFIG_MTD_PARTITIONS + if (pdata->chip.part_probe_types) { + res = parse_mtd_partitions(&data->mtd, + pdata->chip.part_probe_types, + &data->parts, 0); + if (res > 0) { + add_mtd_partitions(&data->mtd, data->parts, res); + return 0; + } + } + if (pdata->chip.partitions) { + data->parts = pdata->chip.partitions; + res = add_mtd_partitions(&data->mtd, data->parts, + pdata->chip.nr_partitions); + } else +#endif + res = add_mtd_device(&data->mtd); + + if (!res) + return res; + + nand_release(&data->mtd); +out: + platform_set_drvdata(pdev, NULL); + iounmap(data->io_base); + kfree(data); + return res; +} + +/* + * Remove a NAND device. + */ +static int __devexit plat_nand_remove(struct platform_device *pdev) +{ + struct plat_nand_data *data = platform_get_drvdata(pdev); + struct platform_nand_data *pdata = pdev->dev.platform_data; + + nand_release(&data->mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (data->parts && data->parts != pdata->chip.partitions) + kfree(data->parts); +#endif + iounmap(data->io_base); + kfree(data); + + return 0; +} + +static struct platform_driver plat_nand_driver = { + .probe = plat_nand_probe, + .remove = plat_nand_remove, + .driver = { + .name = "gen_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init plat_nand_init(void) +{ + return platform_driver_register(&plat_nand_driver); +} + +static void __exit plat_nand_exit(void) +{ + platform_driver_unregister(&plat_nand_driver); +} + +module_init(plat_nand_init); +module_exit(plat_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vitaly Wool"); +MODULE_DESCRIPTION("Simple generic NAND driver"); -- cgit v1.2.3 From b9811d7fde6136bb3dc78dd4f4dd4f54be725b25 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 8 May 2007 11:31:07 +0100 Subject: [ARM] Mark SMP local timer and IPI as exception entries This allows the backtrace to dump the exception stack contents. Signed-off-by: Russell King --- arch/arm/kernel/smp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 070bcb7a630..1b76d87fa33 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -486,7 +486,7 @@ static void ipi_timer(void) } #ifdef CONFIG_LOCAL_TIMERS -asmlinkage void do_local_timer(struct pt_regs *regs) +asmlinkage void __exception do_local_timer(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); int cpu = smp_processor_id(); @@ -551,7 +551,7 @@ static void ipi_cpu_stop(unsigned int cpu) * * Bit 0 - Inter-processor function call */ -asmlinkage void do_IPI(struct pt_regs *regs) +asmlinkage void __exception do_IPI(struct pt_regs *regs) { unsigned int cpu = smp_processor_id(); struct ipi_data *ipi = &per_cpu(ipi_data, cpu); -- cgit v1.2.3 From 4efb4482729d2cc7926f49f997a28370e701073f Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 8 May 2007 12:39:37 +0100 Subject: [ARM] Shut up warning about init_thread_union Fix false warning: WARNING: arch/arm/kernel/init_task.o - Section mismatch: reference to .init.task:init_thread_union from .data between 'init_task' (at offset 0x4) and 'init_sighand' caused by the section name starting with ".init". Signed-off-by: Russell King --- arch/arm/kernel/init_task.c | 2 +- arch/arm/kernel/vmlinux.lds.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c index a00cca0000b..bd4ef53bc6b 100644 --- a/arch/arm/kernel/init_task.c +++ b/arch/arm/kernel/init_task.c @@ -31,7 +31,7 @@ EXPORT_SYMBOL(init_mm); * The things we do for performance.. */ union thread_union init_thread_union - __attribute__((__section__(".init.task"))) = + __attribute__((__section__(".data.init_task"))) = { INIT_THREAD_INFO(init_task) }; /* diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 6be67296f33..faf32de5c5a 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -119,7 +119,7 @@ SECTIONS * first, the init task union, aligned * to an 8192 byte boundary. */ - *(.init.task) + *(.data.init_task) #ifdef CONFIG_XIP_KERNEL . = ALIGN(4096); -- cgit v1.2.3 From 08fdffd4cf4ddd4eb4b32e78f93f4ff53ccec78f Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 8 May 2007 15:15:45 +0100 Subject: [ARM] Ensure head text is always placed at the start of kernel Commit 86c0baf123e474b6eb404798926ecf62b426bf3a highlighted that we may end up with the head text placed elsewhere in the kernel image. Introduce a new .text.head section to contain the initial kernel startup code, and always place this section at the beginning of the kernel image. Signed-off-by: Russell King --- arch/arm/kernel/head-nommu.S | 2 +- arch/arm/kernel/head.S | 2 +- arch/arm/kernel/vmlinux.lds.S | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 0119c0d5f97..5d78ffb8a9a 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -33,7 +33,7 @@ * numbers for r1. * */ - __INIT + .section ".text.head", "ax" .type stext, %function ENTRY(stext) msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 1d35edacc01..41f98b4ba2e 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -73,7 +73,7 @@ * crap here - that's what the boot loader (or in extreme, well justified * circumstances, zImage) is for. */ - __INIT + .section ".text.head", "ax" .type stext, %function ENTRY(stext) msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index faf32de5c5a..e4156e7868c 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -23,11 +23,15 @@ SECTIONS #else . = PAGE_OFFSET + TEXT_OFFSET; #endif - .init : { /* Init code and data */ + .text.head : { _stext = .; - _sinittext = .; + _sinittext = .; + *(.text.head) + } + + .init : { /* Init code and data */ *(.init.text) - _einittext = .; + _einittext = .; __proc_info_begin = .; *(.proc.info.init) __proc_info_end = .; -- cgit v1.2.3 From f5cdf30618cf855c2043e5c0c131ebb120929864 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 21 Apr 2007 17:56:29 -0500 Subject: [PATCH] ieee80211: add ieee80211_channel_to_freq The routines that interrogate the ieee80211_geo struct are missing a channel to frequency entry. This patch adds it. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- include/net/ieee80211.h | 2 ++ net/ieee80211/ieee80211_geo.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h index d56b2923d61..bbd85cd61ed 100644 --- a/include/net/ieee80211.h +++ b/include/net/ieee80211.h @@ -1291,6 +1291,8 @@ extern u8 ieee80211_get_channel_flags(struct ieee80211_device *ieee, extern const struct ieee80211_channel *ieee80211_get_channel(struct ieee80211_device *ieee, u8 channel); +extern u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, + u8 channel); /* ieee80211_wx.c */ extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, diff --git a/net/ieee80211/ieee80211_geo.c b/net/ieee80211/ieee80211_geo.c index 305a09de85a..960ad13f5e9 100644 --- a/net/ieee80211/ieee80211_geo.c +++ b/net/ieee80211/ieee80211_geo.c @@ -94,6 +94,21 @@ int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel) return -1; } +u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, u8 channel) +{ + const struct ieee80211_channel * ch; + + /* Driver needs to initialize the geography map before using + * these helper functions */ + if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) + return 0; + + ch = ieee80211_get_channel(ieee, channel); + if (!ch->channel) + return 0; + return ch->freq; +} + u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq) { int i; @@ -174,6 +189,7 @@ EXPORT_SYMBOL(ieee80211_get_channel); EXPORT_SYMBOL(ieee80211_get_channel_flags); EXPORT_SYMBOL(ieee80211_is_valid_channel); EXPORT_SYMBOL(ieee80211_freq_to_channel); +EXPORT_SYMBOL(ieee80211_channel_to_freq); EXPORT_SYMBOL(ieee80211_channel_to_index); EXPORT_SYMBOL(ieee80211_set_geo); EXPORT_SYMBOL(ieee80211_get_geo); -- cgit v1.2.3 From 93afe3da36440456ecec8ae9846b12729713cec7 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 21 Apr 2007 17:56:43 -0500 Subject: [PATCH] ieee80211: include frequency in scan results In ieee80211, the output of scan results lists channels, but not frequencies, which are needed by NetworkManager. This patch uses the new ieee80211_channel_to_freq routine to add the frequency to the output. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_wx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index cee5e13bc42..523a137d49d 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -89,15 +89,17 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); } - /* Add frequency/channel */ + /* Add channel and frequency */ iwe.cmd = SIOCGIWFREQ; -/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode); - iwe.u.freq.e = 3; */ iwe.u.freq.m = network->channel; iwe.u.freq.e = 0; iwe.u.freq.i = 0; start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); + iwe.u.freq.e = 6; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; if (network->capability & WLAN_CAPABILITY_PRIVACY) -- cgit v1.2.3 From 019a67555857d33b34b105774acb3d2f59553424 Mon Sep 17 00:00:00 2001 From: Ulrich Kunitz Date: Tue, 1 May 2007 17:13:51 +0100 Subject: [PATCH] zd1211rw: Added new USB id for Planex GW-US54ZGL Alan Tam asked for inclusion of this device into the tree. zd1211b chip 0053:5301 v4810 high 00-90-cc AL2230_RF pa0 ---N Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index e04cffc8adf..51cee7d97e2 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -67,6 +67,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} -- cgit v1.2.3 From aa1d3a1841ff795f6c393a9a3f1b230cf70ff42d Mon Sep 17 00:00:00 2001 From: Matthew Davidson Date: Tue, 1 May 2007 17:14:30 +0100 Subject: [PATCH] zd1211rw: Add ID for Sitecom WL-117 This is another "driverless" device which first presents itself as a USB CDROM drive. A separate patch has been submitted to make usb-storage ignore that device, so that zd1211rw can eject it. zd1211 chip 0df6:9075 v4916 full 00-0c-f6 AL2230_RF pa0 ---- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 51cee7d97e2..17cf29546f6 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -40,6 +40,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, @@ -70,6 +71,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, + { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, {} }; -- cgit v1.2.3 From 187bb417d478067b092c30c7b97bb3e13541a08c Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 1 May 2007 17:14:51 +0100 Subject: [PATCH] zd1211rw: Add ID for ZyXEL AG-225H v2 Tested by davo on IRC zd1211b chip 0586:3413 v4810 full 00-13-49 AL7230B_RF pa0 ----- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 17cf29546f6..8459549d0ce 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -68,6 +68,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, -- cgit v1.2.3 From 47c93d2faf9d7e94393852823480ea61c868caee Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 3 May 2007 20:01:02 -0500 Subject: [PATCH] bcm43xx: Remove dead configuration variable CONFIG_947XX The CONFIG_BCM947XX configuration variable was designed for use by the embedded device used by the OpenWRT project. The device has been shifted to the ssb driver in bcm43xx-mac80211 and will not be used with SoftMAC. Accordingly, this "dead" configuration variable is removed. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx.h | 18 ++----- drivers/net/wireless/bcm43xx/bcm43xx_dma.c | 4 -- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 81 ----------------------------- drivers/net/wireless/bcm43xx/bcm43xx_main.h | 19 ------- 4 files changed, 4 insertions(+), 118 deletions(-) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index f8483c179e4..10e07e86542 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -658,12 +658,6 @@ struct bcm43xx_pio { #define BCM43xx_MAX_80211_CORES 2 -#ifdef CONFIG_BCM947XX -#define core_offset(bcm) (bcm)->current_core_offset -#else -#define core_offset(bcm) 0 -#endif - /* Generic information about a core. */ struct bcm43xx_coreinfo { u8 available:1, @@ -789,10 +783,6 @@ struct bcm43xx_private { /* The currently active core. */ struct bcm43xx_coreinfo *current_core; -#ifdef CONFIG_BCM947XX - /** current core memory offset */ - u32 current_core_offset; -#endif struct bcm43xx_coreinfo *active_80211_core; /* coreinfo structs for all possible cores follow. * Note that a core might not exist. @@ -943,25 +933,25 @@ struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy, static inline u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset) { - return ioread16(bcm->mmio_addr + core_offset(bcm) + offset); + return ioread16(bcm->mmio_addr + offset); } static inline void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value) { - iowrite16(value, bcm->mmio_addr + core_offset(bcm) + offset); + iowrite16(value, bcm->mmio_addr + offset); } static inline u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset) { - return ioread32(bcm->mmio_addr + core_offset(bcm) + offset); + return ioread32(bcm->mmio_addr + offset); } static inline void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value) { - iowrite32(value, bcm->mmio_addr + core_offset(bcm) + offset); + iowrite32(value, bcm->mmio_addr + offset); } static inline diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c index e3d2e61a31e..1f7731fcfbd 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c @@ -660,10 +660,6 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, ring->routing = BCM43xx_DMA32_CLIENTTRANS; if (dma64) ring->routing = BCM43xx_DMA64_CLIENTTRANS; -#ifdef CONFIG_BCM947XX - if (bcm->pci_dev->bus->number == 0) - ring->routing = dma64 ? BCM43xx_DMA64_NOTRANS : BCM43xx_DMA32_NOTRANS; -#endif ring->bcm = bcm; ring->nr_slots = nr_slots; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 5e96bca6730..ef6b253a92c 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -61,10 +61,6 @@ MODULE_AUTHOR("Stefano Brivio"); MODULE_AUTHOR("Michael Buesch"); MODULE_LICENSE("GPL"); -#ifdef CONFIG_BCM947XX -extern char *nvram_get(char *name); -#endif - #if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO) static int modparam_pio; module_param_named(pio, modparam_pio, int, 0444); @@ -142,10 +138,6 @@ MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for using multiple fi { PCI_VENDOR_ID_BROADCOM, 0x4324, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Broadcom 43XG 802.11b/g */ { PCI_VENDOR_ID_BROADCOM, 0x4325, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, -#ifdef CONFIG_BCM947XX - /* SB bus on BCM947xx */ - { PCI_VENDOR_ID_BROADCOM, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, -#endif { 0 }, }; MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl); @@ -786,9 +778,6 @@ static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) { u16 value; u16 *sprom; -#ifdef CONFIG_BCM947XX - char *c; -#endif sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16), GFP_KERNEL); @@ -796,28 +785,7 @@ static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) printk(KERN_ERR PFX "sprom_extract OOM\n"); return -ENOMEM; } -#ifdef CONFIG_BCM947XX - sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2")); - sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags")); - - if ((c = nvram_get("il0macaddr")) != NULL) - e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR])); - - if ((c = nvram_get("et1macaddr")) != NULL) - e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR])); - - sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0")); - sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1")); - sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2")); - - sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0")); - sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1")); - sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2")); - - sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev")); -#else bcm43xx_sprom_read(bcm, sprom); -#endif /* boardflags2 */ value = sprom[BCM43xx_SPROM_BOARDFLAGS2]; @@ -1225,12 +1193,6 @@ static int _switch_core(struct bcm43xx_private *bcm, int core) goto error; udelay(10); } -#ifdef CONFIG_BCM947XX - if (bcm->pci_dev->bus->number == 0) - bcm->current_core_offset = 0x1000 * core; - else - bcm->current_core_offset = 0; -#endif return 0; error: @@ -1387,19 +1349,6 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) if ((bcm43xx_core_enabled(bcm)) && !bcm43xx_using_pio(bcm)) { -//FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here? -#if 0 -#ifndef CONFIG_BCM947XX - /* reset all used DMA controllers. */ - bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); - bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA2_BASE); - bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA3_BASE); - bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); - bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); - if (bcm->current_core->rev < 5) - bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); -#endif -#endif } if (bcm43xx_status(bcm) == BCM43xx_STAT_SHUTTINGDOWN) { bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, @@ -2140,32 +2089,11 @@ out: return err; } -#ifdef CONFIG_BCM947XX -static struct pci_device_id bcm43xx_47xx_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, - { 0 } -}; -#endif - static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) { int err; bcm->irq = bcm->pci_dev->irq; -#ifdef CONFIG_BCM947XX - if (bcm->pci_dev->bus->number == 0) { - struct pci_dev *d; - struct pci_device_id *id; - for (id = bcm43xx_47xx_ids; id->vendor; id++) { - d = pci_get_device(id->vendor, id->device, NULL); - if (d != NULL) { - bcm->irq = d->irq; - pci_dev_put(d); - break; - } - } - } -#endif err = request_irq(bcm->irq, bcm43xx_interrupt_handler, IRQF_SHARED, KBUILD_MODNAME, bcm); if (err) @@ -2645,10 +2573,6 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) chip_id_16 = 0x4610; else if ((pci_device >= 0x4710) && (pci_device <= 0x4715)) chip_id_16 = 0x4710; -#ifdef CONFIG_BCM947XX - else if ((pci_device >= 0x4320) && (pci_device <= 0x4325)) - chip_id_16 = 0x4309; -#endif else { printk(KERN_ERR PFX "Could not determine Chip ID\n"); return -ENODEV; @@ -4144,11 +4068,6 @@ static int __devinit bcm43xx_init_one(struct pci_dev *pdev, struct bcm43xx_private *bcm; int err; -#ifdef CONFIG_BCM947XX - if ((pdev->bus->number == 0) && (pdev->device != 0x0800)) - return -ENODEV; -#endif - #ifdef DEBUG_SINGLE_DEVICE_ONLY if (strcmp(pci_name(pdev), DEBUG_SINGLE_DEVICE_ONLY)) return -ENODEV; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h index f76357178e4..c8f3c532bab 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -33,25 +33,6 @@ #include "bcm43xx.h" -#ifdef CONFIG_BCM947XX -#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0) - -static inline void e_aton(char *str, char *dest) -{ - int i = 0; - u16 *d = (u16 *) dest; - - for (;;) { - dest[i++] = (char) simple_strtoul(str, NULL, 16); - str += 2; - if (!*str++ || i == 6) - break; - } - for (i = 0; i < 3; i++) - d[i] = cpu_to_be16(d[i]); -} -#endif - #define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] #define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) /* Magic helper macro to pad structures. Ignore those above. It's magic. */ -- cgit v1.2.3 From 9d1d4cc89f3f6952978d37c29607c2d89d350767 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 8 May 2007 10:00:28 -0700 Subject: [IA64] Removal of percpu TR cleanup in kexec code The kexec code wasn't in the kernel when Ken Chen created this patch (00b65985fb2fc542b855b03fcda0d0f2bab4f442) to change the mapping of percpu area from a TR to a TC. Zou Nanhai spotted this extra piece, and Simon Horman concurred. Signed-off-by: Tony Luck --- arch/ia64/kernel/relocate_kernel.S | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/ia64/kernel/relocate_kernel.S b/arch/ia64/kernel/relocate_kernel.S index ae473e3f2a0..903babd22d6 100644 --- a/arch/ia64/kernel/relocate_kernel.S +++ b/arch/ia64/kernel/relocate_kernel.S @@ -94,7 +94,7 @@ GLOBAL_ENTRY(relocate_new_kernel) 4: srlz.i ;; - //purge TR entry for kernel text and data + // purge TR entry for kernel text and data movl r16=KERNEL_START mov r18=KERNEL_TR_PAGE_SHIFT<<2 ;; @@ -104,15 +104,6 @@ GLOBAL_ENTRY(relocate_new_kernel) srlz.i ;; - // purge TR entry for percpu data - movl r16=PERCPU_ADDR - mov r18=PERCPU_PAGE_SHIFT<<2 - ;; - ptr.d r16,r18 - ;; - srlz.d - ;; - // purge TR entry for pal code mov r16=in3 mov r18=IA64_GRANULE_SHIFT<<2 -- cgit v1.2.3 From bb8416bf8b93d88e23cbbfde962ef85acda5bd5f Mon Sep 17 00:00:00 2001 From: "Siddha, Suresh B" Date: Tue, 8 May 2007 09:22:47 -0700 Subject: [IA64] save and restore cpus_allowed in cpu_idle_wait Save and Restore the task's cpus_allowed mask, across the set_cpus_allowed() in cpu_idle_wait(). Without this, we will endup corrupting task's cpu affinity. Signed-off-by: Suresh Siddha Signed-off-by: Tony Luck --- arch/ia64/kernel/process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index ae96d417699..cba6ac39873 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -237,6 +237,7 @@ void cpu_idle_wait(void) { unsigned int cpu, this_cpu = get_cpu(); cpumask_t map; + cpumask_t tmp = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(this_cpu)); put_cpu(); @@ -258,6 +259,7 @@ void cpu_idle_wait(void) } cpus_and(map, map, cpu_online_map); } while (!cpus_empty(map)); + set_cpus_allowed(current, tmp); } EXPORT_SYMBOL_GPL(cpu_idle_wait); -- cgit v1.2.3 From 0e17b560985afb5190e859d5d4609237a91bb732 Mon Sep 17 00:00:00 2001 From: John Keller Date: Wed, 2 May 2007 09:09:18 -0500 Subject: [IA64] - Altix: hotplug after intr redirect can crash system When redirecting a device interrupt on SN, not all links between platform specific structures are being updated. This can result in a system crash if an interrupt redirection is followed by an unplug of that device. The complete fix also requires a prom update. Though, this patch is backward compatable and not dependent on the prom patch. Signed-off-by: John Keller Signed-off-by: Tony Luck --- arch/ia64/sn/kernel/irq.c | 58 ++++++++++++++++++++++++++++++++------------ include/asm-ia64/sn/sn_sal.h | 1 + 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 8d2a1bfbfe7..7f6d2360a26 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -59,6 +59,22 @@ void sn_intr_free(nasid_t local_nasid, int local_widget, (u64) sn_irq_info->irq_cookie, 0, 0); } +u64 sn_intr_redirect(nasid_t local_nasid, int local_widget, + struct sn_irq_info *sn_irq_info, + nasid_t req_nasid, int req_slice) +{ + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT, + (u64) SAL_INTR_REDIRECT, (u64) local_nasid, + (u64) local_widget, __pa(sn_irq_info), + (u64) req_nasid, (u64) req_slice, 0); + + return ret_stuff.status; +} + static unsigned int sn_startup_irq(unsigned int irq) { return 0; @@ -127,15 +143,8 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, struct sn_irq_info *new_irq_info; struct sn_pcibus_provider *pci_provider; - new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); - if (new_irq_info == NULL) - return NULL; - - memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info)); - - bridge = (u64) new_irq_info->irq_bridge; + bridge = (u64) sn_irq_info->irq_bridge; if (!bridge) { - kfree(new_irq_info); return NULL; /* irq is not a device interrupt */ } @@ -145,8 +154,25 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, local_widget = TIO_SWIN_WIDGETNUM(bridge); else local_widget = SWIN_WIDGETNUM(bridge); - vector = sn_irq_info->irq_irq; + + /* Make use of SAL_INTR_REDIRECT if PROM supports it */ + status = sn_intr_redirect(local_nasid, local_widget, sn_irq_info, nasid, slice); + if (!status) { + new_irq_info = sn_irq_info; + goto finish_up; + } + + /* + * PROM does not support SAL_INTR_REDIRECT, or it failed. + * Revert to old method. + */ + new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); + if (new_irq_info == NULL) + return NULL; + + memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info)); + /* Free the old PROM new_irq_info structure */ sn_intr_free(local_nasid, local_widget, new_irq_info); unregister_intr_pda(new_irq_info); @@ -162,11 +188,18 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, return NULL; } + register_intr_pda(new_irq_info); + spin_lock(&sn_irq_info_lock); + list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); + spin_unlock(&sn_irq_info_lock); + call_rcu(&sn_irq_info->rcu, sn_irq_info_free); + + +finish_up: /* Update kernels new_irq_info with new target info */ cpuid = nasid_slice_to_cpuid(new_irq_info->irq_nasid, new_irq_info->irq_slice); new_irq_info->irq_cpuid = cpuid; - register_intr_pda(new_irq_info); pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type]; @@ -178,11 +211,6 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, pci_provider && pci_provider->target_interrupt) (pci_provider->target_interrupt)(new_irq_info); - spin_lock(&sn_irq_info_lock); - list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); - spin_unlock(&sn_irq_info_lock); - call_rcu(&sn_irq_info->rcu, sn_irq_info_free); - #ifdef CONFIG_SMP cpuphys = cpu_physical_id(cpuid); set_irq_affinity_info((vector & 0xff), cpuphys, 0); diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 2c4004eb5a6..291e8ceed6e 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -106,6 +106,7 @@ /* interrupt handling */ #define SAL_INTR_ALLOC 1 #define SAL_INTR_FREE 2 +#define SAL_INTR_REDIRECT 3 /* * operations available on the generic SN_SAL_SYSCTL_OP -- cgit v1.2.3 From 66761522a7bca951c1214498b80260533957e3ad Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 23 Apr 2007 12:00:05 +0200 Subject: [IA64] fix stack alignment for ia32 signal handlers This fixes the setup of the alignment of the signal frame, so that all signal handlers are run with a properly aligned stack frame. The current code "over-aligns" the stack pointer so that the stack frame is effectively always mis-aligned by 4 bytes. But what we really want is that on function entry ((sp + 4) & 15) == 0, which matches what would happen if the stack were aligned before a "call" instruction. i386 and x86_64 are already fixed by d347f372273c2b3d86a66e2e1c94c790c208e166 Signed-off-by: Markus F.X.J. Oberhumer Signed-off-by: Tony Luck --- arch/ia64/ia32/ia32_signal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c index b3355a9ca2c..7b38b73e782 100644 --- a/arch/ia64/ia32/ia32_signal.c +++ b/arch/ia64/ia32/ia32_signal.c @@ -811,7 +811,11 @@ get_sigframe (struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) } /* Legacy stack switching not supported */ - return (void __user *)((esp - frame_size) & -8ul); + esp -= frame_size; + /* Align the stack pointer according to the i386 ABI, + * i.e. so that on function entry ((sp + 4) & 15) == 0. */ + esp = ((esp + 4) & -16ul) - 4; + return (void __user *) esp; } static int -- cgit v1.2.3 From 1cfb9c94b6525763ee0eb9551842ded093c5827f Mon Sep 17 00:00:00 2001 From: vignesh babu Date: Tue, 8 May 2007 11:53:27 -0700 Subject: [IA64-SN2][KJ] mmtimer.c-kzalloc Replacing kmalloc/memset combination with kzalloc. Signed-off-by: vignesh babu Signed-off-by: Tony Luck --- drivers/char/mmtimer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index c09160383a5..6e55cfb9c65 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -705,15 +705,13 @@ static int __init mmtimer_init(void) maxn++; /* Allocate list of node ptrs to mmtimer_t's */ - timers = kmalloc(sizeof(mmtimer_t *)*maxn, GFP_KERNEL); + timers = kzalloc(sizeof(mmtimer_t *)*maxn, GFP_KERNEL); if (timers == NULL) { printk(KERN_ERR "%s: failed to allocate memory for device\n", MMTIMER_NAME); goto out3; } - memset(timers,0,(sizeof(mmtimer_t *)*maxn)); - /* Allocate mmtimer_t's for each online node */ for_each_online_node(node) { timers[node] = kmalloc_node(sizeof(mmtimer_t)*NUM_COMPARATORS, GFP_KERNEL, node); -- cgit v1.2.3 From 8737d59579c5e61ea3d5da4bd63303159fd1cf7e Mon Sep 17 00:00:00 2001 From: Milind Arun Choudhary Date: Sun, 15 Apr 2007 22:51:23 +0530 Subject: [IA64] SPIN_LOCK_UNLOCKED macro cleanup in arch/ia64 SPIN_LOCK_UNLOCKED macro cleanup, use __SPIN_LOCK_UNLOCKED instead. Signed-off-by: Milind Arun Choudhary Signed-off-by: Tony Luck --- arch/ia64/kernel/traps.c | 6 +++--- arch/ia64/kernel/unwind.c | 2 +- arch/ia64/mm/tlb.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 765cbe5ba6a..cd5189f9962 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -59,9 +59,9 @@ die (const char *str, struct pt_regs *regs, long err) u32 lock_owner; int lock_owner_depth; } die = { - .lock = SPIN_LOCK_UNLOCKED, - .lock_owner = -1, - .lock_owner_depth = 0 + .lock = __SPIN_LOCK_UNLOCKED(die.lock), + .lock_owner = -1, + .lock_owner_depth = 0 }; static int die_counter; int cpu = get_cpu(); diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c index 93d5a3b41f6..e3be21c7bd4 100644 --- a/arch/ia64/kernel/unwind.c +++ b/arch/ia64/kernel/unwind.c @@ -145,7 +145,7 @@ static struct { # endif } unw = { .tables = &unw.kernel_table, - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(unw.lock), .save_order = { UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR, UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index ffad7624436..fa4e6d4810f 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c @@ -32,9 +32,9 @@ static struct { } purge; struct ia64_ctx ia64_ctx = { - .lock = SPIN_LOCK_UNLOCKED, - .next = 1, - .max_ctx = ~0U + .lock = __SPIN_LOCK_UNLOCKED(ia64_ctx.lock), + .next = 1, + .max_ctx = ~0U }; DEFINE_PER_CPU(u8, ia64_need_tlb_flush); -- cgit v1.2.3 From 8678c1f04277daaa914abb107fb9fe71298d916d Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 8 May 2007 20:03:09 +0100 Subject: [ARM] Fix ASID version switch Close a hole in the ASID version switch, particularly the following scenario: CPU0 MM PID CPU1 MM PID idle A pid(A) A idle(lazy tlb) * new asid version triggered by B * B pid(B) A pid(A) * MM A gets new asid version * A idle(lazy tlb) A pid(A) * CPU1 doesn't see the new ASID * The result is that CPU1 continues running with the hardware set for the original (stale) ASID value, but mm->context.id contains the new ASID value. The result is that the next MM fault on CPU1 updates the page table entries, but flush_tlb_page() fails due to wrong ASID. There is a related case with a threaded application is allocated a new ASID on one CPU while another of its threads is running on some different CPU. This scenario is not fixed by this commit. Signed-off-by: Russell King --- arch/arm/mm/context.c | 10 +++++++--- include/asm-arm/mmu_context.h | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 9da43a0fdcd..930c04c4f53 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -14,7 +14,8 @@ #include #include -unsigned int cpu_last_asid = { 1 << ASID_BITS }; +static DEFINE_SPINLOCK(cpu_asid_lock); +unsigned int cpu_last_asid = ASID_FIRST_VERSION; /* * We fork()ed a process, and we need a new context for the child @@ -31,15 +32,16 @@ void __new_context(struct mm_struct *mm) { unsigned int asid; + spin_lock(&cpu_asid_lock); asid = ++cpu_last_asid; if (asid == 0) - asid = cpu_last_asid = 1 << ASID_BITS; + asid = cpu_last_asid = ASID_FIRST_VERSION; /* * If we've used up all our ASIDs, we need * to start a new version and flush the TLB. */ - if ((asid & ~ASID_MASK) == 0) { + if (unlikely((asid & ~ASID_MASK) == 0)) { asid = ++cpu_last_asid; /* set the reserved ASID before flushing the TLB */ asm("mcr p15, 0, %0, c13, c0, 1 @ set reserved context ID\n" @@ -48,6 +50,8 @@ void __new_context(struct mm_struct *mm) isb(); flush_tlb_all(); } + spin_unlock(&cpu_asid_lock); + mm->cpu_vm_mask = cpumask_of_cpu(smp_processor_id()); mm->context.id = asid; } diff --git a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h index f8755c818b5..4981ad41919 100644 --- a/include/asm-arm/mmu_context.h +++ b/include/asm-arm/mmu_context.h @@ -36,8 +36,9 @@ void __check_kvm_seq(struct mm_struct *mm); * The context ID is used by debuggers and trace logic, and * should be unique within all running processes. */ -#define ASID_BITS 8 -#define ASID_MASK ((~0) << ASID_BITS) +#define ASID_BITS 8 +#define ASID_MASK ((~0) << ASID_BITS) +#define ASID_FIRST_VERSION (1 << ASID_BITS) extern unsigned int cpu_last_asid; @@ -96,8 +97,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, #ifdef CONFIG_MMU unsigned int cpu = smp_processor_id(); - if (prev != next) { - cpu_set(cpu, next->cpu_vm_mask); + if (!cpu_test_and_set(cpu, next->cpu_vm_mask) || prev != next) { check_context(next); cpu_switch_mm(next->pgd, next); if (cache_is_vivt()) -- cgit v1.2.3 From 277d58efadd67e73aa2860e41042d8d7fc750cea Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 6 Dec 2006 17:13:59 -0800 Subject: ARM: OMAP: gpio init section cleanups Minor GPIO cleanups: remove needless #include, and omap_gpio_init() should be __init, as well as all the board init code calling it. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap1/board-fsample.c | 2 +- arch/arm/mach-omap1/board-h3.c | 2 +- arch/arm/mach-omap1/board-innovator.c | 2 +- arch/arm/mach-omap1/board-perseus2.c | 2 +- arch/arm/plat-omap/gpio.c | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index 62e42c7a628..f65baa95986 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -246,7 +246,7 @@ static void __init fsample_init_smc91x(void) mdelay(50); } -void omap_fsample_init_irq(void) +static void __init omap_fsample_init_irq(void) { omap1_init_common_hw(); omap_init_irq(); diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 9d2346fb68f..7b260b7c537 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -455,7 +455,7 @@ static void __init h3_init_smc91x(void) } } -void h3_init_irq(void) +static void __init h3_init_irq(void) { omap1_init_common_hw(); omap_init_irq(); diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index cb00530ad27..7e63a41e37c 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -308,7 +308,7 @@ static void __init innovator_init_smc91x(void) } } -void innovator_init_irq(void) +static void __init innovator_init_irq(void) { omap1_init_common_hw(); omap_init_irq(); diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index fa4be962df6..1d5c8d50972 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -246,7 +246,7 @@ static void __init perseus2_init_smc91x(void) mdelay(50); } -void omap_perseus2_init_irq(void) +static void __init omap_perseus2_init_irq(void) { omap1_init_common_hw(); omap_init_irq(); diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index 9dc6d3617bd..337455dfe64 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -1545,7 +1544,7 @@ void omap2_gpio_resume_after_retention(void) * This may get called early from board specific init * for boards that have interrupts routed via FPGA. */ -int omap_gpio_init(void) +int __init omap_gpio_init(void) { if (!initialized) return _omap_gpio_init(); -- cgit v1.2.3 From 3151369d7467a37386e955e6f1ba5b9b5bea7500 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 6 Dec 2006 17:13:47 -0800 Subject: ARM: OMAP: Add DMA IRQ sanity checks Add DMA IRQ sanity checks Signed-off-by: Juha Yrjola Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/dma.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index f3f84fbf8b8..2d86b106ff3 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -925,10 +925,17 @@ static int omap2_dma_handle_ch(int ch) { u32 status = OMAP_DMA_CSR_REG(ch); - if (!status) + if (!status) { + if (printk_ratelimit()) + printk(KERN_WARNING "Spurious DMA IRQ for lch %d\n", ch); return 0; - if (unlikely(dma_chan[ch].dev_id == -1)) + } + if (unlikely(dma_chan[ch].dev_id == -1)) { + if (printk_ratelimit()) + printk(KERN_WARNING "IRQ %04x for non-allocated DMA" + "channel %d\n", status, ch); return 0; + } if (unlikely(status & OMAP_DMA_DROP_IRQ)) printk(KERN_INFO "DMA synchronization event drop occurred with device " @@ -959,11 +966,15 @@ static irqreturn_t omap2_dma_irq_handler(int irq, void *dev_id) int i; val = omap_readl(OMAP_DMA4_IRQSTATUS_L0); - - for (i = 1; i <= OMAP_LOGICAL_DMA_CH_COUNT; i++) { - int active = val & (1 << (i - 1)); - if (active) - omap2_dma_handle_ch(i - 1); + if (val == 0) { + if (printk_ratelimit()) + printk(KERN_WARNING "Spurious DMA IRQ\n"); + return IRQ_HANDLED; + } + for (i = 0; i < OMAP_LOGICAL_DMA_CH_COUNT && val != 0; i++) { + if (val & 1) + omap2_dma_handle_ch(i); + val >>= 1; } return IRQ_HANDLED; -- cgit v1.2.3 From 0ce3356394dadc4704d42beb3788d7d78bda8ff2 Mon Sep 17 00:00:00 2001 From: Juha Yrjola Date: Wed, 6 Dec 2006 17:13:49 -0800 Subject: ARM: OMAP: Add function to print clock usecounts Useful for debugging power management code. Signed-off-by: Juha Yrjola Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/clock.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index f1179ad4be1..3d017b04784 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -33,6 +33,41 @@ static DEFINE_SPINLOCK(clockfw_lock); static struct clk_functions *arch_clock; +#ifdef CONFIG_PM_DEBUG + +static void print_parents(struct clk *clk) +{ + struct clk *p; + int printed = 0; + + list_for_each_entry(p, &clocks, node) { + if (p->parent == clk && p->usecount) { + if (!clk->usecount && !printed) { + printk("MISMATCH: %s\n", clk->name); + printed = 1; + } + printk("\t%-15s\n", p->name); + } + } +} + +void clk_print_usecounts(void) +{ + unsigned long flags; + struct clk *p; + + spin_lock_irqsave(&clockfw_lock, flags); + list_for_each_entry(p, &clocks, node) { + if (p->usecount) + printk("%-15s: %d\n", p->name, p->usecount); + print_parents(p); + + } + spin_unlock_irqrestore(&clockfw_lock, flags); +} + +#endif + /*------------------------------------------------------------------------- * Standard clock functions defined in include/linux/clk.h *-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 771af222eb98a60629aa1437901c7f266ff4dc89 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 6 Dec 2006 17:13:50 -0800 Subject: ARM: OMAP: FB: add controller platform data Add controller platform data Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/fb.c | 5 +++++ include/asm-arm/arch-omap/omapfb.h | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 56acb8720f7..91ebdafcca3 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -55,6 +55,11 @@ void omapfb_reserve_mem(void) } } +void omapfb_set_ctrl_platform_data(void *data) +{ + omapfb_config.ctrl_platform_data = data; +} + static inline int omap_init_fb(void) { const struct omap_lcd_config *conf; diff --git a/include/asm-arm/arch-omap/omapfb.h b/include/asm-arm/arch-omap/omapfb.h index fccdb3db025..ce80a71ce8f 100644 --- a/include/asm-arm/arch-omap/omapfb.h +++ b/include/asm-arm/arch-omap/omapfb.h @@ -292,8 +292,9 @@ struct omapfb_device { }; struct omapfb_platform_data { - struct omap_lcd_config lcd; - struct omap_fbmem_config fbmem; + struct omap_lcd_config lcd; + struct omapfb_mem_desc mem_desc; + void *ctrl_platform_data; }; #define OMAPFB_EVENT_READY 1 @@ -317,8 +318,9 @@ extern int omapfb_update_window_async(struct omapfb_update_window *win, void (*callback)(void *), void *callback_data); -/* in arch/arm/plat-omap/devices.c */ +/* in arch/arm/plat-omap/fb.c */ extern void omapfb_reserve_mem(void); +extern void omapfb_set_ctrl_platform_data(void *pdata); #endif /* __KERNEL__ */ -- cgit v1.2.3 From 994c84ea526ea1c348f63dc3aa7cfeec931bb612 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 6 Dec 2006 17:14:03 -0800 Subject: ARM: OMAP: h4 must have blinky leds!! This adds generic support for the "debug board" LEDs used by most of TI's OMAP reference boards, and board-specific support for the H4. It's derived from the not-as-generic stuff used by OMAP1 H2/H3/P2. Those should be able to switch easily to this version, and clean up some of the omap1-specific code. In addition to H4 support, one key improvement is supporting not just the "old" ARM debug LED API (with timer and idle LEDs, plus four that can be handy for kernel debugging), but it also supports the "new" generic LED API (most useful for usermode stuff IMO). Either or both APIs can be enabled. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap2/Kconfig | 1 + arch/arm/mach-omap2/board-h4.c | 14 ++ arch/arm/plat-omap/Kconfig | 5 + arch/arm/plat-omap/Makefile | 2 +- arch/arm/plat-omap/debug-leds.c | 319 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 arch/arm/plat-omap/debug-leds.c diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index aab97ccf1e6..2a1aca7a637 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -20,6 +20,7 @@ config MACH_OMAP_GENERIC config MACH_OMAP_H4 bool "OMAP 2420 H4 board" depends on ARCH_OMAP2 && ARCH_OMAP24XX + select OMAP_DEBUG_LEDS if LEDS || LEDS_OMAP_DEBUG config MACH_OMAP_APOLLON bool "OMAP 2420 Apollon board" diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index 1e7ed6d22ca..452193f0153 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -266,12 +266,26 @@ static struct platform_device h4_lcd_device = { .id = -1, }; +static struct resource h4_led_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device h4_led_device = { + .name = "omap_dbg_led", + .id = -1, + .num_resources = ARRAY_SIZE(h4_led_resources), + .resource = h4_led_resources, +}; + static struct platform_device *h4_devices[] __initdata = { &h4_smc91x_device, &h4_flash_device, &h4_irda_device, &h4_kp_device, &h4_lcd_device, + &h4_led_device, }; static inline void __init h4_init_smc91x(void) diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index f2dc363de66..ce8bc3304e1 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -19,6 +19,11 @@ endchoice comment "OMAP Feature Selections" +config OMAP_DEBUG_LEDS + bool + help + For debug card leds on TI reference boards. + config OMAP_RESET_CLOCKS bool "Reset unused clocks during boot" depends on ARCH_OMAP diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 2896b454641..4ce277d3485 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -16,4 +16,4 @@ obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o obj-$(CONFIG_CPU_FREQ) += cpu-omap.o obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o - +obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c new file mode 100644 index 00000000000..511d6a50041 --- /dev/null +++ b/arch/arm/plat-omap/debug-leds.c @@ -0,0 +1,319 @@ +/* + * linux/arch/arm/plat-omap/debug-leds.c + * + * Copyright 2003 by Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +/* Many OMAP development platforms reuse the same "debug board"; these + * platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the + * debug board (all green), accessed through FPGA registers. + * + * The "surfer" expansion board and H2 sample board also have two-color + * green+red LEDs (in parallel), used here for timer and idle indicators + * in preference to the ones on the debug board, for a "Disco LED" effect. + * + * This driver exports either the original ARM LED API, the new generic + * one, or both. + */ + +static spinlock_t lock; +static struct h2p2_dbg_fpga __iomem *fpga; +static u16 led_state, hw_led_state; + + +#ifdef CONFIG_LEDS +#define old_led_api() 1 +#else +#define old_led_api() 0 +#endif + +#ifdef CONFIG_LEDS_OMAP_DEBUG +#define new_led_api() 1 +#else +#define new_led_api() 0 +#endif + + +/*-------------------------------------------------------------------------*/ + +/* original ARM debug LED API: + * - timer and idle leds (some boards use non-FPGA leds here); + * - up to 4 generic leds, easily accessed in-kernel (any context) + */ + +#define GPIO_LED_RED 3 +#define GPIO_LED_GREEN OMAP_MPUIO(4) + +#define LED_STATE_ENABLED 0x01 +#define LED_STATE_CLAIMED 0x02 +#define LED_TIMER_ON 0x04 + +#define GPIO_IDLE GPIO_LED_GREEN +#define GPIO_TIMER GPIO_LED_RED + +static void h2p2_dbg_leds_event(led_event_t evt) +{ + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + + if (!(led_state & LED_STATE_ENABLED) && evt != led_start) + goto done; + + switch (evt) { + case led_start: + if (fpga) + led_state |= LED_STATE_ENABLED; + break; + + case led_stop: + case led_halted: + /* all leds off during suspend or shutdown */ + + if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) { + omap_set_gpio_dataout(GPIO_TIMER, 0); + omap_set_gpio_dataout(GPIO_IDLE, 0); + } + + __raw_writew(~0, &fpga->leds); + led_state &= ~LED_STATE_ENABLED; + goto done; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + led_state ^= LED_TIMER_ON; + + if (machine_is_omap_perseus2() || machine_is_omap_h4()) + hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER; + else { + omap_set_gpio_dataout(GPIO_TIMER, + led_state & LED_TIMER_ON); + goto done; + } + + break; +#endif + +#ifdef CONFIG_LEDS_CPU + /* LED lit iff busy */ + case led_idle_start: + if (machine_is_omap_perseus2() || machine_is_omap_h4()) + hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE; + else { + omap_set_gpio_dataout(GPIO_IDLE, 1); + goto done; + } + + break; + + case led_idle_end: + if (machine_is_omap_perseus2() || machine_is_omap_h4()) + hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE; + else { + omap_set_gpio_dataout(GPIO_IDLE, 0); + goto done; + } + + break; +#endif + + case led_green_on: + hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; + break; + case led_green_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; + break; + + case led_amber_on: + hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; + break; + case led_amber_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; + break; + + case led_red_on: + hw_led_state |= H2P2_DBG_FPGA_LED_RED; + break; + case led_red_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; + break; + + case led_blue_on: + hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; + break; + case led_blue_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; + break; + + default: + break; + } + + + /* + * Actually burn the LEDs + */ + if (led_state & LED_STATE_ENABLED) + __raw_writew(~hw_led_state, &fpga->leds); + +done: + spin_unlock_irqrestore(&lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +/* "new" LED API + * - with syfs access and generic triggering + * - not readily accessible to in-kernel drivers + */ + +struct dbg_led { + struct led_classdev cdev; + u16 mask; +}; + +static struct dbg_led dbg_leds[] = { + /* REVISIT at least H2 uses different timer & cpu leds... */ +#ifndef CONFIG_LEDS_TIMER + { .mask = 1 << 0, .cdev.name = "d4:green", }, /* timer */ +#endif +#ifndef CONFIG_LEDS_CPU + { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */ +#endif + { .mask = 1 << 2, .cdev.name = "d6:green", }, + { .mask = 1 << 3, .cdev.name = "d7:green", }, + + { .mask = 1 << 4, .cdev.name = "d8:green", }, + { .mask = 1 << 5, .cdev.name = "d9:green", }, + { .mask = 1 << 6, .cdev.name = "d10:green", }, + { .mask = 1 << 7, .cdev.name = "d11:green", }, + + { .mask = 1 << 8, .cdev.name = "d12:green", }, + { .mask = 1 << 9, .cdev.name = "d13:green", }, + { .mask = 1 << 10, .cdev.name = "d14:green", }, + { .mask = 1 << 11, .cdev.name = "d15:green", }, + +#ifndef CONFIG_LEDS + { .mask = 1 << 12, .cdev.name = "d16:green", }, + { .mask = 1 << 13, .cdev.name = "d17:green", }, + { .mask = 1 << 14, .cdev.name = "d18:green", }, + { .mask = 1 << 15, .cdev.name = "d19:green", }, +#endif +}; + +static void +fpga_led_set(struct led_classdev *cdev, enum led_brightness value) +{ + struct dbg_led *led = container_of(cdev, struct dbg_led, cdev); + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + if (value == LED_OFF) + hw_led_state &= ~led->mask; + else + hw_led_state |= led->mask; + __raw_writew(~hw_led_state, &fpga->leds); + spin_unlock_irqrestore(&lock, flags); +} + +static void __init newled_init(struct device *dev) +{ + unsigned i; + struct dbg_led *led; + int status; + + for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) { + led->cdev.brightness_set = fpga_led_set; + status = led_classdev_register(dev, &led->cdev); + if (status < 0) + break; + } + return; +} + + +/*-------------------------------------------------------------------------*/ + +static int /* __init */ fpga_probe(struct platform_device *pdev) +{ + struct resource *iomem; + + spin_lock_init(&lock); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) + return -ENODEV; + + fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE); + __raw_writew(~0, &fpga->leds); + + if (old_led_api()) { + leds_event = h2p2_dbg_leds_event; + leds_event(led_start); + } + + if (new_led_api()) { + newled_init(&pdev->dev); + } + + return 0; +} + +static int fpga_suspend_late(struct platform_device *pdev, pm_message_t mesg) +{ + __raw_writew(~0, &fpga->leds); + return 0; +} + +static int fpga_resume_early(struct platform_device *pdev) +{ + __raw_writew(~hw_led_state, &fpga->leds); + return 0; +} + + +static struct platform_driver led_driver = { + .driver.name = "omap_dbg_led", + .probe = fpga_probe, + .suspend_late = fpga_suspend_late, + .resume_early = fpga_resume_early, +}; + +static int __init fpga_init(void) +{ + if (machine_is_omap_h4() + || machine_is_omap_h3() + || machine_is_omap_h2() + || machine_is_omap_perseus2() + ) + return platform_driver_register(&led_driver); + return 0; +} +fs_initcall(fpga_init); -- cgit v1.2.3 From f4e4c324a5f81f18156499d1ade3732ba1f5b523 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 7 Dec 2006 13:57:38 -0800 Subject: ARM: OMAP: Sync headers with linux-omap This patch syncs omap specific headers with linux-omap. Most of the changes needed because of bitrot caused by driver changes in linux-omap tree. Integrating this is needed for adding support for various omap drivers. Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- include/asm-arm/arch-omap/aic23.h | 4 +- include/asm-arm/arch-omap/board-apollon.h | 9 -- include/asm-arm/arch-omap/board-h4.h | 3 - include/asm-arm/arch-omap/board.h | 41 +++-- include/asm-arm/arch-omap/dsp.h | 250 ------------------------------ include/asm-arm/arch-omap/dsp_common.h | 32 ++-- include/asm-arm/arch-omap/gpio-switch.h | 54 +++++++ include/asm-arm/arch-omap/gpmc.h | 2 + include/asm-arm/arch-omap/hardware.h | 9 ++ include/asm-arm/arch-omap/io.h | 11 ++ include/asm-arm/arch-omap/irqs.h | 20 ++- include/asm-arm/arch-omap/lcd_lph8923.h | 14 -- include/asm-arm/arch-omap/lcd_mipid.h | 24 +++ include/asm-arm/arch-omap/led.h | 24 +++ include/asm-arm/arch-omap/mcspi.h | 1 - include/asm-arm/arch-omap/memory.h | 13 ++ include/asm-arm/arch-omap/menelaus.h | 17 +- include/asm-arm/arch-omap/omap16xx.h | 12 +- include/asm-arm/arch-omap/omap24xx.h | 9 ++ include/asm-arm/arch-omap/omapfb.h | 131 +++++++++------- include/asm-arm/arch-omap/sram.h | 4 +- include/asm-arm/arch-omap/usb.h | 40 ++++- 22 files changed, 335 insertions(+), 389 deletions(-) delete mode 100644 include/asm-arm/arch-omap/dsp.h create mode 100644 include/asm-arm/arch-omap/gpio-switch.h delete mode 100644 include/asm-arm/arch-omap/lcd_lph8923.h create mode 100644 include/asm-arm/arch-omap/lcd_mipid.h create mode 100644 include/asm-arm/arch-omap/led.h diff --git a/include/asm-arm/arch-omap/aic23.h b/include/asm-arm/arch-omap/aic23.h index 6513065941d..aec2d656362 100644 --- a/include/asm-arm/arch-omap/aic23.h +++ b/include/asm-arm/arch-omap/aic23.h @@ -110,7 +110,7 @@ #define TLV320AIC23ID1 (0x1a) // cs low #define TLV320AIC23ID2 (0x1b) // cs high -void tlv320aic23_power_up(void); -void tlv320aic23_power_down(void); +void aic23_power_up(void); +void aic23_power_down(void); #endif /* __ASM_ARCH_AIC23_H */ diff --git a/include/asm-arm/arch-omap/board-apollon.h b/include/asm-arm/arch-omap/board-apollon.h index de0c5b792c5..dcb587b311f 100644 --- a/include/asm-arm/arch-omap/board-apollon.h +++ b/include/asm-arm/arch-omap/board-apollon.h @@ -30,16 +30,7 @@ #define __ASM_ARCH_OMAP_APOLLON_H /* Placeholder for APOLLON specific defines */ -/* GPMC CS0 */ -#define APOLLON_CS0_BASE 0x00000000 -/* GPMC CS1 */ -#define APOLLON_CS1_BASE 0x08000000 -#define APOLLON_ETHR_START (APOLLON_CS1_BASE + 0x300) #define APOLLON_ETHR_GPIO_IRQ 74 -/* GPMC CS2 - reserved for OneNAND */ -#define APOLLON_CS2_BASE 0x10000000 -/* GPMC CS3 - reserved for NOR or NAND */ -#define APOLLON_CS3_BASE 0x18000000 #endif /* __ASM_ARCH_OMAP_APOLLON_H */ diff --git a/include/asm-arm/arch-omap/board-h4.h b/include/asm-arm/arch-omap/board-h4.h index 7ef664bc9e3..7e0efef4bb6 100644 --- a/include/asm-arm/arch-omap/board-h4.h +++ b/include/asm-arm/arch-omap/board-h4.h @@ -30,9 +30,6 @@ #define __ASM_ARCH_OMAP_H4_H /* Placeholder for H4 specific defines */ -/* GPMC CS1 */ -#define OMAP24XX_ETHR_START 0x08000300 #define OMAP24XX_ETHR_GPIO_IRQ 92 -#define H4_CS0_BASE 0x04000000 #endif /* __ASM_ARCH_OMAP_H4_H */ diff --git a/include/asm-arm/arch-omap/board.h b/include/asm-arm/arch-omap/board.h index edf1dc6ad91..2075f556bfe 100644 --- a/include/asm-arm/arch-omap/board.h +++ b/include/asm-arm/arch-omap/board.h @@ -12,6 +12,8 @@ #include +#include + /* Different peripheral ids */ #define OMAP_TAG_CLOCK 0x4f01 #define OMAP_TAG_MMC 0x4f02 @@ -99,26 +101,31 @@ struct omap_usb_config { struct omap_lcd_config { char panel_name[16]; char ctrl_name[16]; + s16 nreset_gpio; + u8 data_lines; +}; + +struct device; +struct fb_info; +struct omap_backlight_config { + int default_intensity; + int (*set_power)(struct device *dev, int state); + int (*check_fb)(struct fb_info *fb); }; struct omap_fbmem_config { - u32 fb_sram_start; - u32 fb_sram_size; - u32 fb_sdram_start; - u32 fb_sdram_size; -}; - -/* Cover: - * high -> closed - * low -> open - * Connection: - * high -> connected - * low -> disconnected - */ -#define OMAP_GPIO_SWITCH_TYPE_COVER 0x0000 -#define OMAP_GPIO_SWITCH_TYPE_CONNECTION 0x0001 -#define OMAP_GPIO_SWITCH_FLAG_INVERTED 0x0001 -#define OMAP_GPIO_SWITCH_FLAG_OUTPUT 0x0002 + u32 start; + u32 size; +}; + +struct omap_pwm_led_platform_data { + const char *name; + int intensity_timer; + int blink_timer; + void (*set_power)(struct omap_pwm_led_platform_data *self, int on_off); +}; + +/* See include/asm-arm/arch-omap/gpio-switch.h for definitions */ struct omap_gpio_switch_config { char name[12]; u16 gpio; diff --git a/include/asm-arm/arch-omap/dsp.h b/include/asm-arm/arch-omap/dsp.h deleted file mode 100644 index 06dad83dd41..00000000000 --- a/include/asm-arm/arch-omap/dsp.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * linux/include/asm-arm/arch-omap/dsp.h - * - * Header for OMAP DSP driver - * - * Copyright (C) 2002-2005 Nokia Corporation - * - * Written by Toshihiro Kobayashi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * 2005/06/01: DSP Gateway version 3.3 - */ - -#ifndef ASM_ARCH_DSP_H -#define ASM_ARCH_DSP_H - - -/* - * for /dev/dspctl/ctl - */ -#define OMAP_DSP_IOCTL_RESET 1 -#define OMAP_DSP_IOCTL_RUN 2 -#define OMAP_DSP_IOCTL_SETRSTVECT 3 -#define OMAP_DSP_IOCTL_CPU_IDLE 4 -#define OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON 5 -#define OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF 6 -#define OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON 7 -#define OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF 8 -#define OMAP_DSP_IOCTL_GBL_IDLE 9 -#define OMAP_DSP_IOCTL_DSPCFG 10 -#define OMAP_DSP_IOCTL_DSPUNCFG 11 -#define OMAP_DSP_IOCTL_TASKCNT 12 -#define OMAP_DSP_IOCTL_POLL 13 -#define OMAP_DSP_IOCTL_REGMEMR 40 -#define OMAP_DSP_IOCTL_REGMEMW 41 -#define OMAP_DSP_IOCTL_REGIOR 42 -#define OMAP_DSP_IOCTL_REGIOW 43 -#define OMAP_DSP_IOCTL_GETVAR 44 -#define OMAP_DSP_IOCTL_SETVAR 45 -#define OMAP_DSP_IOCTL_RUNLEVEL 50 -#define OMAP_DSP_IOCTL_SUSPEND 51 -#define OMAP_DSP_IOCTL_RESUME 52 -#define OMAP_DSP_IOCTL_FBEN 53 -#define OMAP_DSP_IOCTL_FBDIS 54 -#define OMAP_DSP_IOCTL_MBSEND 99 - -/* - * for taskdev - * (ioctls below should be >= 0x10000) - */ -#define OMAP_DSP_TASK_IOCTL_BFLSH 0x10000 -#define OMAP_DSP_TASK_IOCTL_SETBSZ 0x10001 -#define OMAP_DSP_TASK_IOCTL_LOCK 0x10002 -#define OMAP_DSP_TASK_IOCTL_UNLOCK 0x10003 -#define OMAP_DSP_TASK_IOCTL_GETNAME 0x10004 - -/* - * for /dev/dspctl/mem - */ -#define OMAP_DSP_MEM_IOCTL_EXMAP 1 -#define OMAP_DSP_MEM_IOCTL_EXUNMAP 2 -#define OMAP_DSP_MEM_IOCTL_EXMAP_FLUSH 3 -#define OMAP_DSP_MEM_IOCTL_FBEXPORT 5 -#define OMAP_DSP_MEM_IOCTL_MMUITACK 7 -#define OMAP_DSP_MEM_IOCTL_MMUINIT 9 -#define OMAP_DSP_MEM_IOCTL_KMEM_RESERVE 11 -#define OMAP_DSP_MEM_IOCTL_KMEM_RELEASE 12 - -struct omap_dsp_mapinfo { - unsigned long dspadr; - unsigned long size; -}; - -/* - * for /dev/dspctl/twch - */ -#define OMAP_DSP_TWCH_IOCTL_MKDEV 1 -#define OMAP_DSP_TWCH_IOCTL_RMDEV 2 -#define OMAP_DSP_TWCH_IOCTL_TADD 11 -#define OMAP_DSP_TWCH_IOCTL_TDEL 12 -#define OMAP_DSP_TWCH_IOCTL_TKILL 13 - -#define OMAP_DSP_DEVSTATE_NOTASK 0x00000001 -#define OMAP_DSP_DEVSTATE_ATTACHED 0x00000002 -#define OMAP_DSP_DEVSTATE_GARBAGE 0x00000004 -#define OMAP_DSP_DEVSTATE_INVALID 0x00000008 -#define OMAP_DSP_DEVSTATE_ADDREQ 0x00000100 -#define OMAP_DSP_DEVSTATE_DELREQ 0x00000200 -#define OMAP_DSP_DEVSTATE_ADDFAIL 0x00001000 -#define OMAP_DSP_DEVSTATE_ADDING 0x00010000 -#define OMAP_DSP_DEVSTATE_DELING 0x00020000 -#define OMAP_DSP_DEVSTATE_KILLING 0x00040000 -#define OMAP_DSP_DEVSTATE_STATE_MASK 0x7fffffff -#define OMAP_DSP_DEVSTATE_STALE 0x80000000 - -struct omap_dsp_taddinfo { - unsigned char minor; - unsigned long taskadr; -}; -#define OMAP_DSP_TADD_ABORTADR 0xffffffff - - -/* - * error cause definition (for error detection device) - */ -#define OMAP_DSP_ERRDT_WDT 0x00000001 -#define OMAP_DSP_ERRDT_MMU 0x00000002 - - -/* - * mailbox protocol definitions - */ - -struct omap_dsp_mailbox_cmd { - unsigned short cmd; - unsigned short data; -}; - -struct omap_dsp_reginfo { - unsigned short adr; - unsigned short val; -}; - -struct omap_dsp_varinfo { - unsigned char varid; - unsigned short val[0]; -}; - -#define OMAP_DSP_MBPROT_REVISION 0x0019 - -#define OMAP_DSP_MBCMD_WDSND 0x10 -#define OMAP_DSP_MBCMD_WDREQ 0x11 -#define OMAP_DSP_MBCMD_BKSND 0x20 -#define OMAP_DSP_MBCMD_BKREQ 0x21 -#define OMAP_DSP_MBCMD_BKYLD 0x23 -#define OMAP_DSP_MBCMD_BKSNDP 0x24 -#define OMAP_DSP_MBCMD_BKREQP 0x25 -#define OMAP_DSP_MBCMD_TCTL 0x30 -#define OMAP_DSP_MBCMD_TCTLDATA 0x31 -#define OMAP_DSP_MBCMD_POLL 0x32 -#define OMAP_DSP_MBCMD_WDT 0x50 /* v3.3: obsolete */ -#define OMAP_DSP_MBCMD_RUNLEVEL 0x51 -#define OMAP_DSP_MBCMD_PM 0x52 -#define OMAP_DSP_MBCMD_SUSPEND 0x53 -#define OMAP_DSP_MBCMD_KFUNC 0x54 -#define OMAP_DSP_MBCMD_TCFG 0x60 -#define OMAP_DSP_MBCMD_TADD 0x62 -#define OMAP_DSP_MBCMD_TDEL 0x63 -#define OMAP_DSP_MBCMD_TSTOP 0x65 -#define OMAP_DSP_MBCMD_DSPCFG 0x70 -#define OMAP_DSP_MBCMD_REGRW 0x72 -#define OMAP_DSP_MBCMD_GETVAR 0x74 -#define OMAP_DSP_MBCMD_SETVAR 0x75 -#define OMAP_DSP_MBCMD_ERR 0x78 -#define OMAP_DSP_MBCMD_DBG 0x79 - -#define OMAP_DSP_MBCMD_TCTL_TINIT 0x0000 -#define OMAP_DSP_MBCMD_TCTL_TEN 0x0001 -#define OMAP_DSP_MBCMD_TCTL_TDIS 0x0002 -#define OMAP_DSP_MBCMD_TCTL_TCLR 0x0003 -#define OMAP_DSP_MBCMD_TCTL_TCLR_FORCE 0x0004 - -#define OMAP_DSP_MBCMD_RUNLEVEL_USER 0x01 -#define OMAP_DSP_MBCMD_RUNLEVEL_SUPER 0x0e -#define OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY 0x10 - -#define OMAP_DSP_MBCMD_PM_DISABLE 0x00 -#define OMAP_DSP_MBCMD_PM_ENABLE 0x01 - -#define OMAP_DSP_MBCMD_KFUNC_FBCTL 0x00 -#define OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR 0x01 - -#define OMAP_DSP_MBCMD_FBCTL_UPD 0x0000 -#define OMAP_DSP_MBCMD_FBCTL_ENABLE 0x0002 -#define OMAP_DSP_MBCMD_FBCTL_DISABLE 0x0003 - -#define OMAP_DSP_MBCMD_AUDIO_PWR_UP 0x0000 -#define OMAP_DSP_MBCMD_AUDIO_PWR_DOWN1 0x0001 -#define OMAP_DSP_MBCMD_AUDIO_PWR_DOWN2 0x0002 - -#define OMAP_DSP_MBCMD_TDEL_SAFE 0x0000 -#define OMAP_DSP_MBCMD_TDEL_KILL 0x0001 - -#define OMAP_DSP_MBCMD_DSPCFG_REQ 0x00 -#define OMAP_DSP_MBCMD_DSPCFG_SYSADRH 0x28 -#define OMAP_DSP_MBCMD_DSPCFG_SYSADRL 0x29 -#define OMAP_DSP_MBCMD_DSPCFG_PROTREV 0x70 -#define OMAP_DSP_MBCMD_DSPCFG_ABORT 0x78 -#define OMAP_DSP_MBCMD_DSPCFG_LAST 0x80 - -#define OMAP_DSP_MBCMD_REGRW_MEMR 0x00 -#define OMAP_DSP_MBCMD_REGRW_MEMW 0x01 -#define OMAP_DSP_MBCMD_REGRW_IOR 0x02 -#define OMAP_DSP_MBCMD_REGRW_IOW 0x03 -#define OMAP_DSP_MBCMD_REGRW_DATA 0x04 - -#define OMAP_DSP_MBCMD_VARID_ICRMASK 0x00 -#define OMAP_DSP_MBCMD_VARID_LOADINFO 0x01 - -#define OMAP_DSP_TTYP_ARCV 0x0001 -#define OMAP_DSP_TTYP_ASND 0x0002 -#define OMAP_DSP_TTYP_BKMD 0x0004 -#define OMAP_DSP_TTYP_BKDM 0x0008 -#define OMAP_DSP_TTYP_PVMD 0x0010 -#define OMAP_DSP_TTYP_PVDM 0x0020 - -#define OMAP_DSP_EID_BADTID 0x10 -#define OMAP_DSP_EID_BADTCN 0x11 -#define OMAP_DSP_EID_BADBID 0x20 -#define OMAP_DSP_EID_BADCNT 0x21 -#define OMAP_DSP_EID_NOTLOCKED 0x22 -#define OMAP_DSP_EID_STVBUF 0x23 -#define OMAP_DSP_EID_BADADR 0x24 -#define OMAP_DSP_EID_BADTCTL 0x30 -#define OMAP_DSP_EID_BADPARAM 0x50 -#define OMAP_DSP_EID_FATAL 0x58 -#define OMAP_DSP_EID_NOMEM 0xc0 -#define OMAP_DSP_EID_NORES 0xc1 -#define OMAP_DSP_EID_IPBFULL 0xc2 -#define OMAP_DSP_EID_WDT 0xd0 -#define OMAP_DSP_EID_TASKNOTRDY 0xe0 -#define OMAP_DSP_EID_TASKBSY 0xe1 -#define OMAP_DSP_EID_TASKERR 0xef -#define OMAP_DSP_EID_BADCFGTYP 0xf0 -#define OMAP_DSP_EID_DEBUG 0xf8 -#define OMAP_DSP_EID_BADSEQ 0xfe -#define OMAP_DSP_EID_BADCMD 0xff - -#define OMAP_DSP_TNM_LEN 16 - -#define OMAP_DSP_TID_FREE 0xff -#define OMAP_DSP_TID_ANON 0xfe - -#define OMAP_DSP_BID_NULL 0xffff -#define OMAP_DSP_BID_PVT 0xfffe - -#endif /* ASM_ARCH_DSP_H */ diff --git a/include/asm-arm/arch-omap/dsp_common.h b/include/asm-arm/arch-omap/dsp_common.h index 16a459dfa71..c61f868f24e 100644 --- a/include/asm-arm/arch-omap/dsp_common.h +++ b/include/asm-arm/arch-omap/dsp_common.h @@ -1,38 +1,34 @@ /* - * linux/include/asm-arm/arch-omap/dsp_common.h + * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1) * - * Header for OMAP DSP subsystem control + * Copyright (C) 2004-2006 Nokia Corporation. All rights reserved. * - * Copyright (C) 2004,2005 Nokia Corporation + * Contact: Toshihiro Kobayashi * - * Written by Toshihiro Kobayashi + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA * - * 2005/06/03: DSP Gateway version 3.3 */ #ifndef ASM_ARCH_DSP_COMMON_H #define ASM_ARCH_DSP_COMMON_H +#ifdef CONFIG_ARCH_OMAP1 extern void omap_dsp_request_mpui(void); extern void omap_dsp_release_mpui(void); extern int omap_dsp_request_mem(void); extern int omap_dsp_release_mem(void); - -extern void (*omap_dsp_audio_pwr_up_request)(int stage); -extern void (*omap_dsp_audio_pwr_down_request)(int stage); +#endif #endif /* ASM_ARCH_DSP_COMMON_H */ diff --git a/include/asm-arm/arch-omap/gpio-switch.h b/include/asm-arm/arch-omap/gpio-switch.h new file mode 100644 index 00000000000..10da0e07c0c --- /dev/null +++ b/include/asm-arm/arch-omap/gpio-switch.h @@ -0,0 +1,54 @@ +/* + * GPIO switch definitions + * + * Copyright (C) 2006 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_OMAP_GPIO_SWITCH_H +#define __ASM_ARCH_OMAP_GPIO_SWITCH_H + +#include + +/* Cover: + * high -> closed + * low -> open + * Connection: + * high -> connected + * low -> disconnected + * Activity: + * high -> active + * low -> inactive + * + */ +#define OMAP_GPIO_SWITCH_TYPE_COVER 0x0000 +#define OMAP_GPIO_SWITCH_TYPE_CONNECTION 0x0001 +#define OMAP_GPIO_SWITCH_TYPE_ACTIVITY 0x0002 +#define OMAP_GPIO_SWITCH_FLAG_INVERTED 0x0001 +#define OMAP_GPIO_SWITCH_FLAG_OUTPUT 0x0002 + +struct omap_gpio_switch { + const char *name; + s16 gpio; + unsigned flags:4; + unsigned type:4; + + /* Time in ms to debounce when transitioning from + * inactive state to active state. */ + u16 debounce_rising; + /* Same for transition from active to inactive state. */ + u16 debounce_falling; + + /* notify board-specific code about state changes */ + void (* notify)(void *data, int state); + void *notify_data; +}; + +/* Call at init time only */ +extern void omap_register_gpio_switches(const struct omap_gpio_switch *tbl, + int count); + +#endif diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h index 7c03ef6c14c..fa33149f597 100644 --- a/include/asm-arm/arch-omap/gpmc.h +++ b/include/asm-arm/arch-omap/gpmc.h @@ -87,5 +87,7 @@ extern int gpmc_cs_calc_divider(int cs, unsigned int sync_clk); extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); extern void gpmc_cs_free(int cs); +extern void gpmc_cs_set_reserved(int cs, int reserved); +extern int gpmc_cs_reserved(int cs); #endif diff --git a/include/asm-arm/arch-omap/hardware.h b/include/asm-arm/arch-omap/hardware.h index 481048d6521..e225f4f39b3 100644 --- a/include/asm-arm/arch-omap/hardware.h +++ b/include/asm-arm/arch-omap/hardware.h @@ -266,6 +266,15 @@ #define OMAP_LPG2_LCR (OMAP_LPG2_BASE + 0x00) #define OMAP_LPG2_PMR (OMAP_LPG2_BASE + 0x04) +/* + * ---------------------------------------------------------------------------- + * Pulse-Width Light + * ---------------------------------------------------------------------------- + */ +#define OMAP_PWL_BASE 0xfffb5800 +#define OMAP_PWL_ENABLE (OMAP_PWL_BASE + 0x00) +#define OMAP_PWL_CLK_ENABLE (OMAP_PWL_BASE + 0x04) + /* * --------------------------------------------------------------------------- * Processor specific defines diff --git a/include/asm-arm/arch-omap/io.h b/include/asm-arm/arch-omap/io.h index 78f68e6a4f0..4aca7e3d756 100644 --- a/include/asm-arm/arch-omap/io.h +++ b/include/asm-arm/arch-omap/io.h @@ -77,6 +77,17 @@ #define io_p2v(pa) ((pa) + IO_OFFSET) /* Works for L3 and L4 */ #define io_v2p(va) ((va) - IO_OFFSET) /* Works for L3 and L4 */ +/* DSP */ +#define DSP_MEM_24XX_PHYS OMAP24XX_DSP_MEM_BASE /* 0x58000000 */ +#define DSP_MEM_24XX_VIRT 0xe0000000 +#define DSP_MEM_24XX_SIZE 0x28000 +#define DSP_IPI_24XX_PHYS OMAP24XX_DSP_IPI_BASE /* 0x59000000 */ +#define DSP_IPI_24XX_VIRT 0xe1000000 +#define DSP_IPI_24XX_SIZE SZ_4K +#define DSP_MMU_24XX_PHYS OMAP24XX_DSP_MMU_BASE /* 0x5a000000 */ +#define DSP_MMU_24XX_VIRT 0xe2000000 +#define DSP_MMU_24XX_SIZE SZ_4K + #endif #ifndef __ASSEMBLER__ diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h index c5bb05a69b8..3ede58b51db 100644 --- a/include/asm-arm/arch-omap/irqs.h +++ b/include/asm-arm/arch-omap/irqs.h @@ -37,8 +37,6 @@ #define INT_DSP_MMU_ABORT 7 #define INT_HOST 8 #define INT_ABORT 9 -#define INT_DSP_MAILBOX1 10 -#define INT_DSP_MAILBOX2 11 #define INT_BRIDGE_PRIV 13 #define INT_GPIO_BANK1 14 #define INT_UART3 15 @@ -63,6 +61,8 @@ #define INT_1510_RES2 2 #define INT_1510_SPI_TX 4 #define INT_1510_SPI_RX 5 +#define INT_1510_DSP_MAILBOX1 10 +#define INT_1510_DSP_MAILBOX2 11 #define INT_1510_RES12 12 #define INT_1510_LB_MMU 17 #define INT_1510_RES18 18 @@ -75,6 +75,8 @@ #define INT_1610_IH2_FIQ 2 #define INT_1610_McBSP2_TX 4 #define INT_1610_McBSP2_RX 5 +#define INT_1610_DSP_MAILBOX1 10 +#define INT_1610_DSP_MAILBOX2 11 #define INT_1610_LCD_LINE 12 #define INT_1610_GPTIMER1 17 #define INT_1610_GPTIMER2 18 @@ -131,11 +133,11 @@ #define INT_RTC_TIMER (25 + IH2_BASE) #define INT_RTC_ALARM (26 + IH2_BASE) #define INT_MEM_STICK (27 + IH2_BASE) -#define INT_DSP_MMU (28 + IH2_BASE) /* * OMAP-1510 specific IRQ numbers for interrupt handler 2 */ +#define INT_1510_DSP_MMU (28 + IH2_BASE) #define INT_1510_COM_SPI_RO (31 + IH2_BASE) /* @@ -146,6 +148,7 @@ #define INT_1610_USB_OTG (8 + IH2_BASE) #define INT_1610_SoSSI (9 + IH2_BASE) #define INT_1610_SoSSI_MATCH (19 + IH2_BASE) +#define INT_1610_DSP_MMU (28 + IH2_BASE) #define INT_1610_McBSP2RX_OF (31 + IH2_BASE) #define INT_1610_STI (32 + IH2_BASE) #define INT_1610_STI_WAKEUP (33 + IH2_BASE) @@ -239,10 +242,15 @@ #define INT_24XX_SDMA_IRQ3 15 #define INT_24XX_CAM_IRQ 24 #define INT_24XX_DSS_IRQ 25 +#define INT_24XX_MAIL_U0_MPU 26 +#define INT_24XX_DSP_UMA 27 +#define INT_24XX_DSP_MMU 28 #define INT_24XX_GPIO_BANK1 29 #define INT_24XX_GPIO_BANK2 30 #define INT_24XX_GPIO_BANK3 31 #define INT_24XX_GPIO_BANK4 32 +#define INT_24XX_GPIO_BANK5 33 +#define INT_24XX_MAIL_U3_MPU 34 #define INT_24XX_GPTIMER1 37 #define INT_24XX_GPTIMER2 38 #define INT_24XX_GPTIMER3 39 @@ -262,6 +270,12 @@ #define INT_24XX_UART1_IRQ 72 #define INT_24XX_UART2_IRQ 73 #define INT_24XX_UART3_IRQ 74 +#define INT_24XX_USB_IRQ_GEN 75 +#define INT_24XX_USB_IRQ_NISO 76 +#define INT_24XX_USB_IRQ_ISO 77 +#define INT_24XX_USB_IRQ_HGEN 78 +#define INT_24XX_USB_IRQ_HSOF 79 +#define INT_24XX_USB_IRQ_OTG 80 #define INT_24XX_MMC_IRQ 83 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and diff --git a/include/asm-arm/arch-omap/lcd_lph8923.h b/include/asm-arm/arch-omap/lcd_lph8923.h deleted file mode 100644 index 004e67e22ca..00000000000 --- a/include/asm-arm/arch-omap/lcd_lph8923.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __LCD_LPH8923_H -#define __LCD_LPH8923_H - -enum lcd_lph8923_test_num { - LCD_LPH8923_TEST_RGB_LINES, -}; - -enum lcd_lph8923_test_result { - LCD_LPH8923_TEST_SUCCESS, - LCD_LPH8923_TEST_INVALID, - LCD_LPH8923_TEST_FAILED, -}; - -#endif diff --git a/include/asm-arm/arch-omap/lcd_mipid.h b/include/asm-arm/arch-omap/lcd_mipid.h new file mode 100644 index 00000000000..f8fbc4801e5 --- /dev/null +++ b/include/asm-arm/arch-omap/lcd_mipid.h @@ -0,0 +1,24 @@ +#ifndef __LCD_MIPID_H +#define __LCD_MIPID_H + +enum mipid_test_num { + MIPID_TEST_RGB_LINES, +}; + +enum mipid_test_result { + MIPID_TEST_SUCCESS, + MIPID_TEST_INVALID, + MIPID_TEST_FAILED, +}; + +#ifdef __KERNEL__ + +struct mipid_platform_data { + int nreset_gpio; + int data_lines; + void (*shutdown)(struct mipid_platform_data *pdata); +}; + +#endif + +#endif diff --git a/include/asm-arm/arch-omap/led.h b/include/asm-arm/arch-omap/led.h new file mode 100644 index 00000000000..f3acae28e2d --- /dev/null +++ b/include/asm-arm/arch-omap/led.h @@ -0,0 +1,24 @@ +/* + * linux/include/asm-arm/arch-omap/led.h + * + * Copyright (C) 2006 Samsung Electronics + * Kyungmin Park + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ASMARM_ARCH_LED_H +#define ASMARM_ARCH_LED_H + +struct omap_led_config { + struct led_classdev cdev; + s16 gpio; +}; + +struct omap_led_platform_data { + s16 nr_leds; + struct omap_led_config *leds; +}; + +#endif diff --git a/include/asm-arm/arch-omap/mcspi.h b/include/asm-arm/arch-omap/mcspi.h index 9e7f40a88e1..1254e4945b6 100644 --- a/include/asm-arm/arch-omap/mcspi.h +++ b/include/asm-arm/arch-omap/mcspi.h @@ -2,7 +2,6 @@ #define _OMAP2_MCSPI_H struct omap2_mcspi_platform_config { - unsigned long base; unsigned short num_cs; }; diff --git a/include/asm-arm/arch-omap/memory.h b/include/asm-arm/arch-omap/memory.h index 48fabc49316..14cba97c18a 100644 --- a/include/asm-arm/arch-omap/memory.h +++ b/include/asm-arm/arch-omap/memory.h @@ -86,5 +86,18 @@ #endif /* CONFIG_ARCH_OMAP15XX */ +/* Override the ARM default */ +#ifdef CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE + +#if (CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE == 0) +#undef CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE +#define CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE 2 +#endif + +#define CONSISTENT_DMA_SIZE \ + (((CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE + 1) & ~1) * 1024 * 1024) + +#endif + #endif diff --git a/include/asm-arm/arch-omap/menelaus.h b/include/asm-arm/arch-omap/menelaus.h index 88cd4c87f0d..82d276a6bd9 100644 --- a/include/asm-arm/arch-omap/menelaus.h +++ b/include/asm-arm/arch-omap/menelaus.h @@ -7,10 +7,19 @@ #ifndef __ASM_ARCH_MENELAUS_H #define __ASM_ARCH_MENELAUS_H -extern void menelaus_mmc_register(void (*callback)(unsigned long data, u8 card_mask), - unsigned long data); -extern void menelaus_mmc_remove(void); -extern void menelaus_mmc_opendrain(int enable); +extern int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask), + void *data); +extern void menelaus_unregister_mmc_callback(void); +extern int menelaus_set_mmc_opendrain(int slot, int enable); +extern int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_on); + +extern int menelaus_set_vmem(unsigned int mV); +extern int menelaus_set_vio(unsigned int mV); +extern int menelaus_set_vmmc(unsigned int mV); +extern int menelaus_set_vaux(unsigned int mV); +extern int menelaus_set_vdcdc(int dcdc, unsigned int mV); +extern int menelaus_set_slot_sel(int enable); +extern int menelaus_get_slot_pin_states(void); #if defined(CONFIG_ARCH_OMAP24XX) && defined(CONFIG_MENELAUS) #define omap_has_menelaus() 1 diff --git a/include/asm-arm/arch-omap/omap16xx.h b/include/asm-arm/arch-omap/omap16xx.h index f0c7f0fb4dc..f7f5cdfdccc 100644 --- a/include/asm-arm/arch-omap/omap16xx.h +++ b/include/asm-arm/arch-omap/omap16xx.h @@ -158,15 +158,6 @@ #define UART3_OSC_12M_SEL (OMAP_UART3_BASE + 0x4C) #define UART3_MVR (OMAP_UART3_BASE + 0x50) -/* - * ---------------------------------------------------------------------------- - * Pulse-Width Light - * ---------------------------------------------------------------------------- - */ -#define OMAP16XX_PWL_BASE (0xfffb5800) -#define OMAP16XX_PWL_ENABLE (OMAP16XX_PWL_BASE + 0x00) -#define OMAP16XX_PWL_CLK_ENABLE (OMAP16XX_PWL_BASE + 0x04) - /* * --------------------------------------------------------------------------- * Watchdog timer @@ -199,5 +190,8 @@ #define WSPR_DISABLE_0 (0x0000aaaa) #define WSPR_DISABLE_1 (0x00005555) +/* Mailbox */ +#define OMAP16XX_MAILBOX_BASE (0xfffcf000) + #endif /* __ASM_ARCH_OMAP16XX_H */ diff --git a/include/asm-arm/arch-omap/omap24xx.h b/include/asm-arm/arch-omap/omap24xx.h index 6e59805fa65..708b2fac77f 100644 --- a/include/asm-arm/arch-omap/omap24xx.h +++ b/include/asm-arm/arch-omap/omap24xx.h @@ -20,5 +20,14 @@ #define OMAP24XX_PRCM_BASE (L4_24XX_BASE + 0x8000) #define OMAP24XX_SDRC_BASE (L3_24XX_BASE + 0x9000) +/* DSP SS */ +#define OMAP24XX_DSP_BASE 0x58000000 +#define OMAP24XX_DSP_MEM_BASE (OMAP24XX_DSP_BASE + 0x0) +#define OMAP24XX_DSP_IPI_BASE (OMAP24XX_DSP_BASE + 0x1000000) +#define OMAP24XX_DSP_MMU_BASE (OMAP24XX_DSP_BASE + 0x2000000) + +/* Mailbox */ +#define OMAP24XX_MAILBOX_BASE (L4_24XX_BASE + 0x94000) + #endif /* __ASM_ARCH_OMAP24XX_H */ diff --git a/include/asm-arm/arch-omap/omapfb.h b/include/asm-arm/arch-omap/omapfb.h index ce80a71ce8f..adf1d81f22d 100644 --- a/include/asm-arm/arch-omap/omapfb.h +++ b/include/asm-arm/arch-omap/omapfb.h @@ -24,6 +24,9 @@ #ifndef __OMAPFB_H #define __OMAPFB_H +#include +#include + /* IOCTL commands. */ #define OMAP_IOW(num, dtype) _IOW('O', num, dtype) @@ -36,14 +39,14 @@ #define OMAPFB_VSYNC OMAP_IO(38) #define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) #define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(41, struct omapfb_update_window_old) -#define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long) #define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) #define OMAPFB_LCD_TEST OMAP_IOW(45, int) #define OMAPFB_CTRL_TEST OMAP_IOW(46, int) #define OMAPFB_UPDATE_WINDOW OMAP_IOW(47, struct omapfb_update_window) -#define OMAPFB_SETUP_PLANE OMAP_IOW(48, struct omapfb_setup_plane) -#define OMAPFB_ENABLE_PLANE OMAP_IOW(49, struct omapfb_enable_plane) #define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) +#define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) +#define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) +#define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) #define OMAPFB_CAPS_GENERIC_MASK 0x00000fff #define OMAPFB_CAPS_LCDC_MASK 0x00fff000 @@ -56,6 +59,9 @@ #define OMAPFB_FORMAT_MASK 0x00ff #define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 +#define OMAPFB_EVENT_READY 1 +#define OMAPFB_EVENT_DISABLED 2 + enum omapfb_color_format { OMAPFB_COLOR_RGB565 = 0, OMAPFB_COLOR_YUV422, @@ -64,6 +70,8 @@ enum omapfb_color_format { OMAPFB_COLOR_CLUT_4BPP, OMAPFB_COLOR_CLUT_2BPP, OMAPFB_COLOR_CLUT_1BPP, + OMAPFB_COLOR_RGB444, + OMAPFB_COLOR_YUY422, }; struct omapfb_update_window { @@ -88,18 +96,16 @@ enum omapfb_channel_out { OMAPFB_CHANNEL_OUT_DIGIT, }; -struct omapfb_setup_plane { - __u8 plane; +struct omapfb_plane_info { + __u32 pos_x; + __u32 pos_y; + __u8 enabled; __u8 channel_out; - __u32 offset; - __u32 pos_x, pos_y; - __u32 width, height; - __u32 color_mode; -}; - -struct omapfb_enable_plane { - __u8 plane; - __u8 enable; + __u8 mirror; + __u8 reserved1; + __u32 out_width; + __u32 out_height; + __u32 reserved2[12]; }; enum omapfb_color_key_type { @@ -141,6 +147,9 @@ enum omapfb_update_mode { #define OMAP_LCDC_PANEL_TFT 0x0100 +#define OMAPFB_PLANE_XRES_MIN 8 +#define OMAPFB_PLANE_YRES_MIN 8 + #ifdef CONFIG_ARCH_OMAP1 #define OMAPFB_PLANE_NUM 1 #else @@ -169,15 +178,17 @@ struct lcd_panel { int pcd; /* pixel clock divider. Obsolete use pixel_clock instead */ - int (*init) (struct omapfb_device *fbdev); - void (*cleanup) (void); - int (*enable) (void); - void (*disable) (void); - unsigned long (*get_caps) (void); - int (*set_bklight_level)(unsigned int level); - unsigned int (*get_bklight_level)(void); - unsigned int (*get_bklight_max) (void); - int (*run_test) (int test_num); + int (*init) (struct lcd_panel *panel, + struct omapfb_device *fbdev); + void (*cleanup) (struct lcd_panel *panel); + int (*enable) (struct lcd_panel *panel); + void (*disable) (struct lcd_panel *panel); + unsigned long (*get_caps) (struct lcd_panel *panel); + int (*set_bklight_level)(struct lcd_panel *panel, + unsigned int level); + unsigned int (*get_bklight_level)(struct lcd_panel *panel); + unsigned int (*get_bklight_max) (struct lcd_panel *panel); + int (*run_test) (struct lcd_panel *panel, int test_num); }; struct omapfb_device; @@ -202,7 +213,7 @@ struct extif_timings { }; struct lcd_ctrl_extif { - int (*init) (void); + int (*init) (struct omapfb_device *fbdev); void (*cleanup) (void); void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); int (*convert_timings) (struct extif_timings *timings); @@ -213,30 +224,41 @@ struct lcd_ctrl_extif { void (*write_data) (const void *buf, unsigned int len); void (*transfer_area) (int width, int height, void (callback)(void * data), void *data); + unsigned long max_transmit_size; }; struct omapfb_notifier_block { struct notifier_block nb; void *data; + int plane_idx; }; -typedef int (*omapfb_notifier_callback_t)(struct omapfb_notifier_block *, - unsigned long event, - struct omapfb_device *fbdev); +typedef int (*omapfb_notifier_callback_t)(struct notifier_block *, + unsigned long event, + void *fbi); + +struct omapfb_mem_region { + dma_addr_t paddr; + void *vaddr; + unsigned long size; + int alloc:1; +}; + +struct omapfb_mem_desc { + int region_cnt; + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; +}; struct lcd_ctrl { const char *name; void *data; int (*init) (struct omapfb_device *fbdev, - int ext_mode, int req_vram_size); + int ext_mode, + struct omapfb_mem_desc *req_md); void (*cleanup) (void); void (*bind_client) (struct omapfb_notifier_block *nb); - void (*get_vram_layout)(unsigned long *size, - void **virt_base, - dma_addr_t *phys_base); - int (*mmap) (struct vm_area_struct *vma); unsigned long (*get_caps) (void); int (*set_update_mode)(enum omapfb_update_mode mode); enum omapfb_update_mode (*get_update_mode)(void); @@ -245,8 +267,12 @@ struct lcd_ctrl { int screen_width, int pos_x, int pos_y, int width, int height, int color_mode); + int (*set_scale) (int plane, + int orig_width, int orig_height, + int out_width, int out_height); int (*enable_plane) (int plane, int enable); - int (*update_window) (struct omapfb_update_window *win, + int (*update_window) (struct fb_info *fbi, + struct omapfb_update_window *win, void (*callback)(void *), void *callback_data); void (*sync) (void); @@ -257,6 +283,7 @@ struct lcd_ctrl { u16 blue, u16 transp, int update_hw_mem); int (*set_color_key) (struct omapfb_color_key *ck); + int (*get_color_key) (struct omapfb_color_key *ck); }; @@ -266,19 +293,20 @@ enum omapfb_state { OMAPFB_ACTIVE = 100 }; +struct omapfb_plane_struct { + int idx; + struct omapfb_plane_info info; + enum omapfb_color_format color_mode; + struct omapfb_device *fbdev; +}; + struct omapfb_device { int state; int ext_lcdc; /* Using external LCD controller */ struct mutex rqueue_mutex; - void *vram_virt_base; - dma_addr_t vram_phys_base; - unsigned long vram_size; - - int color_mode; int palette_size; - int mirror; u32 pseudo_palette[17]; struct lcd_panel *panel; /* LCD panel */ @@ -286,20 +314,18 @@ struct omapfb_device { struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */ struct lcd_ctrl_extif *ext_if; /* LCD ctrl external interface */ - struct fb_info *fb_info; - struct device *dev; + + struct omapfb_mem_desc mem_desc; + struct fb_info *fb_info[OMAPFB_PLANE_NUM]; }; struct omapfb_platform_data { - struct omap_lcd_config lcd; - struct omapfb_mem_desc mem_desc; - void *ctrl_platform_data; + struct omap_lcd_config lcd; + struct omapfb_mem_desc mem_desc; + void *ctrl_platform_data; }; -#define OMAPFB_EVENT_READY 1 -#define OMAPFB_EVENT_DISABLED 2 - #ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl omap1_lcd_ctrl; #else @@ -311,12 +337,13 @@ extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); extern void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event); extern int omapfb_register_client(struct omapfb_notifier_block *nb, - omapfb_notifier_callback_t callback, - void *callback_data); + omapfb_notifier_callback_t callback, + void *callback_data); extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); -extern int omapfb_update_window_async(struct omapfb_update_window *win, - void (*callback)(void *), - void *callback_data); +extern int omapfb_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); /* in arch/arm/plat-omap/fb.c */ extern void omapfb_reserve_mem(void); diff --git a/include/asm-arm/arch-omap/sram.h b/include/asm-arm/arch-omap/sram.h index 6fc0dd57b7c..cc6161d6398 100644 --- a/include/asm-arm/arch-omap/sram.h +++ b/include/asm-arm/arch-omap/sram.h @@ -20,8 +20,8 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, u32 mem_type); extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); -extern unsigned long omap_fb_sram_start; -extern unsigned long omap_fb_sram_size; +extern int omap_fb_sram_plane; +extern int omap_fb_sram_valid; /* Do not use these */ extern void sram_reprogram_clock(u32 ckctl, u32 dpllctl); diff --git a/include/asm-arm/arch-omap/usb.h b/include/asm-arm/arch-omap/usb.h index 054fb9a8e0c..99ae9eabaf7 100644 --- a/include/asm-arm/arch-omap/usb.h +++ b/include/asm-arm/arch-omap/usb.h @@ -7,9 +7,27 @@ /*-------------------------------------------------------------------------*/ -#define OTG_BASE 0xfffb0400 -#define UDC_BASE 0xfffb4000 -#define OMAP_OHCI_BASE 0xfffba000 +#define OMAP1_OTG_BASE 0xfffb0400 +#define OMAP1_UDC_BASE 0xfffb4000 +#define OMAP1_OHCI_BASE 0xfffba000 + +#define OMAP2_OHCI_BASE 0x4805e000 +#define OMAP2_UDC_BASE 0x4805e200 +#define OMAP2_OTG_BASE 0x4805e300 + +#ifdef CONFIG_ARCH_OMAP1 + +#define OTG_BASE OMAP1_OTG_BASE +#define UDC_BASE OMAP1_UDC_BASE +#define OMAP_OHCI_BASE OMAP1_OHCI_BASE + +#else + +#define OTG_BASE OMAP2_OTG_BASE +#define UDC_BASE OMAP2_UDC_BASE +#define OMAP_OHCI_BASE OMAP2_OHCI_BASE + +#endif /*-------------------------------------------------------------------------*/ @@ -28,6 +46,7 @@ # define HST_IDLE_EN (1 << 14) # define DEV_IDLE_EN (1 << 13) # define OTG_RESET_DONE (1 << 2) +# define OTG_SOFT_RESET (1 << 1) #define OTG_SYSCON_2_REG OTG_REG32(0x08) # define OTG_EN (1 << 31) # define USBX_SYNCHRO (1 << 30) @@ -103,6 +122,7 @@ /*-------------------------------------------------------------------------*/ +/* OMAP1 */ #define USB_TRANSCEIVER_CTRL_REG __REG32(0xfffe1000 + 0x0064) # define CONF_USB2_UNI_R (1 << 8) # define CONF_USB1_UNI_R (1 << 7) @@ -111,7 +131,17 @@ # define CONF_USB_PWRDN_DM_R (1 << 2) # define CONF_USB_PWRDN_DP_R (1 << 1) - - +/* OMAP2 */ +#define CONTROL_DEVCONF_REG __REG32(L4_24XX_BASE + 0x0274) +# define USB_UNIDIR 0x0 +# define USB_UNIDIR_TLL 0x1 +# define USB_BIDIR 0x2 +# define USB_BIDIR_TLL 0x3 +# define USBT0WRMODEI(x) ((x) << 22) +# define USBT1WRMODEI(x) ((x) << 20) +# define USBT2WRMODEI(x) ((x) << 18) +# define USBT2TLL5PI (1 << 17) +# define USB0PUENACTLOI (1 << 16) +# define USBSTANDBYCTRL (1 << 15) #endif /* __ASM_ARCH_OMAP_USB_H */ -- cgit v1.2.3 From c40fae9525e6c29c87a4f4361ff0a8d67a36e448 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 7 Dec 2006 13:58:10 -0800 Subject: ARM: OMAP: Sync core code with linux-omap This patch syncs omap specific core code with linux-omap. Most of the changes are needed to fix bitrot caused by driver updates in linux-omap tree. Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap1/Kconfig | 3 + arch/arm/mach-omap1/Makefile | 1 - arch/arm/mach-omap1/devices.c | 71 ++++++++------- arch/arm/mach-omap2/Kconfig | 1 + arch/arm/mach-omap2/devices.c | 60 +++++++++++-- arch/arm/mach-omap2/gpmc.c | 12 ++- arch/arm/mach-omap2/io.c | 18 +++- arch/arm/plat-omap/Kconfig | 8 ++ arch/arm/plat-omap/Makefile | 3 +- arch/arm/plat-omap/common.c | 8 +- arch/arm/plat-omap/devices.c | 70 ++++++++++++++- arch/arm/plat-omap/dmtimer.c | 2 +- arch/arm/plat-omap/fb.c | 72 +++++++++++---- arch/arm/plat-omap/sram.c | 77 +++++++++------- arch/arm/plat-omap/usb.c | 199 +++++++++++++++++++++++++++++++++--------- 15 files changed, 470 insertions(+), 135 deletions(-) diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 8781aaeb576..18ea7313a49 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -22,6 +22,7 @@ comment "OMAP Board Type" config MACH_OMAP_INNOVATOR bool "TI Innovator" depends on ARCH_OMAP1 && (ARCH_OMAP15XX || ARCH_OMAP16XX) + select OMAP_MCBSP help TI OMAP 1510 or 1610 Innovator board support. Say Y here if you have such a board. @@ -29,6 +30,7 @@ config MACH_OMAP_INNOVATOR config MACH_OMAP_H2 bool "TI H2 Support" depends on ARCH_OMAP1 && ARCH_OMAP16XX + select OMAP_MCBSP help TI OMAP 1610/1611B H2 board support. Say Y here if you have such a board. @@ -36,6 +38,7 @@ config MACH_OMAP_H2 config MACH_OMAP_H3 bool "TI H3 Support" depends on ARCH_OMAP1 && ARCH_OMAP16XX + select GPIOEXPANDER_OMAP help TI OMAP 1710 H3 board support. Say Y here if you have such a board. diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 7165f74f78d..a8b9a00cea2 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -37,4 +37,3 @@ led-$(CONFIG_MACH_OMAP_INNOVATOR) += leds-innovator.o led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-h2p2-debug.o led-$(CONFIG_MACH_OMAP_OSK) += leds-osk.o obj-$(CONFIG_LEDS) += $(led-y) - diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index 6dcd10ab449..da8a3ac47e1 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -24,35 +24,6 @@ #include #include -#if defined(CONFIG_OMAP1610_IR) || defined(CONFIG_OMAP161O_IR_MODULE) - -static u64 irda_dmamask = 0xffffffff; - -static struct platform_device omap1610ir_device = { - .name = "omap1610-ir", - .id = -1, - .dev = { - .dma_mask = &irda_dmamask, - }, -}; - -static void omap_init_irda(void) -{ - /* FIXME define and use a boot tag, members something like: - * u8 uart; // uart1, or uart3 - * ... but driver only handles uart3 for now - * s16 fir_sel; // gpio for SIR vs FIR - * ... may prefer a callback for SIR/MIR/FIR mode select; - * while h2 uses a GPIO, H3 uses a gpio expander - */ - if (machine_is_omap_h2() - || machine_is_omap_h3()) - (void) platform_device_register(&omap1610ir_device); -} -#else -static inline void omap_init_irda(void) {} -#endif - /*-------------------------------------------------------------------------*/ #if defined(CONFIG_RTC_DRV_OMAP) || defined(CONFIG_RTC_DRV_OMAP_MODULE) @@ -90,6 +61,45 @@ static void omap_init_rtc(void) static inline void omap_init_rtc(void) {} #endif +#if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE) + +#if defined(CONFIG_ARCH_OMAP15XX) +# define OMAP1_MBOX_SIZE 0x23 +# define INT_DSP_MAILBOX1 INT_1510_DSP_MAILBOX1 +#elif defined(CONFIG_ARCH_OMAP16XX) +# define OMAP1_MBOX_SIZE 0x2f +# define INT_DSP_MAILBOX1 INT_1610_DSP_MAILBOX1 +#endif + +#define OMAP1_MBOX_BASE IO_ADDRESS(OMAP16XX_MAILBOX_BASE) + +static struct resource mbox_resources[] = { + { + .start = OMAP1_MBOX_BASE, + .end = OMAP1_MBOX_BASE + OMAP1_MBOX_SIZE, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_DSP_MAILBOX1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mbox_device = { + .name = "mailbox", + .id = -1, + .num_resources = ARRAY_SIZE(mbox_resources), + .resource = mbox_resources, +}; + +static inline void omap_init_mbox(void) +{ + platform_device_register(&mbox_device); +} +#else +static inline void omap_init_mbox(void) { } +#endif + #if defined(CONFIG_OMAP_STI) #define OMAP1_STI_BASE IO_ADDRESS(0xfffea000) @@ -154,7 +164,8 @@ static int __init omap1_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ - omap_init_irda(); + + omap_init_mbox(); omap_init_rtc(); omap_init_sti(); diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 2a1aca7a637..7393109f5c3 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -9,6 +9,7 @@ config ARCH_OMAP2420 bool "OMAP2420 support" depends on ARCH_OMAP24XX select OMAP_DM_TIMER + select ARCH_OMAP_OTG comment "OMAP Board Type" depends on ARCH_OMAP2 diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index aa4322451e8..b3ac9622f4a 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -66,6 +66,40 @@ static void omap_init_i2c(void) {} #endif +#if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE) +#define OMAP2_MBOX_BASE IO_ADDRESS(OMAP24XX_MAILBOX_BASE) + +static struct resource mbox_resources[] = { + { + .start = OMAP2_MBOX_BASE, + .end = OMAP2_MBOX_BASE + 0x11f, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_24XX_MAIL_U0_MPU, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_24XX_MAIL_U3_MPU, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mbox_device = { + .name = "mailbox", + .id = -1, + .num_resources = ARRAY_SIZE(mbox_resources), + .resource = mbox_resources, +}; + +static inline void omap_init_mbox(void) +{ + platform_device_register(&mbox_device); +} +#else +static inline void omap_init_mbox(void) { } +#endif + #if defined(CONFIG_OMAP_STI) #define OMAP2_STI_BASE IO_ADDRESS(0x48068000) @@ -111,29 +145,45 @@ static inline void omap_init_sti(void) {} #define OMAP2_MCSPI1_BASE 0x48098000 #define OMAP2_MCSPI2_BASE 0x4809a000 -/* FIXME: use resources instead */ - static struct omap2_mcspi_platform_config omap2_mcspi1_config = { - .base = io_p2v(OMAP2_MCSPI1_BASE), .num_cs = 4, }; +static struct resource omap2_mcspi1_resources[] = { + { + .start = OMAP2_MCSPI1_BASE, + .end = OMAP2_MCSPI1_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, +}; + struct platform_device omap2_mcspi1 = { .name = "omap2_mcspi", .id = 1, + .num_resources = ARRAY_SIZE(omap2_mcspi1_resources), + .resource = omap2_mcspi1_resources, .dev = { .platform_data = &omap2_mcspi1_config, }, }; static struct omap2_mcspi_platform_config omap2_mcspi2_config = { - .base = io_p2v(OMAP2_MCSPI2_BASE), .num_cs = 2, }; +static struct resource omap2_mcspi2_resources[] = { + { + .start = OMAP2_MCSPI2_BASE, + .end = OMAP2_MCSPI2_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, +}; + struct platform_device omap2_mcspi2 = { .name = "omap2_mcspi", .id = 2, + .num_resources = ARRAY_SIZE(omap2_mcspi2_resources), + .resource = omap2_mcspi2_resources, .dev = { .platform_data = &omap2_mcspi2_config, }, @@ -157,10 +207,10 @@ static int __init omap2_init_devices(void) * in alphabetical order so they're easier to sort through. */ omap_init_i2c(); + omap_init_mbox(); omap_init_mcspi(); omap_init_sti(); return 0; } arch_initcall(omap2_init_devices); - diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index d8f57824423..54c836a9845 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -246,14 +246,22 @@ static int gpmc_cs_mem_enabled(int cs) return l & (1 << 6); } -static void gpmc_cs_set_reserved(int cs, int reserved) +int gpmc_cs_set_reserved(int cs, int reserved) { + if (cs > GPMC_CS_NUM) + return -ENODEV; + gpmc_cs_map &= ~(1 << cs); gpmc_cs_map |= (reserved ? 1 : 0) << cs; + + return 0; } -static int gpmc_cs_reserved(int cs) +int gpmc_cs_reserved(int cs) { + if (cs > GPMC_CS_NUM) + return -ENODEV; + return gpmc_cs_map & (1 << cs); } diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index a0728c33e5d..59458642e9e 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -40,9 +40,21 @@ static struct map_desc omap2_io_desc[] __initdata = { .type = MT_DEVICE }, { - .virtual = L4_24XX_VIRT, - .pfn = __phys_to_pfn(L4_24XX_PHYS), - .length = L4_24XX_SIZE, + .virtual = DSP_MEM_24XX_VIRT, + .pfn = __phys_to_pfn(DSP_MEM_24XX_PHYS), + .length = DSP_MEM_24XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = DSP_IPI_24XX_VIRT, + .pfn = __phys_to_pfn(DSP_IPI_24XX_PHYS), + .length = DSP_IPI_24XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = DSP_MMU_24XX_VIRT, + .pfn = __phys_to_pfn(DSP_MMU_24XX_PHYS), + .length = DSP_MMU_24XX_SIZE, .type = MT_DEVICE } }; diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index ce8bc3304e1..a347fb5946c 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -62,6 +62,14 @@ config OMAP_MUX_WARNINGS to change the pin multiplexing setup. When there are no warnings printed, it's safe to deselect OMAP_MUX for your product. +config OMAP_MCBSP + bool "McBSP support" + depends on ARCH_OMAP + default y + help + Say Y here if you want support for the OMAP Multichannel + Buffered Serial Port. + choice prompt "System timer" default OMAP_MPU_TIMER diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 4ce277d3485..41a3c1cf3bd 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -3,7 +3,8 @@ # # Common support -obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o fb.o +obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o \ + usb.o fb.o obj-m := obj-n := obj- := diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c index 57b7b93674a..ffb94aa164a 100644 --- a/arch/arm/plat-omap/common.c +++ b/arch/arm/plat-omap/common.c @@ -93,8 +93,12 @@ static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) * in the kernel. */ for (i = 0; i < omap_board_config_size; i++) { if (omap_board_config[i].tag == tag) { - kinfo = &omap_board_config[i]; - break; + if (skip == 0) { + kinfo = &omap_board_config[i]; + break; + } else { + skip--; + } } } if (kinfo == NULL) diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index eeb33fed6f7..2fac7d3f2af 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -25,7 +25,71 @@ #include #include -#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) +#if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE) + +#include "../plat-omap/dsp/dsp_common.h" + +static struct dsp_platform_data dsp_pdata = { + .kdev_list = LIST_HEAD_INIT(dsp_pdata.kdev_list), +}; + +static struct resource omap_dsp_resources[] = { + { + .name = "dsp_mmu", + .start = -1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device omap_dsp_device = { + .name = "dsp", + .id = -1, + .num_resources = ARRAY_SIZE(omap_dsp_resources), + .resource = omap_dsp_resources, + .dev = { + .platform_data = &dsp_pdata, + }, +}; + +static inline void omap_init_dsp(void) +{ + struct resource *res; + int irq; + + if (cpu_is_omap15xx()) + irq = INT_1510_DSP_MMU; + else if (cpu_is_omap16xx()) + irq = INT_1610_DSP_MMU; + else if (cpu_is_omap24xx()) + irq = INT_24XX_DSP_MMU; + + res = platform_get_resource_byname(&omap_dsp_device, + IORESOURCE_IRQ, "dsp_mmu"); + res->start = irq; + + platform_device_register(&omap_dsp_device); +} + +int dsp_kfunc_device_register(struct dsp_kfunc_device *kdev) +{ + static DEFINE_MUTEX(dsp_pdata_lock); + + mutex_init(&kdev->lock); + + mutex_lock(&dsp_pdata_lock); + list_add_tail(&kdev->entry, &dsp_pdata.kdev_list); + mutex_unlock(&dsp_pdata_lock); + + return 0; +} +EXPORT_SYMBOL(dsp_kfunc_device_register); + +#else +static inline void omap_init_dsp(void) { } +#endif /* CONFIG_OMAP_DSP */ + +/*-------------------------------------------------------------------------*/ +#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) #define OMAP1_I2C_BASE 0xfffb3800 #define OMAP2_I2C_BASE1 0x48070000 @@ -376,7 +440,7 @@ static inline void omap_init_wdt(void) {} /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_OMAP_RNG) || defined(CONFIG_OMAP_RNG_MODULE) +#if defined(CONFIG_HW_RANDOM_OMAP) || defined(CONFIG_HW_RANDOM_OMAP_MODULE) #ifdef CONFIG_ARCH_OMAP24XX #define OMAP_RNG_BASE 0x480A0000 @@ -436,6 +500,7 @@ static int __init omap_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ + omap_init_dsp(); omap_init_i2c(); omap_init_kp(); omap_init_mmc(); @@ -446,4 +511,3 @@ static int __init omap_init_devices(void) return 0; } arch_initcall(omap_init_devices); - diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 659619f235c..36073dfaa4d 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -372,7 +372,7 @@ void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) /* When the functional clock disappears, too quick writes seem to * cause an abort. */ - __delay(15000); + __delay(150000); } #endif diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 91ebdafcca3..a302d9194f5 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -1,3 +1,26 @@ +/* + * File: arch/arm/plat-omap/fb.c + * + * Framebuffer device registration for TI OMAP platforms + * + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + #include #include #include @@ -34,25 +57,42 @@ static struct platform_device omap_fb_device = { void omapfb_reserve_mem(void) { const struct omap_fbmem_config *fbmem_conf; + unsigned long total_size; + int i; + + if (!omap_fb_sram_valid) { + /* FBMEM SRAM configuration was already found to be invalid. + * Ignore the whole configuration block. */ + omapfb_config.mem_desc.region_cnt = 0; + return; + } + + i = 0; + total_size = 0; + while ((fbmem_conf = omap_get_nr_config(OMAP_TAG_FBMEM, + struct omap_fbmem_config, i)) != NULL) { + unsigned long start; + unsigned long size; - omapfb_config.fbmem.fb_sram_start = omap_fb_sram_start; - omapfb_config.fbmem.fb_sram_size = omap_fb_sram_size; - - fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); - - if (fbmem_conf != NULL) { - /* indicate that the bootloader already initialized the - * fb device, so we'll skip that part in the fb driver - */ - omapfb_config.fbmem.fb_sdram_start = fbmem_conf->fb_sdram_start; - omapfb_config.fbmem.fb_sdram_size = fbmem_conf->fb_sdram_size; - if (fbmem_conf->fb_sdram_size) { - pr_info("Reserving %u bytes SDRAM for frame buffer\n", - fbmem_conf->fb_sdram_size); - reserve_bootmem(fbmem_conf->fb_sdram_start, - fbmem_conf->fb_sdram_size); + if (i == OMAPFB_PLANE_NUM) { + printk(KERN_ERR "ignoring extra plane info\n"); + break; } + start = fbmem_conf->start; + size = fbmem_conf->size; + omapfb_config.mem_desc.region[i].paddr = start; + omapfb_config.mem_desc.region[i].size = size; + if (omap_fb_sram_plane != i && start) { + reserve_bootmem(start, size); + total_size += size; + } + i++; } + omapfb_config.mem_desc.region_cnt = i; + if (total_size) + pr_info("Reserving %lu bytes SDRAM for frame buffer\n", + total_size); + } void omapfb_set_ctrl_platform_data(void *data) diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index 19014b2ff4c..7e5f8877e05 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -46,12 +46,13 @@ #define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) +static unsigned long omap_sram_start; static unsigned long omap_sram_base; static unsigned long omap_sram_size; static unsigned long omap_sram_ceil; -unsigned long omap_fb_sram_start; -unsigned long omap_fb_sram_size; +int omap_fb_sram_plane = -1; +int omap_fb_sram_valid; /* Depending on the target RAMFS firewall setup, the public usable amount of * SRAM varies. The default accessable size for all device types is 2k. A GP @@ -77,30 +78,43 @@ static int is_sram_locked(void) return 1; /* assume locked with no PPA or security driver */ } -void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, - unsigned long *start, unsigned long *size) +static int get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, + unsigned long *start, int *plane_idx) { const struct omap_fbmem_config *fbmem_conf; - - fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); - if (fbmem_conf != NULL) { - *start = fbmem_conf->fb_sram_start; - *size = fbmem_conf->fb_sram_size; - } else { - *size = 0; - *start = 0; + unsigned long size = 0; + int i; + + i = 0; + *start = 0; + *plane_idx = -1; + while ((fbmem_conf = omap_get_nr_config(OMAP_TAG_FBMEM, + struct omap_fbmem_config, i)) != NULL) { + u32 paddr, end; + + paddr = fbmem_conf->start; + end = fbmem_conf->start + fbmem_conf->size; + if (paddr > omap_sram_start && + paddr < omap_sram_start + omap_sram_size) { + if (*plane_idx != -1 || paddr < start_avail || + paddr == end || + end > start_avail + size_avail) { + printk(KERN_ERR "invalid FB SRAM configuration"); + *start = 0; + return -1; + } + *plane_idx = i; + *start = fbmem_conf->start; + size = fbmem_conf->size; + } + i++; } - if (*size && ( - *start < start_avail || - *start + *size > start_avail + size_avail)) { - printk(KERN_ERR "invalid FB SRAM configuration\n"); - *start = start_avail; - *size = size_avail; - } + if (*plane_idx >= 0) + pr_info("Reserving %lu bytes SRAM frame buffer " + "for plane %d\n", size, *plane_idx); - if (*size) - pr_info("Reserving %lu bytes SRAM for frame buffer\n", *size); + return 0; } /* @@ -111,16 +125,16 @@ void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, */ void __init omap_detect_sram(void) { - unsigned long sram_start; + unsigned long fb_sram_start; if (cpu_is_omap24xx()) { if (is_sram_locked()) { omap_sram_base = OMAP2_SRAM_PUB_VA; - sram_start = OMAP2_SRAM_PUB_PA; + omap_sram_start = OMAP2_SRAM_PUB_PA; omap_sram_size = 0x800; /* 2K */ } else { omap_sram_base = OMAP2_SRAM_VA; - sram_start = OMAP2_SRAM_PA; + omap_sram_start = OMAP2_SRAM_PA; if (cpu_is_omap242x()) omap_sram_size = 0xa0000; /* 640K */ else if (cpu_is_omap243x()) @@ -128,7 +142,7 @@ void __init omap_detect_sram(void) } } else { omap_sram_base = OMAP1_SRAM_VA; - sram_start = OMAP1_SRAM_PA; + omap_sram_start = OMAP1_SRAM_PA; if (cpu_is_omap730()) omap_sram_size = 0x32000; /* 200K */ @@ -144,12 +158,13 @@ void __init omap_detect_sram(void) omap_sram_size = 0x4000; } } - get_fb_sram_conf(sram_start + SRAM_BOOTLOADER_SZ, - omap_sram_size - SRAM_BOOTLOADER_SZ, - &omap_fb_sram_start, &omap_fb_sram_size); - if (omap_fb_sram_size) - omap_sram_size -= sram_start + omap_sram_size - - omap_fb_sram_start; + if (get_fb_sram_conf(omap_sram_start + SRAM_BOOTLOADER_SZ, + omap_sram_size - SRAM_BOOTLOADER_SZ, + &fb_sram_start, &omap_fb_sram_plane) == 0) + omap_fb_sram_valid = 1; + if (omap_fb_sram_valid && omap_fb_sram_plane >= 0) + omap_sram_size -= omap_sram_start + omap_sram_size - + fb_sram_start; omap_sram_ceil = omap_sram_base + omap_sram_size; } diff --git a/arch/arm/plat-omap/usb.c b/arch/arm/plat-omap/usb.c index 7e8096809be..25489aafb11 100644 --- a/arch/arm/plat-omap/usb.c +++ b/arch/arm/plat-omap/usb.c @@ -37,9 +37,27 @@ #include #include +#ifdef CONFIG_ARCH_OMAP1 + +#define INT_USB_IRQ_GEN IH2_BASE + 20 +#define INT_USB_IRQ_NISO IH2_BASE + 30 +#define INT_USB_IRQ_ISO IH2_BASE + 29 +#define INT_USB_IRQ_HGEN INT_USB_HHC_1 +#define INT_USB_IRQ_OTG IH2_BASE + 8 + +#else + +#define INT_USB_IRQ_GEN INT_24XX_USB_IRQ_GEN +#define INT_USB_IRQ_NISO INT_24XX_USB_IRQ_NISO +#define INT_USB_IRQ_ISO INT_24XX_USB_IRQ_ISO +#define INT_USB_IRQ_HGEN INT_24XX_USB_IRQ_HGEN +#define INT_USB_IRQ_OTG INT_24XX_USB_IRQ_OTG + +#endif + + /* These routines should handle the standard chip-specific modes * for usb0/1/2 ports, covering basic mux and transceiver setup. - * Call omap_usb_init() once, from INIT_MACHINE(). * * Some board-*.c files will need to set up additional mux options, * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. @@ -96,19 +114,26 @@ static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) { u32 syscon1 = 0; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG &= ~USBT0WRMODEI(USB_BIDIR_TLL); + if (nwires == 0) { - if (!cpu_is_omap15xx()) { + if (cpu_class_is_omap1() && !cpu_is_omap15xx()) { /* pulldown D+/D- */ USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); } return 0; } - if (is_device) - omap_cfg_reg(W4_USB_PUEN); + if (is_device) { + if (cpu_is_omap24xx()) + omap_cfg_reg(J20_24XX_USB0_PUEN); + else + omap_cfg_reg(W4_USB_PUEN); + } - /* internal transceiver */ - if (nwires == 2) { + /* internal transceiver (unavailable on 17xx, 24xx) */ + if (!cpu_class_is_omap2() && nwires == 2) { // omap_cfg_reg(P9_USB_DP); // omap_cfg_reg(R8_USB_DM); @@ -136,29 +161,50 @@ static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) return 0; } - omap_cfg_reg(V6_USB0_TXD); - omap_cfg_reg(W9_USB0_TXEN); - omap_cfg_reg(W5_USB0_SE0); + if (cpu_is_omap24xx()) { + omap_cfg_reg(K18_24XX_USB0_DAT); + omap_cfg_reg(K19_24XX_USB0_TXEN); + omap_cfg_reg(J14_24XX_USB0_SE0); + if (nwires != 3) + omap_cfg_reg(J18_24XX_USB0_RCV); + } else { + omap_cfg_reg(V6_USB0_TXD); + omap_cfg_reg(W9_USB0_TXEN); + omap_cfg_reg(W5_USB0_SE0); + if (nwires != 3) + omap_cfg_reg(Y5_USB0_RCV); + } - /* NOTE: SPEED and SUSP aren't configured here */ + /* NOTE: SPEED and SUSP aren't configured here. OTG hosts + * may be able to use I2C requests to set those bits along + * with VBUS switching and overcurrent detction. + */ - if (nwires != 3) - omap_cfg_reg(Y5_USB0_RCV); - if (nwires != 6) + if (cpu_class_is_omap1() && nwires != 6) USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; switch (nwires) { case 3: syscon1 = 2; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT0WRMODEI(USB_BIDIR); break; case 4: syscon1 = 1; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT0WRMODEI(USB_BIDIR); break; case 6: syscon1 = 3; - omap_cfg_reg(AA9_USB0_VP); - omap_cfg_reg(R9_USB0_VM); - USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + if (cpu_is_omap24xx()) { + omap_cfg_reg(J19_24XX_USB0_VP); + omap_cfg_reg(K20_24XX_USB0_VM); + CONTROL_DEVCONF_REG |= USBT0WRMODEI(USB_UNIDIR); + } else { + omap_cfg_reg(AA9_USB0_VP); + omap_cfg_reg(R9_USB0_VM); + USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + } break; default: printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", @@ -171,14 +217,22 @@ static u32 __init omap_usb1_init(unsigned nwires) { u32 syscon1 = 0; - if (nwires != 6 && !cpu_is_omap15xx()) + if (cpu_class_is_omap1() && !cpu_is_omap15xx() && nwires != 6) USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG &= ~USBT1WRMODEI(USB_BIDIR_TLL); + if (nwires == 0) return 0; /* external transceiver */ - omap_cfg_reg(USB1_TXD); - omap_cfg_reg(USB1_TXEN); + if (cpu_class_is_omap1()) { + omap_cfg_reg(USB1_TXD); + omap_cfg_reg(USB1_TXEN); + if (nwires != 3) + omap_cfg_reg(USB1_RCV); + } + if (cpu_is_omap15xx()) { omap_cfg_reg(USB1_SEO); omap_cfg_reg(USB1_SPEED); @@ -190,20 +244,38 @@ static u32 __init omap_usb1_init(unsigned nwires) } else if (cpu_is_omap1710()) { omap_cfg_reg(R13_1710_USB1_SE0); // SUSP + } else if (cpu_is_omap24xx()) { + /* NOTE: board-specific code must set up pin muxing for usb1, + * since each signal could come out on either of two balls. + */ } else { - pr_debug("usb unrecognized\n"); + pr_debug("usb%d cpu unrecognized\n", 1); + return 0; } - if (nwires != 3) - omap_cfg_reg(USB1_RCV); switch (nwires) { + case 2: + if (!cpu_is_omap24xx()) + goto bad; + /* NOTE: board-specific code must override this setting if + * this TLL link is not using DP/DM + */ + syscon1 = 1; + CONTROL_DEVCONF_REG |= USBT1WRMODEI(USB_BIDIR_TLL); + break; case 3: syscon1 = 2; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT1WRMODEI(USB_BIDIR); break; case 4: syscon1 = 1; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT1WRMODEI(USB_BIDIR); break; case 6: + if (cpu_is_omap24xx()) + goto bad; syscon1 = 3; omap_cfg_reg(USB1_VP); omap_cfg_reg(USB1_VM); @@ -211,6 +283,7 @@ static u32 __init omap_usb1_init(unsigned nwires) USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; break; default: +bad: printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", 1, nwires); } @@ -221,10 +294,17 @@ static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) { u32 syscon1 = 0; - /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ + if (cpu_is_omap24xx()) { + CONTROL_DEVCONF_REG &= ~(USBT2WRMODEI(USB_BIDIR_TLL) + | USBT2TLL5PI); + alt_pingroup = 0; + } + + /* NOTE omap1 erratum: must leave USB2_UNI_R set if usb0 in use */ if (alt_pingroup || nwires == 0) return 0; - if (nwires != 6 && !cpu_is_omap15xx()) + + if (cpu_class_is_omap1() && !cpu_is_omap15xx() && nwires != 6) USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; /* external transceiver */ @@ -242,19 +322,54 @@ static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) if (nwires != 3) omap_cfg_reg(Y5_USB2_RCV); // FIXME omap_cfg_reg(USB2_SPEED); + } else if (cpu_is_omap24xx()) { + omap_cfg_reg(Y11_24XX_USB2_DAT); + omap_cfg_reg(AA10_24XX_USB2_SE0); + if (nwires > 2) + omap_cfg_reg(AA12_24XX_USB2_TXEN); + if (nwires > 3) + omap_cfg_reg(AA6_24XX_USB2_RCV); } else { - pr_debug("usb unrecognized\n"); + pr_debug("usb%d cpu unrecognized\n", 1); + return 0; } - // omap_cfg_reg(USB2_SUSP); + // if (cpu_class_is_omap1()) omap_cfg_reg(USB2_SUSP); switch (nwires) { + case 2: + if (!cpu_is_omap24xx()) + goto bad; + /* NOTE: board-specific code must override this setting if + * this TLL link is not using DP/DM + */ + syscon1 = 1; + CONTROL_DEVCONF_REG |= USBT2WRMODEI(USB_BIDIR_TLL); + break; case 3: syscon1 = 2; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT2WRMODEI(USB_BIDIR); break; case 4: syscon1 = 1; + if (cpu_is_omap24xx()) + CONTROL_DEVCONF_REG |= USBT2WRMODEI(USB_BIDIR); + break; + case 5: + if (!cpu_is_omap24xx()) + goto bad; + omap_cfg_reg(AA4_24XX_USB2_TLLSE0); + /* NOTE: board-specific code must override this setting if + * this TLL link is not using DP/DM. Something must also + * set up OTG_SYSCON2.HMC_TLL{ATTACH,SPEED} + */ + syscon1 = 3; + CONTROL_DEVCONF_REG |= USBT2WRMODEI(USB_UNIDIR_TLL) + | USBT2TLL5PI; break; case 6: + if (cpu_is_omap24xx()) + goto bad; syscon1 = 3; if (cpu_is_omap15xx()) { omap_cfg_reg(USB2_VP); @@ -266,6 +381,7 @@ static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) } break; default: +bad: printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", 2, nwires); } @@ -294,13 +410,13 @@ static struct resource udc_resources[] = { .end = UDC_BASE + 0xff, .flags = IORESOURCE_MEM, }, { /* general IRQ */ - .start = IH2_BASE + 20, + .start = INT_USB_IRQ_GEN, .flags = IORESOURCE_IRQ, }, { /* PIO IRQ */ - .start = IH2_BASE + 30, + .start = INT_USB_IRQ_NISO, .flags = IORESOURCE_IRQ, }, { /* SOF IRQ */ - .start = IH2_BASE + 29, + .start = INT_USB_IRQ_ISO, .flags = IORESOURCE_IRQ, }, }; @@ -329,11 +445,11 @@ static u64 ohci_dmamask = ~(u32)0; static struct resource ohci_resources[] = { { .start = OMAP_OHCI_BASE, - .end = OMAP_OHCI_BASE + 4096 - 1, + .end = OMAP_OHCI_BASE + 0xff, .flags = IORESOURCE_MEM, }, { - .start = INT_USB_HHC_1, + .start = INT_USB_IRQ_HGEN, .flags = IORESOURCE_IRQ, }, }; @@ -361,7 +477,7 @@ static struct resource otg_resources[] = { .end = OTG_BASE + 0xff, .flags = IORESOURCE_MEM, }, { - .start = IH2_BASE + 8, + .start = INT_USB_IRQ_OTG, .flags = IORESOURCE_IRQ, }, }; @@ -385,7 +501,7 @@ static struct platform_device otg_device = { // FIXME correct answer depends on hmc_mode, -// as does any nonzero value for config->otg port number +// as does (on omap1) any nonzero value for config->otg port number #ifdef CONFIG_USB_GADGET_OMAP #define is_usb0_device(config) 1 #else @@ -426,12 +542,13 @@ omap_otg_init(struct omap_usb_config *config) if (config->otg) syscon |= OTG_EN; #endif - pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); + if (cpu_class_is_omap1()) + pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); OTG_SYSCON_2_REG = syscon; printk("USB: hmc %d", config->hmc_mode); - if (alt_pingroup) + if (!alt_pingroup) printk(", usb2 alt %d wires", config->pins[2]); else if (config->pins[0]) printk(", usb0 %d wires%s", config->pins[0], @@ -444,10 +561,12 @@ omap_otg_init(struct omap_usb_config *config) printk(", Mini-AB on usb%d", config->otg - 1); printk("\n"); - /* leave USB clocks/controllers off until needed */ - ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; - ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; - ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; + if (cpu_class_is_omap1()) { + /* leave USB clocks/controllers off until needed */ + ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; + ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; + ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; + } syscon = OTG_SYSCON_1_REG; syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; @@ -585,7 +704,7 @@ omap_usb_init(void) } platform_data = *config; - if (cpu_is_omap730() || cpu_is_omap16xx()) + if (cpu_is_omap730() || cpu_is_omap16xx() || cpu_is_omap24xx()) omap_otg_init(&platform_data); else if (cpu_is_omap15xx()) omap_1510_usb_init(&platform_data); -- cgit v1.2.3 From 8eef3896b352a8760ab04293174444da4e8c8127 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Mon, 7 May 2007 15:17:47 +0100 Subject: [ARM] 4361/1: AT91: Build error Fix a build error due to a missing semicolon. Signed-off-by: Andrew Victor Signed-off-by: Russell King --- include/asm-arm/arch-at91/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/asm-arm/arch-at91/board.h b/include/asm-arm/arch-at91/board.h index 7a34a5b1fed..0ce6ee98ed0 100644 --- a/include/asm-arm/arch-at91/board.h +++ b/include/asm-arm/arch-at91/board.h @@ -121,7 +121,7 @@ extern void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data); /* AC97 */ struct atmel_ac97_data { u8 reset_pin; /* reset */ -} +}; extern void __init at91_add_device_ac97(struct atmel_ac97_data *data); /* LEDs */ -- cgit v1.2.3 From b85fe92766dfc0c4e803cb0cead4c780cd0c02f9 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Tue, 8 May 2007 07:51:23 +0100 Subject: [ARM] 4363/1: AT91: Remove legacy PIO definitions Remove the legacy PIO pin definitions for the AT91 processors. The standard (and portable between the different AT91 processors) method is to use the AT91_PIN_* defines and the GPIO API. Signed-off-by: Andrew Victor Signed-off-by: Russell King --- include/asm-arm/arch-at91/at91rm9200.h | 181 ------------------------------ include/asm-arm/arch-at91/at91sam9260.h | 9 -- include/asm-arm/arch-at91/at91sam9261.h | 191 -------------------------------- include/asm-arm/arch-at91/at91sam9263.h | 8 -- 4 files changed, 389 deletions(-) diff --git a/include/asm-arm/arch-at91/at91rm9200.h b/include/asm-arm/arch-at91/at91rm9200.h index a12ac8ab2ad..802891a9cd8 100644 --- a/include/asm-arm/arch-at91/at91rm9200.h +++ b/include/asm-arm/arch-at91/at91rm9200.h @@ -107,185 +107,4 @@ #define AT91RM9200_UHP_BASE 0x00300000 /* USB Host controller */ -#if 0 -/* - * PIO pin definitions (peripheral A/B multiplexing). - */ -#define AT91_PA0_MISO (1 << 0) /* A: SPI Master-In Slave-Out */ -#define AT91_PA0_PCK3 (1 << 0) /* B: PMC Programmable Clock Output 3 */ -#define AT91_PA1_MOSI (1 << 1) /* A: SPI Master-Out Slave-In */ -#define AT91_PA1_PCK0 (1 << 1) /* B: PMC Programmable Clock Output 0 */ -#define AT91_PA2_SPCK (1 << 2) /* A: SPI Serial Clock */ -#define AT91_PA2_IRQ4 (1 << 2) /* B: External Interrupt 4 */ -#define AT91_PA3_NPCS0 (1 << 3) /* A: SPI Peripheral Chip Select 0 */ -#define AT91_PA3_IRQ5 (1 << 3) /* B: External Interrupt 5 */ -#define AT91_PA4_NPCS1 (1 << 4) /* A: SPI Peripheral Chip Select 1 */ -#define AT91_PA4_PCK1 (1 << 4) /* B: PMC Programmable Clock Output 1 */ -#define AT91_PA5_NPCS2 (1 << 5) /* A: SPI Peripheral Chip Select 2 */ -#define AT91_PA5_TXD3 (1 << 5) /* B: USART Transmit Data 3 */ -#define AT91_PA6_NPCS3 (1 << 6) /* A: SPI Peripheral Chip Select 3 */ -#define AT91_PA6_RXD3 (1 << 6) /* B: USART Receive Data 3 */ -#define AT91_PA7_ETXCK_EREFCK (1 << 7) /* A: Ethernet Reference Clock / Transmit Clock */ -#define AT91_PA7_PCK2 (1 << 7) /* B: PMC Programmable Clock Output 2 */ -#define AT91_PA8_ETXEN (1 << 8) /* A: Ethernet Transmit Enable */ -#define AT91_PA8_MCCDB (1 << 8) /* B: MMC Multimedia Card B Command */ -#define AT91_PA9_ETX0 (1 << 9) /* A: Ethernet Transmit Data 0 */ -#define AT91_PA9_MCDB0 (1 << 9) /* B: MMC Multimedia Card B Data 0 */ -#define AT91_PA10_ETX1 (1 << 10) /* A: Ethernet Transmit Data 1 */ -#define AT91_PA10_MCDB1 (1 << 10) /* B: MMC Multimedia Card B Data 1 */ -#define AT91_PA11_ECRS_ECRSDV (1 << 11) /* A: Ethernet Carrier Sense / Data Valid */ -#define AT91_PA11_MCDB2 (1 << 11) /* B: MMC Multimedia Card B Data 2 */ -#define AT91_PA12_ERX0 (1 << 12) /* A: Ethernet Receive Data 0 */ -#define AT91_PA12_MCDB3 (1 << 12) /* B: MMC Multimedia Card B Data 3 */ -#define AT91_PA13_ERX1 (1 << 13) /* A: Ethernet Receive Data 1 */ -#define AT91_PA13_TCLK0 (1 << 13) /* B: TC External Clock Input 0 */ -#define AT91_PA14_ERXER (1 << 14) /* A: Ethernet Receive Error */ -#define AT91_PA14_TCLK1 (1 << 14) /* B: TC External Clock Input 1 */ -#define AT91_PA15_EMDC (1 << 15) /* A: Ethernet Management Data Clock */ -#define AT91_PA15_TCLK2 (1 << 15) /* B: TC External Clock Input 2 */ -#define AT91_PA16_EMDIO (1 << 16) /* A: Ethernet Management Data I/O */ -#define AT91_PA16_IRQ6 (1 << 16) /* B: External Interrupt 6 */ -#define AT91_PA17_TXD0 (1 << 17) /* A: USART Transmit Data 0 */ -#define AT91_PA17_TIOA0 (1 << 17) /* B: TC I/O Line A 0 */ -#define AT91_PA18_RXD0 (1 << 18) /* A: USART Receive Data 0 */ -#define AT91_PA18_TIOB0 (1 << 18) /* B: TC I/O Line B 0 */ -#define AT91_PA19_SCK0 (1 << 19) /* A: USART Serial Clock 0 */ -#define AT91_PA19_TIOA1 (1 << 19) /* B: TC I/O Line A 1 */ -#define AT91_PA20_CTS0 (1 << 20) /* A: USART Clear To Send 0 */ -#define AT91_PA20_TIOB1 (1 << 20) /* B: TC I/O Line B 1 */ -#define AT91_PA21_RTS0 (1 << 21) /* A: USART Ready To Send 0 */ -#define AT91_PA21_TIOA2 (1 << 21) /* B: TC I/O Line A 2 */ -#define AT91_PA22_RXD2 (1 << 22) /* A: USART Receive Data 2 */ -#define AT91_PA22_TIOB2 (1 << 22) /* B: TC I/O Line B 2 */ -#define AT91_PA23_TXD2 (1 << 23) /* A: USART Transmit Data 2 */ -#define AT91_PA23_IRQ3 (1 << 23) /* B: External Interrupt 3 */ -#define AT91_PA24_SCK2 (1 << 24) /* A: USART Serial Clock 2 */ -#define AT91_PA24_PCK1 (1 << 24) /* B: PMC Programmable Clock Output 1 */ -#define AT91_PA25_TWD (1 << 25) /* A: TWI Two-wire Serial Data */ -#define AT91_PA25_IRQ2 (1 << 25) /* B: External Interrupt 2 */ -#define AT91_PA26_TWCK (1 << 26) /* A: TWI Two-wire Serial Clock */ -#define AT91_PA26_IRQ1 (1 << 26) /* B: External Interrupt 1 */ -#define AT91_PA27_MCCK (1 << 27) /* A: MMC Multimedia Card Clock */ -#define AT91_PA27_TCLK3 (1 << 27) /* B: TC External Clock Input 3 */ -#define AT91_PA28_MCCDA (1 << 28) /* A: MMC Multimedia Card A Command */ -#define AT91_PA28_TCLK4 (1 << 28) /* B: TC External Clock Input 4 */ -#define AT91_PA29_MCDA0 (1 << 29) /* A: MMC Multimedia Card A Data 0 */ -#define AT91_PA29_TCLK5 (1 << 29) /* B: TC External Clock Input 5 */ -#define AT91_PA30_DRXD (1 << 30) /* A: DBGU Receive Data */ -#define AT91_PA30_CTS2 (1 << 30) /* B: USART Clear To Send 2 */ -#define AT91_PA31_DTXD (1 << 31) /* A: DBGU Transmit Data */ -#define AT91_PA31_RTS2 (1 << 31) /* B: USART Ready To Send 2 */ - -#define AT91_PB0_TF0 (1 << 0) /* A: SSC Transmit Frame Sync 0 */ -#define AT91_PB0_RTS3 (1 << 0) /* B: USART Ready To Send 3 */ -#define AT91_PB1_TK0 (1 << 1) /* A: SSC Transmit Clock 0 */ -#define AT91_PB1_CTS3 (1 << 1) /* B: USART Clear To Send 3 */ -#define AT91_PB2_TD0 (1 << 2) /* A: SSC Transmit Data 0 */ -#define AT91_PB2_SCK3 (1 << 2) /* B: USART Serial Clock 3 */ -#define AT91_PB3_RD0 (1 << 3) /* A: SSC Receive Data 0 */ -#define AT91_PB3_MCDA1 (1 << 3) /* B: MMC Multimedia Card A Data 1 */ -#define AT91_PB4_RK0 (1 << 4) /* A: SSC Receive Clock 0 */ -#define AT91_PB4_MCDA2 (1 << 4) /* B: MMC Multimedia Card A Data 2 */ -#define AT91_PB5_RF0 (1 << 5) /* A: SSC Receive Frame Sync 0 */ -#define AT91_PB5_MCDA3 (1 << 5) /* B: MMC Multimedia Card A Data 3 */ -#define AT91_PB6_TF1 (1 << 6) /* A: SSC Transmit Frame Sync 1 */ -#define AT91_PB6_TIOA3 (1 << 6) /* B: TC I/O Line A 3 */ -#define AT91_PB7_TK1 (1 << 7) /* A: SSC Transmit Clock 1 */ -#define AT91_PB7_TIOB3 (1 << 7) /* B: TC I/O Line B 3 */ -#define AT91_PB8_TD1 (1 << 8) /* A: SSC Transmit Data 1 */ -#define AT91_PB8_TIOA4 (1 << 8) /* B: TC I/O Line A 4 */ -#define AT91_PB9_RD1 (1 << 9) /* A: SSC Receive Data 1 */ -#define AT91_PB9_TIOB4 (1 << 9) /* B: TC I/O Line B 4 */ -#define AT91_PB10_RK1 (1 << 10) /* A: SSC Receive Clock 1 */ -#define AT91_PB10_TIOA5 (1 << 10) /* B: TC I/O Line A 5 */ -#define AT91_PB11_RF1 (1 << 11) /* A: SSC Receive Frame Sync 1 */ -#define AT91_PB11_TIOB5 (1 << 11) /* B: TC I/O Line B 5 */ -#define AT91_PB12_TF2 (1 << 12) /* A: SSC Transmit Frame Sync 2 */ -#define AT91_PB12_ETX2 (1 << 12) /* B: Ethernet Transmit Data 2 */ -#define AT91_PB13_TK2 (1 << 13) /* A: SSC Transmit Clock 3 */ -#define AT91_PB13_ETX3 (1 << 13) /* B: Ethernet Transmit Data 3 */ -#define AT91_PB14_TD2 (1 << 14) /* A: SSC Transmit Data 2 */ -#define AT91_PB14_ETXER (1 << 14) /* B: Ethernet Transmit Coding Error */ -#define AT91_PB15_RD2 (1 << 15) /* A: SSC Receive Data 2 */ -#define AT91_PB15_ERX2 (1 << 15) /* B: Ethernet Receive Data 2 */ -#define AT91_PB16_RK2 (1 << 16) /* A: SSC Receive Clock 2 */ -#define AT91_PB16_ERX3 (1 << 16) /* B: Ethernet Receive Data 3 */ -#define AT91_PB17_RF2 (1 << 17) /* A: SSC Receive Frame Sync 2 */ -#define AT91_PB17_ERXDV (1 << 17) /* B: Ethernet Receive Data Valid */ -#define AT91_PB18_RI1 (1 << 18) /* A: USART Ring Indicator 1 */ -#define AT91_PB18_ECOL (1 << 18) /* B: Ethernet Collision Detected */ -#define AT91_PB19_DTR1 (1 << 19) /* A: USART Data Terminal Ready 1 */ -#define AT91_PB19_ERXCK (1 << 19) /* B: Ethernet Receive Clock */ -#define AT91_PB20_TXD1 (1 << 20) /* A: USART Transmit Data 1 */ -#define AT91_PB21_RXD1 (1 << 21) /* A: USART Receive Data 1 */ -#define AT91_PB22_SCK1 (1 << 22) /* A: USART Serial Clock 1 */ -#define AT91_PB23_DCD1 (1 << 23) /* A: USART Data Carrier Detect 1 */ -#define AT91_PB24_CTS1 (1 << 24) /* A: USART Clear To Send 1 */ -#define AT91_PB25_DSR1 (1 << 25) /* A: USART Data Set Ready 1 */ -#define AT91_PB25_EF100 (1 << 25) /* B: Ethernet Force 100 Mbit */ -#define AT91_PB26_RTS1 (1 << 26) /* A: USART Ready To Send 1 */ -#define AT91_PB27_PCK0 (1 << 27) /* B: PMC Programmable Clock Output 0 */ -#define AT91_PB28_FIQ (1 << 28) /* A: Fast Interrupt */ -#define AT91_PB29_IRQ0 (1 << 29) /* A: External Interrupt 0 */ - -#define AT91_PC0_BFCK (1 << 0) /* A: Burst Flash Clock */ -#define AT91_PC1_BFRDY_SMOE (1 << 1) /* A: Burst Flash Ready / SmartMedia Output Enable */ -#define AT91_PC2_BFAVD (1 << 2) /* A: Burst Flash Address Valid */ -#define AT91_PC3_BFBAA_SMWE (1 << 3) /* A: Burst Flash Address Advance / SmartMedia Write Enable */ -#define AT91_PC4_BFOE (1 << 4) /* A: Burst Flash Output Enable */ -#define AT91_PC5_BFWE (1 << 5) /* A: Burst Flash Write Enable */ -#define AT91_PC6_NWAIT (1 << 6) /* A: SMC Wait Signal */ -#define AT91_PC7_A23 (1 << 7) /* A: Address Bus 23 */ -#define AT91_PC8_A24 (1 << 8) /* A: Address Bus 24 */ -#define AT91_PC9_A25_CFRNW (1 << 9) /* A: Address Bus 25 / Compact Flash Read Not Write */ -#define AT91_PC10_NCS4_CFCS (1 << 10) /* A: SMC Chip Select 4 / Compact Flash Chip Select */ -#define AT91_PC11_NCS5_CFCE1 (1 << 11) /* A: SMC Chip Select 5 / Compact Flash Chip Enable 1 */ -#define AT91_PC12_NCS6_CFCE2 (1 << 12) /* A: SMC Chip Select 6 / Compact Flash Chip Enable 2 */ -#define AT91_PC13_NCS7 (1 << 13) /* A: Chip Select 7 */ - -#define AT91_PD0_ETX0 (1 << 0) /* A: Ethernet Transmit Data 0 */ -#define AT91_PD1_ETX1 (1 << 1) /* A: Ethernet Transmit Data 1 */ -#define AT91_PD2_ETX2 (1 << 2) /* A: Ethernet Transmit Data 2 */ -#define AT91_PD3_ETX3 (1 << 3) /* A: Ethernet Transmit Data 3 */ -#define AT91_PD4_ETXEN (1 << 4) /* A: Ethernet Transmit Enable */ -#define AT91_PD5_ETXER (1 << 5) /* A: Ethernet Transmit Coding Error */ -#define AT91_PD6_DTXD (1 << 6) /* A: DBGU Transmit Data */ -#define AT91_PD7_PCK0 (1 << 7) /* A: PMC Programmable Clock Output 0 */ -#define AT91_PD7_TSYNC (1 << 7) /* B: ETM Trace Synchronization Signal */ -#define AT91_PD8_PCK1 (1 << 8) /* A: PMC Programmable Clock Output 1 */ -#define AT91_PD8_TCLK (1 << 8) /* B: ETM Trace Clock */ -#define AT91_PD9_PCK2 (1 << 9) /* A: PMC Programmable Clock Output 2 */ -#define AT91_PD9_TPS0 (1 << 9) /* B: ETM Trace ARM Pipeline Status 0 */ -#define AT91_PD10_PCK3 (1 << 10) /* A: PMC Programmable Clock Output 3 */ -#define AT91_PD10_TPS1 (1 << 10) /* B: ETM Trace ARM Pipeline Status 1 */ -#define AT91_PD11_TPS2 (1 << 11) /* B: ETM Trace ARM Pipeline Status 2 */ -#define AT91_PD12_TPK0 (1 << 12) /* B: ETM Trace Packet Port 0 */ -#define AT91_PD13_TPK1 (1 << 13) /* B: ETM Trace Packet Port 1 */ -#define AT91_PD14_TPK2 (1 << 14) /* B: ETM Trace Packet Port 2 */ -#define AT91_PD15_TD0 (1 << 15) /* A: SSC Transmit Data 0 */ -#define AT91_PD15_TPK3 (1 << 15) /* B: ETM Trace Packet Port 3 */ -#define AT91_PD16_TD1 (1 << 16) /* A: SSC Transmit Data 1 */ -#define AT91_PD16_TPK4 (1 << 16) /* B: ETM Trace Packet Port 4 */ -#define AT91_PD17_TD2 (1 << 17) /* A: SSC Transmit Data 2 */ -#define AT91_PD17_TPK5 (1 << 17) /* B: ETM Trace Packet Port 5 */ -#define AT91_PD18_NPCS1 (1 << 18) /* A: SPI Peripheral Chip Select 1 */ -#define AT91_PD18_TPK6 (1 << 18) /* B: ETM Trace Packet Port 6 */ -#define AT91_PD19_NPCS2 (1 << 19) /* A: SPI Peripheral Chip Select 2 */ -#define AT91_PD19_TPK7 (1 << 19) /* B: ETM Trace Packet Port 7 */ -#define AT91_PD20_NPCS3 (1 << 20) /* A: SPI Peripheral Chip Select 3 */ -#define AT91_PD20_TPK8 (1 << 20) /* B: ETM Trace Packet Port 8 */ -#define AT91_PD21_RTS0 (1 << 21) /* A: USART Ready To Send 0 */ -#define AT91_PD21_TPK9 (1 << 21) /* B: ETM Trace Packet Port 9 */ -#define AT91_PD22_RTS1 (1 << 22) /* A: USART Ready To Send 1 */ -#define AT91_PD22_TPK10 (1 << 22) /* B: ETM Trace Packet Port 10 */ -#define AT91_PD23_RTS2 (1 << 23) /* A: USART Ready To Send 2 */ -#define AT91_PD23_TPK11 (1 << 23) /* B: ETM Trace Packet Port 11 */ -#define AT91_PD24_RTS3 (1 << 24) /* A: USART Ready To Send 3 */ -#define AT91_PD24_TPK12 (1 << 24) /* B: ETM Trace Packet Port 12 */ -#define AT91_PD25_DTR1 (1 << 25) /* A: USART Data Terminal Ready 1 */ -#define AT91_PD25_TPK13 (1 << 25) /* B: ETM Trace Packet Port 13 */ -#define AT91_PD26_TPK14 (1 << 26) /* B: ETM Trace Packet Port 14 */ -#define AT91_PD27_TPK15 (1 << 27) /* B: ETM Trace Packet Port 15 */ -#endif - #endif diff --git a/include/asm-arm/arch-at91/at91sam9260.h b/include/asm-arm/arch-at91/at91sam9260.h index 2cadebc36af..0427f8698c0 100644 --- a/include/asm-arm/arch-at91/at91sam9260.h +++ b/include/asm-arm/arch-at91/at91sam9260.h @@ -117,13 +117,4 @@ #define AT91SAM9XE_SRAM_BASE 0x00300000 /* Internal SRAM base address */ -#if 0 -/* - * PIO pin definitions (peripheral A/B multiplexing). - */ - -// TODO: Add - -#endif - #endif diff --git a/include/asm-arm/arch-at91/at91sam9261.h b/include/asm-arm/arch-at91/at91sam9261.h index 01b58ffe2e2..9eb45957033 100644 --- a/include/asm-arm/arch-at91/at91sam9261.h +++ b/include/asm-arm/arch-at91/at91sam9261.h @@ -98,195 +98,4 @@ #define AT91SAM9261_LCDC_BASE 0x00600000 /* LDC controller */ -#if 0 -/* - * PIO pin definitions (peripheral A/B multiplexing). - */ -#define AT91_PA0_SPI0_MISO (1 << 0) /* A: SPI0 Master In Slave */ -#define AT91_PA0_MCDA0 (1 << 0) /* B: Multimedia Card A Data 0 */ -#define AT91_PA1_SPI0_MOSI (1 << 1) /* A: SPI0 Master Out Slave */ -#define AT91_PA1_MCCDA (1 << 1) /* B: Multimedia Card A Command */ -#define AT91_PA2_SPI0_SPCK (1 << 2) /* A: SPI0 Serial Clock */ -#define AT91_PA2_MCCK (1 << 2) /* B: Multimedia Card Clock */ -#define AT91_PA3_SPI0_NPCS0 (1 << 3) /* A: SPI0 Peripheral Chip Select 0 */ -#define AT91_PA4_SPI0_NPCS1 (1 << 4) /* A: SPI0 Peripheral Chip Select 1 */ -#define AT91_PA4_MCDA1 (1 << 4) /* B: Multimedia Card A Data 1 */ -#define AT91_PA5_SPI0_NPCS2 (1 << 5) /* A: SPI0 Peripheral Chip Select 2 */ -#define AT91_PA5_MCDA2 (1 << 5) /* B: Multimedia Card A Data 2 */ -#define AT91_PA6_SPI0_NPCS3 (1 << 6) /* A: SPI0 Peripheral Chip Select 3 */ -#define AT91_PA6_MCDA3 (1 << 6) /* B: Multimedia Card A Data 3 */ -#define AT91_PA7_TWD (1 << 7) /* A: TWI Two-wire Serial Data */ -#define AT91_PA7_PCK0 (1 << 7) /* B: PMC Programmable clock Output 0 */ -#define AT91_PA8_TWCK (1 << 8) /* A: TWI Two-wire Serial Clock */ -#define AT91_PA8_PCK1 (1 << 8) /* B: PMC Programmable clock Output 1 */ -#define AT91_PA9_DRXD (1 << 9) /* A: DBGU Debug Receive Data */ -#define AT91_PA9_PCK2 (1 << 9) /* B: PMC Programmable clock Output 2 */ -#define AT91_PA10_DTXD (1 << 10) /* A: DBGU Debug Transmit Data */ -#define AT91_PA10_PCK3 (1 << 10) /* B: PMC Programmable clock Output 3 */ -#define AT91_PA11_TSYNC (1 << 11) /* A: Trace Synchronization Signal */ -#define AT91_PA11_SCK1 (1 << 11) /* B: USART1 Serial Clock */ -#define AT91_PA12_TCLK (1 << 12) /* A: Trace Clock */ -#define AT91_PA12_RTS1 (1 << 12) /* B: USART1 Ready To Send */ -#define AT91_PA13_TPS0 (1 << 13) /* A: Trace ARM Pipeline Status 0 */ -#define AT91_PA13_CTS1 (1 << 13) /* B: USART1 Clear To Send */ -#define AT91_PA14_TPS1 (1 << 14) /* A: Trace ARM Pipeline Status 1 */ -#define AT91_PA14_SCK2 (1 << 14) /* B: USART2 Serial Clock */ -#define AT91_PA15_TPS2 (1 << 15) /* A: Trace ARM Pipeline Status 2 */ -#define AT91_PA15_RTS2 (1 << 15) /* B: USART2 Ready To Send */ -#define AT91_PA16_TPK0 (1 << 16) /* A: Trace Packet Port 0 */ -#define AT91_PA16_CTS2 (1 << 16) /* B: USART2 Clear To Send */ -#define AT91_PA17_TPK1 (1 << 17) /* A: Trace Packet Port 1 */ -#define AT91_PA17_TF1 (1 << 17) /* B: SSC1 Transmit Frame Sync */ -#define AT91_PA18_TPK2 (1 << 18) /* A: Trace Packet Port 2 */ -#define AT91_PA18_TK1 (1 << 18) /* B: SSC1 Transmit Clock */ -#define AT91_PA19_TPK3 (1 << 19) /* A: Trace Packet Port 3 */ -#define AT91_PA19_TD1 (1 << 19) /* B: SSC1 Transmit Data */ -#define AT91_PA20_TPK4 (1 << 20) /* A: Trace Packet Port 4 */ -#define AT91_PA20_RD1 (1 << 20) /* B: SSC1 Receive Data */ -#define AT91_PA21_TPK5 (1 << 21) /* A: Trace Packet Port 5 */ -#define AT91_PA21_RK1 (1 << 21) /* B: SSC1 Receive Clock */ -#define AT91_PA22_TPK6 (1 << 22) /* A: Trace Packet Port 6 */ -#define AT91_PA22_RF1 (1 << 22) /* B: SSC1 Receive Frame Sync */ -#define AT91_PA23_TPK7 (1 << 23) /* A: Trace Packet Port 7 */ -#define AT91_PA23_RTS0 (1 << 23) /* B: USART0 Ready To Send */ -#define AT91_PA24_TPK8 (1 << 24) /* A: Trace Packet Port 8 */ -#define AT91_PA24_SPI1_NPCS1 (1 << 24) /* B: SPI1 Peripheral Chip Select 1 */ -#define AT91_PA25_TPK9 (1 << 25) /* A: Trace Packet Port 9 */ -#define AT91_PA25_SPI1_NPCS2 (1 << 25) /* B: SPI1 Peripheral Chip Select 2 */ -#define AT91_PA26_TPK10 (1 << 26) /* A: Trace Packet Port 10 */ -#define AT91_PA26_SPI1_NPCS3 (1 << 26) /* B: SPI1 Peripheral Chip Select 3 */ -#define AT91_PA27_TPK11 (1 << 27) /* A: Trace Packet Port 11 */ -#define AT91_PA27_SPI0_NPCS1 (1 << 27) /* B: SPI0 Peripheral Chip Select 1 */ -#define AT91_PA28_TPK12 (1 << 28) /* A: Trace Packet Port 12 */ -#define AT91_PA28_SPI0_NPCS2 (1 << 28) /* B: SPI0 Peripheral Chip Select 2 */ -#define AT91_PA29_TPK13 (1 << 29) /* A: Trace Packet Port 13 */ -#define AT91_PA29_SPI0_NPCS3 (1 << 29) /* B: SPI0 Peripheral Chip Select 3 */ -#define AT91_PA30_TPK14 (1 << 30) /* A: Trace Packet Port 14 */ -#define AT91_PA30_A23 (1 << 30) /* B: Address Bus bit 23 */ -#define AT91_PA31_TPK15 (1 << 31) /* A: Trace Packet Port 15 */ -#define AT91_PA31_A24 (1 << 31) /* B: Address Bus bit 24 */ - -#define AT91_PB0_LCDVSYNC (1 << 0) /* A: LCD Vertical Synchronization */ -#define AT91_PB1_LCDHSYNC (1 << 1) /* A: LCD Horizontal Synchronization */ -#define AT91_PB2_LCDDOTCK (1 << 2) /* A: LCD Dot Clock */ -#define AT91_PB2_PCK0 (1 << 2) /* B: PMC Programmable clock Output 0 */ -#define AT91_PB3_LCDDEN (1 << 3) /* A: LCD Data Enable */ -#define AT91_PB4_LCDCC (1 << 4) /* A: LCD Contrast Control */ -#define AT91_PB4_LCDD2 (1 << 4) /* B: LCD Data Bus Bit 2 */ -#define AT91_PB5_LCDD0 (1 << 5) /* A: LCD Data Bus Bit 0 */ -#define AT91_PB5_LCDD3 (1 << 5) /* B: LCD Data Bus Bit 3 */ -#define AT91_PB6_LCDD1 (1 << 6) /* A: LCD Data Bus Bit 1 */ -#define AT91_PB6_LCDD4 (1 << 6) /* B: LCD Data Bus Bit 4 */ -#define AT91_PB7_LCDD2 (1 << 7) /* A: LCD Data Bus Bit 2 */ -#define AT91_PB7_LCDD5 (1 << 7) /* B: LCD Data Bus Bit 5 */ -#define AT91_PB8_LCDD3 (1 << 8) /* A: LCD Data Bus Bit 3 */ -#define AT91_PB8_LCDD6 (1 << 8) /* B: LCD Data Bus Bit 6 */ -#define AT91_PB9_LCDD4 (1 << 9) /* A: LCD Data Bus Bit 4 */ -#define AT91_PB9_LCDD7 (1 << 9) /* B: LCD Data Bus Bit 7 */ -#define AT91_PB10_LCDD5 (1 << 10) /* A: LCD Data Bus Bit 5 */ -#define AT91_PB10_LCDD10 (1 << 10) /* B: LCD Data Bus Bit 10 */ -#define AT91_PB11_LCDD6 (1 << 11) /* A: LCD Data Bus Bit 6 */ -#define AT91_PB11_LCDD11 (1 << 11) /* B: LCD Data Bus Bit 11 */ -#define AT91_PB12_LCDD7 (1 << 12) /* A: LCD Data Bus Bit 7 */ -#define AT91_PB12_LCDD12 (1 << 12) /* B: LCD Data Bus Bit 12 */ -#define AT91_PB13_LCDD8 (1 << 13) /* A: LCD Data Bus Bit 8 */ -#define AT91_PB13_LCDD13 (1 << 13) /* B: LCD Data Bus Bit 13 */ -#define AT91_PB14_LCDD9 (1 << 14) /* A: LCD Data Bus Bit 9 */ -#define AT91_PB14_LCDD14 (1 << 14) /* B: LCD Data Bus Bit 14 */ -#define AT91_PB15_LCDD10 (1 << 15) /* A: LCD Data Bus Bit 10 */ -#define AT91_PB15_LCDD15 (1 << 15) /* B: LCD Data Bus Bit 15 */ -#define AT91_PB16_LCDD11 (1 << 16) /* A: LCD Data Bus Bit 11 */ -#define AT91_PB16_LCDD19 (1 << 16) /* B: LCD Data Bus Bit 19 */ -#define AT91_PB17_LCDD12 (1 << 17) /* A: LCD Data Bus Bit 12 */ -#define AT91_PB17_LCDD20 (1 << 17) /* B: LCD Data Bus Bit 20 */ -#define AT91_PB18_LCDD13 (1 << 18) /* A: LCD Data Bus Bit 13 */ -#define AT91_PB18_LCDD21 (1 << 18) /* B: LCD Data Bus Bit 21 */ -#define AT91_PB19_LCDD14 (1 << 19) /* A: LCD Data Bus Bit 14 */ -#define AT91_PB19_LCDD22 (1 << 19) /* B: LCD Data Bus Bit 22 */ -#define AT91_PB20_LCDD15 (1 << 20) /* A: LCD Data Bus Bit 15 */ -#define AT91_PB20_LCDD23 (1 << 20) /* B: LCD Data Bus Bit 23 */ -#define AT91_PB21_TF0 (1 << 21) /* A: SSC0 Transmit Frame Sync */ -#define AT91_PB21_LCDD16 (1 << 21) /* B: LCD Data Bus Bit 16 */ -#define AT91_PB22_TK0 (1 << 22) /* A: SSC0 Transmit Clock */ -#define AT91_PB22_LCDD17 (1 << 22) /* B: LCD Data Bus Bit 17 */ -#define AT91_PB23_TD0 (1 << 23) /* A: SSC0 Transmit Data */ -#define AT91_PB23_LCDD18 (1 << 23) /* B: LCD Data Bus Bit 18 */ -#define AT91_PB24_RD0 (1 << 24) /* A: SSC0 Receive Data */ -#define AT91_PB24_LCDD19 (1 << 24) /* B: LCD Data Bus Bit 19 */ -#define AT91_PB25_RK0 (1 << 25) /* A: SSC0 Receive Clock */ -#define AT91_PB25_LCDD20 (1 << 25) /* B: LCD Data Bus Bit 20 */ -#define AT91_PB26_RF0 (1 << 26) /* A: SSC0 Receive Frame Sync */ -#define AT91_PB26_LCDD21 (1 << 26) /* B: LCD Data Bus Bit 21 */ -#define AT91_PB27_SPI1_NPCS1 (1 << 27) /* A: SPI1 Peripheral Chip Select 1 */ -#define AT91_PB27_LCDD22 (1 << 27) /* B: LCD Data Bus Bit 22 */ -#define AT91_PB28_SPI1_NPCS0 (1 << 28) /* A: SPI1 Peripheral Chip Select 0 */ -#define AT91_PB28_LCDD23 (1 << 28) /* B: LCD Data Bus Bit 23 */ -#define AT91_PB29_SPI1_SPCK (1 << 29) /* A: SPI1 Serial Clock */ -#define AT91_PB29_IRQ2 (1 << 29) /* B: Interrupt input 2 */ -#define AT91_PB30_SPI1_MISO (1 << 30) /* A: SPI1 Master In Slave */ -#define AT91_PB30_IRQ1 (1 << 30) /* B: Interrupt input 1 */ -#define AT91_PB31_SPI1_MOSI (1 << 31) /* A: SPI1 Master Out Slave */ -#define AT91_PB31_PCK2 (1 << 31) /* B: PMC Programmable clock Output 2 */ - -#define AT91_PC0_SMOE (1 << 0) /* A: SmartMedia Output Enable */ -#define AT91_PC0_NCS6 (1 << 0) /* B: Chip Select 6 */ -#define AT91_PC1_SMWE (1 << 1) /* A: SmartMedia Write Enable */ -#define AT91_PC1_NCS7 (1 << 1) /* B: Chip Select 7 */ -#define AT91_PC2_NWAIT (1 << 2) /* A: NWAIT */ -#define AT91_PC2_IRQ0 (1 << 2) /* B: Interrupt input 0 */ -#define AT91_PC3_A25_CFRNW (1 << 3) /* A: Address Bus[25] / Compact Flash Read Not Write */ -#define AT91_PC4_NCS4_CFCS0 (1 << 4) /* A: Chip Select 4 / CompactFlash Chip Select 0 */ -#define AT91_PC5_NCS5_CFCS1 (1 << 5) /* A: Chip Select 5 / CompactFlash Chip Select 1 */ -#define AT91_PC6_CFCE1 (1 << 6) /* A: CompactFlash Chip Enable 1 */ -#define AT91_PC7_CFCE2 (1 << 7) /* A: CompactFlash Chip Enable 2 */ -#define AT91_PC8_TXD0 (1 << 8) /* A: USART0 Transmit Data */ -#define AT91_PC8_PCK2 (1 << 8) /* B: PMC Programmable clock Output 2 */ -#define AT91_PC9_RXD0 (1 << 9) /* A: USART0 Receive Data */ -#define AT91_PC9_PCK3 (1 << 9) /* B: PMC Programmable clock Output 3 */ -#define AT91_PC10_RTS0 (1 << 10) /* A: USART0 Ready To Send */ -#define AT91_PC10_SCK0 (1 << 10) /* B: USART0 Serial Clock */ -#define AT91_PC11_CTS0 (1 << 11) /* A: USART0 Clear To Send */ -#define AT91_PC11_FIQ (1 << 11) /* B: AIC Fast Interrupt Input */ -#define AT91_PC12_TXD1 (1 << 12) /* A: USART1 Transmit Data */ -#define AT91_PC12_NCS6 (1 << 12) /* B: Chip Select 6 */ -#define AT91_PC13_RXD1 (1 << 13) /* A: USART1 Receive Data */ -#define AT91_PC13_NCS7 (1 << 13) /* B: Chip Select 7 */ -#define AT91_PC14_TXD2 (1 << 14) /* A: USART2 Transmit Data */ -#define AT91_PC14_SPI1_NPCS2 (1 << 14) /* B: SPI1 Peripheral Chip Select 2 */ -#define AT91_PC15_RXD2 (1 << 15) /* A: USART2 Receive Data */ -#define AT91_PC15_SPI1_NPCS3 (1 << 15) /* B: SPI1 Peripheral Chip Select 3 */ -#define AT91_PC16_D16 (1 << 16) /* A: Data Bus [16] */ -#define AT91_PC16_TCLK0 (1 << 16) /* B: Timer Counter 0 external clock input */ -#define AT91_PC17_D17 (1 << 17) /* A: Data Bus [17] */ -#define AT91_PC17_TCLK1 (1 << 17) /* B: Timer Counter 1 external clock input */ -#define AT91_PC18_D18 (1 << 18) /* A: Data Bus [18] */ -#define AT91_PC18_TCLK2 (1 << 18) /* B: Timer Counter 2 external clock input */ -#define AT91_PC19_D19 (1 << 19) /* A: Data Bus [19] */ -#define AT91_PC19_TIOA0 (1 << 19) /* B: Timer Counter 0 Multipurpose Timer I/O Pin A */ -#define AT91_PC20_D20 (1 << 20) /* A: Data Bus [20] */ -#define AT91_PC20_TIOB0 (1 << 20) /* B: Timer Counter 0 Multipurpose Timer I/O Pin B */ -#define AT91_PC21_D21 (1 << 21) /* A: Data Bus [21] */ -#define AT91_PC21_TIOA1 (1 << 21) /* B: Timer Counter 1 Multipurpose Timer I/O Pin A */ -#define AT91_PC22_D22 (1 << 22) /* A: Data Bus [22] */ -#define AT91_PC22_TIOB1 (1 << 22) /* B: Timer Counter 1 Multipurpose Timer I/O Pin B */ -#define AT91_PC23_D23 (1 << 23) /* A: Data Bus [23] */ -#define AT91_PC23_TIOA2 (1 << 23) /* B: Timer Counter 2 Multipurpose Timer I/O Pin A */ -#define AT91_PC24_D24 (1 << 24) /* A: Data Bus [24] */ -#define AT91_PC24_TIOB2 (1 << 24) /* B: Timer Counter 2 Multipurpose Timer I/O Pin B */ -#define AT91_PC25_D25 (1 << 25) /* A: Data Bus [25] */ -#define AT91_PC25_TF2 (1 << 25) /* B: SSC2 Transmit Frame Sync */ -#define AT91_PC26_D26 (1 << 26) /* A: Data Bus [26] */ -#define AT91_PC26_TK2 (1 << 26) /* B: SSC2 Transmit Clock */ -#define AT91_PC27_D27 (1 << 27) /* A: Data Bus [27] */ -#define AT91_PC27_TD2 (1 << 27) /* B: SSC2 Transmit Data */ -#define AT91_PC28_D28 (1 << 28) /* A: Data Bus [28] */ -#define AT91_PC28_RD2 (1 << 28) /* B: SSC2 Receive Data */ -#define AT91_PC29_D29 (1 << 29) /* A: Data Bus [29] */ -#define AT91_PC29_RK2 (1 << 29) /* B: SSC2 Receive Clock */ -#define AT91_PC30_D30 (1 << 30) /* A: Data Bus [30] */ -#define AT91_PC30_RF2 (1 << 30) /* B: SSC2 Receive Frame Sync */ -#define AT91_PC31_D31 (1 << 31) /* A: Data Bus [31] */ -#define AT91_PC31_PCK1 (1 << 31) /* B: PMC Programmable clock Output 1 */ -#endif - #endif diff --git a/include/asm-arm/arch-at91/at91sam9263.h b/include/asm-arm/arch-at91/at91sam9263.h index f4af68ae0ea..115c47ac7eb 100644 --- a/include/asm-arm/arch-at91/at91sam9263.h +++ b/include/asm-arm/arch-at91/at91sam9263.h @@ -119,13 +119,5 @@ #define AT91SAM9263_DMAC_BASE 0x00800000 /* DMA Controller */ #define AT91SAM9263_UHP_BASE 0x00a00000 /* USB Host controller */ -#if 0 -/* - * PIO pin definitions (peripheral A/B multiplexing). - */ - -// TODO: Add - -#endif #endif -- cgit v1.2.3 From c5f125031f416ba6350e84462e9039737b6e2bab Mon Sep 17 00:00:00 2001 From: Kevin Welton Date: Tue, 8 May 2007 22:05:25 +0100 Subject: [ARM] Fix ARM branch relocation range Branches in the ARM architecture are restricted to a range of +/- 32MB. However, the code in .../arch/arm/kernel/module.c::apply_relocate() was checking offset against a range of +/- 64MB. Signed-off-by: Kevin Welton Signed-off-by: Russell King --- arch/arm/kernel/module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 1b061583408..79b7e5cf541 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -116,8 +116,8 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, offset += sym->st_value - loc; if (offset & 3 || - offset <= (s32)0xfc000000 || - offset >= (s32)0x04000000) { + offset <= (s32)0xfe000000 || + offset >= (s32)0x02000000) { printk(KERN_ERR "%s: relocation out of range, section " "%d reloc %d sym '%s'\n", module->name, -- cgit v1.2.3 From 3be44b9cc33d26930cb3bb014f35f582c6522481 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Tue, 8 May 2007 14:50:43 -0700 Subject: [IA64] Optional method to purge the TLB on SN systems This patch adds an optional method for purging the TLB on SN IA64 systems. The change should not affect any non-SN system. Signed-off-by: Jack Steiner Signed-off-by: Tony Luck --- arch/ia64/kernel/irq_ia64.c | 27 +++++++++++++--- arch/ia64/kernel/smp.c | 68 +++++++++++++++++++++++++++++++++++++++ arch/ia64/sn/kernel/sn2/sn2_smp.c | 65 ++++++++++++++++++++++++++++++++++--- include/asm-ia64/hw_irq.h | 1 + include/asm-ia64/tlbflush.h | 11 +++++++ 5 files changed, 162 insertions(+), 10 deletions(-) diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index 456f57b087c..9a5f41be760 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c @@ -39,6 +39,7 @@ #include #include #include +#include #ifdef CONFIG_PERFMON # include @@ -127,8 +128,10 @@ void destroy_irq(unsigned int irq) #ifdef CONFIG_SMP # define IS_RESCHEDULE(vec) (vec == IA64_IPI_RESCHEDULE) +# define IS_LOCAL_TLB_FLUSH(vec) (vec == IA64_IPI_LOCAL_TLB_FLUSH) #else # define IS_RESCHEDULE(vec) (0) +# define IS_LOCAL_TLB_FLUSH(vec) (0) #endif /* * That's where the IVT branches when we get an external @@ -180,8 +183,11 @@ ia64_handle_irq (ia64_vector vector, struct pt_regs *regs) saved_tpr = ia64_getreg(_IA64_REG_CR_TPR); ia64_srlz_d(); while (vector != IA64_SPURIOUS_INT_VECTOR) { - if (unlikely(IS_RESCHEDULE(vector))) - kstat_this_cpu.irqs[vector]++; + if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) { + smp_local_flush_tlb(); + kstat_this_cpu.irqs[vector]++; + } else if (unlikely(IS_RESCHEDULE(vector))) + kstat_this_cpu.irqs[vector]++; else { ia64_setreg(_IA64_REG_CR_TPR, vector); ia64_srlz_d(); @@ -227,8 +233,11 @@ void ia64_process_pending_intr(void) * Perform normal interrupt style processing */ while (vector != IA64_SPURIOUS_INT_VECTOR) { - if (unlikely(IS_RESCHEDULE(vector))) - kstat_this_cpu.irqs[vector]++; + if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) { + smp_local_flush_tlb(); + kstat_this_cpu.irqs[vector]++; + } else if (unlikely(IS_RESCHEDULE(vector))) + kstat_this_cpu.irqs[vector]++; else { struct pt_regs *old_regs = set_irq_regs(NULL); @@ -260,12 +269,12 @@ void ia64_process_pending_intr(void) #ifdef CONFIG_SMP -extern irqreturn_t handle_IPI (int irq, void *dev_id); static irqreturn_t dummy_handler (int irq, void *dev_id) { BUG(); } +extern irqreturn_t handle_IPI (int irq, void *dev_id); static struct irqaction ipi_irqaction = { .handler = handle_IPI, @@ -278,6 +287,13 @@ static struct irqaction resched_irqaction = { .flags = IRQF_DISABLED, .name = "resched" }; + +static struct irqaction tlb_irqaction = { + .handler = dummy_handler, + .flags = SA_INTERRUPT, + .name = "tlb_flush" +}; + #endif void @@ -303,6 +319,7 @@ init_IRQ (void) #ifdef CONFIG_SMP register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction); register_percpu_irq(IA64_IPI_RESCHEDULE, &resched_irqaction); + register_percpu_irq(IA64_IPI_LOCAL_TLB_FLUSH, &tlb_irqaction); #endif #ifdef CONFIG_PERFMON pfm_init_percpu(); diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index 55ddd809b02..221de380456 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -49,6 +49,18 @@ #include #include +/* + * Note: alignment of 4 entries/cacheline was empirically determined + * to be a good tradeoff between hot cachelines & spreading the array + * across too many cacheline. + */ +static struct local_tlb_flush_counts { + unsigned int count; +} __attribute__((__aligned__(32))) local_tlb_flush_counts[NR_CPUS]; + +static DEFINE_PER_CPU(unsigned int, shadow_flush_counts[NR_CPUS]) ____cacheline_aligned; + + /* * Structure and data for smp_call_function(). This is designed to minimise static memory * requirements. It also looks cleaner. @@ -248,6 +260,62 @@ smp_send_reschedule (int cpu) platform_send_ipi(cpu, IA64_IPI_RESCHEDULE, IA64_IPI_DM_INT, 0); } +/* + * Called with preeemption disabled. + */ +static void +smp_send_local_flush_tlb (int cpu) +{ + platform_send_ipi(cpu, IA64_IPI_LOCAL_TLB_FLUSH, IA64_IPI_DM_INT, 0); +} + +void +smp_local_flush_tlb(void) +{ + /* + * Use atomic ops. Otherwise, the load/increment/store sequence from + * a "++" operation can have the line stolen between the load & store. + * The overhead of the atomic op in negligible in this case & offers + * significant benefit for the brief periods where lots of cpus + * are simultaneously flushing TLBs. + */ + ia64_fetchadd(1, &local_tlb_flush_counts[smp_processor_id()].count, acq); + local_flush_tlb_all(); +} + +#define FLUSH_DELAY 5 /* Usec backoff to eliminate excessive cacheline bouncing */ + +void +smp_flush_tlb_cpumask(cpumask_t xcpumask) +{ + unsigned int *counts = __ia64_per_cpu_var(shadow_flush_counts); + cpumask_t cpumask = xcpumask; + int mycpu, cpu, flush_mycpu = 0; + + preempt_disable(); + mycpu = smp_processor_id(); + + for_each_cpu_mask(cpu, cpumask) + counts[cpu] = local_tlb_flush_counts[cpu].count; + + mb(); + for_each_cpu_mask(cpu, cpumask) { + if (cpu == mycpu) + flush_mycpu = 1; + else + smp_send_local_flush_tlb(cpu); + } + + if (flush_mycpu) + smp_local_flush_tlb(); + + for_each_cpu_mask(cpu, cpumask) + while(counts[cpu] == local_tlb_flush_counts[cpu].count) + udelay(FLUSH_DELAY); + + preempt_enable(); +} + void smp_flush_tlb_all (void) { diff --git a/arch/ia64/sn/kernel/sn2/sn2_smp.c b/arch/ia64/sn/kernel/sn2/sn2_smp.c index 601747b1e22..5d318b579fb 100644 --- a/arch/ia64/sn/kernel/sn2/sn2_smp.c +++ b/arch/ia64/sn/kernel/sn2/sn2_smp.c @@ -46,6 +46,9 @@ DECLARE_PER_CPU(struct ptc_stats, ptcstats); static __cacheline_aligned DEFINE_SPINLOCK(sn2_global_ptc_lock); +/* 0 = old algorithm (no IPI flushes), 1 = ipi deadlock flush, 2 = ipi instead of SHUB ptc, >2 = always ipi */ +static int sn2_flush_opt = 0; + extern unsigned long sn2_ptc_deadlock_recovery_core(volatile unsigned long *, unsigned long, volatile unsigned long *, unsigned long, @@ -76,6 +79,8 @@ struct ptc_stats { unsigned long shub_itc_clocks; unsigned long shub_itc_clocks_max; unsigned long shub_ptc_flushes_not_my_mm; + unsigned long shub_ipi_flushes; + unsigned long shub_ipi_flushes_itc_clocks; }; #define sn2_ptctest 0 @@ -121,6 +126,18 @@ void sn_tlb_migrate_finish(struct mm_struct *mm) flush_tlb_mm(mm); } +static void +sn2_ipi_flush_all_tlb(struct mm_struct *mm) +{ + unsigned long itc; + + itc = ia64_get_itc(); + smp_flush_tlb_cpumask(mm->cpu_vm_mask); + itc = ia64_get_itc() - itc; + __get_cpu_var(ptcstats).shub_ipi_flushes_itc_clocks += itc; + __get_cpu_var(ptcstats).shub_ipi_flushes++; +} + /** * sn2_global_tlb_purge - globally purge translation cache of virtual address range * @mm: mm_struct containing virtual address range @@ -154,7 +171,12 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, unsigned long itc, itc2, flags, data0 = 0, data1 = 0, rr_value, old_rr = 0; short nasids[MAX_NUMNODES], nix; nodemask_t nodes_flushed; - int active, max_active, deadlock; + int active, max_active, deadlock, flush_opt = sn2_flush_opt; + + if (flush_opt > 2) { + sn2_ipi_flush_all_tlb(mm); + return; + } nodes_clear(nodes_flushed); i = 0; @@ -189,6 +211,12 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, return; } + if (flush_opt == 2) { + sn2_ipi_flush_all_tlb(mm); + preempt_enable(); + return; + } + itc = ia64_get_itc(); nix = 0; for_each_node_mask(cnode, nodes_flushed) @@ -256,6 +284,8 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, } if (active >= max_active || i == (nix - 1)) { if ((deadlock = wait_piowc())) { + if (flush_opt == 1) + goto done; sn2_ptc_deadlock_recovery(nasids, ibegin, i, mynasid, ptc0, data0, ptc1, data1); if (reset_max_active_on_deadlock()) max_active = 1; @@ -267,6 +297,7 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, start += (1UL << nbits); } while (start < end); +done: itc2 = ia64_get_itc() - itc2; __get_cpu_var(ptcstats).shub_itc_clocks += itc2; if (itc2 > __get_cpu_var(ptcstats).shub_itc_clocks_max) @@ -279,6 +310,11 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, spin_unlock_irqrestore(PTC_LOCK(shub1), flags); + if (flush_opt == 1 && deadlock) { + __get_cpu_var(ptcstats).deadlocks++; + sn2_ipi_flush_all_tlb(mm); + } + preempt_enable(); } @@ -425,24 +461,42 @@ static int sn2_ptc_seq_show(struct seq_file *file, void *data) if (!cpu) { seq_printf(file, - "# cpu ptc_l newrid ptc_flushes nodes_flushed deadlocks lock_nsec shub_nsec shub_nsec_max not_my_mm deadlock2\n"); - seq_printf(file, "# ptctest %d\n", sn2_ptctest); + "# cpu ptc_l newrid ptc_flushes nodes_flushed deadlocks lock_nsec shub_nsec shub_nsec_max not_my_mm deadlock2 ipi_fluches ipi_nsec\n"); + seq_printf(file, "# ptctest %d, flushopt %d\n", sn2_ptctest, sn2_flush_opt); } if (cpu < NR_CPUS && cpu_online(cpu)) { stat = &per_cpu(ptcstats, cpu); - seq_printf(file, "cpu %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", cpu, stat->ptc_l, + seq_printf(file, "cpu %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", cpu, stat->ptc_l, stat->change_rid, stat->shub_ptc_flushes, stat->nodes_flushed, stat->deadlocks, 1000 * stat->lock_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec, 1000 * stat->shub_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec, 1000 * stat->shub_itc_clocks_max / per_cpu(cpu_info, cpu).cyc_per_usec, stat->shub_ptc_flushes_not_my_mm, - stat->deadlocks2); + stat->deadlocks2, + stat->shub_ipi_flushes, + 1000 * stat->shub_ipi_flushes_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec); } return 0; } +static ssize_t sn2_ptc_proc_write(struct file *file, const char __user *user, size_t count, loff_t *data) +{ + int cpu; + char optstr[64]; + + if (copy_from_user(optstr, user, count)) + return -EFAULT; + optstr[count - 1] = '\0'; + sn2_flush_opt = simple_strtoul(optstr, NULL, 0); + + for_each_online_cpu(cpu) + memset(&per_cpu(ptcstats, cpu), 0, sizeof(struct ptc_stats)); + + return count; +} + static struct seq_operations sn2_ptc_seq_ops = { .start = sn2_ptc_seq_start, .next = sn2_ptc_seq_next, @@ -458,6 +512,7 @@ static int sn2_ptc_proc_open(struct inode *inode, struct file *file) static const struct file_operations proc_sn2_ptc_operations = { .open = sn2_ptc_proc_open, .read = seq_read, + .write = sn2_ptc_proc_write, .llseek = seq_lseek, .release = seq_release, }; diff --git a/include/asm-ia64/hw_irq.h b/include/asm-ia64/hw_irq.h index 27f9df6b914..c054d7a9aaa 100644 --- a/include/asm-ia64/hw_irq.h +++ b/include/asm-ia64/hw_irq.h @@ -66,6 +66,7 @@ extern int ia64_last_device_vector; #define IA64_PERFMON_VECTOR 0xee /* performanc monitor interrupt vector */ #define IA64_TIMER_VECTOR 0xef /* use highest-prio group 15 interrupt for timer */ #define IA64_MCA_WAKEUP_VECTOR 0xf0 /* MCA wakeup (must be >MCA_RENDEZ_VECTOR) */ +#define IA64_IPI_LOCAL_TLB_FLUSH 0xfc /* SMP flush local TLB */ #define IA64_IPI_RESCHEDULE 0xfd /* SMP reschedule */ #define IA64_IPI_VECTOR 0xfe /* inter-processor interrupt vector */ diff --git a/include/asm-ia64/tlbflush.h b/include/asm-ia64/tlbflush.h index cf9acb9bb1f..e37f9fbf33a 100644 --- a/include/asm-ia64/tlbflush.h +++ b/include/asm-ia64/tlbflush.h @@ -27,9 +27,11 @@ extern void local_flush_tlb_all (void); #ifdef CONFIG_SMP extern void smp_flush_tlb_all (void); extern void smp_flush_tlb_mm (struct mm_struct *mm); + extern void smp_flush_tlb_cpumask (cpumask_t xcpumask); # define flush_tlb_all() smp_flush_tlb_all() #else # define flush_tlb_all() local_flush_tlb_all() +# define smp_flush_tlb_cpumask(m) local_flush_tlb_all() #endif static inline void @@ -94,6 +96,15 @@ flush_tlb_pgtables (struct mm_struct *mm, unsigned long start, unsigned long end */ } +/* + * Flush the local TLB. Invoked from another cpu using an IPI. + */ +#ifdef CONFIG_SMP +void smp_local_flush_tlb(void); +#else +#define smp_local_flush_tlb() +#endif + #define flush_tlb_kernel_range(start, end) flush_tlb_all() /* XXX fix me */ #endif /* _ASM_IA64_TLBFLUSH_H */ -- cgit v1.2.3 From 690def21414fa43fac1b8053fd952c0366c476de Mon Sep 17 00:00:00 2001 From: Kirill Korotaev Date: Mon, 19 Mar 2007 14:04:20 +0300 Subject: [IA64] unwind did not work for processes born with CLONE_STOPPED Minor problem for mainstream. Big problem for checkpoint/restore, because all the stopped/traced processes are born in this state, hence they cannot be checkpointed again due to failing unwind. The problem was identified as assumption in kernel unwind library that top level frame is different of syscall frame. It is the case unless process was born with CLONE_STOPPED. Author: Alexey Kuznetsov Signed-Off-By: Alexey Kuznetsov Signed-Off-By: Kirill Korotaev Signed-off-by: Tony Luck --- arch/ia64/kernel/unwind.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c index e3be21c7bd4..fe1426266b9 100644 --- a/arch/ia64/kernel/unwind.c +++ b/arch/ia64/kernel/unwind.c @@ -60,6 +60,7 @@ # define UNW_DEBUG_ON(n) unw_debug_level >= n /* Do not code a printk level, not all debug lines end in newline */ # define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) printk(__VA_ARGS__) +# undef inline # define inline #else /* !UNW_DEBUG */ # define UNW_DEBUG_ON(n) 0 @@ -1943,9 +1944,9 @@ EXPORT_SYMBOL(unw_unwind); int unw_unwind_to_user (struct unw_frame_info *info) { - unsigned long ip, sp, pr = 0; + unsigned long ip, sp, pr = info->pr; - while (unw_unwind(info) >= 0) { + do { unw_get_sp(info, &sp); if ((long)((unsigned long)info->task + IA64_STK_OFFSET - sp) < IA64_PT_REGS_SIZE) { @@ -1963,7 +1964,7 @@ unw_unwind_to_user (struct unw_frame_info *info) __FUNCTION__, ip); return -1; } - } + } while (unw_unwind(info) >= 0); unw_get_ip(info, &ip); UNW_DPRINT(0, "unwind.%s: failed to unwind to user-level (ip=0x%lx)\n", __FUNCTION__, ip); -- cgit v1.2.3 From 4a177cbf84f827cf9f1d6cfa5264fafd3cc33ce0 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 23 Jan 2007 19:03:17 +0300 Subject: [IA64] Add TIF_RESTORE_SIGMASK Preparation for pselect and ppoll. ia32 compat code not tested. :-( Signed-off-by: Alexey Kuznetsov Signed-off-by: Alexey Dobriyan Signed-off-by: Tony Luck --- arch/ia64/ia32/ia32_entry.S | 39 +---------------------- arch/ia64/ia32/ia32_signal.c | 59 ++++++----------------------------- arch/ia64/kernel/entry.S | 26 ---------------- arch/ia64/kernel/process.c | 6 ++-- arch/ia64/kernel/sigframe.h | 2 +- arch/ia64/kernel/signal.c | 71 ++++++++++++++---------------------------- include/asm-ia64/thread_info.h | 4 ++- include/asm-ia64/unistd.h | 2 ++ 8 files changed, 43 insertions(+), 166 deletions(-) diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S index 687e5fdc968..99b665e2b1d 100644 --- a/arch/ia64/ia32/ia32_entry.S +++ b/arch/ia64/ia32/ia32_entry.S @@ -52,43 +52,6 @@ ENTRY(ia32_clone) br.ret.sptk.many rp END(ia32_clone) -ENTRY(sys32_rt_sigsuspend) - .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) - alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs - mov loc0=rp - mov out0=in0 // mask - mov out1=in1 // sigsetsize - mov out2=sp // out2 = &sigscratch - .fframe 16 - adds sp=-16,sp // allocate dummy "sigscratch" - ;; - .body - br.call.sptk.many rp=ia32_rt_sigsuspend -1: .restore sp - adds sp=16,sp - mov rp=loc0 - mov ar.pfs=loc1 - br.ret.sptk.many rp -END(sys32_rt_sigsuspend) - -ENTRY(sys32_sigsuspend) - .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) - alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs - mov loc0=rp - mov out0=in2 // mask (first two args are ignored) - ;; - mov out1=sp // out1 = &sigscratch - .fframe 16 - adds sp=-16,sp // allocate dummy "sigscratch" - .body - br.call.sptk.many rp=ia32_sigsuspend -1: .restore sp - adds sp=16,sp - mov rp=loc0 - mov ar.pfs=loc1 - br.ret.sptk.many rp -END(sys32_sigsuspend) - GLOBAL_ENTRY(ia32_ret_from_clone) PT_REGS_UNWIND_INFO(0) { /* @@ -389,7 +352,7 @@ ia32_syscall_table: data8 sys_rt_sigpending data8 compat_sys_rt_sigtimedwait data8 sys32_rt_sigqueueinfo - data8 sys32_rt_sigsuspend + data8 compat_sys_rt_sigsuspend data8 sys32_pread /* 180 */ data8 sys32_pwrite data8 sys_chown /* 16-bit version */ diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c index 7b38b73e782..b2bb7f22792 100644 --- a/arch/ia64/ia32/ia32_signal.c +++ b/arch/ia64/ia32/ia32_signal.c @@ -452,59 +452,20 @@ sigact_set_handler (struct k_sigaction *sa, unsigned int handler, unsigned int r sa->sa.sa_handler = (__sighandler_t) (((unsigned long) restorer << 32) | handler); } -long -__ia32_rt_sigsuspend (compat_sigset_t *sset, unsigned int sigsetsize, struct sigscratch *scr) +asmlinkage long +sys32_sigsuspend (int history0, int history1, old_sigset_t mask) { - extern long ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall); - sigset_t oldset, set; - - scr->scratch_unat = 0; /* avoid leaking kernel bits to user level */ - memset(&set, 0, sizeof(set)); - - memcpy(&set.sig, &sset->sig, sigsetsize); - - sigdelsetmask(&set, ~_BLOCKABLE); - + mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); - { - oldset = current->blocked; - current->blocked = set; - recalc_sigpending(); - } + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - /* - * The return below usually returns to the signal handler. We need to pre-set the - * correct error code here to ensure that the right values get saved in sigcontext - * by ia64_do_signal. - */ - scr->pt.r8 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (ia64_do_signal(&oldset, scr, 1)) - return -EINTR; - } -} - -asmlinkage long -ia32_rt_sigsuspend (compat_sigset_t __user *uset, unsigned int sigsetsize, struct sigscratch *scr) -{ - compat_sigset_t set; - - if (sigsetsize > sizeof(compat_sigset_t)) - return -EINVAL; - - if (copy_from_user(&set.sig, &uset->sig, sigsetsize)) - return -EFAULT; - - return __ia32_rt_sigsuspend(&set, sigsetsize, scr); -} - -asmlinkage long -ia32_sigsuspend (unsigned int mask, struct sigscratch *scr) -{ - return __ia32_rt_sigsuspend((compat_sigset_t *) &mask, sizeof(mask), scr); + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } asmlinkage long diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 55fd2d5471e..156a6fa031f 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1199,32 +1199,6 @@ ENTRY(notify_resume_user) br.ret.sptk.many rp END(notify_resume_user) -GLOBAL_ENTRY(sys_rt_sigsuspend) - .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) - alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! - mov r9=ar.unat - mov loc0=rp // save return address - mov out0=in0 // mask - mov out1=in1 // sigsetsize - adds out2=8,sp // out2=&sigscratch->ar_pfs - ;; - .fframe 16 - .spillsp ar.unat, 16 - st8 [sp]=r9,-16 // allocate space for ar.unat and save it - st8 [out2]=loc1,-8 // save ar.pfs, out2=&sigscratch - .body - br.call.sptk.many rp=ia64_rt_sigsuspend -.ret17: .restore sp - adds sp=16,sp // pop scratch stack space - ;; - ld8 r9=[sp] // load new unat from sw->caller_unat - mov rp=loc0 - ;; - mov ar.unat=r9 - mov ar.pfs=loc1 - br.ret.sptk.many rp -END(sys_rt_sigsuspend) - ENTRY(sys_rt_sigreturn) PT_REGS_UNWIND_INFO(0) /* diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index cba6ac39873..d7b7d3da1eb 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -156,7 +156,7 @@ show_regs (struct pt_regs *regs) } void -do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall) +do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall) { if (fsys_mode(current, &scr->pt)) { /* defer signal-handling etc. until we return to privilege-level 0. */ @@ -171,8 +171,8 @@ do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall #endif /* deal with pending signal delivery */ - if (test_thread_flag(TIF_SIGPENDING)) - ia64_do_signal(oldset, scr, in_syscall); + if (test_thread_flag(TIF_SIGPENDING)||test_thread_flag(TIF_RESTORE_SIGMASK)) + ia64_do_signal(scr, in_syscall); } static int pal_halt = 1; diff --git a/arch/ia64/kernel/sigframe.h b/arch/ia64/kernel/sigframe.h index 37b986cb86e..9fd9a1933b3 100644 --- a/arch/ia64/kernel/sigframe.h +++ b/arch/ia64/kernel/sigframe.h @@ -22,4 +22,4 @@ struct sigframe { struct sigcontext sc; }; -extern long ia64_do_signal (sigset_t *, struct sigscratch *, long); +extern void ia64_do_signal (struct sigscratch *, long); diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index 77f8b49c788..034b81d62bb 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -41,47 +41,6 @@ # define GET_SIGSET(k,u) __get_user((k)->sig[0], &(u)->sig[0]) #endif -long -ia64_rt_sigsuspend (sigset_t __user *uset, size_t sigsetsize, struct sigscratch *scr) -{ - sigset_t oldset, set; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (!access_ok(VERIFY_READ, uset, sigsetsize)) - return -EFAULT; - - if (GET_SIGSET(&set, uset)) - return -EFAULT; - - sigdelsetmask(&set, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - { - oldset = current->blocked; - current->blocked = set; - recalc_sigpending(); - } - spin_unlock_irq(¤t->sighand->siglock); - - /* - * The return below usually returns to the signal handler. We need to - * pre-set the correct error code here to ensure that the right values - * get saved in sigcontext by ia64_do_signal. - */ - scr->pt.r8 = EINTR; - scr->pt.r10 = -1; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (ia64_do_signal(&oldset, scr, 1)) - return -EINTR; - } -} - asmlinkage long sys_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, long arg2, long arg3, long arg4, long arg5, long arg6, long arg7, @@ -478,10 +437,11 @@ handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigse * Note that `init' is a special process: it doesn't get signals it doesn't want to * handle. Thus you cannot kill init even with a SIGKILL even by mistake. */ -long -ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) +void +ia64_do_signal (struct sigscratch *scr, long in_syscall) { struct k_sigaction ka; + sigset_t *oldset; siginfo_t info; long restart = in_syscall; long errno = scr->pt.r8; @@ -493,9 +453,11 @@ ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) * doing anything if so. */ if (!user_mode(&scr->pt)) - return 0; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; /* @@ -558,8 +520,15 @@ ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) * Whee! Actually deliver the signal. If the delivery failed, we need to * continue to iterate in this loop so we can deliver the SIGSEGV... */ - if (handle_signal(signr, &ka, &info, oldset, scr)) - return 1; + if (handle_signal(signr, &ka, &info, oldset, scr)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + return; + } } /* Did we come from a system call? */ @@ -585,5 +554,11 @@ ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) } } } - return 0; + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h index 91698599f91..077d6778575 100644 --- a/include/asm-ia64/thread_info.h +++ b/include/asm-ia64/thread_info.h @@ -85,6 +85,7 @@ struct thread_info { #define TIF_SYSCALL_TRACE 3 /* syscall trace active */ #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ #define TIF_SINGLESTEP 5 /* restore singlestep on return to user mode */ +#define TIF_RESTORE_SIGMASK 6 /* restore signal mask in do_signal() */ #define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_MEMDIE 17 #define TIF_MCA_INIT 18 /* this task is processing MCA or INIT */ @@ -96,6 +97,7 @@ struct thread_info { #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) #define _TIF_SYSCALL_TRACEAUDIT (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) @@ -104,7 +106,7 @@ struct thread_info { #define _TIF_FREEZE (1 << TIF_FREEZE) /* "work to do on user-return" bits */ -#define TIF_ALLWORK_MASK (_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) +#define TIF_ALLWORK_MASK (_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_RESTORE_SIGMASK) /* like TIF_ALLWORK_BITS but sans TIF_SYSCALL_TRACE or TIF_SYSCALL_AUDIT */ #define TIF_WORK_MASK (TIF_ALLWORK_MASK&~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)) diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index a9e1fa4cac4..e586739bcc1 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -300,6 +300,7 @@ #define NR_syscalls 281 /* length of syscall table */ #define __ARCH_WANT_SYS_RT_SIGACTION +#define __ARCH_WANT_SYS_RT_SIGSUSPEND #ifdef CONFIG_IA32_SUPPORT # define __ARCH_WANT_SYS_FADVISE64 @@ -310,6 +311,7 @@ # define __ARCH_WANT_SYS_OLDUMOUNT # define __ARCH_WANT_SYS_SIGPENDING # define __ARCH_WANT_SYS_SIGPROCMASK +# define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND # define __ARCH_WANT_COMPAT_SYS_TIME #endif -- cgit v1.2.3 From bbe888864ec32435e93923c40b9d6ce2bb73844b Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 8 May 2007 22:27:46 +0100 Subject: [ARM] armv7: add support for ARMv7 cores. This patch adds support for the ARMv7 cores. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/mm/abort-ev7.S | 32 ++++++ arch/arm/mm/cache-v7.S | 253 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/proc-macros.S | 12 ++ arch/arm/mm/proc-v7.S | 262 +++++++++++++++++++++++++++++++++++++++++++ include/asm-arm/cacheflush.h | 8 ++ include/asm-arm/glue.h | 9 ++ include/asm-arm/proc-fns.h | 8 ++ include/asm-arm/system.h | 1 + 8 files changed, 585 insertions(+) create mode 100644 arch/arm/mm/abort-ev7.S create mode 100644 arch/arm/mm/cache-v7.S create mode 100644 arch/arm/mm/proc-v7.S diff --git a/arch/arm/mm/abort-ev7.S b/arch/arm/mm/abort-ev7.S new file mode 100644 index 00000000000..eb90bce38e1 --- /dev/null +++ b/arch/arm/mm/abort-ev7.S @@ -0,0 +1,32 @@ +#include +#include +/* + * Function: v7_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 11 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + */ + .align 5 +ENTRY(v7_early_abort) + /* + * The effect of data aborts on on the exclusive access monitor are + * UNPREDICTABLE. Do a CLREX to clear the state + */ + clrex + + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + + /* + * V6 code adjusts the returned DFSR. + * New designs should not need to patch up faults. + */ + mov pc, lr diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S new file mode 100644 index 00000000000..35ffc4d9599 --- /dev/null +++ b/arch/arm/mm/cache-v7.S @@ -0,0 +1,253 @@ +/* + * linux/arch/arm/mm/cache-v7.S + * + * Copyright (C) 2001 Deep Blue Solutions Ltd. + * Copyright (C) 2005 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is the "shell" of the ARMv7 processor support. + */ +#include +#include +#include + +#include "proc-macros.S" + +/* + * v7_flush_dcache_all() + * + * Flush the whole D-cache. + * + * Corrupted registers: r0-r5, r7, r9-r11 + * + * - mm - mm_struct describing address space + */ +ENTRY(v7_flush_dcache_all) + mrc p15, 1, r0, c0, c0, 1 @ read clidr + ands r3, r0, #0x7000000 @ extract loc from clidr + mov r3, r3, lsr #23 @ left align loc bit field + beq finished @ if loc is 0, then no need to clean + mov r10, #0 @ start clean at cache level 0 +loop1: + add r2, r10, r10, lsr #1 @ work out 3x current cache level + mov r1, r0, lsr r2 @ extract cache type bits from clidr + and r1, r1, #7 @ mask of the bits for current cache only + cmp r1, #2 @ see what cache we have at this level + blt skip @ skip if no cache, or just i-cache + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + isb @ isb to sych the new cssr&csidr + mrc p15, 1, r1, c0, c0, 0 @ read the new csidr + and r2, r1, #7 @ extract the length of the cache lines + add r2, r2, #4 @ add 4 (line length offset) + ldr r4, =0x3ff + ands r4, r4, r1, lsr #3 @ find maximum number on the way size + clz r5, r4 @ find bit position of way size increment + ldr r7, =0x7fff + ands r7, r7, r1, lsr #13 @ extract max number of the index size +loop2: + mov r9, r4 @ create working copy of max way size +loop3: + orr r11, r10, r9, lsl r5 @ factor way and cache number into r11 + orr r11, r11, r7, lsl r2 @ factor index number into r11 + mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way + subs r9, r9, #1 @ decrement the way + bge loop3 + subs r7, r7, #1 @ decrement the index + bge loop2 +skip: + add r10, r10, #2 @ increment cache number + cmp r3, r10 + bgt loop1 +finished: + mov r10, #0 @ swith back to cache level 0 + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + isb + mov pc, lr + +/* + * v7_flush_cache_all() + * + * Flush the entire cache system. + * The data cache flush is now achieved using atomic clean / invalidates + * working outwards from L1 cache. This is done using Set/Way based cache + * maintainance instructions. + * The instruction cache can still be invalidated back to the point of + * unification in a single instruction. + * + */ +ENTRY(v7_flush_kern_cache_all) + stmfd sp!, {r4-r5, r7, r9-r11, lr} + bl v7_flush_dcache_all + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate + ldmfd sp!, {r4-r5, r7, r9-r11, lr} + mov pc, lr + +/* + * v7_flush_cache_all() + * + * Flush all TLB entries in a particular address space + * + * - mm - mm_struct describing address space + */ +ENTRY(v7_flush_user_cache_all) + /*FALLTHROUGH*/ + +/* + * v7_flush_cache_range(start, end, flags) + * + * Flush a range of TLB entries in the specified address space. + * + * - start - start address (may not be aligned) + * - end - end address (exclusive, may not be aligned) + * - flags - vm_area_struct flags describing address space + * + * It is assumed that: + * - we have a VIPT cache. + */ +ENTRY(v7_flush_user_cache_range) + mov pc, lr + +/* + * v7_coherent_kern_range(start,end) + * + * Ensure that the I and D caches are coherent within specified + * region. This is typically used when code has been written to + * a memory region, and will be executed. + * + * - start - virtual start address of region + * - end - virtual end address of region + * + * It is assumed that: + * - the Icache does not read data from the write buffer + */ +ENTRY(v7_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * v7_coherent_user_range(start,end) + * + * Ensure that the I and D caches are coherent within specified + * region. This is typically used when code has been written to + * a memory region, and will be executed. + * + * - start - virtual start address of region + * - end - virtual end address of region + * + * It is assumed that: + * - the Icache does not read data from the write buffer + */ +ENTRY(v7_coherent_user_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + bic r0, r0, r3 +1: mcr p15, 0, r0, c7, c11, 1 @ clean D line to the point of unification + dsb + mcr p15, 0, r0, c7, c5, 1 @ invalidate I line + add r0, r0, r2 + cmp r0, r1 + blo 1b + mov r0, #0 + mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB + dsb + isb + mov pc, lr + +/* + * v7_flush_kern_dcache_page(kaddr) + * + * Ensure that the data held in the page kaddr is written back + * to the page in question. + * + * - kaddr - kernel address (guaranteed to be page aligned) + */ +ENTRY(v7_flush_kern_dcache_page) + dcache_line_size r2, r3 + add r1, r0, #PAGE_SZ +1: + mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line / unified line + add r0, r0, r2 + cmp r0, r1 + blo 1b + dsb + mov pc, lr + +/* + * v7_dma_inv_range(start,end) + * + * Invalidate the data cache within the specified region; we will + * be performing a DMA operation in this region and we want to + * purge old data in the cache. + * + * - start - virtual start address of region + * - end - virtual end address of region + */ +ENTRY(v7_dma_inv_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + tst r0, r3 + bic r0, r0, r3 + mcrne p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line + + tst r1, r3 + bic r1, r1, r3 + mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D / U line +1: + mcr p15, 0, r0, c7, c6, 1 @ invalidate D / U line + add r0, r0, r2 + cmp r0, r1 + blo 1b + dsb + mov pc, lr + +/* + * v7_dma_clean_range(start,end) + * - start - virtual start address of region + * - end - virtual end address of region + */ +ENTRY(v7_dma_clean_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + bic r0, r0, r3 +1: + mcr p15, 0, r0, c7, c10, 1 @ clean D / U line + add r0, r0, r2 + cmp r0, r1 + blo 1b + dsb + mov pc, lr + +/* + * v7_dma_flush_range(start,end) + * - start - virtual start address of region + * - end - virtual end address of region + */ +ENTRY(v7_dma_flush_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + bic r0, r0, r3 +1: + mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line + add r0, r0, r2 + cmp r0, r1 + blo 1b + dsb + mov pc, lr + + __INITDATA + + .type v7_cache_fns, #object +ENTRY(v7_cache_fns) + .long v7_flush_kern_cache_all + .long v7_flush_user_cache_all + .long v7_flush_user_cache_range + .long v7_coherent_kern_range + .long v7_coherent_user_range + .long v7_flush_kern_dcache_page + .long v7_dma_inv_range + .long v7_dma_clean_range + .long v7_dma_flush_range + .size v7_cache_fns, . - v7_cache_fns diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index 9e2c89eb211..b13150052a7 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S @@ -59,3 +59,15 @@ .word \ucset #endif .endm + +/* + * cache_line_size - get the cache line size from the CSIDR register + * (available on ARMv7+). It assumes that the CSSR register was configured + * to access the L1 data cache CSIDR. + */ + .macro dcache_line_size, reg, tmp + mrc p15, 1, \tmp, c0, c0, 0 @ read CSIDR + and \tmp, \tmp, #7 @ cache line size encoding + mov \reg, #16 @ size offset + mov \reg, \reg, lsl \tmp @ actual cache line size + .endm diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S new file mode 100644 index 00000000000..dd823dd4a37 --- /dev/null +++ b/arch/arm/mm/proc-v7.S @@ -0,0 +1,262 @@ +/* + * linux/arch/arm/mm/proc-v7.S + * + * Copyright (C) 2001 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is the "shell" of the ARMv7 processor support. + */ +#include +#include +#include +#include +#include +#include + +#include "proc-macros.S" + +#define TTB_C (1 << 0) +#define TTB_S (1 << 1) +#define TTB_RGN_OC_WT (2 << 3) +#define TTB_RGN_OC_WB (3 << 3) + +ENTRY(cpu_v7_proc_init) + mov pc, lr + +ENTRY(cpu_v7_proc_fin) + mov pc, lr + +/* + * cpu_v7_reset(loc) + * + * Perform a soft reset of the system. Put the CPU into the + * same state as it would be if it had been reset, and branch + * to what would be the reset vector. + * + * - loc - location to jump to for soft reset + * + * It is assumed that: + */ + .align 5 +ENTRY(cpu_v7_reset) + mov pc, r0 + +/* + * cpu_v7_do_idle() + * + * Idle the processor (eg, wait for interrupt). + * + * IRQs are already disabled. + */ +ENTRY(cpu_v7_do_idle) + .long 0xe320f003 @ ARM V7 WFI instruction + mov pc, lr + +ENTRY(cpu_v7_dcache_clean_area) +#ifndef TLB_CAN_READ_FROM_L1_CACHE + dcache_line_size r2, r3 +1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, r2 + subs r1, r1, r2 + bhi 1b + dsb +#endif + mov pc, lr + +/* + * cpu_v7_switch_mm(pgd_phys, tsk) + * + * Set the translation table base pointer to be pgd_phys + * + * - pgd_phys - physical address of new TTB + * + * It is assumed that: + * - we are not using split page tables + */ +ENTRY(cpu_v7_switch_mm) + mov r2, #0 + ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id + orr r0, r0, #TTB_RGN_OC_WB @ mark PTWs outer cacheable, WB + mcr p15, 0, r2, c13, c0, 1 @ set reserved context ID + isb +1: mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 + isb + mcr p15, 0, r1, c13, c0, 1 @ set context ID + isb + mov pc, lr + +/* + * cpu_v7_set_pte_ext(ptep, pte) + * + * Set a level 2 translation table entry. + * + * - ptep - pointer to level 2 translation table entry + * (hardware version is stored at -1024 bytes) + * - pte - PTE value to store + * - ext - value for extended PTE bits + * + * Permissions: + * YUWD APX AP1 AP0 SVC User + * 0xxx 0 0 0 no acc no acc + * 100x 1 0 1 r/o no acc + * 10x0 1 0 1 r/o no acc + * 1011 0 0 1 r/w no acc + * 110x 0 1 0 r/w r/o + * 11x0 0 1 0 r/w r/o + * 1111 0 1 1 r/w r/w + */ +ENTRY(cpu_v7_set_pte_ext) + str r1, [r0], #-2048 @ linux version + + bic r3, r1, #0x000003f0 + bic r3, r3, #0x00000003 + orr r3, r3, r2 + orr r3, r3, #PTE_EXT_AP0 | 2 + + tst r1, #L_PTE_WRITE + tstne r1, #L_PTE_DIRTY + orreq r3, r3, #PTE_EXT_APX + + tst r1, #L_PTE_USER + orrne r3, r3, #PTE_EXT_AP1 + tstne r3, #PTE_EXT_APX + bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0 + + tst r1, #L_PTE_YOUNG + biceq r3, r3, #PTE_EXT_APX | PTE_EXT_AP_MASK + + tst r1, #L_PTE_EXEC + orreq r3, r3, #PTE_EXT_XN + + tst r1, #L_PTE_PRESENT + moveq r3, #0 + + str r3, [r0] + mcr p15, 0, r0, c7, c10, 1 @ flush_pte + mov pc, lr + +cpu_v7_name: + .ascii "ARMv7 Processor" + .align + + .section ".text.init", #alloc, #execinstr + +/* + * __v7_setup + * + * Initialise TLB, Caches, and MMU state ready to switch the MMU + * on. Return in r0 the new CP15 C1 control register setting. + * + * We automatically detect if we have a Harvard cache, and use the + * Harvard cache control instructions insead of the unified cache + * control instructions. + * + * This should be able to cover all ARMv7 cores. + * + * It is assumed that: + * - cache type register is implemented + */ +__v7_setup: + adr r12, __v7_setup_stack @ the local stack + stmia r12, {r0-r5, r7, r9, r11, lr} + bl v7_flush_dcache_all + ldmia r12, {r0-r5, r7, r9, r11, lr} + mov r10, #0 +#ifdef HARVARD_CACHE + mcr p15, 0, r10, c7, c5, 0 @ I+BTB cache invalidate +#endif + dsb + mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs + mcr p15, 0, r10, c2, c0, 2 @ TTB control register + orr r4, r4, #TTB_RGN_OC_WB @ mark PTWs outer cacheable, WB + mcr p15, 0, r4, c2, c0, 0 @ load TTB0 + mcr p15, 0, r4, c2, c0, 1 @ load TTB1 + mov r10, #0x1f @ domains 0, 1 = manager + mcr p15, 0, r10, c3, c0, 0 @ load domain access register +#ifndef CONFIG_CPU_L2CACHE_DISABLE + @ L2 cache configuration in the L2 aux control register + mrc p15, 1, r10, c9, c0, 2 + bic r10, r10, #(1 << 16) @ L2 outer cache + mcr p15, 1, r10, c9, c0, 2 + @ L2 cache is enabled in the aux control register + mrc p15, 0, r10, c1, c0, 1 + orr r10, r10, #2 + mcr p15, 0, r10, c1, c0, 1 +#endif + mrc p15, 0, r0, c1, c0, 0 @ read control register + ldr r10, cr1_clear @ get mask for bits to clear + bic r0, r0, r10 @ clear bits them + ldr r10, cr1_set @ get mask for bits to set + orr r0, r0, r10 @ set them + mov pc, lr @ return to head.S:__ret + + /* + * V X F I D LR + * .... ...E PUI. .T.T 4RVI ZFRS BLDP WCAM + * rrrr rrrx xxx0 0101 xxxx xxxx x111 xxxx < forced + * 0 110 0011 1.00 .111 1101 < we want + */ + .type cr1_clear, #object + .type cr1_set, #object +cr1_clear: + .word 0x0120c302 +cr1_set: + .word 0x00c0387d + +__v7_setup_stack: + .space 4 * 11 @ 11 registers + + .type v7_processor_functions, #object +ENTRY(v7_processor_functions) + .word v7_early_abort + .word cpu_v7_proc_init + .word cpu_v7_proc_fin + .word cpu_v7_reset + .word cpu_v7_do_idle + .word cpu_v7_dcache_clean_area + .word cpu_v7_switch_mm + .word cpu_v7_set_pte_ext + .size v7_processor_functions, . - v7_processor_functions + + .type cpu_arch_name, #object +cpu_arch_name: + .asciz "armv7" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object +cpu_elf_name: + .asciz "v7" + .size cpu_elf_name, . - cpu_elf_name + .align + + .section ".proc.info.init", #alloc, #execinstr + + /* + * Match any ARMv7 processor core. + */ + .type __v7_proc_info, #object +__v7_proc_info: + .long 0x000f0000 @ Required ID value + .long 0x000f0000 @ Mask for ID + .long PMD_TYPE_SECT | \ + PMD_SECT_BUFFERABLE | \ + PMD_SECT_CACHEABLE | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ + .long PMD_TYPE_SECT | \ + PMD_SECT_XN | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ + b __v7_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP + .long cpu_v7_name + .long v7_processor_functions + .long v6wbi_tlb_fns + .long v6_user_fns + .long v7_cache_fns + .size __v7_proc_info, . - __v7_proc_info diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index afad32c76e6..b87226192be 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -102,6 +102,14 @@ //# endif #endif +#if defined(CONFIG_CPU_V7) +//# ifdef _CACHE +# define MULTI_CACHE 1 +//# else +//# define _CACHE v7 +//# endif +#endif + #if !defined(_CACHE) && !defined(MULTI_CACHE) #error Unknown cache maintainence model #endif diff --git a/include/asm-arm/glue.h b/include/asm-arm/glue.h index 0cc5d3b10ce..22274ce8137 100644 --- a/include/asm-arm/glue.h +++ b/include/asm-arm/glue.h @@ -38,6 +38,7 @@ * v5tej_early - ARMv5 with Thumb and Java early abort handler * xscale - ARMv5 with Thumb with Xscale extensions * v6_early - ARMv6 generic early abort handler + * v7_early - ARMv7 generic early abort handler */ #undef CPU_ABORT_HANDLER #undef MULTI_ABORT @@ -106,6 +107,14 @@ # endif #endif +#ifdef CONFIG_CPU_ABRT_EV7 +# ifdef CPU_ABORT_HANDLER +# define MULTI_ABORT 1 +# else +# define CPU_ABORT_HANDLER v7_early_abort +# endif +#endif + #ifndef CPU_ABORT_HANDLER #error Unknown data abort handler type #endif diff --git a/include/asm-arm/proc-fns.h b/include/asm-arm/proc-fns.h index ea7e54c319b..5599d4e5e70 100644 --- a/include/asm-arm/proc-fns.h +++ b/include/asm-arm/proc-fns.h @@ -193,6 +193,14 @@ # define CPU_NAME cpu_v6 # endif # endif +# ifdef CONFIG_CPU_V7 +# ifdef CPU_NAME +# undef MULTI_CPU +# define MULTI_CPU +# else +# define CPU_NAME cpu_v7 +# endif +# endif #endif #ifndef __ASSEMBLY__ diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index 63b3080bdac..c57555c3709 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -14,6 +14,7 @@ #define CPU_ARCH_ARMv5TE 6 #define CPU_ARCH_ARMv5TEJ 7 #define CPU_ARCH_ARMv6 8 +#define CPU_ARCH_ARMv7 9 /* * CR1 bits (CP#15 CR1) -- cgit v1.2.3 From aaf83acba9fb1f93d2e656c7e4dda4e38c1cb490 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 8 May 2007 22:38:40 +0100 Subject: [ARM] armv7: Add ARMv7 cacheid macros This patch renames the old __cacheid_* macros to __cacheid_*_prev7 and adds support for the new format. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- include/asm-arm/cacheflush.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index b87226192be..6832ef96bc1 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -426,11 +426,18 @@ static inline void flush_anon_page(struct vm_area_struct *vma, */ #define flush_icache_page(vma,page) do { } while (0) -#define __cacheid_present(val) (val != read_cpuid(CPUID_ID)) -#define __cacheid_vivt(val) ((val & (15 << 25)) != (14 << 25)) -#define __cacheid_vipt(val) ((val & (15 << 25)) == (14 << 25)) -#define __cacheid_vipt_nonaliasing(val) ((val & (15 << 25 | 1 << 23)) == (14 << 25)) -#define __cacheid_vipt_aliasing(val) ((val & (15 << 25 | 1 << 23)) == (14 << 25 | 1 << 23)) +#define __cacheid_present(val) (val != read_cpuid(CPUID_ID)) +#define __cacheid_type_v7(val) ((val & (7 << 29)) == (4 << 29)) + +#define __cacheid_vivt_prev7(val) ((val & (15 << 25)) != (14 << 25)) +#define __cacheid_vipt_prev7(val) ((val & (15 << 25)) == (14 << 25)) +#define __cacheid_vipt_nonaliasing_prev7(val) ((val & (15 << 25 | 1 << 23)) == (14 << 25)) +#define __cacheid_vipt_aliasing_prev7(val) ((val & (15 << 25 | 1 << 23)) == (14 << 25 | 1 << 23)) + +#define __cacheid_vivt(val) (__cacheid_type_v7(val) ? 0 : __cacheid_vivt_prev7(val)) +#define __cacheid_vipt(val) (__cacheid_type_v7(val) ? 1 : __cacheid_vipt_prev7(val)) +#define __cacheid_vipt_nonaliasing(val) (__cacheid_type_v7(val) ? 1 : __cacheid_vipt_nonaliasing_prev7(val)) +#define __cacheid_vipt_aliasing(val) (__cacheid_type_v7(val) ? 0 : __cacheid_vipt_aliasing_prev7(val)) #if defined(CONFIG_CPU_CACHE_VIVT) && !defined(CONFIG_CPU_CACHE_VIPT) -- cgit v1.2.3 From 56163fcf194fb688fcf3cefa9b90c5ad41f74059 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 8 May 2007 22:53:44 +0100 Subject: [ARM] armv7: add dedicated ARMv7 barrier instructions Starting with ARMv7, there are dedicated instruction for the ISB, DSB and DMB barriers and there is no need to execute them as CP15 operations. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- include/asm-arm/system.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index c57555c3709..41dd49151da 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -158,7 +158,11 @@ extern unsigned int user_debug; #define vectors_high() (0) #endif -#if defined(CONFIG_CPU_XSC3) || __LINUX_ARM_ARCH__ >= 6 +#if __LINUX_ARM_ARCH__ >= 7 +#define isb() __asm__ __volatile__ ("isb" : : : "memory") +#define dsb() __asm__ __volatile__ ("dsb" : : : "memory") +#define dmb() __asm__ __volatile__ ("dmb" : : : "memory") +#elif defined(CONFIG_CPU_XSC3) || __LINUX_ARM_ARCH__ == 6 #define isb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" \ : : "r" (0) : "memory") #define dsb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" \ -- cgit v1.2.3 From e180583b85f4a48bd55924712c88e5d8eb182e08 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 8 May 2007 15:57:59 -0700 Subject: [IA64] wire up pselect, ppoll Signed-off-by: Alexey Kuznetsov Signed-off-by: Alexey Dobriyan Signed-off-by: Tony Luck --- arch/ia64/kernel/entry.S | 4 ++-- include/asm-ia64/unistd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 156a6fa031f..b50bf208678 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1572,8 +1572,8 @@ sys_call_table: data8 sys_readlinkat data8 sys_fchmodat data8 sys_faccessat - data8 sys_ni_syscall // reserved for pselect - data8 sys_ni_syscall // 1295 reserved for ppoll + data8 sys_pselect6 + data8 sys_ppoll data8 sys_unshare data8 sys_splice data8 sys_set_robust_list diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index e586739bcc1..861c8ec87b0 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -283,7 +283,8 @@ #define __NR_readlinkat 1291 #define __NR_fchmodat 1292 #define __NR_faccessat 1293 -/* 1294, 1295 reserved for pselect/ppoll */ +#define __NR_pselect6 1294 +#define __NR_ppoll 1295 #define __NR_unshare 1296 #define __NR_splice 1297 #define __NR_set_robust_list 1298 -- cgit v1.2.3 From f7c6a7b5d59980b076abbf2ceeb8735591290285 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Sun, 4 Mar 2007 16:15:11 -0800 Subject: IB/uverbs: Export ib_umem_get()/ib_umem_release() to modules Export ib_umem_get()/ib_umem_release() and put low-level drivers in control of when to call ib_umem_get() to pin and DMA map userspace, rather than always calling it in ib_uverbs_reg_mr() before calling the low-level driver's reg_user_mr method. Also move these functions to be in the ib_core module instead of ib_uverbs, so that driver modules using them do not depend on ib_uverbs. This has a number of advantages: - It is better design from the standpoint of making generic code a library that can be used or overridden by device-specific code as the details of specific devices dictate. - Drivers that do not need to pin userspace memory regions do not need to take the performance hit of calling ib_mem_get(). For example, although I have not tried to implement it in this patch, the ipath driver should be able to avoid pinning memory and just use copy_{to,from}_user() to access userspace memory regions. - Buffers that need special mapping treatment can be identified by the low-level driver. For example, it may be possible to solve some Altix-specific memory ordering issues with mthca CQs in userspace by mapping CQ buffers with extra flags. - Drivers that need to pin and DMA map userspace memory for things other than memory regions can use ib_umem_get() directly, instead of hacks using extra parameters to their reg_phys_mr method. For example, the mlx4 driver that is pending being merged needs to pin and DMA map QP and CQ buffers, but it does not need to create a memory key for these buffers. So the cleanest solution is for mlx4 to call ib_umem_get() in the create_qp and create_cq methods. Signed-off-by: Roland Dreier --- drivers/infiniband/Kconfig | 5 + drivers/infiniband/core/Makefile | 4 +- drivers/infiniband/core/device.c | 2 + drivers/infiniband/core/umem.c | 273 +++++++++++++++++++++++++++ drivers/infiniband/core/uverbs.h | 6 +- drivers/infiniband/core/uverbs_cmd.c | 60 ++---- drivers/infiniband/core/uverbs_main.c | 11 +- drivers/infiniband/core/uverbs_mem.c | 225 ---------------------- drivers/infiniband/hw/amso1100/c2_provider.c | 42 +++-- drivers/infiniband/hw/amso1100/c2_provider.h | 1 + drivers/infiniband/hw/cxgb3/iwch_provider.c | 28 ++- drivers/infiniband/hw/cxgb3/iwch_provider.h | 1 + drivers/infiniband/hw/ehca/ehca_classes.h | 1 + drivers/infiniband/hw/ehca/ehca_iverbs.h | 3 +- drivers/infiniband/hw/ehca/ehca_mrmw.c | 69 ++++--- drivers/infiniband/hw/ipath/ipath_mr.c | 38 ++-- drivers/infiniband/hw/ipath/ipath_verbs.h | 5 +- drivers/infiniband/hw/mthca/mthca_provider.c | 38 +++- drivers/infiniband/hw/mthca/mthca_provider.h | 1 + include/rdma/ib_umem.h | 78 ++++++++ include/rdma/ib_verbs.h | 28 +-- 21 files changed, 536 insertions(+), 383 deletions(-) create mode 100644 drivers/infiniband/core/umem.c delete mode 100644 drivers/infiniband/core/uverbs_mem.c create mode 100644 include/rdma/ib_umem.h diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 66b36de9fa6..82afba5c0bf 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -29,6 +29,11 @@ config INFINIBAND_USER_ACCESS libibverbs, libibcm and a hardware driver library from . +config INFINIBAND_USER_MEM + bool + depends on INFINIBAND_USER_ACCESS != n + default y + config INFINIBAND_ADDR_TRANS bool depends on INFINIBAND && INET diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index 189e5d4b9b1..cb1ab3ea499 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \ ib_core-y := packer.o ud_header.o verbs.o sysfs.o \ device.o fmr_pool.o cache.o +ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o ib_mad-y := mad.o smi.o agent.o mad_rmpp.o @@ -28,5 +29,4 @@ ib_umad-y := user_mad.o ib_ucm-y := ucm.o -ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_mem.o \ - uverbs_marshall.o +ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 7fabb425b03..592c90aa318 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -613,6 +613,8 @@ static void __exit ib_core_cleanup(void) { ib_cache_cleanup(); ib_sysfs_cleanup(); + /* Make sure that any pending umem accounting work is done. */ + flush_scheduled_work(); } module_init(ib_core_init); diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c new file mode 100644 index 00000000000..48e854cf416 --- /dev/null +++ b/drivers/infiniband/core/umem.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Cisco Systems. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id: uverbs_mem.c 2743 2005-06-28 22:27:59Z roland $ + */ + +#include +#include + +#include "uverbs.h" + +struct ib_umem_account_work { + struct work_struct work; + struct mm_struct *mm; + unsigned long diff; +}; + + +static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) +{ + struct ib_umem_chunk *chunk, *tmp; + int i; + + list_for_each_entry_safe(chunk, tmp, &umem->chunk_list, list) { + ib_dma_unmap_sg(dev, chunk->page_list, + chunk->nents, DMA_BIDIRECTIONAL); + for (i = 0; i < chunk->nents; ++i) { + if (umem->writable && dirty) + set_page_dirty_lock(chunk->page_list[i].page); + put_page(chunk->page_list[i].page); + } + + kfree(chunk); + } +} + +/** + * ib_umem_get - Pin and DMA map userspace memory. + * @context: userspace context to pin memory for + * @addr: userspace virtual address to start at + * @size: length of region to pin + * @access: IB_ACCESS_xxx flags for memory being pinned + */ +struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, + size_t size, int access) +{ + struct ib_umem *umem; + struct page **page_list; + struct ib_umem_chunk *chunk; + unsigned long locked; + unsigned long lock_limit; + unsigned long cur_base; + unsigned long npages; + int ret; + int off; + int i; + + if (!can_do_mlock()) + return ERR_PTR(-EPERM); + + umem = kmalloc(sizeof *umem, GFP_KERNEL); + if (!umem) + return ERR_PTR(-ENOMEM); + + umem->context = context; + umem->length = size; + umem->offset = addr & ~PAGE_MASK; + umem->page_size = PAGE_SIZE; + /* + * We ask for writable memory if any access flags other than + * "remote read" are set. "Local write" and "remote write" + * obviously require write access. "Remote atomic" can do + * things like fetch and add, which will modify memory, and + * "MW bind" can change permissions by binding a window. + */ + umem->writable = !!(access & ~IB_ACCESS_REMOTE_READ); + + INIT_LIST_HEAD(&umem->chunk_list); + + page_list = (struct page **) __get_free_page(GFP_KERNEL); + if (!page_list) { + kfree(umem); + return ERR_PTR(-ENOMEM); + } + + npages = PAGE_ALIGN(size + umem->offset) >> PAGE_SHIFT; + + down_write(¤t->mm->mmap_sem); + + locked = npages + current->mm->locked_vm; + lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; + + if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { + ret = -ENOMEM; + goto out; + } + + cur_base = addr & PAGE_MASK; + + while (npages) { + ret = get_user_pages(current, current->mm, cur_base, + min_t(int, npages, + PAGE_SIZE / sizeof (struct page *)), + 1, !umem->writable, page_list, NULL); + + if (ret < 0) + goto out; + + cur_base += ret * PAGE_SIZE; + npages -= ret; + + off = 0; + + while (ret) { + chunk = kmalloc(sizeof *chunk + sizeof (struct scatterlist) * + min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK), + GFP_KERNEL); + if (!chunk) { + ret = -ENOMEM; + goto out; + } + + chunk->nents = min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK); + for (i = 0; i < chunk->nents; ++i) { + chunk->page_list[i].page = page_list[i + off]; + chunk->page_list[i].offset = 0; + chunk->page_list[i].length = PAGE_SIZE; + } + + chunk->nmap = ib_dma_map_sg(context->device, + &chunk->page_list[0], + chunk->nents, + DMA_BIDIRECTIONAL); + if (chunk->nmap <= 0) { + for (i = 0; i < chunk->nents; ++i) + put_page(chunk->page_list[i].page); + kfree(chunk); + + ret = -ENOMEM; + goto out; + } + + ret -= chunk->nents; + off += chunk->nents; + list_add_tail(&chunk->list, &umem->chunk_list); + } + + ret = 0; + } + +out: + if (ret < 0) { + __ib_umem_release(context->device, umem, 0); + kfree(umem); + } else + current->mm->locked_vm = locked; + + up_write(¤t->mm->mmap_sem); + free_page((unsigned long) page_list); + + return ret < 0 ? ERR_PTR(ret) : umem; +} +EXPORT_SYMBOL(ib_umem_get); + +static void ib_umem_account(struct work_struct *_work) +{ + struct ib_umem_account_work *work = + container_of(_work, struct ib_umem_account_work, work); + + down_write(&work->mm->mmap_sem); + work->mm->locked_vm -= work->diff; + up_write(&work->mm->mmap_sem); + mmput(work->mm); + kfree(work); +} + +/** + * ib_umem_release - release memory pinned with ib_umem_get + * @umem: umem struct to release + */ +void ib_umem_release(struct ib_umem *umem) +{ + struct ib_umem_account_work *work; + struct ib_ucontext *context = umem->context; + struct mm_struct *mm; + unsigned long diff; + + __ib_umem_release(umem->context->device, umem, 1); + + mm = get_task_mm(current); + if (!mm) + return; + + diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; + kfree(umem); + + /* + * We may be called with the mm's mmap_sem already held. This + * can happen when a userspace munmap() is the call that drops + * the last reference to our file and calls our release + * method. If there are memory regions to destroy, we'll end + * up here and not be able to take the mmap_sem. In that case + * we defer the vm_locked accounting to the system workqueue. + */ + if (context->closing && !down_write_trylock(&mm->mmap_sem)) { + work = kmalloc(sizeof *work, GFP_KERNEL); + if (!work) { + mmput(mm); + return; + } + + INIT_WORK(&work->work, ib_umem_account); + work->mm = mm; + work->diff = diff; + + schedule_work(&work->work); + return; + } else + down_write(&mm->mmap_sem); + + current->mm->locked_vm -= diff; + up_write(&mm->mmap_sem); + mmput(mm); +} +EXPORT_SYMBOL(ib_umem_release); + +int ib_umem_page_count(struct ib_umem *umem) +{ + struct ib_umem_chunk *chunk; + int shift; + int i; + int n; + + shift = ilog2(umem->page_size); + + n = 0; + list_for_each_entry(chunk, &umem->chunk_list, list) + for (i = 0; i < chunk->nmap; ++i) + n += sg_dma_len(&chunk->page_list[i]) >> shift; + + return n; +} +EXPORT_SYMBOL(ib_umem_page_count); diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index 102a59c033f..c33546f9e96 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -45,6 +45,7 @@ #include #include +#include #include /* @@ -163,11 +164,6 @@ void ib_uverbs_srq_event_handler(struct ib_event *event, void *context_ptr); void ib_uverbs_event_handler(struct ib_event_handler *handler, struct ib_event *event); -int ib_umem_get(struct ib_device *dev, struct ib_umem *mem, - void *addr, size_t size, int write); -void ib_umem_release(struct ib_device *dev, struct ib_umem *umem); -void ib_umem_release_on_close(struct ib_device *dev, struct ib_umem *umem); - #define IB_UVERBS_DECLARE_CMD(name) \ ssize_t ib_uverbs_##name(struct ib_uverbs_file *file, \ const char __user *buf, int in_len, \ diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index bab66769be1..01d70084aeb 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2005 Topspin Communications. All rights reserved. - * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems. All rights reserved. * Copyright (c) 2005 PathScale, Inc. All rights reserved. * Copyright (c) 2006 Mellanox Technologies. All rights reserved. * @@ -295,6 +295,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, INIT_LIST_HEAD(&ucontext->qp_list); INIT_LIST_HEAD(&ucontext->srq_list); INIT_LIST_HEAD(&ucontext->ah_list); + ucontext->closing = 0; resp.num_comp_vectors = file->device->num_comp_vectors; @@ -573,7 +574,7 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, struct ib_uverbs_reg_mr cmd; struct ib_uverbs_reg_mr_resp resp; struct ib_udata udata; - struct ib_umem_object *obj; + struct ib_uobject *uobj; struct ib_pd *pd; struct ib_mr *mr; int ret; @@ -599,35 +600,21 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, !(cmd.access_flags & IB_ACCESS_LOCAL_WRITE)) return -EINVAL; - obj = kmalloc(sizeof *obj, GFP_KERNEL); - if (!obj) + uobj = kmalloc(sizeof *uobj, GFP_KERNEL); + if (!uobj) return -ENOMEM; - init_uobj(&obj->uobject, 0, file->ucontext, &mr_lock_key); - down_write(&obj->uobject.mutex); - - /* - * We ask for writable memory if any access flags other than - * "remote read" are set. "Local write" and "remote write" - * obviously require write access. "Remote atomic" can do - * things like fetch and add, which will modify memory, and - * "MW bind" can change permissions by binding a window. - */ - ret = ib_umem_get(file->device->ib_dev, &obj->umem, - (void *) (unsigned long) cmd.start, cmd.length, - !!(cmd.access_flags & ~IB_ACCESS_REMOTE_READ)); - if (ret) - goto err_free; - - obj->umem.virt_base = cmd.hca_va; + init_uobj(uobj, 0, file->ucontext, &mr_lock_key); + down_write(&uobj->mutex); pd = idr_read_pd(cmd.pd_handle, file->ucontext); if (!pd) { ret = -EINVAL; - goto err_release; + goto err_free; } - mr = pd->device->reg_user_mr(pd, &obj->umem, cmd.access_flags, &udata); + mr = pd->device->reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va, + cmd.access_flags, &udata); if (IS_ERR(mr)) { ret = PTR_ERR(mr); goto err_put; @@ -635,19 +622,19 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, mr->device = pd->device; mr->pd = pd; - mr->uobject = &obj->uobject; + mr->uobject = uobj; atomic_inc(&pd->usecnt); atomic_set(&mr->usecnt, 0); - obj->uobject.object = mr; - ret = idr_add_uobj(&ib_uverbs_mr_idr, &obj->uobject); + uobj->object = mr; + ret = idr_add_uobj(&ib_uverbs_mr_idr, uobj); if (ret) goto err_unreg; memset(&resp, 0, sizeof resp); resp.lkey = mr->lkey; resp.rkey = mr->rkey; - resp.mr_handle = obj->uobject.id; + resp.mr_handle = uobj->id; if (copy_to_user((void __user *) (unsigned long) cmd.response, &resp, sizeof resp)) { @@ -658,17 +645,17 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, put_pd_read(pd); mutex_lock(&file->mutex); - list_add_tail(&obj->uobject.list, &file->ucontext->mr_list); + list_add_tail(&uobj->list, &file->ucontext->mr_list); mutex_unlock(&file->mutex); - obj->uobject.live = 1; + uobj->live = 1; - up_write(&obj->uobject.mutex); + up_write(&uobj->mutex); return in_len; err_copy: - idr_remove_uobj(&ib_uverbs_mr_idr, &obj->uobject); + idr_remove_uobj(&ib_uverbs_mr_idr, uobj); err_unreg: ib_dereg_mr(mr); @@ -676,11 +663,8 @@ err_unreg: err_put: put_pd_read(pd); -err_release: - ib_umem_release(file->device->ib_dev, &obj->umem); - err_free: - put_uobj_write(&obj->uobject); + put_uobj_write(uobj); return ret; } @@ -691,7 +675,6 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, struct ib_uverbs_dereg_mr cmd; struct ib_mr *mr; struct ib_uobject *uobj; - struct ib_umem_object *memobj; int ret = -EINVAL; if (copy_from_user(&cmd, buf, sizeof cmd)) @@ -701,8 +684,7 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, if (!uobj) return -EINVAL; - memobj = container_of(uobj, struct ib_umem_object, uobject); - mr = uobj->object; + mr = uobj->object; ret = ib_dereg_mr(mr); if (!ret) @@ -719,8 +701,6 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, list_del(&uobj->list); mutex_unlock(&file->mutex); - ib_umem_release(file->device->ib_dev, &memobj->umem); - put_uobj(uobj); return in_len; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index d44e5479965..14d7ccd8919 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -183,6 +183,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, if (!context) return 0; + context->closing = 1; + list_for_each_entry_safe(uobj, tmp, &context->ah_list, list) { struct ib_ah *ah = uobj->object; @@ -230,16 +232,10 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, list_for_each_entry_safe(uobj, tmp, &context->mr_list, list) { struct ib_mr *mr = uobj->object; - struct ib_device *mrdev = mr->device; - struct ib_umem_object *memobj; idr_remove_uobj(&ib_uverbs_mr_idr, uobj); ib_dereg_mr(mr); - - memobj = container_of(uobj, struct ib_umem_object, uobject); - ib_umem_release_on_close(mrdev, &memobj->umem); - - kfree(memobj); + kfree(uobj); } list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) { @@ -906,7 +902,6 @@ static void __exit ib_uverbs_cleanup(void) unregister_filesystem(&uverbs_event_fs); class_destroy(uverbs_class); unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES); - flush_scheduled_work(); idr_destroy(&ib_uverbs_pd_idr); idr_destroy(&ib_uverbs_mr_idr); idr_destroy(&ib_uverbs_mw_idr); diff --git a/drivers/infiniband/core/uverbs_mem.c b/drivers/infiniband/core/uverbs_mem.c deleted file mode 100644 index c95fe952abd..00000000000 --- a/drivers/infiniband/core/uverbs_mem.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2005 Topspin Communications. All rights reserved. - * Copyright (c) 2005 Cisco Systems. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. 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 - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * $Id: uverbs_mem.c 2743 2005-06-28 22:27:59Z roland $ - */ - -#include -#include - -#include "uverbs.h" - -struct ib_umem_account_work { - struct work_struct work; - struct mm_struct *mm; - unsigned long diff; -}; - - -static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) -{ - struct ib_umem_chunk *chunk, *tmp; - int i; - - list_for_each_entry_safe(chunk, tmp, &umem->chunk_list, list) { - ib_dma_unmap_sg(dev, chunk->page_list, - chunk->nents, DMA_BIDIRECTIONAL); - for (i = 0; i < chunk->nents; ++i) { - if (umem->writable && dirty) - set_page_dirty_lock(chunk->page_list[i].page); - put_page(chunk->page_list[i].page); - } - - kfree(chunk); - } -} - -int ib_umem_get(struct ib_device *dev, struct ib_umem *mem, - void *addr, size_t size, int write) -{ - struct page **page_list; - struct ib_umem_chunk *chunk; - unsigned long locked; - unsigned long lock_limit; - unsigned long cur_base; - unsigned long npages; - int ret = 0; - int off; - int i; - - if (!can_do_mlock()) - return -EPERM; - - page_list = (struct page **) __get_free_page(GFP_KERNEL); - if (!page_list) - return -ENOMEM; - - mem->user_base = (unsigned long) addr; - mem->length = size; - mem->offset = (unsigned long) addr & ~PAGE_MASK; - mem->page_size = PAGE_SIZE; - mem->writable = write; - - INIT_LIST_HEAD(&mem->chunk_list); - - npages = PAGE_ALIGN(size + mem->offset) >> PAGE_SHIFT; - - down_write(¤t->mm->mmap_sem); - - locked = npages + current->mm->locked_vm; - lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; - - if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { - ret = -ENOMEM; - goto out; - } - - cur_base = (unsigned long) addr & PAGE_MASK; - - while (npages) { - ret = get_user_pages(current, current->mm, cur_base, - min_t(int, npages, - PAGE_SIZE / sizeof (struct page *)), - 1, !write, page_list, NULL); - - if (ret < 0) - goto out; - - cur_base += ret * PAGE_SIZE; - npages -= ret; - - off = 0; - - while (ret) { - chunk = kmalloc(sizeof *chunk + sizeof (struct scatterlist) * - min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK), - GFP_KERNEL); - if (!chunk) { - ret = -ENOMEM; - goto out; - } - - chunk->nents = min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK); - for (i = 0; i < chunk->nents; ++i) { - chunk->page_list[i].page = page_list[i + off]; - chunk->page_list[i].offset = 0; - chunk->page_list[i].length = PAGE_SIZE; - } - - chunk->nmap = ib_dma_map_sg(dev, - &chunk->page_list[0], - chunk->nents, - DMA_BIDIRECTIONAL); - if (chunk->nmap <= 0) { - for (i = 0; i < chunk->nents; ++i) - put_page(chunk->page_list[i].page); - kfree(chunk); - - ret = -ENOMEM; - goto out; - } - - ret -= chunk->nents; - off += chunk->nents; - list_add_tail(&chunk->list, &mem->chunk_list); - } - - ret = 0; - } - -out: - if (ret < 0) - __ib_umem_release(dev, mem, 0); - else - current->mm->locked_vm = locked; - - up_write(¤t->mm->mmap_sem); - free_page((unsigned long) page_list); - - return ret; -} - -void ib_umem_release(struct ib_device *dev, struct ib_umem *umem) -{ - __ib_umem_release(dev, umem, 1); - - down_write(¤t->mm->mmap_sem); - current->mm->locked_vm -= - PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; - up_write(¤t->mm->mmap_sem); -} - -static void ib_umem_account(struct work_struct *_work) -{ - struct ib_umem_account_work *work = - container_of(_work, struct ib_umem_account_work, work); - - down_write(&work->mm->mmap_sem); - work->mm->locked_vm -= work->diff; - up_write(&work->mm->mmap_sem); - mmput(work->mm); - kfree(work); -} - -void ib_umem_release_on_close(struct ib_device *dev, struct ib_umem *umem) -{ - struct ib_umem_account_work *work; - struct mm_struct *mm; - - __ib_umem_release(dev, umem, 1); - - mm = get_task_mm(current); - if (!mm) - return; - - /* - * We may be called with the mm's mmap_sem already held. This - * can happen when a userspace munmap() is the call that drops - * the last reference to our file and calls our release - * method. If there are memory regions to destroy, we'll end - * up here and not be able to take the mmap_sem. Therefore we - * defer the vm_locked accounting to the system workqueue. - */ - - work = kmalloc(sizeof *work, GFP_KERNEL); - if (!work) { - mmput(mm); - return; - } - - INIT_WORK(&work->work, ib_umem_account); - work->mm = mm; - work->diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; - - schedule_work(&work->work); -} diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index 109166223c0..997cf153076 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -56,6 +56,7 @@ #include #include +#include #include #include "c2.h" #include "c2_provider.h" @@ -396,6 +397,7 @@ static struct ib_mr *c2_reg_phys_mr(struct ib_pd *ib_pd, } mr->pd = to_c2pd(ib_pd); + mr->umem = NULL; pr_debug("%s - page shift %d, pbl_depth %d, total_len %u, " "*iova_start %llx, first pa %llx, last pa %llx\n", __FUNCTION__, page_shift, pbl_depth, total_len, @@ -428,8 +430,8 @@ static struct ib_mr *c2_get_dma_mr(struct ib_pd *pd, int acc) return c2_reg_phys_mr(pd, &bl, 1, acc, &kva); } -static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, - int acc, struct ib_udata *udata) +static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int acc, struct ib_udata *udata) { u64 *pages; u64 kva = 0; @@ -441,15 +443,23 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, struct c2_mr *c2mr; pr_debug("%s:%u\n", __FUNCTION__, __LINE__); - shift = ffs(region->page_size) - 1; c2mr = kmalloc(sizeof(*c2mr), GFP_KERNEL); if (!c2mr) return ERR_PTR(-ENOMEM); c2mr->pd = c2pd; + c2mr->umem = ib_umem_get(pd->uobject->context, start, length, acc); + if (IS_ERR(c2mr->umem)) { + err = PTR_ERR(c2mr->umem); + kfree(c2mr); + return ERR_PTR(err); + } + + shift = ffs(c2mr->umem->page_size) - 1; + n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &c2mr->umem->chunk_list, list) n += chunk->nents; pages = kmalloc(n * sizeof(u64), GFP_KERNEL); @@ -459,35 +469,34 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, } i = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) { + list_for_each_entry(chunk, &c2mr->umem->chunk_list, list) { for (j = 0; j < chunk->nmap; ++j) { len = sg_dma_len(&chunk->page_list[j]) >> shift; for (k = 0; k < len; ++k) { pages[i++] = sg_dma_address(&chunk->page_list[j]) + - (region->page_size * k); + (c2mr->umem->page_size * k); } } } - kva = (u64)region->virt_base; + kva = virt; err = c2_nsmr_register_phys_kern(to_c2dev(pd->device), pages, - region->page_size, + c2mr->umem->page_size, i, - region->length, - region->offset, + length, + c2mr->umem->offset, &kva, c2_convert_access(acc), c2mr); kfree(pages); - if (err) { - kfree(c2mr); - return ERR_PTR(err); - } + if (err) + goto err; return &c2mr->ibmr; err: + ib_umem_release(c2mr->umem); kfree(c2mr); return ERR_PTR(err); } @@ -502,8 +511,11 @@ static int c2_dereg_mr(struct ib_mr *ib_mr) err = c2_stag_dealloc(to_c2dev(ib_mr->device), ib_mr->lkey); if (err) pr_debug("c2_stag_dealloc failed: %d\n", err); - else + else { + if (mr->umem) + ib_umem_release(mr->umem); kfree(mr); + } return err; } diff --git a/drivers/infiniband/hw/amso1100/c2_provider.h b/drivers/infiniband/hw/amso1100/c2_provider.h index fc906223220..1076df2ee96 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.h +++ b/drivers/infiniband/hw/amso1100/c2_provider.h @@ -73,6 +73,7 @@ struct c2_pd { struct c2_mr { struct ib_mr ibmr; struct c2_pd *pd; + struct ib_umem *umem; }; struct c2_av; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index a891493fd34..e7c2c394803 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include "cxio_hal.h" @@ -443,6 +444,8 @@ static int iwch_dereg_mr(struct ib_mr *ib_mr) remove_handle(rhp, &rhp->mmidr, mmid); if (mhp->kva) kfree((void *) (unsigned long) mhp->kva); + if (mhp->umem) + ib_umem_release(mhp->umem); PDBG("%s mmid 0x%x ptr %p\n", __FUNCTION__, mmid, mhp); kfree(mhp); return 0; @@ -577,8 +580,8 @@ static int iwch_reregister_phys_mem(struct ib_mr *mr, } -static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, - int acc, struct ib_udata *udata) +static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int acc, struct ib_udata *udata) { __be64 *pages; int shift, n, len; @@ -591,7 +594,6 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, struct iwch_reg_user_mr_resp uresp; PDBG("%s ib_pd %p\n", __FUNCTION__, pd); - shift = ffs(region->page_size) - 1; php = to_iwch_pd(pd); rhp = php->rhp; @@ -599,8 +601,17 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, if (!mhp) return ERR_PTR(-ENOMEM); + mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc); + if (IS_ERR(mhp->umem)) { + err = PTR_ERR(mhp->umem); + kfree(mhp); + return ERR_PTR(err); + } + + shift = ffs(mhp->umem->page_size) - 1; + n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &mhp->umem->chunk_list, list) n += chunk->nents; pages = kmalloc(n * sizeof(u64), GFP_KERNEL); @@ -611,13 +622,13 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, i = n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &mhp->umem->chunk_list, list) for (j = 0; j < chunk->nmap; ++j) { len = sg_dma_len(&chunk->page_list[j]) >> shift; for (k = 0; k < len; ++k) { pages[i++] = cpu_to_be64(sg_dma_address( &chunk->page_list[j]) + - region->page_size * k); + mhp->umem->page_size * k); } } @@ -625,9 +636,9 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, mhp->attr.pdid = php->pdid; mhp->attr.zbva = 0; mhp->attr.perms = iwch_ib_to_tpt_access(acc); - mhp->attr.va_fbo = region->virt_base; + mhp->attr.va_fbo = virt; mhp->attr.page_size = shift - 12; - mhp->attr.len = (u32) region->length; + mhp->attr.len = (u32) length; mhp->attr.pbl_size = i; err = iwch_register_mem(rhp, php, mhp, shift, pages); kfree(pages); @@ -650,6 +661,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, return &mhp->ibmr; err: + ib_umem_release(mhp->umem); kfree(mhp); return ERR_PTR(err); } diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.h b/drivers/infiniband/hw/cxgb3/iwch_provider.h index 93bcc56756b..48833f3f3bd 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.h +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.h @@ -73,6 +73,7 @@ struct tpt_attributes { struct iwch_mr { struct ib_mr ibmr; + struct ib_umem *umem; struct iwch_dev *rhp; u64 kva; struct tpt_attributes attr; diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index 10fb8fbafa0..f64d42b0867 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -176,6 +176,7 @@ struct ehca_mr { struct ib_mr ib_mr; /* must always be first in ehca_mr */ struct ib_fmr ib_fmr; /* must always be first in ehca_mr */ } ib; + struct ib_umem *umem; spinlock_t mrlock; enum ehca_mr_flag flags; diff --git a/drivers/infiniband/hw/ehca/ehca_iverbs.h b/drivers/infiniband/hw/ehca/ehca_iverbs.h index e14b029332c..37e7fe0908c 100644 --- a/drivers/infiniband/hw/ehca/ehca_iverbs.h +++ b/drivers/infiniband/hw/ehca/ehca_iverbs.h @@ -78,8 +78,7 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, int num_phys_buf, int mr_access_flags, u64 *iova_start); -struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, - struct ib_umem *region, +struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, int mr_access_flags, struct ib_udata *udata); int ehca_rereg_phys_mr(struct ib_mr *mr, diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index d22ab563633..84c5bb49856 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -39,6 +39,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include + #include #include "ehca_iverbs.h" @@ -238,10 +240,8 @@ reg_phys_mr_exit0: /*----------------------------------------------------------------------*/ -struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, - struct ib_umem *region, - int mr_access_flags, - struct ib_udata *udata) +struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, + int mr_access_flags, struct ib_udata *udata) { struct ib_mr *ib_mr; struct ehca_mr *e_mr; @@ -257,11 +257,7 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, ehca_gen_err("bad pd=%p", pd); return ERR_PTR(-EFAULT); } - if (!region) { - ehca_err(pd->device, "bad input values: region=%p", region); - ib_mr = ERR_PTR(-EINVAL); - goto reg_user_mr_exit0; - } + if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) && !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) || ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) && @@ -275,17 +271,10 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, ib_mr = ERR_PTR(-EINVAL); goto reg_user_mr_exit0; } - if (region->page_size != PAGE_SIZE) { - ehca_err(pd->device, "page size not supported, " - "region->page_size=%x", region->page_size); - ib_mr = ERR_PTR(-EINVAL); - goto reg_user_mr_exit0; - } - if ((region->length == 0) || - ((region->virt_base + region->length) < region->virt_base)) { + if (length == 0 || virt + length < virt) { ehca_err(pd->device, "bad input values: length=%lx " - "virt_base=%lx", region->length, region->virt_base); + "virt_base=%lx", length, virt); ib_mr = ERR_PTR(-EINVAL); goto reg_user_mr_exit0; } @@ -297,40 +286,55 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, goto reg_user_mr_exit0; } + e_mr->umem = ib_umem_get(pd->uobject->context, start, length, + mr_access_flags); + if (IS_ERR(e_mr->umem)) { + ib_mr = (void *) e_mr->umem; + goto reg_user_mr_exit1; + } + + if (e_mr->umem->page_size != PAGE_SIZE) { + ehca_err(pd->device, "page size not supported, " + "e_mr->umem->page_size=%x", e_mr->umem->page_size); + ib_mr = ERR_PTR(-EINVAL); + goto reg_user_mr_exit2; + } + /* determine number of MR pages */ - num_pages_mr = (((region->virt_base % PAGE_SIZE) + region->length + - PAGE_SIZE - 1) / PAGE_SIZE); - num_pages_4k = (((region->virt_base % EHCA_PAGESIZE) + region->length + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE); + num_pages_mr = (((virt % PAGE_SIZE) + length + PAGE_SIZE - 1) / + PAGE_SIZE); + num_pages_4k = (((virt % EHCA_PAGESIZE) + length + EHCA_PAGESIZE - 1) / + EHCA_PAGESIZE); /* register MR on HCA */ pginfo.type = EHCA_MR_PGI_USER; pginfo.num_pages = num_pages_mr; pginfo.num_4k = num_pages_4k; - pginfo.region = region; - pginfo.next_4k = region->offset / EHCA_PAGESIZE; + pginfo.region = e_mr->umem; + pginfo.next_4k = e_mr->umem->offset / EHCA_PAGESIZE; pginfo.next_chunk = list_prepare_entry(pginfo.next_chunk, - (®ion->chunk_list), + (&e_mr->umem->chunk_list), list); - ret = ehca_reg_mr(shca, e_mr, (u64*)region->virt_base, - region->length, mr_access_flags, e_pd, &pginfo, - &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey); + ret = ehca_reg_mr(shca, e_mr, (u64*) virt, length, mr_access_flags, e_pd, + &pginfo, &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey); if (ret) { ib_mr = ERR_PTR(ret); - goto reg_user_mr_exit1; + goto reg_user_mr_exit2; } /* successful registration of all pages */ return &e_mr->ib.ib_mr; +reg_user_mr_exit2: + ib_umem_release(e_mr->umem); reg_user_mr_exit1: ehca_mr_delete(e_mr); reg_user_mr_exit0: if (IS_ERR(ib_mr)) - ehca_err(pd->device, "rc=%lx pd=%p region=%p mr_access_flags=%x" + ehca_err(pd->device, "rc=%lx pd=%p mr_access_flags=%x" " udata=%p", - PTR_ERR(ib_mr), pd, region, mr_access_flags, udata); + PTR_ERR(ib_mr), pd, mr_access_flags, udata); return ib_mr; } /* end ehca_reg_user_mr() */ @@ -596,6 +600,9 @@ int ehca_dereg_mr(struct ib_mr *mr) goto dereg_mr_exit0; } + if (e_mr->umem) + ib_umem_release(e_mr->umem); + /* successful deregistration */ ehca_mr_delete(e_mr); diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c index 31e70732e36..bdeef8d4f27 100644 --- a/drivers/infiniband/hw/ipath/ipath_mr.c +++ b/drivers/infiniband/hw/ipath/ipath_mr.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include @@ -147,6 +148,7 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd, mr->mr.offset = 0; mr->mr.access_flags = acc; mr->mr.max_segs = num_phys_buf; + mr->umem = NULL; m = 0; n = 0; @@ -170,46 +172,56 @@ bail: /** * ipath_reg_user_mr - register a userspace memory region * @pd: protection domain for this memory region - * @region: the user memory region + * @start: starting userspace address + * @length: length of region to register + * @virt_addr: virtual address to use (from HCA's point of view) * @mr_access_flags: access flags for this memory region * @udata: unused by the InfiniPath driver * * Returns the memory region on success, otherwise returns an errno. */ -struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, - int mr_access_flags, struct ib_udata *udata) +struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, + struct ib_udata *udata) { struct ipath_mr *mr; + struct ib_umem *umem; struct ib_umem_chunk *chunk; int n, m, i; struct ib_mr *ret; - if (region->length == 0) { + if (length == 0) { ret = ERR_PTR(-EINVAL); goto bail; } + umem = ib_umem_get(pd->uobject->context, start, length, mr_access_flags); + if (IS_ERR(umem)) + return (void *) umem; + n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &umem->chunk_list, list) n += chunk->nents; mr = alloc_mr(n, &to_idev(pd->device)->lk_table); if (!mr) { ret = ERR_PTR(-ENOMEM); + ib_umem_release(umem); goto bail; } mr->mr.pd = pd; - mr->mr.user_base = region->user_base; - mr->mr.iova = region->virt_base; - mr->mr.length = region->length; - mr->mr.offset = region->offset; + mr->mr.user_base = start; + mr->mr.iova = virt_addr; + mr->mr.length = length; + mr->mr.offset = umem->offset; mr->mr.access_flags = mr_access_flags; mr->mr.max_segs = n; + mr->umem = umem; m = 0; n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) { + list_for_each_entry(chunk, &umem->chunk_list, list) { for (i = 0; i < chunk->nents; i++) { void *vaddr; @@ -219,7 +231,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, goto bail; } mr->mr.map[m]->segs[n].vaddr = vaddr; - mr->mr.map[m]->segs[n].length = region->page_size; + mr->mr.map[m]->segs[n].length = umem->page_size; n++; if (n == IPATH_SEGSZ) { m++; @@ -253,6 +265,10 @@ int ipath_dereg_mr(struct ib_mr *ibmr) i--; kfree(mr->mr.map[i]); } + + if (mr->umem) + ib_umem_release(mr->umem); + kfree(mr); return 0; } diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index 7064fc22272..088b837ebea 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -251,6 +251,7 @@ struct ipath_sge { /* Memory region */ struct ipath_mr { struct ib_mr ibmr; + struct ib_umem *umem; struct ipath_mregion mr; /* must be last */ }; @@ -751,8 +752,8 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd, struct ib_phys_buf *buffer_list, int num_phys_buf, int acc, u64 *iova_start); -struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, - int mr_access_flags, +struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, struct ib_udata *udata); int ipath_dereg_mr(struct ib_mr *ibmr); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 1c05486c3c6..6bcde1cb968 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -37,6 +37,7 @@ */ #include +#include #include #include @@ -908,6 +909,8 @@ static struct ib_mr *mthca_get_dma_mr(struct ib_pd *pd, int acc) return ERR_PTR(err); } + mr->umem = NULL; + return &mr->ibmr; } @@ -1003,11 +1006,13 @@ static struct ib_mr *mthca_reg_phys_mr(struct ib_pd *pd, } kfree(page_list); + mr->umem = NULL; + return &mr->ibmr; } -static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, - int acc, struct ib_udata *udata) +static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int acc, struct ib_udata *udata) { struct mthca_dev *dev = to_mdev(pd->device); struct ib_umem_chunk *chunk; @@ -1018,20 +1023,26 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, int err = 0; int write_mtt_size; - shift = ffs(region->page_size) - 1; - mr = kmalloc(sizeof *mr, GFP_KERNEL); if (!mr) return ERR_PTR(-ENOMEM); + mr->umem = ib_umem_get(pd->uobject->context, start, length, acc); + if (IS_ERR(mr->umem)) { + err = PTR_ERR(mr->umem); + goto err; + } + + shift = ffs(mr->umem->page_size) - 1; + n = 0; - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &mr->umem->chunk_list, list) n += chunk->nents; mr->mtt = mthca_alloc_mtt(dev, n); if (IS_ERR(mr->mtt)) { err = PTR_ERR(mr->mtt); - goto err; + goto err_umem; } pages = (u64 *) __get_free_page(GFP_KERNEL); @@ -1044,12 +1055,12 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, write_mtt_size = min(mthca_write_mtt_size(dev), (int) (PAGE_SIZE / sizeof *pages)); - list_for_each_entry(chunk, ®ion->chunk_list, list) + list_for_each_entry(chunk, &mr->umem->chunk_list, list) for (j = 0; j < chunk->nmap; ++j) { len = sg_dma_len(&chunk->page_list[j]) >> shift; for (k = 0; k < len; ++k) { pages[i++] = sg_dma_address(&chunk->page_list[j]) + - region->page_size * k; + mr->umem->page_size * k; /* * Be friendly to write_mtt and pass it chunks * of appropriate size. @@ -1071,8 +1082,8 @@ mtt_done: if (err) goto err_mtt; - err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, shift, region->virt_base, - region->length, convert_access(acc), mr); + err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, shift, virt, length, + convert_access(acc), mr); if (err) goto err_mtt; @@ -1082,6 +1093,9 @@ mtt_done: err_mtt: mthca_free_mtt(dev, mr->mtt); +err_umem: + ib_umem_release(mr->umem); + err: kfree(mr); return ERR_PTR(err); @@ -1090,8 +1104,12 @@ err: static int mthca_dereg_mr(struct ib_mr *mr) { struct mthca_mr *mmr = to_mmr(mr); + mthca_free_mr(to_mdev(mr->device), mmr); + if (mmr->umem) + ib_umem_release(mmr->umem); kfree(mmr); + return 0; } diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index 1d266ac2e09..262616c8ebb 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h @@ -73,6 +73,7 @@ struct mthca_mtt; struct mthca_mr { struct ib_mr ibmr; + struct ib_umem *umem; struct mthca_mtt *mtt; }; diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h new file mode 100644 index 00000000000..06307f7e43e --- /dev/null +++ b/include/rdma/ib_umem.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2007 Cisco Systems. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IB_UMEM_H +#define IB_UMEM_H + +#include +#include + +struct ib_ucontext; + +struct ib_umem { + struct ib_ucontext *context; + size_t length; + int offset; + int page_size; + int writable; + struct list_head chunk_list; +}; + +struct ib_umem_chunk { + struct list_head list; + int nents; + int nmap; + struct scatterlist page_list[0]; +}; + +#ifdef CONFIG_INFINIBAND_USER_MEM + +struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, + size_t size, int access); +void ib_umem_release(struct ib_umem *umem); +int ib_umem_page_count(struct ib_umem *umem); + +#else /* CONFIG_INFINIBAND_USER_MEM */ + +#include + +static inline struct ib_umem *ib_umem_get(struct ib_ucontext *context, + unsigned long addr, size_t size, + int access) { + return ERR_PTR(-EINVAL); +} +static inline void ib_umem_release(struct ib_umem *umem) { } +static inline int ib_umem_page_count(struct ib_umem *umem) { return 0; } + +#endif /* CONFIG_INFINIBAND_USER_MEM */ + +#endif /* IB_UMEM_H */ diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 5342ac64ed1..47cefca59c8 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -5,7 +5,7 @@ * Copyright (c) 2004 Topspin Corporation. All rights reserved. * Copyright (c) 2004 Voltaire Corporation. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. - * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems. 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 @@ -710,6 +710,7 @@ struct ib_ucontext { struct list_head qp_list; struct list_head srq_list; struct list_head ah_list; + int closing; }; struct ib_uobject { @@ -723,23 +724,6 @@ struct ib_uobject { int live; }; -struct ib_umem { - unsigned long user_base; - unsigned long virt_base; - size_t length; - int offset; - int page_size; - int writable; - struct list_head chunk_list; -}; - -struct ib_umem_chunk { - struct list_head list; - int nents; - int nmap; - struct scatterlist page_list[0]; -}; - struct ib_udata { void __user *inbuf; void __user *outbuf; @@ -752,11 +736,6 @@ struct ib_udata { ((void *) &((struct ib_umem_chunk *) 0)->page_list[1] - \ (void *) &((struct ib_umem_chunk *) 0)->page_list[0])) -struct ib_umem_object { - struct ib_uobject uobject; - struct ib_umem umem; -}; - struct ib_pd { struct ib_device *device; struct ib_uobject *uobject; @@ -1003,7 +982,8 @@ struct ib_device { int mr_access_flags, u64 *iova_start); struct ib_mr * (*reg_user_mr)(struct ib_pd *pd, - struct ib_umem *region, + u64 start, u64 length, + u64 virt_addr, int mr_access_flags, struct ib_udata *udata); int (*query_mr)(struct ib_mr *mr, -- cgit v1.2.3 From 1bf66a30421ca772820f489d88c16d0c430d6a67 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 18 Apr 2007 20:20:28 -0700 Subject: IB: Put rlimit accounting struct in struct ib_umem When memory pinned with ib_umem_get() is released, ib_umem_release() needs to subtract the amount of memory being unpinned from mm->locked_vm. However, ib_umem_release() may be called with mm->mmap_sem already held for writing if the memory is being released as part of an munmap() call, so it is sometimes necessary to defer this accounting into a workqueue. However, the work struct used to defer this accounting is dynamically allocated before it is queued, so there is the possibility of failing that allocation. If the allocation fails, then ib_umem_release has no choice except to bail out and leave the process with a permanently elevated locked_vm. Fix this by allocating the structure to defer accounting as part of the original struct ib_umem, so there's no possibility of failing a later allocation if creating the struct ib_umem and pinning memory succeeds. Signed-off-by: Roland Dreier --- drivers/infiniband/core/umem.c | 41 +++++++++++++---------------------------- include/rdma/ib_umem.h | 3 +++ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 48e854cf416..f32ca5fbb26 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -39,13 +39,6 @@ #include "uverbs.h" -struct ib_umem_account_work { - struct work_struct work; - struct mm_struct *mm; - unsigned long diff; -}; - - static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) { struct ib_umem_chunk *chunk, *tmp; @@ -192,16 +185,15 @@ out: } EXPORT_SYMBOL(ib_umem_get); -static void ib_umem_account(struct work_struct *_work) +static void ib_umem_account(struct work_struct *work) { - struct ib_umem_account_work *work = - container_of(_work, struct ib_umem_account_work, work); - - down_write(&work->mm->mmap_sem); - work->mm->locked_vm -= work->diff; - up_write(&work->mm->mmap_sem); - mmput(work->mm); - kfree(work); + struct ib_umem *umem = container_of(work, struct ib_umem, work); + + down_write(&umem->mm->mmap_sem); + umem->mm->locked_vm -= umem->diff; + up_write(&umem->mm->mmap_sem); + mmput(umem->mm); + kfree(umem); } /** @@ -210,7 +202,6 @@ static void ib_umem_account(struct work_struct *_work) */ void ib_umem_release(struct ib_umem *umem) { - struct ib_umem_account_work *work; struct ib_ucontext *context = umem->context; struct mm_struct *mm; unsigned long diff; @@ -222,7 +213,6 @@ void ib_umem_release(struct ib_umem *umem) return; diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; - kfree(umem); /* * We may be called with the mm's mmap_sem already held. This @@ -233,17 +223,11 @@ void ib_umem_release(struct ib_umem *umem) * we defer the vm_locked accounting to the system workqueue. */ if (context->closing && !down_write_trylock(&mm->mmap_sem)) { - work = kmalloc(sizeof *work, GFP_KERNEL); - if (!work) { - mmput(mm); - return; - } + INIT_WORK(&umem->work, ib_umem_account); + umem->mm = mm; + umem->diff = diff; - INIT_WORK(&work->work, ib_umem_account); - work->mm = mm; - work->diff = diff; - - schedule_work(&work->work); + schedule_work(&umem->work); return; } else down_write(&mm->mmap_sem); @@ -251,6 +235,7 @@ void ib_umem_release(struct ib_umem *umem) current->mm->locked_vm -= diff; up_write(&mm->mmap_sem); mmput(mm); + kfree(umem); } EXPORT_SYMBOL(ib_umem_release); diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 06307f7e43e..b3a36f7d79e 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -45,6 +45,9 @@ struct ib_umem { int page_size; int writable; struct list_head chunk_list; + struct work_struct work; + struct mm_struct *mm; + unsigned long diff; }; struct ib_umem_chunk { -- cgit v1.2.3 From 225c7b1feef1b41170f7037a5b10a65cd8a42c54 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 8 May 2007 18:00:38 -0700 Subject: IB/mlx4: Add a driver Mellanox ConnectX InfiniBand adapters Add an InfiniBand driver for Mellanox ConnectX adapters. Because these adapters can also be used as ethernet NICs and Fibre Channel HBAs, the driver is split into two modules: mlx4_core: Handles low-level things like device initialization and processing firmware commands. Also controls resource allocation so that the InfiniBand, ethernet and FC functions can share a device without stepping on each other. mlx4_ib: Handles InfiniBand-specific things; plugs into the InfiniBand midlayer. Signed-off-by: Roland Dreier --- drivers/infiniband/Kconfig | 2 + drivers/infiniband/Makefile | 1 + drivers/infiniband/hw/mlx4/Kconfig | 9 + drivers/infiniband/hw/mlx4/Makefile | 3 + drivers/infiniband/hw/mlx4/ah.c | 100 +++ drivers/infiniband/hw/mlx4/cq.c | 525 +++++++++++++ drivers/infiniband/hw/mlx4/doorbell.c | 216 ++++++ drivers/infiniband/hw/mlx4/mad.c | 339 +++++++++ drivers/infiniband/hw/mlx4/main.c | 651 +++++++++++++++++ drivers/infiniband/hw/mlx4/mlx4_ib.h | 285 ++++++++ drivers/infiniband/hw/mlx4/mr.c | 184 +++++ drivers/infiniband/hw/mlx4/qp.c | 1294 +++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mlx4/srq.c | 334 +++++++++ drivers/infiniband/hw/mlx4/user.h | 92 +++ drivers/net/Kconfig | 14 + drivers/net/Makefile | 1 + drivers/net/mlx4/Makefile | 4 + drivers/net/mlx4/alloc.c | 179 +++++ drivers/net/mlx4/catas.c | 70 ++ drivers/net/mlx4/cmd.c | 429 +++++++++++ drivers/net/mlx4/cq.c | 254 +++++++ drivers/net/mlx4/eq.c | 696 ++++++++++++++++++ drivers/net/mlx4/fw.c | 775 ++++++++++++++++++++ drivers/net/mlx4/fw.h | 167 +++++ drivers/net/mlx4/icm.c | 379 ++++++++++ drivers/net/mlx4/icm.h | 135 ++++ drivers/net/mlx4/intf.c | 165 +++++ drivers/net/mlx4/main.c | 936 ++++++++++++++++++++++++ drivers/net/mlx4/mcg.c | 380 ++++++++++ drivers/net/mlx4/mlx4.h | 348 +++++++++ drivers/net/mlx4/mr.c | 479 ++++++++++++ drivers/net/mlx4/pd.c | 102 +++ drivers/net/mlx4/profile.c | 238 ++++++ drivers/net/mlx4/qp.c | 280 +++++++ drivers/net/mlx4/reset.c | 181 +++++ drivers/net/mlx4/srq.c | 227 ++++++ include/linux/mlx4/cmd.h | 178 +++++ include/linux/mlx4/cq.h | 123 ++++ include/linux/mlx4/device.h | 331 +++++++++ include/linux/mlx4/doorbell.h | 97 +++ include/linux/mlx4/driver.h | 59 ++ include/linux/mlx4/qp.h | 288 ++++++++ include/linux/mlx4/srq.h | 42 ++ 43 files changed, 11592 insertions(+) create mode 100644 drivers/infiniband/hw/mlx4/Kconfig create mode 100644 drivers/infiniband/hw/mlx4/Makefile create mode 100644 drivers/infiniband/hw/mlx4/ah.c create mode 100644 drivers/infiniband/hw/mlx4/cq.c create mode 100644 drivers/infiniband/hw/mlx4/doorbell.c create mode 100644 drivers/infiniband/hw/mlx4/mad.c create mode 100644 drivers/infiniband/hw/mlx4/main.c create mode 100644 drivers/infiniband/hw/mlx4/mlx4_ib.h create mode 100644 drivers/infiniband/hw/mlx4/mr.c create mode 100644 drivers/infiniband/hw/mlx4/qp.c create mode 100644 drivers/infiniband/hw/mlx4/srq.c create mode 100644 drivers/infiniband/hw/mlx4/user.h create mode 100644 drivers/net/mlx4/Makefile create mode 100644 drivers/net/mlx4/alloc.c create mode 100644 drivers/net/mlx4/catas.c create mode 100644 drivers/net/mlx4/cmd.c create mode 100644 drivers/net/mlx4/cq.c create mode 100644 drivers/net/mlx4/eq.c create mode 100644 drivers/net/mlx4/fw.c create mode 100644 drivers/net/mlx4/fw.h create mode 100644 drivers/net/mlx4/icm.c create mode 100644 drivers/net/mlx4/icm.h create mode 100644 drivers/net/mlx4/intf.c create mode 100644 drivers/net/mlx4/main.c create mode 100644 drivers/net/mlx4/mcg.c create mode 100644 drivers/net/mlx4/mlx4.h create mode 100644 drivers/net/mlx4/mr.c create mode 100644 drivers/net/mlx4/pd.c create mode 100644 drivers/net/mlx4/profile.c create mode 100644 drivers/net/mlx4/qp.c create mode 100644 drivers/net/mlx4/reset.c create mode 100644 drivers/net/mlx4/srq.c create mode 100644 include/linux/mlx4/cmd.h create mode 100644 include/linux/mlx4/cq.h create mode 100644 include/linux/mlx4/device.h create mode 100644 include/linux/mlx4/doorbell.h create mode 100644 include/linux/mlx4/driver.h create mode 100644 include/linux/mlx4/qp.h create mode 100644 include/linux/mlx4/srq.h diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 82afba5c0bf..37deaae4919 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -45,6 +45,8 @@ source "drivers/infiniband/hw/ehca/Kconfig" source "drivers/infiniband/hw/amso1100/Kconfig" source "drivers/infiniband/hw/cxgb3/Kconfig" +source "drivers/infiniband/hw/mlx4/Kconfig" + source "drivers/infiniband/ulp/ipoib/Kconfig" source "drivers/infiniband/ulp/srp/Kconfig" diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile index da2066c4f22..75f325e40b5 100644 --- a/drivers/infiniband/Makefile +++ b/drivers/infiniband/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INFINIBAND_IPATH) += hw/ipath/ obj-$(CONFIG_INFINIBAND_EHCA) += hw/ehca/ obj-$(CONFIG_INFINIBAND_AMSO1100) += hw/amso1100/ obj-$(CONFIG_INFINIBAND_CXGB3) += hw/cxgb3/ +obj-$(CONFIG_MLX4_INFINIBAND) += hw/mlx4/ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/ obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/ obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/ diff --git a/drivers/infiniband/hw/mlx4/Kconfig b/drivers/infiniband/hw/mlx4/Kconfig new file mode 100644 index 00000000000..b8912cdb966 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/Kconfig @@ -0,0 +1,9 @@ +config MLX4_INFINIBAND + tristate "Mellanox ConnectX HCA support" + depends on INFINIBAND + select MLX4_CORE + ---help--- + This driver provides low-level InfiniBand support for + Mellanox ConnectX PCI Express host channel adapters (HCAs). + This is required to use InfiniBand protocols such as + IP-over-IB or SRP with these devices. diff --git a/drivers/infiniband/hw/mlx4/Makefile b/drivers/infiniband/hw/mlx4/Makefile new file mode 100644 index 00000000000..70f09c7826d --- /dev/null +++ b/drivers/infiniband/hw/mlx4/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MLX4_INFINIBAND) += mlx4_ib.o + +mlx4_ib-y := ah.o cq.o doorbell.o mad.o main.o mr.o qp.o srq.o diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c new file mode 100644 index 00000000000..c75ac9463e2 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mlx4_ib.h" + +struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) +{ + struct mlx4_dev *dev = to_mdev(pd->device)->dev; + struct mlx4_ib_ah *ah; + + ah = kmalloc(sizeof *ah, GFP_ATOMIC); + if (!ah) + return ERR_PTR(-ENOMEM); + + memset(&ah->av, 0, sizeof ah->av); + + ah->av.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24)); + ah->av.g_slid = ah_attr->src_path_bits; + ah->av.dlid = cpu_to_be16(ah_attr->dlid); + if (ah_attr->static_rate) { + ah->av.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET; + while (ah->av.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET && + !(1 << ah->av.stat_rate & dev->caps.stat_rate_support)) + --ah->av.stat_rate; + } + ah->av.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); + if (ah_attr->ah_flags & IB_AH_GRH) { + ah->av.g_slid |= 0x80; + ah->av.gid_index = ah_attr->grh.sgid_index; + ah->av.hop_limit = ah_attr->grh.hop_limit; + ah->av.sl_tclass_flowlabel |= + cpu_to_be32((ah_attr->grh.traffic_class << 20) | + ah_attr->grh.flow_label); + memcpy(ah->av.dgid, ah_attr->grh.dgid.raw, 16); + } + + return &ah->ibah; +} + +int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr) +{ + struct mlx4_ib_ah *ah = to_mah(ibah); + + memset(ah_attr, 0, sizeof *ah_attr); + ah_attr->dlid = be16_to_cpu(ah->av.dlid); + ah_attr->sl = be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28; + ah_attr->port_num = be32_to_cpu(ah->av.port_pd) >> 24; + if (ah->av.stat_rate) + ah_attr->static_rate = ah->av.stat_rate - MLX4_STAT_RATE_OFFSET; + ah_attr->src_path_bits = ah->av.g_slid & 0x7F; + + if (mlx4_ib_ah_grh_present(ah)) { + ah_attr->ah_flags = IB_AH_GRH; + + ah_attr->grh.traffic_class = + be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20; + ah_attr->grh.flow_label = + be32_to_cpu(ah->av.sl_tclass_flowlabel) & 0xfffff; + ah_attr->grh.hop_limit = ah->av.hop_limit; + ah_attr->grh.sgid_index = ah->av.gid_index; + memcpy(ah_attr->grh.dgid.raw, ah->av.dgid, 16); + } + + return 0; +} + +int mlx4_ib_destroy_ah(struct ib_ah *ah) +{ + kfree(to_mah(ah)); + return 0; +} diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c new file mode 100644 index 00000000000..b2a290c6703 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "mlx4_ib.h" +#include "user.h" + +static void mlx4_ib_cq_comp(struct mlx4_cq *cq) +{ + struct ib_cq *ibcq = &to_mibcq(cq)->ibcq; + ibcq->comp_handler(ibcq, ibcq->cq_context); +} + +static void mlx4_ib_cq_event(struct mlx4_cq *cq, enum mlx4_event type) +{ + struct ib_event event; + struct ib_cq *ibcq; + + if (type != MLX4_EVENT_TYPE_CQ_ERROR) { + printk(KERN_WARNING "mlx4_ib: Unexpected event type %d " + "on CQ %06x\n", type, cq->cqn); + return; + } + + ibcq = &to_mibcq(cq)->ibcq; + if (ibcq->event_handler) { + event.device = ibcq->device; + event.event = IB_EVENT_CQ_ERR; + event.element.cq = ibcq; + ibcq->event_handler(&event, ibcq->cq_context); + } +} + +static void *get_cqe_from_buf(struct mlx4_ib_cq_buf *buf, int n) +{ + int offset = n * sizeof (struct mlx4_cqe); + + if (buf->buf.nbufs == 1) + return buf->buf.u.direct.buf + offset; + else + return buf->buf.u.page_list[offset >> PAGE_SHIFT].buf + + (offset & (PAGE_SIZE - 1)); +} + +static void *get_cqe(struct mlx4_ib_cq *cq, int n) +{ + return get_cqe_from_buf(&cq->buf, n); +} + +static void *get_sw_cqe(struct mlx4_ib_cq *cq, int n) +{ + struct mlx4_cqe *cqe = get_cqe(cq, n & cq->ibcq.cqe); + + return (!!(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK) ^ + !!(n & (cq->ibcq.cqe + 1))) ? NULL : cqe; +} + +static struct mlx4_cqe *next_cqe_sw(struct mlx4_ib_cq *cq) +{ + return get_sw_cqe(cq, cq->mcq.cons_index); +} + +struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_cq *cq; + struct mlx4_uar *uar; + int buf_size; + int err; + + if (entries < 1 || entries > dev->dev->caps.max_cqes) + return ERR_PTR(-EINVAL); + + cq = kmalloc(sizeof *cq, GFP_KERNEL); + if (!cq) + return ERR_PTR(-ENOMEM); + + entries = roundup_pow_of_two(entries + 1); + cq->ibcq.cqe = entries - 1; + buf_size = entries * sizeof (struct mlx4_cqe); + spin_lock_init(&cq->lock); + + if (context) { + struct mlx4_ib_create_cq ucmd; + + if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { + err = -EFAULT; + goto err_cq; + } + + cq->umem = ib_umem_get(context, ucmd.buf_addr, buf_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(cq->umem)) { + err = PTR_ERR(cq->umem); + goto err_cq; + } + + err = mlx4_mtt_init(dev->dev, ib_umem_page_count(cq->umem), + ilog2(cq->umem->page_size), &cq->buf.mtt); + if (err) + goto err_buf; + + err = mlx4_ib_umem_write_mtt(dev, &cq->buf.mtt, cq->umem); + if (err) + goto err_mtt; + + err = mlx4_ib_db_map_user(to_mucontext(context), ucmd.db_addr, + &cq->db); + if (err) + goto err_mtt; + + uar = &to_mucontext(context)->uar; + } else { + err = mlx4_ib_db_alloc(dev, &cq->db, 1); + if (err) + goto err_cq; + + cq->mcq.set_ci_db = cq->db.db; + cq->mcq.arm_db = cq->db.db + 1; + *cq->mcq.set_ci_db = 0; + *cq->mcq.arm_db = 0; + + if (mlx4_buf_alloc(dev->dev, buf_size, PAGE_SIZE * 2, &cq->buf.buf)) { + err = -ENOMEM; + goto err_db; + } + + err = mlx4_mtt_init(dev->dev, cq->buf.buf.npages, cq->buf.buf.page_shift, + &cq->buf.mtt); + if (err) + goto err_buf; + + err = mlx4_buf_write_mtt(dev->dev, &cq->buf.mtt, &cq->buf.buf); + if (err) + goto err_mtt; + + uar = &dev->priv_uar; + } + + err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar, + cq->db.dma, &cq->mcq); + if (err) + goto err_dbmap; + + cq->mcq.comp = mlx4_ib_cq_comp; + cq->mcq.event = mlx4_ib_cq_event; + + if (context) + if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof (__u32))) { + err = -EFAULT; + goto err_dbmap; + } + + return &cq->ibcq; + +err_dbmap: + if (context) + mlx4_ib_db_unmap_user(to_mucontext(context), &cq->db); + +err_mtt: + mlx4_mtt_cleanup(dev->dev, &cq->buf.mtt); + +err_buf: + if (context) + ib_umem_release(cq->umem); + else + mlx4_buf_free(dev->dev, entries * sizeof (struct mlx4_cqe), + &cq->buf.buf); + +err_db: + if (!context) + mlx4_ib_db_free(dev, &cq->db); + +err_cq: + kfree(cq); + + return ERR_PTR(err); +} + +int mlx4_ib_destroy_cq(struct ib_cq *cq) +{ + struct mlx4_ib_dev *dev = to_mdev(cq->device); + struct mlx4_ib_cq *mcq = to_mcq(cq); + + mlx4_cq_free(dev->dev, &mcq->mcq); + mlx4_mtt_cleanup(dev->dev, &mcq->buf.mtt); + + if (cq->uobject) { + mlx4_ib_db_unmap_user(to_mucontext(cq->uobject->context), &mcq->db); + ib_umem_release(mcq->umem); + } else { + mlx4_buf_free(dev->dev, (cq->cqe + 1) * sizeof (struct mlx4_cqe), + &mcq->buf.buf); + mlx4_ib_db_free(dev, &mcq->db); + } + + kfree(mcq); + + return 0; +} + +static void dump_cqe(void *cqe) +{ + __be32 *buf = cqe; + + printk(KERN_DEBUG "CQE contents %08x %08x %08x %08x %08x %08x %08x %08x\n", + be32_to_cpu(buf[0]), be32_to_cpu(buf[1]), be32_to_cpu(buf[2]), + be32_to_cpu(buf[3]), be32_to_cpu(buf[4]), be32_to_cpu(buf[5]), + be32_to_cpu(buf[6]), be32_to_cpu(buf[7])); +} + +static void mlx4_ib_handle_error_cqe(struct mlx4_err_cqe *cqe, + struct ib_wc *wc) +{ + if (cqe->syndrome == MLX4_CQE_SYNDROME_LOCAL_QP_OP_ERR) { + printk(KERN_DEBUG "local QP operation err " + "(QPN %06x, WQE index %x, vendor syndrome %02x, " + "opcode = %02x)\n", + be32_to_cpu(cqe->my_qpn), be16_to_cpu(cqe->wqe_index), + cqe->vendor_err_syndrome, + cqe->owner_sr_opcode & ~MLX4_CQE_OWNER_MASK); + dump_cqe(cqe); + } + + switch (cqe->syndrome) { + case MLX4_CQE_SYNDROME_LOCAL_LENGTH_ERR: + wc->status = IB_WC_LOC_LEN_ERR; + break; + case MLX4_CQE_SYNDROME_LOCAL_QP_OP_ERR: + wc->status = IB_WC_LOC_QP_OP_ERR; + break; + case MLX4_CQE_SYNDROME_LOCAL_PROT_ERR: + wc->status = IB_WC_LOC_PROT_ERR; + break; + case MLX4_CQE_SYNDROME_WR_FLUSH_ERR: + wc->status = IB_WC_WR_FLUSH_ERR; + break; + case MLX4_CQE_SYNDROME_MW_BIND_ERR: + wc->status = IB_WC_MW_BIND_ERR; + break; + case MLX4_CQE_SYNDROME_BAD_RESP_ERR: + wc->status = IB_WC_BAD_RESP_ERR; + break; + case MLX4_CQE_SYNDROME_LOCAL_ACCESS_ERR: + wc->status = IB_WC_LOC_ACCESS_ERR; + break; + case MLX4_CQE_SYNDROME_REMOTE_INVAL_REQ_ERR: + wc->status = IB_WC_REM_INV_REQ_ERR; + break; + case MLX4_CQE_SYNDROME_REMOTE_ACCESS_ERR: + wc->status = IB_WC_REM_ACCESS_ERR; + break; + case MLX4_CQE_SYNDROME_REMOTE_OP_ERR: + wc->status = IB_WC_REM_OP_ERR; + break; + case MLX4_CQE_SYNDROME_TRANSPORT_RETRY_EXC_ERR: + wc->status = IB_WC_RETRY_EXC_ERR; + break; + case MLX4_CQE_SYNDROME_RNR_RETRY_EXC_ERR: + wc->status = IB_WC_RNR_RETRY_EXC_ERR; + break; + case MLX4_CQE_SYNDROME_REMOTE_ABORTED_ERR: + wc->status = IB_WC_REM_ABORT_ERR; + break; + default: + wc->status = IB_WC_GENERAL_ERR; + break; + } + + wc->vendor_err = cqe->vendor_err_syndrome; +} + +static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq, + struct mlx4_ib_qp **cur_qp, + struct ib_wc *wc) +{ + struct mlx4_cqe *cqe; + struct mlx4_qp *mqp; + struct mlx4_ib_wq *wq; + struct mlx4_ib_srq *srq; + int is_send; + int is_error; + u16 wqe_ctr; + + cqe = next_cqe_sw(cq); + if (!cqe) + return -EAGAIN; + + ++cq->mcq.cons_index; + + /* + * Make sure we read CQ entry contents after we've checked the + * ownership bit. + */ + rmb(); + + is_send = cqe->owner_sr_opcode & MLX4_CQE_IS_SEND_MASK; + is_error = (cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) == + MLX4_CQE_OPCODE_ERROR; + + if (!*cur_qp || + (be32_to_cpu(cqe->my_qpn) & 0xffffff) != (*cur_qp)->mqp.qpn) { + /* + * We do not have to take the QP table lock here, + * because CQs will be locked while QPs are removed + * from the table. + */ + mqp = __mlx4_qp_lookup(to_mdev(cq->ibcq.device)->dev, + be32_to_cpu(cqe->my_qpn)); + if (unlikely(!mqp)) { + printk(KERN_WARNING "CQ %06x with entry for unknown QPN %06x\n", + cq->mcq.cqn, be32_to_cpu(cqe->my_qpn) & 0xffffff); + return -EINVAL; + } + + *cur_qp = to_mibqp(mqp); + } + + wc->qp = &(*cur_qp)->ibqp; + + if (is_send) { + wq = &(*cur_qp)->sq; + wqe_ctr = be16_to_cpu(cqe->wqe_index); + wq->tail += wqe_ctr - (u16) wq->tail; + wc->wr_id = wq->wrid[wq->tail & (wq->max - 1)]; + ++wq->tail; + } else if ((*cur_qp)->ibqp.srq) { + srq = to_msrq((*cur_qp)->ibqp.srq); + wqe_ctr = be16_to_cpu(cqe->wqe_index); + wc->wr_id = srq->wrid[wqe_ctr]; + mlx4_ib_free_srq_wqe(srq, wqe_ctr); + } else { + wq = &(*cur_qp)->rq; + wc->wr_id = wq->wrid[wq->tail & (wq->max - 1)]; + ++wq->tail; + } + + if (unlikely(is_error)) { + mlx4_ib_handle_error_cqe((struct mlx4_err_cqe *) cqe, wc); + return 0; + } + + wc->status = IB_WC_SUCCESS; + + if (is_send) { + wc->wc_flags = 0; + switch (cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) { + case MLX4_OPCODE_RDMA_WRITE_IMM: + wc->wc_flags |= IB_WC_WITH_IMM; + case MLX4_OPCODE_RDMA_WRITE: + wc->opcode = IB_WC_RDMA_WRITE; + break; + case MLX4_OPCODE_SEND_IMM: + wc->wc_flags |= IB_WC_WITH_IMM; + case MLX4_OPCODE_SEND: + wc->opcode = IB_WC_SEND; + break; + case MLX4_OPCODE_RDMA_READ: + wc->opcode = IB_WC_SEND; + wc->byte_len = be32_to_cpu(cqe->byte_cnt); + break; + case MLX4_OPCODE_ATOMIC_CS: + wc->opcode = IB_WC_COMP_SWAP; + wc->byte_len = 8; + break; + case MLX4_OPCODE_ATOMIC_FA: + wc->opcode = IB_WC_FETCH_ADD; + wc->byte_len = 8; + break; + case MLX4_OPCODE_BIND_MW: + wc->opcode = IB_WC_BIND_MW; + break; + } + } else { + wc->byte_len = be32_to_cpu(cqe->byte_cnt); + + switch (cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) { + case MLX4_RECV_OPCODE_RDMA_WRITE_IMM: + wc->opcode = IB_WC_RECV_RDMA_WITH_IMM; + wc->wc_flags = IB_WC_WITH_IMM; + wc->imm_data = cqe->immed_rss_invalid; + break; + case MLX4_RECV_OPCODE_SEND: + wc->opcode = IB_WC_RECV; + wc->wc_flags = 0; + break; + case MLX4_RECV_OPCODE_SEND_IMM: + wc->opcode = IB_WC_RECV; + wc->wc_flags = IB_WC_WITH_IMM; + wc->imm_data = cqe->immed_rss_invalid; + break; + } + + wc->slid = be16_to_cpu(cqe->rlid); + wc->sl = cqe->sl >> 4; + wc->src_qp = be32_to_cpu(cqe->g_mlpath_rqpn) & 0xffffff; + wc->dlid_path_bits = (be32_to_cpu(cqe->g_mlpath_rqpn) >> 24) & 0x7f; + wc->wc_flags |= be32_to_cpu(cqe->g_mlpath_rqpn) & 0x80000000 ? + IB_WC_GRH : 0; + wc->pkey_index = be32_to_cpu(cqe->immed_rss_invalid) >> 16; + } + + return 0; +} + +int mlx4_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) +{ + struct mlx4_ib_cq *cq = to_mcq(ibcq); + struct mlx4_ib_qp *cur_qp = NULL; + unsigned long flags; + int npolled; + int err = 0; + + spin_lock_irqsave(&cq->lock, flags); + + for (npolled = 0; npolled < num_entries; ++npolled) { + err = mlx4_ib_poll_one(cq, &cur_qp, wc + npolled); + if (err) + break; + } + + if (npolled) + mlx4_cq_set_ci(&cq->mcq); + + spin_unlock_irqrestore(&cq->lock, flags); + + if (err == 0 || err == -EAGAIN) + return npolled; + else + return err; +} + +int mlx4_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) +{ + mlx4_cq_arm(&to_mcq(ibcq)->mcq, + (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ? + MLX4_CQ_DB_REQ_NOT_SOL : MLX4_CQ_DB_REQ_NOT, + to_mdev(ibcq->device)->uar_map, + MLX4_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->uar_lock)); + + return 0; +} + +void __mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq) +{ + u32 prod_index; + int nfreed = 0; + struct mlx4_cqe *cqe; + + /* + * First we need to find the current producer index, so we + * know where to start cleaning from. It doesn't matter if HW + * adds new entries after this loop -- the QP we're worried + * about is already in RESET, so the new entries won't come + * from our QP and therefore don't need to be checked. + */ + for (prod_index = cq->mcq.cons_index; get_sw_cqe(cq, prod_index); ++prod_index) + if (prod_index == cq->mcq.cons_index + cq->ibcq.cqe) + break; + + /* + * Now sweep backwards through the CQ, removing CQ entries + * that match our QP by copying older entries on top of them. + */ + while ((int) --prod_index - (int) cq->mcq.cons_index >= 0) { + cqe = get_cqe(cq, prod_index & cq->ibcq.cqe); + if ((be32_to_cpu(cqe->my_qpn) & 0xffffff) == qpn) { + if (srq && !(cqe->owner_sr_opcode & MLX4_CQE_IS_SEND_MASK)) + mlx4_ib_free_srq_wqe(srq, be16_to_cpu(cqe->wqe_index)); + ++nfreed; + } else if (nfreed) + memcpy(get_cqe(cq, (prod_index + nfreed) & cq->ibcq.cqe), + cqe, sizeof *cqe); + } + + if (nfreed) { + cq->mcq.cons_index += nfreed; + /* + * Make sure update of buffer contents is done before + * updating consumer index. + */ + wmb(); + mlx4_cq_set_ci(&cq->mcq); + } +} + +void mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq) +{ + spin_lock_irq(&cq->lock); + __mlx4_ib_cq_clean(cq, qpn, srq); + spin_unlock_irq(&cq->lock); +} diff --git a/drivers/infiniband/hw/mlx4/doorbell.c b/drivers/infiniband/hw/mlx4/doorbell.c new file mode 100644 index 00000000000..1c36087aef1 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/doorbell.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "mlx4_ib.h" + +struct mlx4_ib_db_pgdir { + struct list_head list; + DECLARE_BITMAP(order0, MLX4_IB_DB_PER_PAGE); + DECLARE_BITMAP(order1, MLX4_IB_DB_PER_PAGE / 2); + unsigned long *bits[2]; + __be32 *db_page; + dma_addr_t db_dma; +}; + +static struct mlx4_ib_db_pgdir *mlx4_ib_alloc_db_pgdir(struct mlx4_ib_dev *dev) +{ + struct mlx4_ib_db_pgdir *pgdir; + + pgdir = kzalloc(sizeof *pgdir, GFP_KERNEL); + if (!pgdir) + return NULL; + + bitmap_fill(pgdir->order1, MLX4_IB_DB_PER_PAGE / 2); + pgdir->bits[0] = pgdir->order0; + pgdir->bits[1] = pgdir->order1; + pgdir->db_page = dma_alloc_coherent(dev->ib_dev.dma_device, + PAGE_SIZE, &pgdir->db_dma, + GFP_KERNEL); + if (!pgdir->db_page) { + kfree(pgdir); + return NULL; + } + + return pgdir; +} + +static int mlx4_ib_alloc_db_from_pgdir(struct mlx4_ib_db_pgdir *pgdir, + struct mlx4_ib_db *db, int order) +{ + int o; + int i; + + for (o = order; o <= 1; ++o) { + i = find_first_bit(pgdir->bits[o], MLX4_IB_DB_PER_PAGE >> o); + if (i < MLX4_IB_DB_PER_PAGE >> o) + goto found; + } + + return -ENOMEM; + +found: + clear_bit(i, pgdir->bits[o]); + + i <<= o; + + if (o > order) + set_bit(i ^ 1, pgdir->bits[order]); + + db->u.pgdir = pgdir; + db->index = i; + db->db = pgdir->db_page + db->index; + db->dma = pgdir->db_dma + db->index * 4; + db->order = order; + + return 0; +} + +int mlx4_ib_db_alloc(struct mlx4_ib_dev *dev, struct mlx4_ib_db *db, int order) +{ + struct mlx4_ib_db_pgdir *pgdir; + int ret = 0; + + mutex_lock(&dev->pgdir_mutex); + + list_for_each_entry(pgdir, &dev->pgdir_list, list) + if (!mlx4_ib_alloc_db_from_pgdir(pgdir, db, order)) + goto out; + + pgdir = mlx4_ib_alloc_db_pgdir(dev); + if (!pgdir) { + ret = -ENOMEM; + goto out; + } + + list_add(&pgdir->list, &dev->pgdir_list); + + /* This should never fail -- we just allocated an empty page: */ + WARN_ON(mlx4_ib_alloc_db_from_pgdir(pgdir, db, order)); + +out: + mutex_unlock(&dev->pgdir_mutex); + + return ret; +} + +void mlx4_ib_db_free(struct mlx4_ib_dev *dev, struct mlx4_ib_db *db) +{ + int o; + int i; + + mutex_lock(&dev->pgdir_mutex); + + o = db->order; + i = db->index; + + if (db->order == 0 && test_bit(i ^ 1, db->u.pgdir->order0)) { + clear_bit(i ^ 1, db->u.pgdir->order0); + ++o; + } + + i >>= o; + set_bit(i, db->u.pgdir->bits[o]); + + if (bitmap_full(db->u.pgdir->order1, MLX4_IB_DB_PER_PAGE / 2)) { + dma_free_coherent(dev->ib_dev.dma_device, PAGE_SIZE, + db->u.pgdir->db_page, db->u.pgdir->db_dma); + list_del(&db->u.pgdir->list); + kfree(db->u.pgdir); + } + + mutex_unlock(&dev->pgdir_mutex); +} + +struct mlx4_ib_user_db_page { + struct list_head list; + struct ib_umem *umem; + unsigned long user_virt; + int refcnt; +}; + +int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt, + struct mlx4_ib_db *db) +{ + struct mlx4_ib_user_db_page *page; + struct ib_umem_chunk *chunk; + int err = 0; + + mutex_lock(&context->db_page_mutex); + + list_for_each_entry(page, &context->db_page_list, list) + if (page->user_virt == (virt & PAGE_MASK)) + goto found; + + page = kmalloc(sizeof *page, GFP_KERNEL); + if (!page) { + err = -ENOMEM; + goto out; + } + + page->user_virt = (virt & PAGE_MASK); + page->refcnt = 0; + page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK, + PAGE_SIZE, 0); + if (IS_ERR(page->umem)) { + err = PTR_ERR(page->umem); + kfree(page); + goto out; + } + + list_add(&page->list, &context->db_page_list); + +found: + chunk = list_entry(page->umem->chunk_list.next, struct ib_umem_chunk, list); + db->dma = sg_dma_address(chunk->page_list) + (virt & ~PAGE_MASK); + db->u.user_page = page; + ++page->refcnt; + +out: + mutex_unlock(&context->db_page_mutex); + + return err; +} + +void mlx4_ib_db_unmap_user(struct mlx4_ib_ucontext *context, struct mlx4_ib_db *db) +{ + mutex_lock(&context->db_page_mutex); + + if (!--db->u.user_page->refcnt) { + list_del(&db->u.user_page->list); + ib_umem_release(db->u.user_page->umem); + kfree(db->u.user_page); + } + + mutex_unlock(&context->db_page_mutex); +} diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c new file mode 100644 index 00000000000..333091787c5 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4_ib.h" + +enum { + MLX4_IB_VENDOR_CLASS1 = 0x9, + MLX4_IB_VENDOR_CLASS2 = 0xa +}; + +int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int ignore_mkey, int ignore_bkey, + int port, struct ib_wc *in_wc, struct ib_grh *in_grh, + void *in_mad, void *response_mad) +{ + struct mlx4_cmd_mailbox *inmailbox, *outmailbox; + void *inbox; + int err; + u32 in_modifier = port; + u8 op_modifier = 0; + + inmailbox = mlx4_alloc_cmd_mailbox(dev->dev); + if (IS_ERR(inmailbox)) + return PTR_ERR(inmailbox); + inbox = inmailbox->buf; + + outmailbox = mlx4_alloc_cmd_mailbox(dev->dev); + if (IS_ERR(outmailbox)) { + mlx4_free_cmd_mailbox(dev->dev, inmailbox); + return PTR_ERR(outmailbox); + } + + memcpy(inbox, in_mad, 256); + + /* + * Key check traps can't be generated unless we have in_wc to + * tell us where to send the trap. + */ + if (ignore_mkey || !in_wc) + op_modifier |= 0x1; + if (ignore_bkey || !in_wc) + op_modifier |= 0x2; + + if (in_wc) { + struct { + __be32 my_qpn; + u32 reserved1; + __be32 rqpn; + u8 sl; + u8 g_path; + u16 reserved2[2]; + __be16 pkey; + u32 reserved3[11]; + u8 grh[40]; + } *ext_info; + + memset(inbox + 256, 0, 256); + ext_info = inbox + 256; + + ext_info->my_qpn = cpu_to_be32(in_wc->qp->qp_num); + ext_info->rqpn = cpu_to_be32(in_wc->src_qp); + ext_info->sl = in_wc->sl << 4; + ext_info->g_path = in_wc->dlid_path_bits | + (in_wc->wc_flags & IB_WC_GRH ? 0x80 : 0); + ext_info->pkey = cpu_to_be16(in_wc->pkey_index); + + if (in_grh) + memcpy(ext_info->grh, in_grh, 40); + + op_modifier |= 0x4; + + in_modifier |= in_wc->slid << 16; + } + + err = mlx4_cmd_box(dev->dev, inmailbox->dma, outmailbox->dma, + in_modifier, op_modifier, + MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C); + + if (!err); + memcpy(response_mad, outmailbox->buf, 256); + + mlx4_free_cmd_mailbox(dev->dev, inmailbox); + mlx4_free_cmd_mailbox(dev->dev, outmailbox); + + return err; +} + +static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl) +{ + struct ib_ah *new_ah; + struct ib_ah_attr ah_attr; + + if (!dev->send_agent[port_num - 1][0]) + return; + + memset(&ah_attr, 0, sizeof ah_attr); + ah_attr.dlid = lid; + ah_attr.sl = sl; + ah_attr.port_num = port_num; + + new_ah = ib_create_ah(dev->send_agent[port_num - 1][0]->qp->pd, + &ah_attr); + if (IS_ERR(new_ah)) + return; + + spin_lock(&dev->sm_lock); + if (dev->sm_ah[port_num - 1]) + ib_destroy_ah(dev->sm_ah[port_num - 1]); + dev->sm_ah[port_num - 1] = new_ah; + spin_unlock(&dev->sm_lock); +} + +/* + * Snoop SM MADs for port info and P_Key table sets, so we can + * synthesize LID change and P_Key change events. + */ +static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad) +{ + struct ib_event event; + + if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED || + mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) && + mad->mad_hdr.method == IB_MGMT_METHOD_SET) { + if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) { + struct ib_port_info *pinfo = + (struct ib_port_info *) ((struct ib_smp *) mad)->data; + + update_sm_ah(to_mdev(ibdev), port_num, + be16_to_cpu(pinfo->sm_lid), + pinfo->neighbormtu_mastersmsl & 0xf); + + event.device = ibdev; + event.element.port_num = port_num; + + if(pinfo->clientrereg_resv_subnetto & 0x80) + event.event = IB_EVENT_CLIENT_REREGISTER; + else + event.event = IB_EVENT_LID_CHANGE; + + ib_dispatch_event(&event); + } + + if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) { + event.device = ibdev; + event.event = IB_EVENT_PKEY_CHANGE; + event.element.port_num = port_num; + ib_dispatch_event(&event); + } + } +} + +static void node_desc_override(struct ib_device *dev, + struct ib_mad *mad) +{ + if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED || + mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) && + mad->mad_hdr.method == IB_MGMT_METHOD_GET_RESP && + mad->mad_hdr.attr_id == IB_SMP_ATTR_NODE_DESC) { + spin_lock(&to_mdev(dev)->sm_lock); + memcpy(((struct ib_smp *) mad)->data, dev->node_desc, 64); + spin_unlock(&to_mdev(dev)->sm_lock); + } +} + +static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *mad) +{ + int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED; + struct ib_mad_send_buf *send_buf; + struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn]; + int ret; + + if (agent) { + send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR, + IB_MGMT_MAD_DATA, GFP_ATOMIC); + /* + * We rely here on the fact that MLX QPs don't use the + * address handle after the send is posted (this is + * wrong following the IB spec strictly, but we know + * it's OK for our devices). + */ + spin_lock(&dev->sm_lock); + memcpy(send_buf->mad, mad, sizeof *mad); + if ((send_buf->ah = dev->sm_ah[port_num - 1])) + ret = ib_post_send_mad(send_buf, NULL); + else + ret = -EINVAL; + spin_unlock(&dev->sm_lock); + + if (ret) + ib_free_send_mad(send_buf); + } +} + +int mlx4_ib_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) +{ + u16 slid; + int err; + + slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE); + + if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP && slid == 0) { + forward_trap(to_mdev(ibdev), port_num, in_mad); + return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED; + } + + if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED || + in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) { + if (in_mad->mad_hdr.method != IB_MGMT_METHOD_GET && + in_mad->mad_hdr.method != IB_MGMT_METHOD_SET && + in_mad->mad_hdr.method != IB_MGMT_METHOD_TRAP_REPRESS) + return IB_MAD_RESULT_SUCCESS; + + /* + * Don't process SMInfo queries or vendor-specific + * MADs -- the SMA can't handle them. + */ + if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO || + ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) == + IB_SMP_ATTR_VENDOR_MASK)) + return IB_MAD_RESULT_SUCCESS; + } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT || + in_mad->mad_hdr.mgmt_class == MLX4_IB_VENDOR_CLASS1 || + in_mad->mad_hdr.mgmt_class == MLX4_IB_VENDOR_CLASS2) { + if (in_mad->mad_hdr.method != IB_MGMT_METHOD_GET && + in_mad->mad_hdr.method != IB_MGMT_METHOD_SET) + return IB_MAD_RESULT_SUCCESS; + } else + return IB_MAD_RESULT_SUCCESS; + + err = mlx4_MAD_IFC(to_mdev(ibdev), + mad_flags & IB_MAD_IGNORE_MKEY, + mad_flags & IB_MAD_IGNORE_BKEY, + port_num, in_wc, in_grh, in_mad, out_mad); + if (err) + return IB_MAD_RESULT_FAILURE; + + if (!out_mad->mad_hdr.status) { + smp_snoop(ibdev, port_num, in_mad); + node_desc_override(ibdev, out_mad); + } + + /* set return bit in status of directed route responses */ + if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) + out_mad->mad_hdr.status |= cpu_to_be16(1 << 15); + + if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) + /* no response for trap repress */ + return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED; + + return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY; +} + +static void send_handler(struct ib_mad_agent *agent, + struct ib_mad_send_wc *mad_send_wc) +{ + ib_free_send_mad(mad_send_wc->send_buf); +} + +int mlx4_ib_mad_init(struct mlx4_ib_dev *dev) +{ + struct ib_mad_agent *agent; + int p, q; + int ret; + + for (p = 0; p < dev->dev->caps.num_ports; ++p) + for (q = 0; q <= 1; ++q) { + agent = ib_register_mad_agent(&dev->ib_dev, p + 1, + q ? IB_QPT_GSI : IB_QPT_SMI, + NULL, 0, send_handler, + NULL, NULL); + if (IS_ERR(agent)) { + ret = PTR_ERR(agent); + goto err; + } + dev->send_agent[p][q] = agent; + } + + return 0; + +err: + for (p = 0; p < dev->dev->caps.num_ports; ++p) + for (q = 0; q <= 1; ++q) + if (dev->send_agent[p][q]) + ib_unregister_mad_agent(dev->send_agent[p][q]); + + return ret; +} + +void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev) +{ + struct ib_mad_agent *agent; + int p, q; + + for (p = 0; p < dev->dev->caps.num_ports; ++p) { + for (q = 0; q <= 1; ++q) { + agent = dev->send_agent[p][q]; + dev->send_agent[p][q] = NULL; + ib_unregister_mad_agent(agent); + } + + if (dev->sm_ah[p]) + ib_destroy_ah(dev->sm_ah[p]); + } +} diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c new file mode 100644 index 00000000000..688ecb4c39f --- /dev/null +++ b/drivers/infiniband/hw/mlx4/main.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "mlx4_ib.h" +#include "user.h" + +#define DRV_NAME "mlx4_ib" +#define DRV_VERSION "0.01" +#define DRV_RELDATE "May 1, 2006" + +MODULE_AUTHOR("Roland Dreier"); +MODULE_DESCRIPTION("Mellanox ConnectX HCA InfiniBand driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); + +static const char mlx4_ib_version[] __devinitdata = + DRV_NAME ": Mellanox ConnectX InfiniBand driver v" + DRV_VERSION " (" DRV_RELDATE ")\n"; + +static void init_query_mad(struct ib_smp *mad) +{ + mad->base_version = 1; + mad->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED; + mad->class_version = 1; + mad->method = IB_MGMT_METHOD_GET; +} + +static int mlx4_ib_query_device(struct ib_device *ibdev, + struct ib_device_attr *props) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_NODE_INFO; + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, 1, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + memset(props, 0, sizeof *props); + + props->fw_ver = dev->dev->caps.fw_ver; + props->device_cap_flags = IB_DEVICE_CHANGE_PHY_PORT | + IB_DEVICE_PORT_ACTIVE_EVENT | + IB_DEVICE_SYS_IMAGE_GUID | + IB_DEVICE_RC_RNR_NAK_GEN; + if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_BAD_PKEY_CNTR) + props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR; + if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_BAD_QKEY_CNTR) + props->device_cap_flags |= IB_DEVICE_BAD_QKEY_CNTR; + if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_APM) + props->device_cap_flags |= IB_DEVICE_AUTO_PATH_MIG; + if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UD_AV_PORT) + props->device_cap_flags |= IB_DEVICE_UD_AV_PORT_ENFORCE; + + props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & + 0xffffff; + props->vendor_part_id = be16_to_cpup((__be16 *) (out_mad->data + 30)); + props->hw_ver = be32_to_cpup((__be32 *) (out_mad->data + 32)); + memcpy(&props->sys_image_guid, out_mad->data + 4, 8); + + props->max_mr_size = ~0ull; + props->page_size_cap = dev->dev->caps.page_size_cap; + props->max_qp = dev->dev->caps.num_qps - dev->dev->caps.reserved_qps; + props->max_qp_wr = dev->dev->caps.max_wqes; + props->max_sge = min(dev->dev->caps.max_sq_sg, + dev->dev->caps.max_rq_sg); + props->max_cq = dev->dev->caps.num_cqs - dev->dev->caps.reserved_cqs; + props->max_cqe = dev->dev->caps.max_cqes; + props->max_mr = dev->dev->caps.num_mpts - dev->dev->caps.reserved_mrws; + props->max_pd = dev->dev->caps.num_pds - dev->dev->caps.reserved_pds; + props->max_qp_rd_atom = dev->dev->caps.max_qp_dest_rdma; + props->max_qp_init_rd_atom = dev->dev->caps.max_qp_init_rdma; + props->max_res_rd_atom = props->max_qp_rd_atom * props->max_qp; + props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; + props->max_srq_wr = dev->dev->caps.max_srq_wqes; + props->max_srq_sge = dev->dev->caps.max_srq_sge; + props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; + props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? + IB_ATOMIC_HCA : IB_ATOMIC_NONE; + props->max_pkeys = dev->dev->caps.pkey_table_len; + props->max_mcast_grp = dev->dev->caps.num_mgms + dev->dev->caps.num_amgms; + props->max_mcast_qp_attach = dev->dev->caps.num_qp_per_mgm; + props->max_total_mcast_qp_attach = props->max_mcast_qp_attach * + props->max_mcast_grp; + props->max_map_per_fmr = (1 << (32 - ilog2(dev->dev->caps.num_mpts))) - 1; + +out: + kfree(in_mad); + kfree(out_mad); + + return err; +} + +static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port, + struct ib_port_attr *props) +{ + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + memset(props, 0, sizeof *props); + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; + in_mad->attr_mod = cpu_to_be32(port); + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16)); + props->lmc = out_mad->data[34] & 0x7; + props->sm_lid = be16_to_cpup((__be16 *) (out_mad->data + 18)); + props->sm_sl = out_mad->data[36] & 0xf; + props->state = out_mad->data[32] & 0xf; + props->phys_state = out_mad->data[33] >> 4; + props->port_cap_flags = be32_to_cpup((__be32 *) (out_mad->data + 20)); + props->gid_tbl_len = to_mdev(ibdev)->dev->caps.gid_table_len; + props->max_msg_sz = 0x80000000; + props->pkey_tbl_len = to_mdev(ibdev)->dev->caps.pkey_table_len; + props->bad_pkey_cntr = be16_to_cpup((__be16 *) (out_mad->data + 46)); + props->qkey_viol_cntr = be16_to_cpup((__be16 *) (out_mad->data + 48)); + props->active_width = out_mad->data[31] & 0xf; + props->active_speed = out_mad->data[35] >> 4; + props->max_mtu = out_mad->data[41] & 0xf; + props->active_mtu = out_mad->data[36] >> 4; + props->subnet_timeout = out_mad->data[51] & 0x1f; + props->max_vl_num = out_mad->data[37] >> 4; + props->init_type_reply = out_mad->data[41] >> 4; + +out: + kfree(in_mad); + kfree(out_mad); + + return err; +} + +static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index, + union ib_gid *gid) +{ + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; + in_mad->attr_mod = cpu_to_be32(port); + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + memcpy(gid->raw, out_mad->data + 8, 8); + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_GUID_INFO; + in_mad->attr_mod = cpu_to_be32(index / 8); + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + memcpy(gid->raw + 8, out_mad->data + (index % 8) * 8, 8); + +out: + kfree(in_mad); + kfree(out_mad); + return err; +} + +static int mlx4_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, + u16 *pkey) +{ + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_PKEY_TABLE; + in_mad->attr_mod = cpu_to_be32(index / 32); + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + *pkey = be16_to_cpu(((__be16 *) out_mad->data)[index % 32]); + +out: + kfree(in_mad); + kfree(out_mad); + return err; +} + +static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask, + struct ib_device_modify *props) +{ + if (mask & ~IB_DEVICE_MODIFY_NODE_DESC) + return -EOPNOTSUPP; + + if (mask & IB_DEVICE_MODIFY_NODE_DESC) { + spin_lock(&to_mdev(ibdev)->sm_lock); + memcpy(ibdev->node_desc, props->node_desc, 64); + spin_unlock(&to_mdev(ibdev)->sm_lock); + } + + return 0; +} + +static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols, + u32 cap_mask) +{ + struct mlx4_cmd_mailbox *mailbox; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev->dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + memset(mailbox->buf, 0, 256); + *(u8 *) mailbox->buf = !!reset_qkey_viols << 6; + ((__be32 *) mailbox->buf)[2] = cpu_to_be32(cap_mask); + + err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B); + + mlx4_free_cmd_mailbox(dev->dev, mailbox); + return err; +} + +static int mlx4_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, + struct ib_port_modify *props) +{ + struct ib_port_attr attr; + u32 cap_mask; + int err; + + mutex_lock(&to_mdev(ibdev)->cap_mask_mutex); + + err = mlx4_ib_query_port(ibdev, port, &attr); + if (err) + goto out; + + cap_mask = (attr.port_cap_flags | props->set_port_cap_mask) & + ~props->clr_port_cap_mask; + + err = mlx4_SET_PORT(to_mdev(ibdev), port, + !!(mask & IB_PORT_RESET_QKEY_CNTR), + cap_mask); + +out: + mutex_unlock(&to_mdev(ibdev)->cap_mask_mutex); + return err; +} + +static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev, + struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_ucontext *context; + struct mlx4_ib_alloc_ucontext_resp resp; + int err; + + 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; + + context = kmalloc(sizeof *context, GFP_KERNEL); + if (!context) + return ERR_PTR(-ENOMEM); + + err = mlx4_uar_alloc(to_mdev(ibdev)->dev, &context->uar); + if (err) { + kfree(context); + return ERR_PTR(err); + } + + INIT_LIST_HEAD(&context->db_page_list); + mutex_init(&context->db_page_mutex); + + err = ib_copy_to_udata(udata, &resp, sizeof resp); + if (err) { + mlx4_uar_free(to_mdev(ibdev)->dev, &context->uar); + kfree(context); + return ERR_PTR(-EFAULT); + } + + return &context->ibucontext; +} + +static int mlx4_ib_dealloc_ucontext(struct ib_ucontext *ibcontext) +{ + struct mlx4_ib_ucontext *context = to_mucontext(ibcontext); + + mlx4_uar_free(to_mdev(ibcontext->device)->dev, &context->uar); + kfree(context); + + return 0; +} + +static int mlx4_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) +{ + struct mlx4_ib_dev *dev = to_mdev(context->device); + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_pgoff == 0) { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, + to_mucontext(context)->uar.pfn, + PAGE_SIZE, vma->vm_page_prot)) + return -EAGAIN; + } else if (vma->vm_pgoff == 1 && dev->dev->caps.bf_reg_size != 0) { + /* FIXME want pgprot_writecombine() for BlueFlame pages */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, + to_mucontext(context)->uar.pfn + + dev->dev->caps.num_uars, + PAGE_SIZE, vma->vm_page_prot)) + return -EAGAIN; + } else + return -EINVAL; + + return 0; +} + +static struct ib_pd *mlx4_ib_alloc_pd(struct ib_device *ibdev, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct mlx4_ib_pd *pd; + int err; + + pd = kmalloc(sizeof *pd, GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + err = mlx4_pd_alloc(to_mdev(ibdev)->dev, &pd->pdn); + if (err) { + kfree(pd); + return ERR_PTR(err); + } + + if (context) + if (ib_copy_to_udata(udata, &pd->pdn, sizeof (__u32))) { + mlx4_pd_free(to_mdev(ibdev)->dev, pd->pdn); + kfree(pd); + return ERR_PTR(-EFAULT); + } + + return &pd->ibpd; +} + +static int mlx4_ib_dealloc_pd(struct ib_pd *pd) +{ + mlx4_pd_free(to_mdev(pd->device)->dev, to_mpd(pd)->pdn); + kfree(pd); + + return 0; +} + +static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + return mlx4_multicast_attach(to_mdev(ibqp->device)->dev, + &to_mqp(ibqp)->mqp, gid->raw); +} + +static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + return mlx4_multicast_detach(to_mdev(ibqp->device)->dev, + &to_mqp(ibqp)->mqp, gid->raw); +} + +static int init_node_data(struct mlx4_ib_dev *dev) +{ + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_NODE_DESC; + + err = mlx4_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + memcpy(dev->ib_dev.node_desc, out_mad->data, 64); + + in_mad->attr_id = IB_SMP_ATTR_NODE_INFO; + + err = mlx4_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + memcpy(&dev->ib_dev.node_guid, out_mad->data + 12, 8); + +out: + kfree(in_mad); + kfree(out_mad); + return err; +} + +static void *mlx4_ib_add(struct mlx4_dev *dev) +{ + struct mlx4_ib_dev *ibdev; + + ibdev = (struct mlx4_ib_dev *) ib_alloc_device(sizeof *ibdev); + if (!ibdev) { + dev_err(&dev->pdev->dev, "Device struct alloc failed\n"); + return NULL; + } + + if (mlx4_pd_alloc(dev, &ibdev->priv_pdn)) + goto err_dealloc; + + if (mlx4_uar_alloc(dev, &ibdev->priv_uar)) + goto err_pd; + + ibdev->uar_map = ioremap(ibdev->priv_uar.pfn << PAGE_SHIFT, PAGE_SIZE); + if (!ibdev->uar_map) + goto err_uar; + + INIT_LIST_HEAD(&ibdev->pgdir_list); + mutex_init(&ibdev->pgdir_mutex); + + ibdev->dev = dev; + + strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX); + ibdev->ib_dev.owner = THIS_MODULE; + ibdev->ib_dev.node_type = RDMA_NODE_IB_CA; + ibdev->ib_dev.phys_port_cnt = dev->caps.num_ports; + ibdev->ib_dev.num_comp_vectors = 1; + ibdev->ib_dev.dma_device = &dev->pdev->dev; + + ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_ABI_VERSION; + ibdev->ib_dev.uverbs_cmd_mask = + (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | + (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) | + (1ull << IB_USER_VERBS_CMD_QUERY_PORT) | + (1ull << IB_USER_VERBS_CMD_ALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_REG_MR) | + (1ull << IB_USER_VERBS_CMD_DEREG_MR) | + (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) | + (1ull << IB_USER_VERBS_CMD_CREATE_CQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) | + (1ull << IB_USER_VERBS_CMD_CREATE_QP) | + (1ull << IB_USER_VERBS_CMD_MODIFY_QP) | + (1ull << IB_USER_VERBS_CMD_DESTROY_QP) | + (1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) | + (1ull << IB_USER_VERBS_CMD_DETACH_MCAST) | + (1ull << IB_USER_VERBS_CMD_CREATE_SRQ) | + (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ); + + ibdev->ib_dev.query_device = mlx4_ib_query_device; + ibdev->ib_dev.query_port = mlx4_ib_query_port; + ibdev->ib_dev.query_gid = mlx4_ib_query_gid; + ibdev->ib_dev.query_pkey = mlx4_ib_query_pkey; + ibdev->ib_dev.modify_device = mlx4_ib_modify_device; + ibdev->ib_dev.modify_port = mlx4_ib_modify_port; + ibdev->ib_dev.alloc_ucontext = mlx4_ib_alloc_ucontext; + ibdev->ib_dev.dealloc_ucontext = mlx4_ib_dealloc_ucontext; + ibdev->ib_dev.mmap = mlx4_ib_mmap; + ibdev->ib_dev.alloc_pd = mlx4_ib_alloc_pd; + ibdev->ib_dev.dealloc_pd = mlx4_ib_dealloc_pd; + ibdev->ib_dev.create_ah = mlx4_ib_create_ah; + ibdev->ib_dev.query_ah = mlx4_ib_query_ah; + ibdev->ib_dev.destroy_ah = mlx4_ib_destroy_ah; + ibdev->ib_dev.create_srq = mlx4_ib_create_srq; + ibdev->ib_dev.modify_srq = mlx4_ib_modify_srq; + ibdev->ib_dev.destroy_srq = mlx4_ib_destroy_srq; + ibdev->ib_dev.post_srq_recv = mlx4_ib_post_srq_recv; + ibdev->ib_dev.create_qp = mlx4_ib_create_qp; + ibdev->ib_dev.modify_qp = mlx4_ib_modify_qp; + ibdev->ib_dev.destroy_qp = mlx4_ib_destroy_qp; + ibdev->ib_dev.post_send = mlx4_ib_post_send; + ibdev->ib_dev.post_recv = mlx4_ib_post_recv; + ibdev->ib_dev.create_cq = mlx4_ib_create_cq; + ibdev->ib_dev.destroy_cq = mlx4_ib_destroy_cq; + ibdev->ib_dev.poll_cq = mlx4_ib_poll_cq; + ibdev->ib_dev.req_notify_cq = mlx4_ib_arm_cq; + ibdev->ib_dev.get_dma_mr = mlx4_ib_get_dma_mr; + ibdev->ib_dev.reg_user_mr = mlx4_ib_reg_user_mr; + ibdev->ib_dev.dereg_mr = mlx4_ib_dereg_mr; + ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach; + ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach; + ibdev->ib_dev.process_mad = mlx4_ib_process_mad; + + if (init_node_data(ibdev)) + goto err_map; + + spin_lock_init(&ibdev->sm_lock); + mutex_init(&ibdev->cap_mask_mutex); + + if (ib_register_device(&ibdev->ib_dev)) + goto err_map; + + if (mlx4_ib_mad_init(ibdev)) + goto err_reg; + + return ibdev; + +err_reg: + ib_unregister_device(&ibdev->ib_dev); + +err_map: + iounmap(ibdev->uar_map); + +err_uar: + mlx4_uar_free(dev, &ibdev->priv_uar); + +err_pd: + mlx4_pd_free(dev, ibdev->priv_pdn); + +err_dealloc: + ib_dealloc_device(&ibdev->ib_dev); + + return NULL; +} + +static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) +{ + struct mlx4_ib_dev *ibdev = ibdev_ptr; + int p; + + for (p = 1; p <= dev->caps.num_ports; ++p) + mlx4_CLOSE_PORT(dev, p); + + mlx4_ib_mad_cleanup(ibdev); + ib_unregister_device(&ibdev->ib_dev); + iounmap(ibdev->uar_map); + mlx4_uar_free(dev, &ibdev->priv_uar); + mlx4_pd_free(dev, ibdev->priv_pdn); + ib_dealloc_device(&ibdev->ib_dev); +} + +static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, + enum mlx4_dev_event event, int subtype, + int port) +{ + struct ib_event ibev; + + switch (event) { + case MLX4_EVENT_TYPE_PORT_CHANGE: + ibev.event = subtype == MLX4_PORT_CHANGE_SUBTYPE_ACTIVE ? + IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR; + break; + + case MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR: + ibev.event = IB_EVENT_DEVICE_FATAL; + break; + + default: + return; + } + + ibev.device = ibdev_ptr; + ibev.element.port_num = port; + + ib_dispatch_event(&ibev); +} + +static struct mlx4_interface mlx4_ib_interface = { + .add = mlx4_ib_add, + .remove = mlx4_ib_remove, + .event = mlx4_ib_event +}; + +static int __init mlx4_ib_init(void) +{ + return mlx4_register_interface(&mlx4_ib_interface); +} + +static void __exit mlx4_ib_cleanup(void) +{ + mlx4_unregister_interface(&mlx4_ib_interface); +} + +module_init(mlx4_ib_init); +module_exit(mlx4_ib_cleanup); diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h new file mode 100644 index 00000000000..93dac71f323 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_IB_H +#define MLX4_IB_H + +#include +#include + +#include +#include + +#include +#include + +enum { + MLX4_IB_DB_PER_PAGE = PAGE_SIZE / 4 +}; + +struct mlx4_ib_db_pgdir; +struct mlx4_ib_user_db_page; + +struct mlx4_ib_db { + __be32 *db; + union { + struct mlx4_ib_db_pgdir *pgdir; + struct mlx4_ib_user_db_page *user_page; + } u; + dma_addr_t dma; + int index; + int order; +}; + +struct mlx4_ib_ucontext { + struct ib_ucontext ibucontext; + struct mlx4_uar uar; + struct list_head db_page_list; + struct mutex db_page_mutex; +}; + +struct mlx4_ib_pd { + struct ib_pd ibpd; + u32 pdn; +}; + +struct mlx4_ib_cq_buf { + struct mlx4_buf buf; + struct mlx4_mtt mtt; +}; + +struct mlx4_ib_cq { + struct ib_cq ibcq; + struct mlx4_cq mcq; + struct mlx4_ib_cq_buf buf; + struct mlx4_ib_db db; + spinlock_t lock; + struct ib_umem *umem; +}; + +struct mlx4_ib_mr { + struct ib_mr ibmr; + struct mlx4_mr mmr; + struct ib_umem *umem; +}; + +struct mlx4_ib_wq { + u64 *wrid; + spinlock_t lock; + int max; + int max_gs; + int offset; + int wqe_shift; + unsigned head; + unsigned tail; +}; + +struct mlx4_ib_qp { + struct ib_qp ibqp; + struct mlx4_qp mqp; + struct mlx4_buf buf; + + struct mlx4_ib_db db; + struct mlx4_ib_wq rq; + + u32 doorbell_qpn; + __be32 sq_signal_bits; + struct mlx4_ib_wq sq; + + struct ib_umem *umem; + struct mlx4_mtt mtt; + int buf_size; + struct mutex mutex; + u8 port; + u8 alt_port; + u8 atomic_rd_en; + u8 resp_depth; + u8 state; +}; + +struct mlx4_ib_srq { + struct ib_srq ibsrq; + struct mlx4_srq msrq; + struct mlx4_buf buf; + struct mlx4_ib_db db; + u64 *wrid; + spinlock_t lock; + int head; + int tail; + u16 wqe_ctr; + struct ib_umem *umem; + struct mlx4_mtt mtt; + struct mutex mutex; +}; + +struct mlx4_ib_ah { + struct ib_ah ibah; + struct mlx4_av av; +}; + +struct mlx4_ib_dev { + struct ib_device ib_dev; + struct mlx4_dev *dev; + void __iomem *uar_map; + + struct list_head pgdir_list; + struct mutex pgdir_mutex; + + struct mlx4_uar priv_uar; + u32 priv_pdn; + MLX4_DECLARE_DOORBELL_LOCK(uar_lock); + + struct ib_mad_agent *send_agent[MLX4_MAX_PORTS][2]; + struct ib_ah *sm_ah[MLX4_MAX_PORTS]; + spinlock_t sm_lock; + + struct mutex cap_mask_mutex; +}; + +static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev) +{ + return container_of(ibdev, struct mlx4_ib_dev, ib_dev); +} + +static inline struct mlx4_ib_ucontext *to_mucontext(struct ib_ucontext *ibucontext) +{ + return container_of(ibucontext, struct mlx4_ib_ucontext, ibucontext); +} + +static inline struct mlx4_ib_pd *to_mpd(struct ib_pd *ibpd) +{ + return container_of(ibpd, struct mlx4_ib_pd, ibpd); +} + +static inline struct mlx4_ib_cq *to_mcq(struct ib_cq *ibcq) +{ + return container_of(ibcq, struct mlx4_ib_cq, ibcq); +} + +static inline struct mlx4_ib_cq *to_mibcq(struct mlx4_cq *mcq) +{ + return container_of(mcq, struct mlx4_ib_cq, mcq); +} + +static inline struct mlx4_ib_mr *to_mmr(struct ib_mr *ibmr) +{ + return container_of(ibmr, struct mlx4_ib_mr, ibmr); +} + +static inline struct mlx4_ib_qp *to_mqp(struct ib_qp *ibqp) +{ + return container_of(ibqp, struct mlx4_ib_qp, ibqp); +} + +static inline struct mlx4_ib_qp *to_mibqp(struct mlx4_qp *mqp) +{ + return container_of(mqp, struct mlx4_ib_qp, mqp); +} + +static inline struct mlx4_ib_srq *to_msrq(struct ib_srq *ibsrq) +{ + return container_of(ibsrq, struct mlx4_ib_srq, ibsrq); +} + +static inline struct mlx4_ib_srq *to_mibsrq(struct mlx4_srq *msrq) +{ + return container_of(msrq, struct mlx4_ib_srq, msrq); +} + +static inline struct mlx4_ib_ah *to_mah(struct ib_ah *ibah) +{ + return container_of(ibah, struct mlx4_ib_ah, ibah); +} + +int mlx4_ib_db_alloc(struct mlx4_ib_dev *dev, struct mlx4_ib_db *db, int order); +void mlx4_ib_db_free(struct mlx4_ib_dev *dev, struct mlx4_ib_db *db); +int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt, + struct mlx4_ib_db *db); +void mlx4_ib_db_unmap_user(struct mlx4_ib_ucontext *context, struct mlx4_ib_db *db); + +struct ib_mr *mlx4_ib_get_dma_mr(struct ib_pd *pd, int acc); +int mlx4_ib_umem_write_mtt(struct mlx4_ib_dev *dev, struct mlx4_mtt *mtt, + struct ib_umem *umem); +struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int access_flags, + struct ib_udata *udata); +int mlx4_ib_dereg_mr(struct ib_mr *mr); + +struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector, + struct ib_ucontext *context, + struct ib_udata *udata); +int mlx4_ib_destroy_cq(struct ib_cq *cq); +int mlx4_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); +int mlx4_ib_arm_cq(struct ib_cq *cq, enum ib_cq_notify_flags flags); +void __mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq); +void mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq); + +struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr); +int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr); +int mlx4_ib_destroy_ah(struct ib_ah *ah); + +struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, + struct ib_srq_init_attr *init_attr, + struct ib_udata *udata); +int mlx4_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, + enum ib_srq_attr_mask attr_mask, struct ib_udata *udata); +int mlx4_ib_destroy_srq(struct ib_srq *srq); +void mlx4_ib_free_srq_wqe(struct mlx4_ib_srq *srq, int wqe_index); +int mlx4_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr); + +struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *init_attr, + struct ib_udata *udata); +int mlx4_ib_destroy_qp(struct ib_qp *qp); +int mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata); +int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr); +int mlx4_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr); + +int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int ignore_mkey, int ignore_bkey, + int port, struct ib_wc *in_wc, struct ib_grh *in_grh, + void *in_mad, void *response_mad); +int mlx4_ib_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); +int mlx4_ib_mad_init(struct mlx4_ib_dev *dev); +void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev); + +static inline int mlx4_ib_ah_grh_present(struct mlx4_ib_ah *ah) +{ + return !!(ah->av.g_slid & 0x80); +} + +#endif /* MLX4_IB_H */ diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c new file mode 100644 index 00000000000..85ae906f1d1 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mlx4_ib.h" + +static u32 convert_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_ATOMIC ? MLX4_PERM_ATOMIC : 0) | + (acc & IB_ACCESS_REMOTE_WRITE ? MLX4_PERM_REMOTE_WRITE : 0) | + (acc & IB_ACCESS_REMOTE_READ ? MLX4_PERM_REMOTE_READ : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? MLX4_PERM_LOCAL_WRITE : 0) | + MLX4_PERM_LOCAL_READ; +} + +struct ib_mr *mlx4_ib_get_dma_mr(struct ib_pd *pd, int acc) +{ + struct mlx4_ib_mr *mr; + int err; + + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + err = mlx4_mr_alloc(to_mdev(pd->device)->dev, to_mpd(pd)->pdn, 0, + ~0ull, convert_access(acc), 0, 0, &mr->mmr); + if (err) + goto err_free; + + err = mlx4_mr_enable(to_mdev(pd->device)->dev, &mr->mmr); + if (err) + goto err_mr; + + mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; + mr->umem = NULL; + + return &mr->ibmr; + +err_mr: + mlx4_mr_free(to_mdev(pd->device)->dev, &mr->mmr); + +err_free: + kfree(mr); + + return ERR_PTR(err); +} + +int mlx4_ib_umem_write_mtt(struct mlx4_ib_dev *dev, struct mlx4_mtt *mtt, + struct ib_umem *umem) +{ + u64 *pages; + struct ib_umem_chunk *chunk; + int i, j, k; + int n; + int len; + int err = 0; + + pages = (u64 *) __get_free_page(GFP_KERNEL); + if (!pages) + return -ENOMEM; + + i = n = 0; + + list_for_each_entry(chunk, &umem->chunk_list, list) + for (j = 0; j < chunk->nmap; ++j) { + len = sg_dma_len(&chunk->page_list[j]) >> mtt->page_shift; + for (k = 0; k < len; ++k) { + pages[i++] = sg_dma_address(&chunk->page_list[j]) + + umem->page_size * k; + /* + * Be friendly to WRITE_MTT firmware + * command, and pass it chunks of + * appropriate size. + */ + if (i == PAGE_SIZE / sizeof (u64) - 2) { + err = mlx4_write_mtt(dev->dev, mtt, n, + i, pages); + if (err) + goto out; + n += i; + i = 0; + } + } + } + + if (i) + err = mlx4_write_mtt(dev->dev, mtt, n, i, pages); + +out: + free_page((unsigned long) pages); + return err; +} + +struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int access_flags, + struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_mr *mr; + int shift; + int err; + int n; + + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->umem = ib_umem_get(pd->uobject->context, start, length, access_flags); + if (IS_ERR(mr->umem)) { + err = PTR_ERR(mr->umem); + goto err_free; + } + + n = ib_umem_page_count(mr->umem); + shift = ilog2(mr->umem->page_size); + + err = mlx4_mr_alloc(dev->dev, to_mpd(pd)->pdn, virt_addr, length, + convert_access(access_flags), n, shift, &mr->mmr); + if (err) + goto err_umem; + + err = mlx4_ib_umem_write_mtt(dev, &mr->mmr.mtt, mr->umem); + if (err) + goto err_mr; + + err = mlx4_mr_enable(dev->dev, &mr->mmr); + if (err) + goto err_mr; + + mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; + + return &mr->ibmr; + +err_mr: + mlx4_mr_free(to_mdev(pd->device)->dev, &mr->mmr); + +err_umem: + ib_umem_release(mr->umem); + +err_free: + kfree(mr); + + return ERR_PTR(err); +} + +int mlx4_ib_dereg_mr(struct ib_mr *ibmr) +{ + struct mlx4_ib_mr *mr = to_mmr(ibmr); + + mlx4_mr_free(to_mdev(ibmr->device)->dev, &mr->mmr); + if (mr->umem) + ib_umem_release(mr->umem); + kfree(mr); + + return 0; +} diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c new file mode 100644 index 00000000000..5cd70690845 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -0,0 +1,1294 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4_ib.h" +#include "user.h" + +enum { + MLX4_IB_ACK_REQ_FREQ = 8, +}; + +enum { + MLX4_IB_DEFAULT_SCHED_QUEUE = 0x83, + MLX4_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f +}; + +enum { + /* + * Largest possible UD header: send with GRH and immediate data. + */ + MLX4_IB_UD_HEADER_SIZE = 72 +}; + +struct mlx4_ib_sqp { + struct mlx4_ib_qp qp; + int pkey_index; + u32 qkey; + u32 send_psn; + struct ib_ud_header ud_header; + u8 header_buf[MLX4_IB_UD_HEADER_SIZE]; +}; + +static const __be32 mlx4_ib_opcode[] = { + [IB_WR_SEND] = __constant_cpu_to_be32(MLX4_OPCODE_SEND), + [IB_WR_SEND_WITH_IMM] = __constant_cpu_to_be32(MLX4_OPCODE_SEND_IMM), + [IB_WR_RDMA_WRITE] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_WRITE), + [IB_WR_RDMA_WRITE_WITH_IMM] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_WRITE_IMM), + [IB_WR_RDMA_READ] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_READ), + [IB_WR_ATOMIC_CMP_AND_SWP] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), + [IB_WR_ATOMIC_FETCH_AND_ADD] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), +}; + +static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp) +{ + return container_of(mqp, struct mlx4_ib_sqp, qp); +} + +static int is_sqp(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp) +{ + return qp->mqp.qpn >= dev->dev->caps.sqp_start && + qp->mqp.qpn <= dev->dev->caps.sqp_start + 3; +} + +static int is_qp0(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp) +{ + return qp->mqp.qpn >= dev->dev->caps.sqp_start && + qp->mqp.qpn <= dev->dev->caps.sqp_start + 1; +} + +static void *get_wqe(struct mlx4_ib_qp *qp, int offset) +{ + if (qp->buf.nbufs == 1) + return qp->buf.u.direct.buf + offset; + else + return qp->buf.u.page_list[offset >> PAGE_SHIFT].buf + + (offset & (PAGE_SIZE - 1)); +} + +static void *get_recv_wqe(struct mlx4_ib_qp *qp, int n) +{ + return get_wqe(qp, qp->rq.offset + (n << qp->rq.wqe_shift)); +} + +static void *get_send_wqe(struct mlx4_ib_qp *qp, int n) +{ + return get_wqe(qp, qp->sq.offset + (n << qp->sq.wqe_shift)); +} + +static void mlx4_ib_qp_event(struct mlx4_qp *qp, enum mlx4_event type) +{ + struct ib_event event; + struct ib_qp *ibqp = &to_mibqp(qp)->ibqp; + + if (type == MLX4_EVENT_TYPE_PATH_MIG) + to_mibqp(qp)->port = to_mibqp(qp)->alt_port; + + if (ibqp->event_handler) { + event.device = ibqp->device; + event.element.qp = ibqp; + switch (type) { + case MLX4_EVENT_TYPE_PATH_MIG: + event.event = IB_EVENT_PATH_MIG; + break; + case MLX4_EVENT_TYPE_COMM_EST: + event.event = IB_EVENT_COMM_EST; + break; + case MLX4_EVENT_TYPE_SQ_DRAINED: + event.event = IB_EVENT_SQ_DRAINED; + break; + case MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE: + event.event = IB_EVENT_QP_LAST_WQE_REACHED; + break; + case MLX4_EVENT_TYPE_WQ_CATAS_ERROR: + event.event = IB_EVENT_QP_FATAL; + break; + case MLX4_EVENT_TYPE_PATH_MIG_FAILED: + event.event = IB_EVENT_PATH_MIG_ERR; + break; + case MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + event.event = IB_EVENT_QP_REQ_ERR; + break; + case MLX4_EVENT_TYPE_WQ_ACCESS_ERROR: + event.event = IB_EVENT_QP_ACCESS_ERR; + break; + default: + printk(KERN_WARNING "mlx4_ib: Unexpected event type %d " + "on QP %06x\n", type, qp->qpn); + return; + } + + ibqp->event_handler(&event, ibqp->qp_context); + } +} + +static int send_wqe_overhead(enum ib_qp_type type) +{ + /* + * UD WQEs must have a datagram segment. + * RC and UC WQEs might have a remote address segment. + * MLX WQEs need two extra inline data segments (for the UD + * header and space for the ICRC). + */ + switch (type) { + case IB_QPT_UD: + return sizeof (struct mlx4_wqe_ctrl_seg) + + sizeof (struct mlx4_wqe_datagram_seg); + case IB_QPT_UC: + return sizeof (struct mlx4_wqe_ctrl_seg) + + sizeof (struct mlx4_wqe_raddr_seg); + case IB_QPT_RC: + return sizeof (struct mlx4_wqe_ctrl_seg) + + sizeof (struct mlx4_wqe_atomic_seg) + + sizeof (struct mlx4_wqe_raddr_seg); + case IB_QPT_SMI: + case IB_QPT_GSI: + return sizeof (struct mlx4_wqe_ctrl_seg) + + ALIGN(MLX4_IB_UD_HEADER_SIZE + + sizeof (struct mlx4_wqe_inline_seg), + sizeof (struct mlx4_wqe_data_seg)) + + ALIGN(4 + + sizeof (struct mlx4_wqe_inline_seg), + sizeof (struct mlx4_wqe_data_seg)); + default: + return sizeof (struct mlx4_wqe_ctrl_seg); + } +} + +static int set_qp_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap, + enum ib_qp_type type, struct mlx4_ib_qp *qp) +{ + /* Sanity check QP size before proceeding */ + if (cap->max_send_wr > dev->dev->caps.max_wqes || + cap->max_recv_wr > dev->dev->caps.max_wqes || + cap->max_send_sge > dev->dev->caps.max_sq_sg || + cap->max_recv_sge > dev->dev->caps.max_rq_sg || + cap->max_inline_data + send_wqe_overhead(type) + + sizeof (struct mlx4_wqe_inline_seg) > dev->dev->caps.max_sq_desc_sz) + return -EINVAL; + + /* + * For MLX transport we need 2 extra S/G entries: + * one for the header and one for the checksum at the end + */ + if ((type == IB_QPT_SMI || type == IB_QPT_GSI) && + cap->max_send_sge + 2 > dev->dev->caps.max_sq_sg) + return -EINVAL; + + qp->rq.max = cap->max_recv_wr ? roundup_pow_of_two(cap->max_recv_wr) : 0; + qp->sq.max = cap->max_send_wr ? roundup_pow_of_two(cap->max_send_wr) : 0; + + qp->rq.wqe_shift = ilog2(roundup_pow_of_two(cap->max_recv_sge * + sizeof (struct mlx4_wqe_data_seg))); + qp->rq.max_gs = (1 << qp->rq.wqe_shift) / sizeof (struct mlx4_wqe_data_seg); + + qp->sq.wqe_shift = ilog2(roundup_pow_of_two(max(cap->max_send_sge * + sizeof (struct mlx4_wqe_data_seg), + cap->max_inline_data + + sizeof (struct mlx4_wqe_inline_seg)) + + send_wqe_overhead(type))); + qp->sq.max_gs = ((1 << qp->sq.wqe_shift) - send_wqe_overhead(type)) / + sizeof (struct mlx4_wqe_data_seg); + + qp->buf_size = (qp->rq.max << qp->rq.wqe_shift) + + (qp->sq.max << qp->sq.wqe_shift); + if (qp->rq.wqe_shift > qp->sq.wqe_shift) { + qp->rq.offset = 0; + qp->sq.offset = qp->rq.max << qp->rq.wqe_shift; + } else { + qp->rq.offset = qp->sq.max << qp->sq.wqe_shift; + qp->sq.offset = 0; + } + + cap->max_send_wr = qp->sq.max; + cap->max_recv_wr = qp->rq.max; + cap->max_send_sge = qp->sq.max_gs; + cap->max_recv_sge = qp->rq.max_gs; + cap->max_inline_data = (1 << qp->sq.wqe_shift) - send_wqe_overhead(type) - + sizeof (struct mlx4_wqe_inline_seg); + + return 0; +} + +static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, + struct ib_qp_init_attr *init_attr, + struct ib_udata *udata, int sqpn, struct mlx4_ib_qp *qp) +{ + struct mlx4_wqe_ctrl_seg *ctrl; + int err; + int i; + + mutex_init(&qp->mutex); + spin_lock_init(&qp->sq.lock); + spin_lock_init(&qp->rq.lock); + + qp->state = IB_QPS_RESET; + qp->atomic_rd_en = 0; + qp->resp_depth = 0; + + qp->rq.head = 0; + qp->rq.tail = 0; + qp->sq.head = 0; + qp->sq.tail = 0; + + err = set_qp_size(dev, &init_attr->cap, init_attr->qp_type, qp); + if (err) + goto err; + + if (pd->uobject) { + struct mlx4_ib_create_qp ucmd; + + if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { + err = -EFAULT; + goto err; + } + + qp->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, + qp->buf_size, 0); + if (IS_ERR(qp->umem)) { + err = PTR_ERR(qp->umem); + goto err; + } + + err = mlx4_mtt_init(dev->dev, ib_umem_page_count(qp->umem), + ilog2(qp->umem->page_size), &qp->mtt); + if (err) + goto err_buf; + + err = mlx4_ib_umem_write_mtt(dev, &qp->mtt, qp->umem); + if (err) + goto err_mtt; + + err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context), + ucmd.db_addr, &qp->db); + if (err) + goto err_mtt; + } else { + err = mlx4_ib_db_alloc(dev, &qp->db, 0); + if (err) + goto err; + + *qp->db.db = 0; + + if (mlx4_buf_alloc(dev->dev, qp->buf_size, PAGE_SIZE * 2, &qp->buf)) { + err = -ENOMEM; + goto err_db; + } + + err = mlx4_mtt_init(dev->dev, qp->buf.npages, qp->buf.page_shift, + &qp->mtt); + if (err) + goto err_buf; + + err = mlx4_buf_write_mtt(dev->dev, &qp->mtt, &qp->buf); + if (err) + goto err_mtt; + + for (i = 0; i < qp->sq.max; ++i) { + ctrl = get_send_wqe(qp, i); + ctrl->owner_opcode = cpu_to_be32(1 << 31); + } + + qp->sq.wrid = kmalloc(qp->sq.max * sizeof (u64), GFP_KERNEL); + qp->rq.wrid = kmalloc(qp->rq.max * sizeof (u64), GFP_KERNEL); + + if (!qp->sq.wrid || !qp->rq.wrid) { + err = -ENOMEM; + goto err_wrid; + } + + /* We don't support inline sends for kernel QPs (yet) */ + init_attr->cap.max_inline_data = 0; + } + + err = mlx4_qp_alloc(dev->dev, sqpn, &qp->mqp); + if (err) + goto err_wrid; + + /* + * Hardware wants QPN written in big-endian order (after + * shifting) for send doorbell. Precompute this value to save + * a little bit when posting sends. + */ + qp->doorbell_qpn = swab32(qp->mqp.qpn << 8); + + if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) + qp->sq_signal_bits = cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE); + else + qp->sq_signal_bits = 0; + + qp->mqp.event = mlx4_ib_qp_event; + + return 0; + +err_wrid: + if (pd->uobject) + mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db); + else { + kfree(qp->sq.wrid); + kfree(qp->rq.wrid); + } + +err_mtt: + mlx4_mtt_cleanup(dev->dev, &qp->mtt); + +err_buf: + if (pd->uobject) + ib_umem_release(qp->umem); + else + mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf); + +err_db: + if (!pd->uobject) + mlx4_ib_db_free(dev, &qp->db); + +err: + return err; +} + +static enum mlx4_qp_state to_mlx4_state(enum ib_qp_state state) +{ + switch (state) { + case IB_QPS_RESET: return MLX4_QP_STATE_RST; + case IB_QPS_INIT: return MLX4_QP_STATE_INIT; + case IB_QPS_RTR: return MLX4_QP_STATE_RTR; + case IB_QPS_RTS: return MLX4_QP_STATE_RTS; + case IB_QPS_SQD: return MLX4_QP_STATE_SQD; + case IB_QPS_SQE: return MLX4_QP_STATE_SQER; + case IB_QPS_ERR: return MLX4_QP_STATE_ERR; + default: return -1; + } +} + +static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq) +{ + if (send_cq == recv_cq) + spin_lock_irq(&send_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 { + spin_lock_irq(&recv_cq->lock); + spin_lock_nested(&send_cq->lock, SINGLE_DEPTH_NESTING); + } +} + +static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq) +{ + if (send_cq == recv_cq) + spin_unlock_irq(&send_cq->lock); + else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { + spin_unlock(&recv_cq->lock); + spin_unlock_irq(&send_cq->lock); + } else { + spin_unlock(&send_cq->lock); + spin_unlock_irq(&recv_cq->lock); + } +} + +static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, + int is_user) +{ + struct mlx4_ib_cq *send_cq, *recv_cq; + + if (qp->state != IB_QPS_RESET) + if (mlx4_qp_modify(dev->dev, NULL, to_mlx4_state(qp->state), + MLX4_QP_STATE_RST, NULL, 0, 0, &qp->mqp)) + printk(KERN_WARNING "mlx4_ib: modify QP %06x to RESET failed.\n", + qp->mqp.qpn); + + send_cq = to_mcq(qp->ibqp.send_cq); + recv_cq = to_mcq(qp->ibqp.recv_cq); + + mlx4_ib_lock_cqs(send_cq, recv_cq); + + if (!is_user) { + __mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn, + qp->ibqp.srq ? to_msrq(qp->ibqp.srq): NULL); + if (send_cq != recv_cq) + __mlx4_ib_cq_clean(send_cq, qp->mqp.qpn, NULL); + } + + mlx4_qp_remove(dev->dev, &qp->mqp); + + mlx4_ib_unlock_cqs(send_cq, recv_cq); + + mlx4_qp_free(dev->dev, &qp->mqp); + mlx4_mtt_cleanup(dev->dev, &qp->mtt); + + if (is_user) { + mlx4_ib_db_unmap_user(to_mucontext(qp->ibqp.uobject->context), + &qp->db); + ib_umem_release(qp->umem); + } else { + kfree(qp->sq.wrid); + kfree(qp->rq.wrid); + mlx4_buf_free(dev->dev, qp->buf_size, &qp->buf); + mlx4_ib_db_free(dev, &qp->db); + } +} + +struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *init_attr, + struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_sqp *sqp; + struct mlx4_ib_qp *qp; + int err; + + switch (init_attr->qp_type) { + case IB_QPT_RC: + case IB_QPT_UC: + case IB_QPT_UD: + { + qp = kmalloc(sizeof *qp, GFP_KERNEL); + if (!qp) + return ERR_PTR(-ENOMEM); + + err = create_qp_common(dev, pd, init_attr, udata, 0, qp); + if (err) { + kfree(qp); + return ERR_PTR(err); + } + + qp->ibqp.qp_num = qp->mqp.qpn; + + break; + } + case IB_QPT_SMI: + case IB_QPT_GSI: + { + /* Userspace is not allowed to create special QPs: */ + if (pd->uobject) + return ERR_PTR(-EINVAL); + + sqp = kmalloc(sizeof *sqp, GFP_KERNEL); + if (!sqp) + return ERR_PTR(-ENOMEM); + + qp = &sqp->qp; + + err = create_qp_common(dev, pd, init_attr, udata, + dev->dev->caps.sqp_start + + (init_attr->qp_type == IB_QPT_SMI ? 0 : 2) + + init_attr->port_num - 1, + qp); + if (err) { + kfree(sqp); + return ERR_PTR(err); + } + + qp->port = init_attr->port_num; + qp->ibqp.qp_num = init_attr->qp_type == IB_QPT_SMI ? 0 : 1; + + break; + } + default: + /* Don't support raw QPs */ + return ERR_PTR(-EINVAL); + } + + return &qp->ibqp; +} + +int mlx4_ib_destroy_qp(struct ib_qp *qp) +{ + struct mlx4_ib_dev *dev = to_mdev(qp->device); + struct mlx4_ib_qp *mqp = to_mqp(qp); + + if (is_qp0(dev, mqp)) + mlx4_CLOSE_PORT(dev->dev, mqp->port); + + destroy_qp_common(dev, mqp, !!qp->pd->uobject); + + if (is_sqp(dev, mqp)) + kfree(to_msqp(mqp)); + else + kfree(mqp); + + return 0; +} + +static void init_port(struct mlx4_ib_dev *dev, int port) +{ + struct mlx4_init_port_param param; + int err; + + memset(¶m, 0, sizeof param); + + param.port_width_cap = dev->dev->caps.port_width_cap; + param.vl_cap = dev->dev->caps.vl_cap; + param.mtu = ib_mtu_enum_to_int(dev->dev->caps.mtu_cap); + param.max_gid = dev->dev->caps.gid_table_len; + param.max_pkey = dev->dev->caps.pkey_table_len; + + err = mlx4_INIT_PORT(dev->dev, ¶m, port); + if (err) + printk(KERN_WARNING "INIT_PORT failed, return code %d.\n", err); +} + +static int to_mlx4_st(enum ib_qp_type type) +{ + switch (type) { + case IB_QPT_RC: return MLX4_QP_ST_RC; + case IB_QPT_UC: return MLX4_QP_ST_UC; + case IB_QPT_UD: return MLX4_QP_ST_UD; + case IB_QPT_SMI: + case IB_QPT_GSI: return MLX4_QP_ST_MLX; + default: return -1; + } +} + +static __be32 to_mlx4_access_flags(struct mlx4_ib_qp *qp, struct ib_qp_attr *attr, + int attr_mask) +{ + u8 dest_rd_atomic; + u32 access_flags; + u32 hw_access_flags = 0; + + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + dest_rd_atomic = attr->max_dest_rd_atomic; + else + dest_rd_atomic = qp->resp_depth; + + if (attr_mask & IB_QP_ACCESS_FLAGS) + access_flags = attr->qp_access_flags; + else + access_flags = qp->atomic_rd_en; + + if (!dest_rd_atomic) + access_flags &= IB_ACCESS_REMOTE_WRITE; + + if (access_flags & IB_ACCESS_REMOTE_READ) + hw_access_flags |= MLX4_QP_BIT_RRE; + if (access_flags & IB_ACCESS_REMOTE_ATOMIC) + hw_access_flags |= MLX4_QP_BIT_RAE; + if (access_flags & IB_ACCESS_REMOTE_WRITE) + hw_access_flags |= MLX4_QP_BIT_RWE; + + return cpu_to_be32(hw_access_flags); +} + +static void store_sqp_attrs(struct mlx4_ib_sqp *sqp, struct ib_qp_attr *attr, + int attr_mask) +{ + if (attr_mask & IB_QP_PKEY_INDEX) + sqp->pkey_index = attr->pkey_index; + if (attr_mask & IB_QP_QKEY) + sqp->qkey = attr->qkey; + if (attr_mask & IB_QP_SQ_PSN) + sqp->send_psn = attr->sq_psn; +} + +static void mlx4_set_sched(struct mlx4_qp_path *path, u8 port) +{ + path->sched_queue = (path->sched_queue & 0xbf) | ((port - 1) << 6); +} + +static int mlx4_set_path(struct mlx4_ib_dev *dev, struct ib_ah_attr *ah, + struct mlx4_qp_path *path, u8 port) +{ + path->grh_mylmc = ah->src_path_bits & 0x7f; + path->rlid = cpu_to_be16(ah->dlid); + if (ah->static_rate) { + path->static_rate = ah->static_rate + MLX4_STAT_RATE_OFFSET; + while (path->static_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET && + !(1 << path->static_rate & dev->dev->caps.stat_rate_support)) + --path->static_rate; + } else + path->static_rate = 0; + path->counter_index = 0xff; + + if (ah->ah_flags & IB_AH_GRH) { + if (ah->grh.sgid_index >= dev->dev->caps.gid_table_len) { + printk(KERN_ERR "sgid_index (%u) too large. max is %d\n", + ah->grh.sgid_index, dev->dev->caps.gid_table_len - 1); + return -1; + } + + path->grh_mylmc |= 1 << 7; + path->mgid_index = ah->grh.sgid_index; + path->hop_limit = ah->grh.hop_limit; + path->tclass_flowlabel = + cpu_to_be32((ah->grh.traffic_class << 20) | + (ah->grh.flow_label)); + memcpy(path->rgid, ah->grh.dgid.raw, 16); + } + + path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE | + ((port - 1) << 6) | ((ah->sl & 0xf) << 2); + + return 0; +} + +int mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(ibqp->device); + struct mlx4_ib_qp *qp = to_mqp(ibqp); + struct mlx4_qp_context *context; + enum mlx4_qp_optpar optpar = 0; + enum ib_qp_state cur_state, new_state; + int sqd_event; + int err = -EINVAL; + + context = kzalloc(sizeof *context, GFP_KERNEL); + if (!context) + return -ENOMEM; + + mutex_lock(&qp->mutex); + + cur_state = attr_mask & IB_QP_CUR_STATE ? attr->cur_qp_state : qp->state; + new_state = attr_mask & IB_QP_STATE ? attr->qp_state : cur_state; + + if (!ib_modify_qp_is_ok(cur_state, new_state, ibqp->qp_type, attr_mask)) + goto out; + + if ((attr_mask & IB_QP_PKEY_INDEX) && + attr->pkey_index >= dev->dev->caps.pkey_table_len) { + goto out; + } + + if ((attr_mask & IB_QP_PORT) && + (attr->port_num == 0 || attr->port_num > dev->dev->caps.num_ports)) { + goto out; + } + + if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC && + attr->max_rd_atomic > dev->dev->caps.max_qp_init_rdma) { + goto out; + } + + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC && + attr->max_dest_rd_atomic > 1 << dev->dev->caps.max_qp_dest_rdma) { + goto out; + } + + context->flags = cpu_to_be32((to_mlx4_state(new_state) << 28) | + (to_mlx4_st(ibqp->qp_type) << 16)); + context->flags |= cpu_to_be32(1 << 8); /* DE? */ + + if (!(attr_mask & IB_QP_PATH_MIG_STATE)) + context->flags |= cpu_to_be32(MLX4_QP_PM_MIGRATED << 11); + else { + optpar |= MLX4_QP_OPTPAR_PM_STATE; + switch (attr->path_mig_state) { + case IB_MIG_MIGRATED: + context->flags |= cpu_to_be32(MLX4_QP_PM_MIGRATED << 11); + break; + case IB_MIG_REARM: + context->flags |= cpu_to_be32(MLX4_QP_PM_REARM << 11); + break; + case IB_MIG_ARMED: + context->flags |= cpu_to_be32(MLX4_QP_PM_ARMED << 11); + break; + } + } + + if (ibqp->qp_type == IB_QPT_GSI || ibqp->qp_type == IB_QPT_SMI || + ibqp->qp_type == IB_QPT_UD) + context->mtu_msgmax = (IB_MTU_4096 << 5) | 11; + else if (attr_mask & IB_QP_PATH_MTU) { + if (attr->path_mtu < IB_MTU_256 || attr->path_mtu > IB_MTU_4096) { + printk(KERN_ERR "path MTU (%u) is invalid\n", + attr->path_mtu); + return -EINVAL; + } + context->mtu_msgmax = (attr->path_mtu << 5) | 31; + } + + if (qp->rq.max) + context->rq_size_stride = ilog2(qp->rq.max) << 3; + context->rq_size_stride |= qp->rq.wqe_shift - 4; + + if (qp->sq.max) + context->sq_size_stride = ilog2(qp->sq.max) << 3; + context->sq_size_stride |= qp->sq.wqe_shift - 4; + + if (qp->ibqp.uobject) + context->usr_page = cpu_to_be32(to_mucontext(ibqp->uobject->context)->uar.index); + else + context->usr_page = cpu_to_be32(dev->priv_uar.index); + + if (attr_mask & IB_QP_DEST_QPN) + context->remote_qpn = cpu_to_be32(attr->dest_qp_num); + + if (attr_mask & IB_QP_PORT) { + if (cur_state == IB_QPS_SQD && new_state == IB_QPS_SQD && + !(attr_mask & IB_QP_AV)) { + mlx4_set_sched(&context->pri_path, attr->port_num); + optpar |= MLX4_QP_OPTPAR_SCHED_QUEUE; + } + } + + if (attr_mask & IB_QP_PKEY_INDEX) { + context->pri_path.pkey_index = attr->pkey_index; + optpar |= MLX4_QP_OPTPAR_PKEY_INDEX; + } + + if (attr_mask & IB_QP_RNR_RETRY) { + context->params1 |= cpu_to_be32(attr->rnr_retry << 13); + optpar |= MLX4_QP_OPTPAR_RNR_RETRY; + } + + if (attr_mask & IB_QP_AV) { + if (mlx4_set_path(dev, &attr->ah_attr, &context->pri_path, + attr_mask & IB_QP_PORT ? attr->port_num : qp->port)) { + err = -EINVAL; + goto out; + } + + optpar |= (MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH | + MLX4_QP_OPTPAR_SCHED_QUEUE); + } + + if (attr_mask & IB_QP_TIMEOUT) { + context->pri_path.ackto = attr->timeout << 3; + optpar |= MLX4_QP_OPTPAR_ACK_TIMEOUT; + } + + if (attr_mask & IB_QP_ALT_PATH) { + if (attr->alt_pkey_index >= dev->dev->caps.pkey_table_len) + return -EINVAL; + + if (attr->alt_port_num == 0 || + attr->alt_port_num > dev->dev->caps.num_ports) + return -EINVAL; + + if (mlx4_set_path(dev, &attr->alt_ah_attr, &context->alt_path, + attr->alt_port_num)) + return -EINVAL; + + context->alt_path.pkey_index = attr->alt_pkey_index; + context->alt_path.ackto = attr->alt_timeout << 3; + optpar |= MLX4_QP_OPTPAR_ALT_ADDR_PATH; + } + + context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn); + context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28); + if (attr_mask & IB_QP_RETRY_CNT) { + context->params1 |= cpu_to_be32(attr->retry_cnt << 16); + optpar |= MLX4_QP_OPTPAR_RETRY_COUNT; + } + + if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) { + if (attr->max_rd_atomic) + context->params1 |= + cpu_to_be32(fls(attr->max_rd_atomic - 1) << 21); + optpar |= MLX4_QP_OPTPAR_SRA_MAX; + } + + if (attr_mask & IB_QP_SQ_PSN) + context->next_send_psn = cpu_to_be32(attr->sq_psn); + + context->cqn_send = cpu_to_be32(to_mcq(ibqp->send_cq)->mcq.cqn); + + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) { + if (attr->max_dest_rd_atomic) + context->params2 |= + cpu_to_be32(fls(attr->max_dest_rd_atomic - 1) << 21); + optpar |= MLX4_QP_OPTPAR_RRA_MAX; + } + + if (attr_mask & (IB_QP_ACCESS_FLAGS | IB_QP_MAX_DEST_RD_ATOMIC)) { + context->params2 |= to_mlx4_access_flags(qp, attr, attr_mask); + optpar |= MLX4_QP_OPTPAR_RWE | MLX4_QP_OPTPAR_RRE | MLX4_QP_OPTPAR_RAE; + } + + if (ibqp->srq) + context->params2 |= cpu_to_be32(MLX4_QP_BIT_RIC); + + if (attr_mask & IB_QP_MIN_RNR_TIMER) { + context->rnr_nextrecvpsn |= cpu_to_be32(attr->min_rnr_timer << 24); + optpar |= MLX4_QP_OPTPAR_RNR_TIMEOUT; + } + if (attr_mask & IB_QP_RQ_PSN) + context->rnr_nextrecvpsn |= cpu_to_be32(attr->rq_psn); + + context->cqn_recv = cpu_to_be32(to_mcq(ibqp->recv_cq)->mcq.cqn); + + if (attr_mask & IB_QP_QKEY) { + context->qkey = cpu_to_be32(attr->qkey); + optpar |= MLX4_QP_OPTPAR_Q_KEY; + } + + if (ibqp->srq) + context->srqn = cpu_to_be32(1 << 24 | to_msrq(ibqp->srq)->msrq.srqn); + + if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) + context->db_rec_addr = cpu_to_be64(qp->db.dma); + + if (cur_state == IB_QPS_INIT && + new_state == IB_QPS_RTR && + (ibqp->qp_type == IB_QPT_GSI || ibqp->qp_type == IB_QPT_SMI || + ibqp->qp_type == IB_QPT_UD)) { + context->pri_path.sched_queue = (qp->port - 1) << 6; + if (is_qp0(dev, qp)) + context->pri_path.sched_queue |= MLX4_IB_DEFAULT_QP0_SCHED_QUEUE; + else + context->pri_path.sched_queue |= MLX4_IB_DEFAULT_SCHED_QUEUE; + } + + if (cur_state == IB_QPS_RTS && new_state == IB_QPS_SQD && + attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY && attr->en_sqd_async_notify) + sqd_event = 1; + else + sqd_event = 0; + + err = mlx4_qp_modify(dev->dev, &qp->mtt, to_mlx4_state(cur_state), + to_mlx4_state(new_state), context, optpar, + sqd_event, &qp->mqp); + if (err) + goto out; + + qp->state = new_state; + + if (attr_mask & IB_QP_ACCESS_FLAGS) + qp->atomic_rd_en = attr->qp_access_flags; + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + qp->resp_depth = attr->max_dest_rd_atomic; + if (attr_mask & IB_QP_PORT) + qp->port = attr->port_num; + if (attr_mask & IB_QP_ALT_PATH) + qp->alt_port = attr->alt_port_num; + + if (is_sqp(dev, qp)) + store_sqp_attrs(to_msqp(qp), attr, attr_mask); + + /* + * If we moved QP0 to RTR, bring the IB link up; if we moved + * QP0 to RESET or ERROR, bring the link back down. + */ + if (is_qp0(dev, qp)) { + if (cur_state != IB_QPS_RTR && new_state == IB_QPS_RTR) + init_port(dev, qp->port); + + if (cur_state != IB_QPS_RESET && cur_state != IB_QPS_ERR && + (new_state == IB_QPS_RESET || new_state == IB_QPS_ERR)) + mlx4_CLOSE_PORT(dev->dev, qp->port); + } + + /* + * If we moved a kernel QP to RESET, clean up all old CQ + * entries and reinitialize the QP. + */ + if (new_state == IB_QPS_RESET && !ibqp->uobject) { + mlx4_ib_cq_clean(to_mcq(ibqp->recv_cq), qp->mqp.qpn, + ibqp->srq ? to_msrq(ibqp->srq): NULL); + if (ibqp->send_cq != ibqp->recv_cq) + mlx4_ib_cq_clean(to_mcq(ibqp->send_cq), qp->mqp.qpn, NULL); + + qp->rq.head = 0; + qp->rq.tail = 0; + qp->sq.head = 0; + qp->sq.tail = 0; + *qp->db.db = 0; + } + +out: + mutex_unlock(&qp->mutex); + kfree(context); + return err; +} + +static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr, + void *wqe) +{ + struct ib_device *ib_dev = &to_mdev(sqp->qp.ibqp.device)->ib_dev; + struct mlx4_wqe_mlx_seg *mlx = wqe; + struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx; + struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah); + u16 pkey; + int send_size; + int header_size; + int i; + + send_size = 0; + for (i = 0; i < wr->num_sge; ++i) + send_size += wr->sg_list[i].length; + + ib_ud_header_init(send_size, mlx4_ib_ah_grh_present(ah), &sqp->ud_header); + + sqp->ud_header.lrh.service_level = + be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28; + sqp->ud_header.lrh.destination_lid = ah->av.dlid; + sqp->ud_header.lrh.source_lid = cpu_to_be16(ah->av.g_slid & 0x7f); + if (mlx4_ib_ah_grh_present(ah)) { + sqp->ud_header.grh.traffic_class = + (be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20) & 0xff; + sqp->ud_header.grh.flow_label = + ah->av.sl_tclass_flowlabel & cpu_to_be32(0xfffff); + ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.port_pd) >> 24, + ah->av.gid_index, &sqp->ud_header.grh.source_gid); + memcpy(sqp->ud_header.grh.destination_gid.raw, + ah->av.dgid, 16); + } + + mlx->flags &= cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE); + mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MLX4_WQE_MLX_VL15 : 0) | + (sqp->ud_header.lrh.destination_lid == + IB_LID_PERMISSIVE ? MLX4_WQE_MLX_SLR : 0) | + (sqp->ud_header.lrh.service_level << 8)); + mlx->rlid = sqp->ud_header.lrh.destination_lid; + + switch (wr->opcode) { + case IB_WR_SEND: + sqp->ud_header.bth.opcode = IB_OPCODE_UD_SEND_ONLY; + sqp->ud_header.immediate_present = 0; + break; + case IB_WR_SEND_WITH_IMM: + sqp->ud_header.bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE; + sqp->ud_header.immediate_present = 1; + sqp->ud_header.immediate_data = wr->imm_data; + break; + default: + return -EINVAL; + } + + sqp->ud_header.lrh.virtual_lane = !sqp->qp.ibqp.qp_num ? 15 : 0; + if (sqp->ud_header.lrh.destination_lid == IB_LID_PERMISSIVE) + sqp->ud_header.lrh.source_lid = IB_LID_PERMISSIVE; + sqp->ud_header.bth.solicited_event = !!(wr->send_flags & IB_SEND_SOLICITED); + if (!sqp->qp.ibqp.qp_num) + ib_get_cached_pkey(ib_dev, sqp->qp.port, sqp->pkey_index, &pkey); + else + ib_get_cached_pkey(ib_dev, sqp->qp.port, wr->wr.ud.pkey_index, &pkey); + sqp->ud_header.bth.pkey = cpu_to_be16(pkey); + sqp->ud_header.bth.destination_qpn = cpu_to_be32(wr->wr.ud.remote_qpn); + sqp->ud_header.bth.psn = cpu_to_be32((sqp->send_psn++) & ((1 << 24) - 1)); + sqp->ud_header.deth.qkey = cpu_to_be32(wr->wr.ud.remote_qkey & 0x80000000 ? + sqp->qkey : wr->wr.ud.remote_qkey); + sqp->ud_header.deth.source_qpn = cpu_to_be32(sqp->qp.ibqp.qp_num); + + header_size = ib_ud_header_pack(&sqp->ud_header, sqp->header_buf); + + if (0) { + printk(KERN_ERR "built UD header of size %d:\n", header_size); + for (i = 0; i < header_size / 4; ++i) { + if (i % 8 == 0) + printk(" [%02x] ", i * 4); + printk(" %08x", + be32_to_cpu(((__be32 *) sqp->header_buf)[i])); + if ((i + 1) % 8 == 0) + printk("\n"); + } + printk("\n"); + } + + inl->byte_count = cpu_to_be32(1 << 31 | header_size); + memcpy(inl + 1, sqp->header_buf, header_size); + + return ALIGN(sizeof (struct mlx4_wqe_inline_seg) + header_size, 16); +} + +static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq) +{ + unsigned cur; + struct mlx4_ib_cq *cq; + + cur = wq->head - wq->tail; + if (likely(cur + nreq < wq->max)) + return 0; + + cq = to_mcq(ib_cq); + spin_lock(&cq->lock); + cur = wq->head - wq->tail; + spin_unlock(&cq->lock); + + return cur + nreq >= wq->max; +} + +int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr) +{ + struct mlx4_ib_qp *qp = to_mqp(ibqp); + void *wqe; + struct mlx4_wqe_ctrl_seg *ctrl; + unsigned long flags; + int nreq; + int err = 0; + int ind; + int size; + int i; + + spin_lock_irqsave(&qp->rq.lock, flags); + + ind = qp->sq.head; + + for (nreq = 0; wr; ++nreq, wr = wr->next) { + if (mlx4_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq)) { + err = -ENOMEM; + *bad_wr = wr; + goto out; + } + + if (unlikely(wr->num_sge > qp->sq.max_gs)) { + err = -EINVAL; + *bad_wr = wr; + goto out; + } + + ctrl = wqe = get_send_wqe(qp, ind & (qp->sq.max - 1)); + qp->sq.wrid[ind & (qp->sq.max - 1)] = wr->wr_id; + + ctrl->srcrb_flags = + (wr->send_flags & IB_SEND_SIGNALED ? + cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE) : 0) | + (wr->send_flags & IB_SEND_SOLICITED ? + cpu_to_be32(MLX4_WQE_CTRL_SOLICITED) : 0) | + qp->sq_signal_bits; + + if (wr->opcode == IB_WR_SEND_WITH_IMM || + wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) + ctrl->imm = wr->imm_data; + else + ctrl->imm = 0; + + wqe += sizeof *ctrl; + size = sizeof *ctrl / 16; + + switch (ibqp->qp_type) { + case IB_QPT_RC: + case IB_QPT_UC: + switch (wr->opcode) { + case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_ATOMIC_FETCH_AND_ADD: + ((struct mlx4_wqe_raddr_seg *) wqe)->raddr = + cpu_to_be64(wr->wr.atomic.remote_addr); + ((struct mlx4_wqe_raddr_seg *) wqe)->rkey = + cpu_to_be32(wr->wr.atomic.rkey); + ((struct mlx4_wqe_raddr_seg *) wqe)->reserved = 0; + + wqe += sizeof (struct mlx4_wqe_raddr_seg); + + if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + ((struct mlx4_wqe_atomic_seg *) wqe)->swap_add = + cpu_to_be64(wr->wr.atomic.swap); + ((struct mlx4_wqe_atomic_seg *) wqe)->compare = + cpu_to_be64(wr->wr.atomic.compare_add); + } else { + ((struct mlx4_wqe_atomic_seg *) wqe)->swap_add = + cpu_to_be64(wr->wr.atomic.compare_add); + ((struct mlx4_wqe_atomic_seg *) wqe)->compare = 0; + } + + wqe += sizeof (struct mlx4_wqe_atomic_seg); + size += (sizeof (struct mlx4_wqe_raddr_seg) + + sizeof (struct mlx4_wqe_atomic_seg)) / 16; + + break; + + case IB_WR_RDMA_READ: + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + ((struct mlx4_wqe_raddr_seg *) wqe)->raddr = + cpu_to_be64(wr->wr.rdma.remote_addr); + ((struct mlx4_wqe_raddr_seg *) wqe)->rkey = + cpu_to_be32(wr->wr.rdma.rkey); + ((struct mlx4_wqe_raddr_seg *) wqe)->reserved = 0; + + wqe += sizeof (struct mlx4_wqe_raddr_seg); + size += sizeof (struct mlx4_wqe_raddr_seg) / 16; + + break; + + default: + /* No extra segments required for sends */ + break; + } + break; + + case IB_QPT_UD: + memcpy(((struct mlx4_wqe_datagram_seg *) wqe)->av, + &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av)); + ((struct mlx4_wqe_datagram_seg *) wqe)->dqpn = + cpu_to_be32(wr->wr.ud.remote_qpn); + ((struct mlx4_wqe_datagram_seg *) wqe)->qkey = + cpu_to_be32(wr->wr.ud.remote_qkey); + + wqe += sizeof (struct mlx4_wqe_datagram_seg); + size += sizeof (struct mlx4_wqe_datagram_seg) / 16; + break; + + case IB_QPT_SMI: + case IB_QPT_GSI: + err = build_mlx_header(to_msqp(qp), wr, ctrl); + if (err < 0) { + *bad_wr = wr; + goto out; + } + wqe += err; + size += err / 16; + + err = 0; + break; + + default: + break; + } + + for (i = 0; i < wr->num_sge; ++i) { + ((struct mlx4_wqe_data_seg *) wqe)->byte_count = + cpu_to_be32(wr->sg_list[i].length); + ((struct mlx4_wqe_data_seg *) wqe)->lkey = + cpu_to_be32(wr->sg_list[i].lkey); + ((struct mlx4_wqe_data_seg *) wqe)->addr = + cpu_to_be64(wr->sg_list[i].addr); + + wqe += sizeof (struct mlx4_wqe_data_seg); + size += sizeof (struct mlx4_wqe_data_seg) / 16; + } + + /* Add one more inline data segment for ICRC for MLX sends */ + if (qp->ibqp.qp_type == IB_QPT_SMI || qp->ibqp.qp_type == IB_QPT_GSI) { + ((struct mlx4_wqe_inline_seg *) wqe)->byte_count = + cpu_to_be32((1 << 31) | 4); + ((u32 *) wqe)[1] = 0; + wqe += sizeof (struct mlx4_wqe_data_seg); + size += sizeof (struct mlx4_wqe_data_seg) / 16; + } + + ctrl->fence_size = (wr->send_flags & IB_SEND_FENCE ? + MLX4_WQE_CTRL_FENCE : 0) | size; + + /* + * Make sure descriptor is fully written before + * setting ownership bit (because HW can start + * executing as soon as we do). + */ + wmb(); + + if (wr->opcode < 0 || wr->opcode > ARRAY_SIZE(mlx4_ib_opcode)) { + err = -EINVAL; + goto out; + } + + ctrl->owner_opcode = mlx4_ib_opcode[wr->opcode] | + (ind & qp->sq.max ? cpu_to_be32(1 << 31) : 0); + + ++ind; + } + +out: + if (likely(nreq)) { + qp->sq.head += nreq; + + /* + * Make sure that descriptors are written before + * doorbell record. + */ + wmb(); + + writel(qp->doorbell_qpn, + to_mdev(ibqp->device)->uar_map + MLX4_SEND_DOORBELL); + + /* + * Make sure doorbells don't leak out of SQ spinlock + * and reach the HCA out of order. + */ + mmiowb(); + } + + spin_unlock_irqrestore(&qp->rq.lock, flags); + + return err; +} + +int mlx4_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct mlx4_ib_qp *qp = to_mqp(ibqp); + struct mlx4_wqe_data_seg *scat; + unsigned long flags; + int err = 0; + int nreq; + int ind; + int i; + + spin_lock_irqsave(&qp->rq.lock, flags); + + ind = qp->rq.head & (qp->rq.max - 1); + + for (nreq = 0; wr; ++nreq, wr = wr->next) { + if (mlx4_wq_overflow(&qp->rq, nreq, qp->ibqp.send_cq)) { + err = -ENOMEM; + *bad_wr = wr; + goto out; + } + + if (unlikely(wr->num_sge > qp->rq.max_gs)) { + err = -EINVAL; + *bad_wr = wr; + goto out; + } + + scat = get_recv_wqe(qp, ind); + + for (i = 0; i < wr->num_sge; ++i) { + scat[i].byte_count = cpu_to_be32(wr->sg_list[i].length); + scat[i].lkey = cpu_to_be32(wr->sg_list[i].lkey); + scat[i].addr = cpu_to_be64(wr->sg_list[i].addr); + } + + if (i < qp->rq.max_gs) { + scat[i].byte_count = 0; + scat[i].lkey = cpu_to_be32(MLX4_INVALID_LKEY); + scat[i].addr = 0; + } + + qp->rq.wrid[ind] = wr->wr_id; + + ind = (ind + 1) & (qp->rq.max - 1); + } + +out: + if (likely(nreq)) { + qp->rq.head += nreq; + + /* + * Make sure that descriptors are written before + * doorbell record. + */ + wmb(); + + *qp->db.db = cpu_to_be32(qp->rq.head & 0xffff); + } + + spin_unlock_irqrestore(&qp->rq.lock, flags); + + return err; +} diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c new file mode 100644 index 00000000000..42ab4a801d6 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "mlx4_ib.h" +#include "user.h" + +static void *get_wqe(struct mlx4_ib_srq *srq, int n) +{ + int offset = n << srq->msrq.wqe_shift; + + if (srq->buf.nbufs == 1) + return srq->buf.u.direct.buf + offset; + else + return srq->buf.u.page_list[offset >> PAGE_SHIFT].buf + + (offset & (PAGE_SIZE - 1)); +} + +static void mlx4_ib_srq_event(struct mlx4_srq *srq, enum mlx4_event type) +{ + struct ib_event event; + struct ib_srq *ibsrq = &to_mibsrq(srq)->ibsrq; + + if (ibsrq->event_handler) { + event.device = ibsrq->device; + event.element.srq = ibsrq; + switch (type) { + case MLX4_EVENT_TYPE_SRQ_LIMIT: + event.event = IB_EVENT_SRQ_LIMIT_REACHED; + break; + case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR: + event.event = IB_EVENT_SRQ_ERR; + break; + default: + printk(KERN_WARNING "mlx4_ib: Unexpected event type %d " + "on SRQ %06x\n", type, srq->srqn); + return; + } + + ibsrq->event_handler(&event, ibsrq->srq_context); + } +} + +struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, + struct ib_srq_init_attr *init_attr, + struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_srq *srq; + struct mlx4_wqe_srq_next_seg *next; + int desc_size; + int buf_size; + int err; + int i; + + /* Sanity check SRQ size before proceeding */ + if (init_attr->attr.max_wr >= dev->dev->caps.max_srq_wqes || + init_attr->attr.max_sge > dev->dev->caps.max_srq_sge) + return ERR_PTR(-EINVAL); + + srq = kmalloc(sizeof *srq, GFP_KERNEL); + if (!srq) + return ERR_PTR(-ENOMEM); + + mutex_init(&srq->mutex); + spin_lock_init(&srq->lock); + srq->msrq.max = roundup_pow_of_two(init_attr->attr.max_wr + 1); + srq->msrq.max_gs = init_attr->attr.max_sge; + + desc_size = max(32UL, + roundup_pow_of_two(sizeof (struct mlx4_wqe_srq_next_seg) + + srq->msrq.max_gs * + sizeof (struct mlx4_wqe_data_seg))); + srq->msrq.wqe_shift = ilog2(desc_size); + + buf_size = srq->msrq.max * desc_size; + + if (pd->uobject) { + struct mlx4_ib_create_srq ucmd; + + if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { + err = -EFAULT; + goto err_srq; + } + + srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, + buf_size, 0); + if (IS_ERR(srq->umem)) { + err = PTR_ERR(srq->umem); + goto err_srq; + } + + err = mlx4_mtt_init(dev->dev, ib_umem_page_count(srq->umem), + ilog2(srq->umem->page_size), &srq->mtt); + if (err) + goto err_buf; + + err = mlx4_ib_umem_write_mtt(dev, &srq->mtt, srq->umem); + if (err) + goto err_mtt; + + err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context), + ucmd.db_addr, &srq->db); + if (err) + goto err_mtt; + } else { + err = mlx4_ib_db_alloc(dev, &srq->db, 0); + if (err) + goto err_srq; + + *srq->db.db = 0; + + if (mlx4_buf_alloc(dev->dev, buf_size, PAGE_SIZE * 2, &srq->buf)) { + err = -ENOMEM; + goto err_db; + } + + srq->head = 0; + srq->tail = srq->msrq.max - 1; + srq->wqe_ctr = 0; + + for (i = 0; i < srq->msrq.max; ++i) { + next = get_wqe(srq, i); + next->next_wqe_index = + cpu_to_be16((i + 1) & (srq->msrq.max - 1)); + } + + err = mlx4_mtt_init(dev->dev, srq->buf.npages, srq->buf.page_shift, + &srq->mtt); + if (err) + goto err_buf; + + err = mlx4_buf_write_mtt(dev->dev, &srq->mtt, &srq->buf); + if (err) + goto err_mtt; + + srq->wrid = kmalloc(srq->msrq.max * sizeof (u64), GFP_KERNEL); + if (!srq->wrid) { + err = -ENOMEM; + goto err_mtt; + } + } + + err = mlx4_srq_alloc(dev->dev, to_mpd(pd)->pdn, &srq->mtt, + srq->db.dma, &srq->msrq); + if (err) + goto err_wrid; + + srq->msrq.event = mlx4_ib_srq_event; + + if (pd->uobject) + if (ib_copy_to_udata(udata, &srq->msrq.srqn, sizeof (__u32))) { + err = -EFAULT; + goto err_wrid; + } + + init_attr->attr.max_wr = srq->msrq.max - 1; + + return &srq->ibsrq; + +err_wrid: + if (pd->uobject) + mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &srq->db); + else + kfree(srq->wrid); + +err_mtt: + mlx4_mtt_cleanup(dev->dev, &srq->mtt); + +err_buf: + if (pd->uobject) + ib_umem_release(srq->umem); + else + mlx4_buf_free(dev->dev, buf_size, &srq->buf); + +err_db: + if (!pd->uobject) + mlx4_ib_db_free(dev, &srq->db); + +err_srq: + kfree(srq); + + return ERR_PTR(err); +} + +int mlx4_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, + enum ib_srq_attr_mask attr_mask, struct ib_udata *udata) +{ + struct mlx4_ib_dev *dev = to_mdev(ibsrq->device); + struct mlx4_ib_srq *srq = to_msrq(ibsrq); + int ret; + + /* We don't support resizing SRQs (yet?) */ + if (attr_mask & IB_SRQ_MAX_WR) + return -EINVAL; + + if (attr_mask & IB_SRQ_LIMIT) { + if (attr->srq_limit >= srq->msrq.max) + return -EINVAL; + + mutex_lock(&srq->mutex); + ret = mlx4_srq_arm(dev->dev, &srq->msrq, attr->srq_limit); + mutex_unlock(&srq->mutex); + + if (ret) + return ret; + } + + return 0; +} + +int mlx4_ib_destroy_srq(struct ib_srq *srq) +{ + struct mlx4_ib_dev *dev = to_mdev(srq->device); + struct mlx4_ib_srq *msrq = to_msrq(srq); + + mlx4_srq_free(dev->dev, &msrq->msrq); + mlx4_mtt_cleanup(dev->dev, &msrq->mtt); + + if (srq->uobject) { + mlx4_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db); + ib_umem_release(msrq->umem); + } else { + kfree(msrq->wrid); + mlx4_buf_free(dev->dev, msrq->msrq.max << msrq->msrq.wqe_shift, + &msrq->buf); + mlx4_ib_db_free(dev, &msrq->db); + } + + kfree(msrq); + + return 0; +} + +void mlx4_ib_free_srq_wqe(struct mlx4_ib_srq *srq, int wqe_index) +{ + struct mlx4_wqe_srq_next_seg *next; + + /* always called with interrupts disabled. */ + spin_lock(&srq->lock); + + next = get_wqe(srq, srq->tail); + next->next_wqe_index = cpu_to_be16(wqe_index); + srq->tail = wqe_index; + + spin_unlock(&srq->lock); +} + +int mlx4_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct mlx4_ib_srq *srq = to_msrq(ibsrq); + struct mlx4_wqe_srq_next_seg *next; + struct mlx4_wqe_data_seg *scat; + unsigned long flags; + int err = 0; + int nreq; + int i; + + spin_lock_irqsave(&srq->lock, flags); + + for (nreq = 0; wr; ++nreq, wr = wr->next) { + if (unlikely(wr->num_sge > srq->msrq.max_gs)) { + err = -EINVAL; + *bad_wr = wr; + break; + } + + srq->wrid[srq->head] = wr->wr_id; + + next = get_wqe(srq, srq->head); + srq->head = be16_to_cpu(next->next_wqe_index); + scat = (struct mlx4_wqe_data_seg *) (next + 1); + + for (i = 0; i < wr->num_sge; ++i) { + scat[i].byte_count = cpu_to_be32(wr->sg_list[i].length); + scat[i].lkey = cpu_to_be32(wr->sg_list[i].lkey); + scat[i].addr = cpu_to_be64(wr->sg_list[i].addr); + } + + if (i < srq->msrq.max_gs) { + scat[i].byte_count = 0; + scat[i].lkey = cpu_to_be32(MLX4_INVALID_LKEY); + scat[i].addr = 0; + } + } + + if (likely(nreq)) { + srq->wqe_ctr += nreq; + + /* + * Make sure that descriptors are written before + * doorbell record. + */ + wmb(); + + *srq->db.db = cpu_to_be32(srq->wqe_ctr); + } + + spin_unlock_irqrestore(&srq->lock, flags); + + return err; +} diff --git a/drivers/infiniband/hw/mlx4/user.h b/drivers/infiniband/hw/mlx4/user.h new file mode 100644 index 00000000000..5b8eddc9fa8 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/user.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_IB_USER_H +#define MLX4_IB_USER_H + +#include + +/* + * Increment this value if any changes that break userspace ABI + * compatibility are made. + */ +#define MLX4_IB_UVERBS_ABI_VERSION 1 + +/* + * Make sure that all structs defined in this file remain laid out so + * that they pack the same way on 32-bit and 64-bit architectures (to + * avoid incompatibility between 32-bit userspace and 64-bit kernels). + * In particular do not use pointer types -- pass pointers in __u64 + * instead. + */ + +struct mlx4_ib_alloc_ucontext_resp { + __u32 qp_tab_size; + __u16 bf_reg_size; + __u16 bf_regs_per_page; +}; + +struct mlx4_ib_alloc_pd_resp { + __u32 pdn; + __u32 reserved; +}; + +struct mlx4_ib_create_cq { + __u64 buf_addr; + __u64 db_addr; +}; + +struct mlx4_ib_create_cq_resp { + __u32 cqn; + __u32 reserved; +}; + +struct mlx4_ib_resize_cq { + __u64 buf_addr; +}; + +struct mlx4_ib_create_srq { + __u64 buf_addr; + __u64 db_addr; +}; + +struct mlx4_ib_create_srq_resp { + __u32 srqn; + __u32 reserved; +}; + +struct mlx4_ib_create_qp { + __u64 buf_addr; + __u64 db_addr; +}; + +#endif /* MLX4_IB_USER_H */ diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b86ccd2ecd5..6db12d0265d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2493,6 +2493,20 @@ config PASEMI_MAC This driver supports the on-chip 1/10Gbit Ethernet controller on PA Semi's PWRficient line of chips. +config MLX4_CORE + tristate + depends on PCI + default n + +config MLX4_DEBUG + bool "Verbose debugging output" if (MLX4_CORE && EMBEDDED) + default y + ---help--- + This option causes debugging code to be compiled into the + mlx4_core driver. The output can be turned on via the + debug_level module parameter (which can also be set after + the driver is loaded through sysfs). + endmenu source "drivers/net/tokenring/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 59c0459a037..7faeeeabc83 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -197,6 +197,7 @@ obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_DM9000) += dm9000.o obj-$(CONFIG_FEC_8XX) += fec_8xx/ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o +obj-$(CONFIG_MLX4_CORE) += mlx4/ obj-$(CONFIG_MACB) += macb.o diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile new file mode 100644 index 00000000000..0952a6528f5 --- /dev/null +++ b/drivers/net/mlx4/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MLX4_CORE) += mlx4_core.o + +mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o icm.o intf.o main.o mcg.o \ + mr.o pd.o profile.o qp.o reset.o srq.o diff --git a/drivers/net/mlx4/alloc.c b/drivers/net/mlx4/alloc.c new file mode 100644 index 00000000000..9ffdb9d29da --- /dev/null +++ b/drivers/net/mlx4/alloc.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "mlx4.h" + +u32 mlx4_bitmap_alloc(struct mlx4_bitmap *bitmap) +{ + u32 obj; + + spin_lock(&bitmap->lock); + + obj = find_next_zero_bit(bitmap->table, bitmap->max, bitmap->last); + if (obj >= bitmap->max) { + bitmap->top = (bitmap->top + bitmap->max) & bitmap->mask; + obj = find_first_zero_bit(bitmap->table, bitmap->max); + } + + if (obj < bitmap->max) { + set_bit(obj, bitmap->table); + obj |= bitmap->top; + bitmap->last = obj + 1; + } else + obj = -1; + + spin_unlock(&bitmap->lock); + + return obj; +} + +void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj) +{ + obj &= bitmap->max - 1; + + spin_lock(&bitmap->lock); + clear_bit(obj, bitmap->table); + bitmap->last = min(bitmap->last, obj); + bitmap->top = (bitmap->top + bitmap->max) & bitmap->mask; + spin_unlock(&bitmap->lock); +} + +int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, u32 reserved) +{ + int i; + + /* num must be a power of 2 */ + if (num != roundup_pow_of_two(num)) + return -EINVAL; + + bitmap->last = 0; + bitmap->top = 0; + bitmap->max = num; + bitmap->mask = mask; + spin_lock_init(&bitmap->lock); + bitmap->table = kzalloc(BITS_TO_LONGS(num) * sizeof (long), GFP_KERNEL); + if (!bitmap->table) + return -ENOMEM; + + for (i = 0; i < reserved; ++i) + set_bit(i, bitmap->table); + + return 0; +} + +void mlx4_bitmap_cleanup(struct mlx4_bitmap *bitmap) +{ + kfree(bitmap->table); +} + +/* + * Handling for queue buffers -- we allocate a bunch of memory and + * register it in a memory region at HCA virtual address 0. If the + * requested size is > max_direct, we split the allocation into + * multiple pages, so we don't require too much contiguous memory. + */ + +int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, + struct mlx4_buf *buf) +{ + dma_addr_t t; + + if (size <= max_direct) { + buf->nbufs = 1; + buf->npages = 1; + buf->page_shift = get_order(size) + PAGE_SHIFT; + buf->u.direct.buf = dma_alloc_coherent(&dev->pdev->dev, + size, &t, GFP_KERNEL); + if (!buf->u.direct.buf) + return -ENOMEM; + + buf->u.direct.map = t; + + while (t & ((1 << buf->page_shift) - 1)) { + --buf->page_shift; + buf->npages *= 2; + } + + memset(buf->u.direct.buf, 0, size); + } else { + int i; + + buf->nbufs = (size + PAGE_SIZE - 1) / PAGE_SIZE; + buf->npages = buf->nbufs; + buf->page_shift = PAGE_SHIFT; + buf->u.page_list = kzalloc(buf->nbufs * sizeof *buf->u.page_list, + GFP_KERNEL); + if (!buf->u.page_list) + return -ENOMEM; + + for (i = 0; i < buf->nbufs; ++i) { + buf->u.page_list[i].buf = + dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + &t, GFP_KERNEL); + if (!buf->u.page_list[i].buf) + goto err_free; + + buf->u.page_list[i].map = t; + + memset(buf->u.page_list[i].buf, 0, PAGE_SIZE); + } + } + + return 0; + +err_free: + mlx4_buf_free(dev, size, buf); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(mlx4_buf_alloc); + +void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf) +{ + int i; + + if (buf->nbufs == 1) + dma_free_coherent(&dev->pdev->dev, size, buf->u.direct.buf, + buf->u.direct.map); + else { + for (i = 0; i < buf->nbufs; ++i) + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + buf->u.page_list[i].buf, + buf->u.page_list[i].map); + kfree(buf->u.page_list); + } +} +EXPORT_SYMBOL_GPL(mlx4_buf_free); diff --git a/drivers/net/mlx4/catas.c b/drivers/net/mlx4/catas.c new file mode 100644 index 00000000000..1bb088aeaf7 --- /dev/null +++ b/drivers/net/mlx4/catas.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mlx4.h" + +void mlx4_handle_catas_err(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + int i; + + mlx4_err(dev, "Catastrophic error detected:\n"); + for (i = 0; i < priv->fw.catas_size; ++i) + mlx4_err(dev, " buf[%02x]: %08x\n", + i, swab32(readl(priv->catas_err.map + i))); + + mlx4_dispatch_event(dev, MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR, 0, 0); +} + +void mlx4_map_catas_buf(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + unsigned long addr; + + addr = pci_resource_start(dev->pdev, priv->fw.catas_bar) + + priv->fw.catas_offset; + + priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4); + if (!priv->catas_err.map) + mlx4_warn(dev, "Failed to map catastrophic error buffer at 0x%lx\n", + addr); + +} + +void mlx4_unmap_catas_buf(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + if (priv->catas_err.map) + iounmap(priv->catas_err.map); +} diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c new file mode 100644 index 00000000000..c1f81a993f5 --- /dev/null +++ b/drivers/net/mlx4/cmd.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include + +#include + +#include "mlx4.h" + +#define CMD_POLL_TOKEN 0xffff + +enum { + /* command completed successfully: */ + CMD_STAT_OK = 0x00, + /* Internal error (such as a bus error) occurred while processing command: */ + CMD_STAT_INTERNAL_ERR = 0x01, + /* Operation/command not supported or opcode modifier not supported: */ + CMD_STAT_BAD_OP = 0x02, + /* Parameter not supported or parameter out of range: */ + CMD_STAT_BAD_PARAM = 0x03, + /* System not enabled or bad system state: */ + CMD_STAT_BAD_SYS_STATE = 0x04, + /* Attempt to access reserved or unallocaterd resource: */ + CMD_STAT_BAD_RESOURCE = 0x05, + /* Requested resource is currently executing a command, or is otherwise busy: */ + CMD_STAT_RESOURCE_BUSY = 0x06, + /* Required capability exceeds device limits: */ + CMD_STAT_EXCEED_LIM = 0x08, + /* Resource is not in the appropriate state or ownership: */ + CMD_STAT_BAD_RES_STATE = 0x09, + /* Index out of range: */ + CMD_STAT_BAD_INDEX = 0x0a, + /* FW image corrupted: */ + CMD_STAT_BAD_NVMEM = 0x0b, + /* Attempt to modify a QP/EE which is not in the presumed state: */ + CMD_STAT_BAD_QP_STATE = 0x10, + /* Bad segment parameters (Address/Size): */ + CMD_STAT_BAD_SEG_PARAM = 0x20, + /* Memory Region has Memory Windows bound to: */ + CMD_STAT_REG_BOUND = 0x21, + /* HCA local attached memory not present: */ + CMD_STAT_LAM_NOT_PRE = 0x22, + /* Bad management packet (silently discarded): */ + CMD_STAT_BAD_PKT = 0x30, + /* More outstanding CQEs in CQ than new CQ size: */ + CMD_STAT_BAD_SIZE = 0x40 +}; + +enum { + HCR_IN_PARAM_OFFSET = 0x00, + HCR_IN_MODIFIER_OFFSET = 0x08, + HCR_OUT_PARAM_OFFSET = 0x0c, + HCR_TOKEN_OFFSET = 0x14, + HCR_STATUS_OFFSET = 0x18, + + HCR_OPMOD_SHIFT = 12, + HCR_T_BIT = 21, + HCR_E_BIT = 22, + HCR_GO_BIT = 23 +}; + +enum { + GO_BIT_TIMEOUT = 10000 +}; + +struct mlx4_cmd_context { + struct completion done; + int result; + int next; + u64 out_param; + u16 token; +}; + +static int mlx4_status_to_errno(u8 status) { + static const int trans_table[] = { + [CMD_STAT_INTERNAL_ERR] = -EIO, + [CMD_STAT_BAD_OP] = -EPERM, + [CMD_STAT_BAD_PARAM] = -EINVAL, + [CMD_STAT_BAD_SYS_STATE] = -ENXIO, + [CMD_STAT_BAD_RESOURCE] = -EBADF, + [CMD_STAT_RESOURCE_BUSY] = -EBUSY, + [CMD_STAT_EXCEED_LIM] = -ENOMEM, + [CMD_STAT_BAD_RES_STATE] = -EBADF, + [CMD_STAT_BAD_INDEX] = -EBADF, + [CMD_STAT_BAD_NVMEM] = -EFAULT, + [CMD_STAT_BAD_QP_STATE] = -EINVAL, + [CMD_STAT_BAD_SEG_PARAM] = -EFAULT, + [CMD_STAT_REG_BOUND] = -EBUSY, + [CMD_STAT_LAM_NOT_PRE] = -EAGAIN, + [CMD_STAT_BAD_PKT] = -EINVAL, + [CMD_STAT_BAD_SIZE] = -ENOMEM, + }; + + if (status >= ARRAY_SIZE(trans_table) || + (status != CMD_STAT_OK && trans_table[status] == 0)) + return -EIO; + + return trans_table[status]; +} + +static int cmd_pending(struct mlx4_dev *dev) +{ + u32 status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); + + return (status & swab32(1 << HCR_GO_BIT)) || + (mlx4_priv(dev)->cmd.toggle == + !!(status & swab32(1 << HCR_T_BIT))); +} + +static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, + u32 in_modifier, u8 op_modifier, u16 op, u16 token, + int event) +{ + struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; + u32 __iomem *hcr = cmd->hcr; + int ret = -EAGAIN; + unsigned long end; + + mutex_lock(&cmd->hcr_mutex); + + end = jiffies; + if (event) + end += HZ * 10; + + while (cmd_pending(dev)) { + if (time_after_eq(jiffies, end)) + goto out; + cond_resched(); + } + + /* + * We use writel (instead of something like memcpy_toio) + * because writes of less than 32 bits to the HCR don't work + * (and some architectures such as ia64 implement memcpy_toio + * in terms of writeb). + */ + __raw_writel((__force u32) cpu_to_be32(in_param >> 32), hcr + 0); + __raw_writel((__force u32) cpu_to_be32(in_param & 0xfffffffful), hcr + 1); + __raw_writel((__force u32) cpu_to_be32(in_modifier), hcr + 2); + __raw_writel((__force u32) cpu_to_be32(out_param >> 32), hcr + 3); + __raw_writel((__force u32) cpu_to_be32(out_param & 0xfffffffful), hcr + 4); + __raw_writel((__force u32) cpu_to_be32(token << 16), hcr + 5); + + /* __raw_writel may not order writes. */ + wmb(); + + __raw_writel((__force u32) cpu_to_be32((1 << HCR_GO_BIT) | + (cmd->toggle << HCR_T_BIT) | + (event ? (1 << HCR_E_BIT) : 0) | + (op_modifier << HCR_OPMOD_SHIFT) | + op), hcr + 6); + cmd->toggle = cmd->toggle ^ 1; + + ret = 0; + +out: + mutex_unlock(&cmd->hcr_mutex); + return ret; +} + +static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + int out_is_imm, u32 in_modifier, u8 op_modifier, + u16 op, unsigned long timeout) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + void __iomem *hcr = priv->cmd.hcr; + int err = 0; + unsigned long end; + + down(&priv->cmd.poll_sem); + + err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, + in_modifier, op_modifier, op, CMD_POLL_TOKEN, 0); + if (err) + goto out; + + end = msecs_to_jiffies(timeout) + jiffies; + while (cmd_pending(dev) && time_before(jiffies, end)) + cond_resched(); + + if (cmd_pending(dev)) { + err = -ETIMEDOUT; + goto out; + } + + if (out_is_imm) + *out_param = + (u64) be32_to_cpu((__force __be32) + __raw_readl(hcr + HCR_OUT_PARAM_OFFSET)) << 32 | + (u64) be32_to_cpu((__force __be32) + __raw_readl(hcr + HCR_OUT_PARAM_OFFSET + 4)); + + err = mlx4_status_to_errno(be32_to_cpu((__force __be32) + __raw_readl(hcr + HCR_STATUS_OFFSET)) >> 24); + +out: + up(&priv->cmd.poll_sem); + return err; +} + +void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_context *context = + &priv->cmd.context[token & priv->cmd.token_mask]; + + /* previously timed out command completing at long last */ + if (token != context->token) + return; + + context->result = mlx4_status_to_errno(status); + context->out_param = out_param; + + context->token += priv->cmd.token_mask + 1; + + complete(&context->done); +} + +static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + int out_is_imm, u32 in_modifier, u8 op_modifier, + u16 op, unsigned long timeout) +{ + struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; + struct mlx4_cmd_context *context; + int err = 0; + + down(&cmd->event_sem); + + spin_lock(&cmd->context_lock); + BUG_ON(cmd->free_head < 0); + context = &cmd->context[cmd->free_head]; + cmd->free_head = context->next; + spin_unlock(&cmd->context_lock); + + init_completion(&context->done); + + mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, + in_modifier, op_modifier, op, context->token, 1); + + if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { + err = -EBUSY; + goto out; + } + + err = context->result; + if (err) + goto out; + + if (out_is_imm) + *out_param = context->out_param; + +out: + spin_lock(&cmd->context_lock); + context->next = cmd->free_head; + cmd->free_head = context - cmd->context; + spin_unlock(&cmd->context_lock); + + up(&cmd->event_sem); + return err; +} + +int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + int out_is_imm, u32 in_modifier, u8 op_modifier, + u16 op, unsigned long timeout) +{ + if (mlx4_priv(dev)->cmd.use_events) + return mlx4_cmd_wait(dev, in_param, out_param, out_is_imm, + in_modifier, op_modifier, op, timeout); + else + return mlx4_cmd_poll(dev, in_param, out_param, out_is_imm, + in_modifier, op_modifier, op, timeout); +} +EXPORT_SYMBOL_GPL(__mlx4_cmd); + +int mlx4_cmd_init(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + mutex_init(&priv->cmd.hcr_mutex); + sema_init(&priv->cmd.poll_sem, 1); + priv->cmd.use_events = 0; + priv->cmd.toggle = 1; + + priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_HCR_BASE, + MLX4_HCR_SIZE); + if (!priv->cmd.hcr) { + mlx4_err(dev, "Couldn't map command register."); + return -ENOMEM; + } + + priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev, + MLX4_MAILBOX_SIZE, + MLX4_MAILBOX_SIZE, 0); + if (!priv->cmd.pool) { + iounmap(priv->cmd.hcr); + return -ENOMEM; + } + + return 0; +} + +void mlx4_cmd_cleanup(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + pci_pool_destroy(priv->cmd.pool); + iounmap(priv->cmd.hcr); +} + +/* + * Switch to using events to issue FW commands (can only be called + * after event queue for command events has been initialized). + */ +int mlx4_cmd_use_events(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int i; + + priv->cmd.context = kmalloc(priv->cmd.max_cmds * + sizeof (struct mlx4_cmd_context), + GFP_KERNEL); + if (!priv->cmd.context) + return -ENOMEM; + + for (i = 0; i < priv->cmd.max_cmds; ++i) { + priv->cmd.context[i].token = i; + priv->cmd.context[i].next = i + 1; + } + + priv->cmd.context[priv->cmd.max_cmds - 1].next = -1; + priv->cmd.free_head = 0; + + sema_init(&priv->cmd.event_sem, priv->cmd.max_cmds); + spin_lock_init(&priv->cmd.context_lock); + + for (priv->cmd.token_mask = 1; + priv->cmd.token_mask < priv->cmd.max_cmds; + priv->cmd.token_mask <<= 1) + ; /* nothing */ + --priv->cmd.token_mask; + + priv->cmd.use_events = 1; + + down(&priv->cmd.poll_sem); + + return 0; +} + +/* + * Switch back to polling (used when shutting down the device) + */ +void mlx4_cmd_use_polling(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int i; + + priv->cmd.use_events = 0; + + for (i = 0; i < priv->cmd.max_cmds; ++i) + down(&priv->cmd.event_sem); + + kfree(priv->cmd.context); + + up(&priv->cmd.poll_sem); +} + +struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) +{ + struct mlx4_cmd_mailbox *mailbox; + + mailbox = kmalloc(sizeof *mailbox, GFP_KERNEL); + if (!mailbox) + return ERR_PTR(-ENOMEM); + + mailbox->buf = pci_pool_alloc(mlx4_priv(dev)->cmd.pool, GFP_KERNEL, + &mailbox->dma); + if (!mailbox->buf) { + kfree(mailbox); + return ERR_PTR(-ENOMEM); + } + + return mailbox; +} +EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); + +void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox) +{ + if (!mailbox) + return; + + pci_pool_free(mlx4_priv(dev)->cmd.pool, mailbox->buf, mailbox->dma); + kfree(mailbox); +} +EXPORT_SYMBOL_GPL(mlx4_free_cmd_mailbox); diff --git a/drivers/net/mlx4/cq.c b/drivers/net/mlx4/cq.c new file mode 100644 index 00000000000..437d78ad091 --- /dev/null +++ b/drivers/net/mlx4/cq.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2004 Voltaire, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4.h" +#include "icm.h" + +struct mlx4_cq_context { + __be32 flags; + u16 reserved1[3]; + __be16 page_offset; + __be32 logsize_usrpage; + u8 reserved2; + u8 cq_period; + u8 reserved3; + u8 cq_max_count; + u8 reserved4[3]; + u8 comp_eqn; + u8 log_page_size; + u8 reserved5[2]; + u8 mtt_base_addr_h; + __be32 mtt_base_addr_l; + __be32 last_notified_index; + __be32 solicit_producer_index; + __be32 consumer_index; + __be32 producer_index; + u8 reserved6[2]; + __be64 db_rec_addr; +}; + +#define MLX4_CQ_STATUS_OK ( 0 << 28) +#define MLX4_CQ_STATUS_OVERFLOW ( 9 << 28) +#define MLX4_CQ_STATUS_WRITE_FAIL (10 << 28) +#define MLX4_CQ_FLAG_CC ( 1 << 18) +#define MLX4_CQ_FLAG_OI ( 1 << 17) +#define MLX4_CQ_STATE_ARMED ( 9 << 8) +#define MLX4_CQ_STATE_ARMED_SOL ( 6 << 8) +#define MLX4_EQ_STATE_FIRED (10 << 8) + +void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn) +{ + struct mlx4_cq *cq; + + cq = radix_tree_lookup(&mlx4_priv(dev)->cq_table.tree, + cqn & (dev->caps.num_cqs - 1)); + if (!cq) { + mlx4_warn(dev, "Completion event for bogus CQ %08x\n", cqn); + return; + } + + ++cq->arm_sn; + + cq->comp(cq); +} + +void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type) +{ + struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table; + struct mlx4_cq *cq; + + spin_lock(&cq_table->lock); + + cq = radix_tree_lookup(&cq_table->tree, cqn & (dev->caps.num_cqs - 1)); + if (cq) + atomic_inc(&cq->refcount); + + spin_unlock(&cq_table->lock); + + if (!cq) { + mlx4_warn(dev, "Async event for bogus CQ %08x\n", cqn); + return; + } + + cq->event(cq, event_type); + + if (atomic_dec_and_test(&cq->refcount)) + complete(&cq->free); +} + +static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int cq_num) +{ + return mlx4_cmd(dev, mailbox->dma, cq_num, 0, MLX4_CMD_SW2HW_CQ, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_HW2SW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int cq_num) +{ + return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, cq_num, + mailbox ? 0 : 1, MLX4_CMD_HW2SW_CQ, + MLX4_CMD_TIME_CLASS_A); +} + +int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt, + struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cq_table *cq_table = &priv->cq_table; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_cq_context *cq_context; + u64 mtt_addr; + int err; + + cq->cqn = mlx4_bitmap_alloc(&cq_table->bitmap); + if (cq->cqn == -1) + return -ENOMEM; + + err = mlx4_table_get(dev, &cq_table->table, cq->cqn); + if (err) + goto err_out; + + err = mlx4_table_get(dev, &cq_table->cmpt_table, cq->cqn); + if (err) + goto err_put; + + spin_lock_irq(&cq_table->lock); + err = radix_tree_insert(&cq_table->tree, cq->cqn, cq); + spin_unlock_irq(&cq_table->lock); + if (err) + goto err_cmpt_put; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = PTR_ERR(mailbox); + goto err_radix; + } + + cq_context = mailbox->buf; + memset(cq_context, 0, sizeof *cq_context); + + cq_context->logsize_usrpage = cpu_to_be32((ilog2(nent) << 24) | uar->index); + cq_context->comp_eqn = priv->eq_table.eq[MLX4_EQ_COMP].eqn; + cq_context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT; + + mtt_addr = mlx4_mtt_addr(dev, mtt); + cq_context->mtt_base_addr_h = mtt_addr >> 32; + cq_context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff); + cq_context->db_rec_addr = cpu_to_be64(db_rec); + + err = mlx4_SW2HW_CQ(dev, mailbox, cq->cqn); + mlx4_free_cmd_mailbox(dev, mailbox); + if (err) + goto err_radix; + + cq->cons_index = 0; + cq->arm_sn = 1; + cq->uar = uar; + atomic_set(&cq->refcount, 1); + init_completion(&cq->free); + + return 0; + +err_radix: + spin_lock_irq(&cq_table->lock); + radix_tree_delete(&cq_table->tree, cq->cqn); + spin_unlock_irq(&cq_table->lock); + +err_cmpt_put: + mlx4_table_put(dev, &cq_table->cmpt_table, cq->cqn); + +err_put: + mlx4_table_put(dev, &cq_table->table, cq->cqn); + +err_out: + mlx4_bitmap_free(&cq_table->bitmap, cq->cqn); + + return err; +} +EXPORT_SYMBOL_GPL(mlx4_cq_alloc); + +void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cq_table *cq_table = &priv->cq_table; + int err; + + err = mlx4_HW2SW_CQ(dev, NULL, cq->cqn); + if (err) + mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn); + + synchronize_irq(priv->eq_table.eq[MLX4_EQ_COMP].irq); + + spin_lock_irq(&cq_table->lock); + radix_tree_delete(&cq_table->tree, cq->cqn); + spin_unlock_irq(&cq_table->lock); + + if (atomic_dec_and_test(&cq->refcount)) + complete(&cq->free); + wait_for_completion(&cq->free); + + mlx4_table_put(dev, &cq_table->table, cq->cqn); + mlx4_bitmap_free(&cq_table->bitmap, cq->cqn); +} +EXPORT_SYMBOL_GPL(mlx4_cq_free); + +int __devinit mlx4_init_cq_table(struct mlx4_dev *dev) +{ + struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table; + int err; + + spin_lock_init(&cq_table->lock); + INIT_RADIX_TREE(&cq_table->tree, GFP_ATOMIC); + + err = mlx4_bitmap_init(&cq_table->bitmap, dev->caps.num_cqs, + dev->caps.num_cqs - 1, dev->caps.reserved_cqs); + if (err) + return err; + + return 0; +} + +void mlx4_cleanup_cq_table(struct mlx4_dev *dev) +{ + /* Nothing to do to clean up radix_tree */ + mlx4_bitmap_cleanup(&mlx4_priv(dev)->cq_table.bitmap); +} diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c new file mode 100644 index 00000000000..acf1c801a1b --- /dev/null +++ b/drivers/net/mlx4/eq.c @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4.h" +#include "fw.h" + +enum { + MLX4_NUM_ASYNC_EQE = 0x100, + MLX4_NUM_SPARE_EQE = 0x80, + MLX4_EQ_ENTRY_SIZE = 0x20 +}; + +/* + * Must be packed because start is 64 bits but only aligned to 32 bits. + */ +struct mlx4_eq_context { + __be32 flags; + u16 reserved1[3]; + __be16 page_offset; + u8 log_eq_size; + u8 reserved2[4]; + u8 eq_period; + u8 reserved3; + u8 eq_max_count; + u8 reserved4[3]; + u8 intr; + u8 log_page_size; + u8 reserved5[2]; + u8 mtt_base_addr_h; + __be32 mtt_base_addr_l; + u32 reserved6[2]; + __be32 consumer_index; + __be32 producer_index; + u32 reserved7[4]; +}; + +#define MLX4_EQ_STATUS_OK ( 0 << 28) +#define MLX4_EQ_STATUS_WRITE_FAIL (10 << 28) +#define MLX4_EQ_OWNER_SW ( 0 << 24) +#define MLX4_EQ_OWNER_HW ( 1 << 24) +#define MLX4_EQ_FLAG_EC ( 1 << 18) +#define MLX4_EQ_FLAG_OI ( 1 << 17) +#define MLX4_EQ_STATE_ARMED ( 9 << 8) +#define MLX4_EQ_STATE_FIRED (10 << 8) +#define MLX4_EQ_STATE_ALWAYS_ARMED (11 << 8) + +#define MLX4_ASYNC_EVENT_MASK ((1ull << MLX4_EVENT_TYPE_PATH_MIG) | \ + (1ull << MLX4_EVENT_TYPE_COMM_EST) | \ + (1ull << MLX4_EVENT_TYPE_SQ_DRAINED) | \ + (1ull << MLX4_EVENT_TYPE_CQ_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_WQ_CATAS_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_EEC_CATAS_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_PATH_MIG_FAILED) | \ + (1ull << MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_WQ_ACCESS_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_PORT_CHANGE) | \ + (1ull << MLX4_EVENT_TYPE_ECC_DETECT) | \ + (1ull << MLX4_EVENT_TYPE_SRQ_CATAS_ERROR) | \ + (1ull << MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ + (1ull << MLX4_EVENT_TYPE_SRQ_LIMIT) | \ + (1ull << MLX4_EVENT_TYPE_CMD)) +#define MLX4_CATAS_EVENT_MASK (1ull << MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR) + +struct mlx4_eqe { + u8 reserved1; + u8 type; + u8 reserved2; + u8 subtype; + union { + u32 raw[6]; + struct { + __be32 cqn; + } __attribute__((packed)) comp; + struct { + u16 reserved1; + __be16 token; + u32 reserved2; + u8 reserved3[3]; + u8 status; + __be64 out_param; + } __attribute__((packed)) cmd; + struct { + __be32 qpn; + } __attribute__((packed)) qp; + struct { + __be32 srqn; + } __attribute__((packed)) srq; + struct { + __be32 cqn; + u32 reserved1; + u8 reserved2[3]; + u8 syndrome; + } __attribute__((packed)) cq_err; + struct { + u32 reserved1[2]; + __be32 port; + } __attribute__((packed)) port_change; + } event; + u8 reserved3[3]; + u8 owner; +} __attribute__((packed)); + +static void eq_set_ci(struct mlx4_eq *eq, int req_not) +{ + __raw_writel((__force u32) cpu_to_be32((eq->cons_index & 0xffffff) | + req_not << 31), + eq->doorbell); + /* We still want ordering, just not swabbing, so add a barrier */ + mb(); +} + +static struct mlx4_eqe *get_eqe(struct mlx4_eq *eq, u32 entry) +{ + unsigned long off = (entry & (eq->nent - 1)) * MLX4_EQ_ENTRY_SIZE; + return eq->page_list[off / PAGE_SIZE].buf + off % PAGE_SIZE; +} + +static struct mlx4_eqe *next_eqe_sw(struct mlx4_eq *eq) +{ + struct mlx4_eqe *eqe = get_eqe(eq, eq->cons_index); + return !!(eqe->owner & 0x80) ^ !!(eq->cons_index & eq->nent) ? NULL : eqe; +} + +static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) +{ + struct mlx4_eqe *eqe; + int cqn; + int eqes_found = 0; + int set_ci = 0; + + while ((eqe = next_eqe_sw(eq))) { + /* + * Make sure we read EQ entry contents after we've + * checked the ownership bit. + */ + rmb(); + + switch (eqe->type) { + case MLX4_EVENT_TYPE_COMP: + cqn = be32_to_cpu(eqe->event.comp.cqn) & 0xffffff; + mlx4_cq_completion(dev, cqn); + break; + + case MLX4_EVENT_TYPE_PATH_MIG: + case MLX4_EVENT_TYPE_COMM_EST: + case MLX4_EVENT_TYPE_SQ_DRAINED: + case MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE: + case MLX4_EVENT_TYPE_WQ_CATAS_ERROR: + case MLX4_EVENT_TYPE_PATH_MIG_FAILED: + case MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + case MLX4_EVENT_TYPE_WQ_ACCESS_ERROR: + mlx4_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, + eqe->type); + break; + + case MLX4_EVENT_TYPE_SRQ_LIMIT: + case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR: + mlx4_srq_event(dev, be32_to_cpu(eqe->event.srq.srqn) & 0xffffff, + eqe->type); + break; + + case MLX4_EVENT_TYPE_CMD: + mlx4_cmd_event(dev, + be16_to_cpu(eqe->event.cmd.token), + eqe->event.cmd.status, + be64_to_cpu(eqe->event.cmd.out_param)); + break; + + case MLX4_EVENT_TYPE_PORT_CHANGE: + mlx4_dispatch_event(dev, eqe->type, eqe->subtype, + be32_to_cpu(eqe->event.port_change.port) >> 28); + break; + + case MLX4_EVENT_TYPE_CQ_ERROR: + mlx4_warn(dev, "CQ %s on CQN %06x\n", + eqe->event.cq_err.syndrome == 1 ? + "overrun" : "access violation", + be32_to_cpu(eqe->event.cq_err.cqn) & 0xffffff); + mlx4_cq_event(dev, be32_to_cpu(eqe->event.cq_err.cqn), + eqe->type); + break; + + case MLX4_EVENT_TYPE_EQ_OVERFLOW: + mlx4_warn(dev, "EQ overrun on EQN %d\n", eq->eqn); + break; + + case MLX4_EVENT_TYPE_EEC_CATAS_ERROR: + case MLX4_EVENT_TYPE_ECC_DETECT: + default: + mlx4_warn(dev, "Unhandled event %02x(%02x) on EQ %d at index %u\n", + eqe->type, eqe->subtype, eq->eqn, eq->cons_index); + break; + }; + + ++eq->cons_index; + eqes_found = 1; + ++set_ci; + + /* + * The HCA will think the queue has overflowed if we + * don't tell it we've been processing events. We + * create our EQs with MLX4_NUM_SPARE_EQE extra + * entries, so we must update our consumer index at + * least that often. + */ + if (unlikely(set_ci >= MLX4_NUM_SPARE_EQE)) { + /* + * Conditional on hca_type is OK here because + * this is a rare case, not the fast path. + */ + eq_set_ci(eq, 0); + set_ci = 0; + } + } + + eq_set_ci(eq, 1); + + return eqes_found; +} + +static irqreturn_t mlx4_interrupt(int irq, void *dev_ptr) +{ + struct mlx4_dev *dev = dev_ptr; + struct mlx4_priv *priv = mlx4_priv(dev); + int work = 0; + int i; + + writel(priv->eq_table.clr_mask, priv->eq_table.clr_int); + + for (i = 0; i < MLX4_EQ_CATAS; ++i) + work |= mlx4_eq_int(dev, &priv->eq_table.eq[i]); + + return IRQ_RETVAL(work); +} + +static irqreturn_t mlx4_msi_x_interrupt(int irq, void *eq_ptr) +{ + struct mlx4_eq *eq = eq_ptr; + struct mlx4_dev *dev = eq->dev; + + mlx4_eq_int(dev, eq); + + /* MSI-X vectors always belong to us */ + return IRQ_HANDLED; +} + +static irqreturn_t mlx4_catas_interrupt(int irq, void *dev_ptr) +{ + mlx4_handle_catas_err(dev_ptr); + + /* MSI-X vectors always belong to us */ + return IRQ_HANDLED; +} + +static int mlx4_MAP_EQ(struct mlx4_dev *dev, u64 event_mask, int unmap, + int eq_num) +{ + return mlx4_cmd(dev, event_mask, (unmap << 31) | eq_num, + 0, MLX4_CMD_MAP_EQ, MLX4_CMD_TIME_CLASS_B); +} + +static int mlx4_SW2HW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int eq_num) +{ + return mlx4_cmd(dev, mailbox->dma, eq_num, 0, MLX4_CMD_SW2HW_EQ, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_HW2SW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int eq_num) +{ + return mlx4_cmd_box(dev, 0, mailbox->dma, eq_num, 0, MLX4_CMD_HW2SW_EQ, + MLX4_CMD_TIME_CLASS_A); +} + +static void __devinit __iomem *mlx4_get_eq_uar(struct mlx4_dev *dev, + struct mlx4_eq *eq) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int index; + + index = eq->eqn / 4 - dev->caps.reserved_eqs / 4; + + if (!priv->eq_table.uar_map[index]) { + priv->eq_table.uar_map[index] = + ioremap(pci_resource_start(dev->pdev, 2) + + ((eq->eqn / 4) << PAGE_SHIFT), + PAGE_SIZE); + if (!priv->eq_table.uar_map[index]) { + mlx4_err(dev, "Couldn't map EQ doorbell for EQN 0x%06x\n", + eq->eqn); + return NULL; + } + } + + return priv->eq_table.uar_map[index] + 0x800 + 8 * (eq->eqn % 4); +} + +static int __devinit mlx4_create_eq(struct mlx4_dev *dev, int nent, + u8 intr, struct mlx4_eq *eq) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_eq_context *eq_context; + int npages; + u64 *dma_list = NULL; + dma_addr_t t; + u64 mtt_addr; + int err = -ENOMEM; + int i; + + eq->dev = dev; + eq->nent = roundup_pow_of_two(max(nent, 2)); + npages = PAGE_ALIGN(eq->nent * MLX4_EQ_ENTRY_SIZE) / PAGE_SIZE; + + eq->page_list = kmalloc(npages * sizeof *eq->page_list, + GFP_KERNEL); + if (!eq->page_list) + goto err_out; + + for (i = 0; i < npages; ++i) + eq->page_list[i].buf = NULL; + + dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL); + if (!dma_list) + goto err_out_free; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + goto err_out_free; + eq_context = mailbox->buf; + + for (i = 0; i < npages; ++i) { + eq->page_list[i].buf = dma_alloc_coherent(&dev->pdev->dev, + PAGE_SIZE, &t, GFP_KERNEL); + if (!eq->page_list[i].buf) + goto err_out_free_pages; + + dma_list[i] = t; + eq->page_list[i].map = t; + + memset(eq->page_list[i].buf, 0, PAGE_SIZE); + } + + eq->eqn = mlx4_bitmap_alloc(&priv->eq_table.bitmap); + if (eq->eqn == -1) + goto err_out_free_pages; + + eq->doorbell = mlx4_get_eq_uar(dev, eq); + if (!eq->doorbell) { + err = -ENOMEM; + goto err_out_free_eq; + } + + err = mlx4_mtt_init(dev, npages, PAGE_SHIFT, &eq->mtt); + if (err) + goto err_out_free_eq; + + err = mlx4_write_mtt(dev, &eq->mtt, 0, npages, dma_list); + if (err) + goto err_out_free_mtt; + + memset(eq_context, 0, sizeof *eq_context); + eq_context->flags = cpu_to_be32(MLX4_EQ_STATUS_OK | + MLX4_EQ_STATE_ARMED); + eq_context->log_eq_size = ilog2(eq->nent); + eq_context->intr = intr; + eq_context->log_page_size = PAGE_SHIFT - MLX4_ICM_PAGE_SHIFT; + + mtt_addr = mlx4_mtt_addr(dev, &eq->mtt); + eq_context->mtt_base_addr_h = mtt_addr >> 32; + eq_context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff); + + err = mlx4_SW2HW_EQ(dev, mailbox, eq->eqn); + if (err) { + mlx4_warn(dev, "SW2HW_EQ failed (%d)\n", err); + goto err_out_free_mtt; + } + + kfree(dma_list); + mlx4_free_cmd_mailbox(dev, mailbox); + + eq->cons_index = 0; + + return err; + +err_out_free_mtt: + mlx4_mtt_cleanup(dev, &eq->mtt); + +err_out_free_eq: + mlx4_bitmap_free(&priv->eq_table.bitmap, eq->eqn); + +err_out_free_pages: + for (i = 0; i < npages; ++i) + if (eq->page_list[i].buf) + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + eq->page_list[i].buf, + eq->page_list[i].map); + + mlx4_free_cmd_mailbox(dev, mailbox); + +err_out_free: + kfree(eq->page_list); + kfree(dma_list); + +err_out: + return err; +} + +static void mlx4_free_eq(struct mlx4_dev *dev, + struct mlx4_eq *eq) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_mailbox *mailbox; + int err; + int npages = PAGE_ALIGN(MLX4_EQ_ENTRY_SIZE * eq->nent) / PAGE_SIZE; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return; + + err = mlx4_HW2SW_EQ(dev, mailbox, eq->eqn); + if (err) + mlx4_warn(dev, "HW2SW_EQ failed (%d)\n", err); + + if (0) { + mlx4_dbg(dev, "Dumping EQ context %02x:\n", eq->eqn); + for (i = 0; i < sizeof (struct mlx4_eq_context) / 4; ++i) { + if (i % 4 == 0) + printk("[%02x] ", i * 4); + printk(" %08x", be32_to_cpup(mailbox->buf + i * 4)); + if ((i + 1) % 4 == 0) + printk("\n"); + } + } + + mlx4_mtt_cleanup(dev, &eq->mtt); + for (i = 0; i < npages; ++i) + pci_free_consistent(dev->pdev, PAGE_SIZE, + eq->page_list[i].buf, + eq->page_list[i].map); + + kfree(eq->page_list); + mlx4_bitmap_free(&priv->eq_table.bitmap, eq->eqn); + mlx4_free_cmd_mailbox(dev, mailbox); +} + +static void mlx4_free_irqs(struct mlx4_dev *dev) +{ + struct mlx4_eq_table *eq_table = &mlx4_priv(dev)->eq_table; + int i; + + if (eq_table->have_irq) + free_irq(dev->pdev->irq, dev); + for (i = 0; i < MLX4_NUM_EQ; ++i) + if (eq_table->eq[i].have_irq) + free_irq(eq_table->eq[i].irq, eq_table->eq + i); +} + +static int __devinit mlx4_map_clr_int(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + priv->clr_base = ioremap(pci_resource_start(dev->pdev, priv->fw.clr_int_bar) + + priv->fw.clr_int_base, MLX4_CLR_INT_SIZE); + if (!priv->clr_base) { + mlx4_err(dev, "Couldn't map interrupt clear register, aborting.\n"); + return -ENOMEM; + } + + return 0; +} + +static void mlx4_unmap_clr_int(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + iounmap(priv->clr_base); +} + +int __devinit 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(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 __devinit mlx4_init_eq_table(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + int i; + + err = mlx4_bitmap_init(&priv->eq_table.bitmap, dev->caps.num_eqs, + dev->caps.num_eqs - 1, dev->caps.reserved_eqs); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(priv->eq_table.uar_map); ++i) + priv->eq_table.uar_map[i] = NULL; + + err = mlx4_map_clr_int(dev); + if (err) + goto err_out_free; + + priv->eq_table.clr_mask = + swab32(1 << (priv->eq_table.inta_pin & 31)); + priv->eq_table.clr_int = priv->clr_base + + (priv->eq_table.inta_pin < 32 ? 4 : 0); + + err = mlx4_create_eq(dev, dev->caps.num_cqs + MLX4_NUM_SPARE_EQE, + (dev->flags & MLX4_FLAG_MSI_X) ? MLX4_EQ_COMP : 0, + &priv->eq_table.eq[MLX4_EQ_COMP]); + if (err) + goto err_out_unmap; + + err = mlx4_create_eq(dev, MLX4_NUM_ASYNC_EQE + MLX4_NUM_SPARE_EQE, + (dev->flags & MLX4_FLAG_MSI_X) ? MLX4_EQ_ASYNC : 0, + &priv->eq_table.eq[MLX4_EQ_ASYNC]); + if (err) + goto err_out_comp; + + if (dev->flags & MLX4_FLAG_MSI_X) { + static const char *eq_name[] = { + [MLX4_EQ_COMP] = DRV_NAME " (comp)", + [MLX4_EQ_ASYNC] = DRV_NAME " (async)", + [MLX4_EQ_CATAS] = DRV_NAME " (catas)" + }; + + err = mlx4_create_eq(dev, 1, MLX4_EQ_CATAS, + &priv->eq_table.eq[MLX4_EQ_CATAS]); + if (err) + goto err_out_async; + + for (i = 0; i < MLX4_EQ_CATAS; ++i) { + err = request_irq(priv->eq_table.eq[i].irq, + mlx4_msi_x_interrupt, + 0, eq_name[i], priv->eq_table.eq + i); + if (err) + goto err_out_catas; + + priv->eq_table.eq[i].have_irq = 1; + } + + err = request_irq(priv->eq_table.eq[MLX4_EQ_CATAS].irq, + mlx4_catas_interrupt, 0, + eq_name[MLX4_EQ_CATAS], dev); + if (err) + goto err_out_catas; + + priv->eq_table.eq[MLX4_EQ_CATAS].have_irq = 1; + } else { + err = request_irq(dev->pdev->irq, mlx4_interrupt, + SA_SHIRQ, DRV_NAME, dev); + if (err) + goto err_out_async; + + priv->eq_table.have_irq = 1; + } + + err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + priv->eq_table.eq[MLX4_EQ_ASYNC].eqn); + if (err) + mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", + priv->eq_table.eq[MLX4_EQ_ASYNC].eqn, err); + + for (i = 0; i < MLX4_EQ_CATAS; ++i) + eq_set_ci(&priv->eq_table.eq[i], 1); + + if (dev->flags & MLX4_FLAG_MSI_X) { + err = mlx4_MAP_EQ(dev, MLX4_CATAS_EVENT_MASK, 0, + priv->eq_table.eq[MLX4_EQ_CATAS].eqn); + if (err) + mlx4_warn(dev, "MAP_EQ for catas EQ %d failed (%d)\n", + priv->eq_table.eq[MLX4_EQ_CATAS].eqn, err); + } + + return 0; + +err_out_catas: + mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_CATAS]); + +err_out_async: + mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_ASYNC]); + +err_out_comp: + mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_COMP]); + +err_out_unmap: + mlx4_unmap_clr_int(dev); + mlx4_free_irqs(dev); + +err_out_free: + mlx4_bitmap_cleanup(&priv->eq_table.bitmap); + return err; +} + +void mlx4_cleanup_eq_table(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int i; + + if (dev->flags & MLX4_FLAG_MSI_X) + mlx4_MAP_EQ(dev, MLX4_CATAS_EVENT_MASK, 1, + priv->eq_table.eq[MLX4_EQ_CATAS].eqn); + + mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, + priv->eq_table.eq[MLX4_EQ_ASYNC].eqn); + + mlx4_free_irqs(dev); + + for (i = 0; i < MLX4_EQ_CATAS; ++i) + mlx4_free_eq(dev, &priv->eq_table.eq[i]); + if (dev->flags & MLX4_FLAG_MSI_X) + mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_CATAS]); + + mlx4_unmap_clr_int(dev); + + for (i = 0; i < ARRAY_SIZE(priv->eq_table.uar_map); ++i) + if (priv->eq_table.uar_map[i]) + iounmap(priv->eq_table.uar_map[i]); + + mlx4_bitmap_cleanup(&priv->eq_table.bitmap); +} diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c new file mode 100644 index 00000000000..c4271731366 --- /dev/null +++ b/drivers/net/mlx4/fw.c @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "fw.h" +#include "icm.h" + +extern void __buggy_use_of_MLX4_GET(void); +extern void __buggy_use_of_MLX4_PUT(void); + +#define MLX4_GET(dest, source, offset) \ + do { \ + void *__p = (char *) (source) + (offset); \ + switch (sizeof (dest)) { \ + case 1: (dest) = *(u8 *) __p; break; \ + case 2: (dest) = be16_to_cpup(__p); break; \ + case 4: (dest) = be32_to_cpup(__p); break; \ + case 8: (dest) = be64_to_cpup(__p); break; \ + default: __buggy_use_of_MLX4_GET(); \ + } \ + } while (0) + +#define MLX4_PUT(dest, source, offset) \ + do { \ + void *__d = ((char *) (dest) + (offset)); \ + switch (sizeof(source)) { \ + case 1: *(u8 *) __d = (source); break; \ + case 2: *(__be16 *) __d = cpu_to_be16(source); break; \ + case 4: *(__be32 *) __d = cpu_to_be32(source); break; \ + case 8: *(__be64 *) __d = cpu_to_be64(source); break; \ + default: __buggy_use_of_MLX4_PUT(); \ + } \ + } while (0) + +static void dump_dev_cap_flags(struct mlx4_dev *dev, u32 flags) +{ + static const char *fname[] = { + [ 0] = "RC transport", + [ 1] = "UC transport", + [ 2] = "UD transport", + [ 3] = "SRC transport", + [ 4] = "reliable multicast", + [ 5] = "FCoIB support", + [ 6] = "SRQ support", + [ 7] = "IPoIB checksum offload", + [ 8] = "P_Key violation counter", + [ 9] = "Q_Key violation counter", + [10] = "VMM", + [16] = "MW support", + [17] = "APM support", + [18] = "Atomic ops support", + [19] = "Raw multicast support", + [20] = "Address vector port checking support", + [21] = "UD multicast support", + [24] = "Demand paging support", + [25] = "Router support" + }; + int i; + + mlx4_dbg(dev, "DEV_CAP flags:\n"); + for (i = 0; i < 32; ++i) + if (fname[i] && (flags & (1 << i))) + mlx4_dbg(dev, " %s\n", fname[i]); +} + +int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) +{ + struct mlx4_cmd_mailbox *mailbox; + u32 *outbox; + u8 field; + u16 size; + u16 stat_rate; + int err; + +#define QUERY_DEV_CAP_OUT_SIZE 0x100 +#define QUERY_DEV_CAP_MAX_SRQ_SZ_OFFSET 0x10 +#define QUERY_DEV_CAP_MAX_QP_SZ_OFFSET 0x11 +#define QUERY_DEV_CAP_RSVD_QP_OFFSET 0x12 +#define QUERY_DEV_CAP_MAX_QP_OFFSET 0x13 +#define QUERY_DEV_CAP_RSVD_SRQ_OFFSET 0x14 +#define QUERY_DEV_CAP_MAX_SRQ_OFFSET 0x15 +#define QUERY_DEV_CAP_RSVD_EEC_OFFSET 0x16 +#define QUERY_DEV_CAP_MAX_EEC_OFFSET 0x17 +#define QUERY_DEV_CAP_MAX_CQ_SZ_OFFSET 0x19 +#define QUERY_DEV_CAP_RSVD_CQ_OFFSET 0x1a +#define QUERY_DEV_CAP_MAX_CQ_OFFSET 0x1b +#define QUERY_DEV_CAP_MAX_MPT_OFFSET 0x1d +#define QUERY_DEV_CAP_RSVD_EQ_OFFSET 0x1e +#define QUERY_DEV_CAP_MAX_EQ_OFFSET 0x1f +#define QUERY_DEV_CAP_RSVD_MTT_OFFSET 0x20 +#define QUERY_DEV_CAP_MAX_MRW_SZ_OFFSET 0x21 +#define QUERY_DEV_CAP_RSVD_MRW_OFFSET 0x22 +#define QUERY_DEV_CAP_MAX_MTT_SEG_OFFSET 0x23 +#define QUERY_DEV_CAP_MAX_AV_OFFSET 0x27 +#define QUERY_DEV_CAP_MAX_REQ_QP_OFFSET 0x29 +#define QUERY_DEV_CAP_MAX_RES_QP_OFFSET 0x2b +#define QUERY_DEV_CAP_MAX_RDMA_OFFSET 0x2f +#define QUERY_DEV_CAP_RSZ_SRQ_OFFSET 0x33 +#define QUERY_DEV_CAP_ACK_DELAY_OFFSET 0x35 +#define QUERY_DEV_CAP_MTU_WIDTH_OFFSET 0x36 +#define QUERY_DEV_CAP_VL_PORT_OFFSET 0x37 +#define QUERY_DEV_CAP_MAX_GID_OFFSET 0x3b +#define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET 0x3c +#define QUERY_DEV_CAP_MAX_PKEY_OFFSET 0x3f +#define QUERY_DEV_CAP_FLAGS_OFFSET 0x44 +#define QUERY_DEV_CAP_RSVD_UAR_OFFSET 0x48 +#define QUERY_DEV_CAP_UAR_SZ_OFFSET 0x49 +#define QUERY_DEV_CAP_PAGE_SZ_OFFSET 0x4b +#define QUERY_DEV_CAP_BF_OFFSET 0x4c +#define QUERY_DEV_CAP_LOG_BF_REG_SZ_OFFSET 0x4d +#define QUERY_DEV_CAP_LOG_MAX_BF_REGS_PER_PAGE_OFFSET 0x4e +#define QUERY_DEV_CAP_LOG_MAX_BF_PAGES_OFFSET 0x4f +#define QUERY_DEV_CAP_MAX_SG_SQ_OFFSET 0x51 +#define QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET 0x52 +#define QUERY_DEV_CAP_MAX_SG_RQ_OFFSET 0x55 +#define QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET 0x56 +#define QUERY_DEV_CAP_MAX_QP_MCG_OFFSET 0x61 +#define QUERY_DEV_CAP_RSVD_MCG_OFFSET 0x62 +#define QUERY_DEV_CAP_MAX_MCG_OFFSET 0x63 +#define QUERY_DEV_CAP_RSVD_PD_OFFSET 0x64 +#define QUERY_DEV_CAP_MAX_PD_OFFSET 0x65 +#define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80 +#define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82 +#define QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET 0x84 +#define QUERY_DEV_CAP_ALTC_ENTRY_SZ_OFFSET 0x86 +#define QUERY_DEV_CAP_EQC_ENTRY_SZ_OFFSET 0x88 +#define QUERY_DEV_CAP_CQC_ENTRY_SZ_OFFSET 0x8a +#define QUERY_DEV_CAP_SRQ_ENTRY_SZ_OFFSET 0x8c +#define QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET 0x8e +#define QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET 0x90 +#define QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET 0x92 +#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x97 +#define QUERY_DEV_CAP_RSVD_LKEY_OFFSET 0x98 +#define QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET 0xa0 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, MLX4_CMD_QUERY_DEV_CAP, + MLX4_CMD_TIME_CLASS_A); + + if (err) + goto out; + + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_QP_OFFSET); + dev_cap->reserved_qps = 1 << (field & 0xf); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_QP_OFFSET); + dev_cap->max_qps = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_SRQ_OFFSET); + dev_cap->reserved_srqs = 1 << (field >> 4); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_SRQ_OFFSET); + dev_cap->max_srqs = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_CQ_SZ_OFFSET); + dev_cap->max_cq_sz = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_CQ_OFFSET); + dev_cap->reserved_cqs = 1 << (field & 0xf); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_CQ_OFFSET); + dev_cap->max_cqs = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MPT_OFFSET); + dev_cap->max_mpts = 1 << (field & 0x3f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_EQ_OFFSET); + dev_cap->reserved_eqs = 1 << (field & 0xf); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_EQ_OFFSET); + dev_cap->max_eqs = 1 << (field & 0x7); + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_MTT_OFFSET); + dev_cap->reserved_mtts = 1 << (field >> 4); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MRW_SZ_OFFSET); + dev_cap->max_mrw_sz = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_MRW_OFFSET); + dev_cap->reserved_mrws = 1 << (field & 0xf); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MTT_SEG_OFFSET); + dev_cap->max_mtt_seg = 1 << (field & 0x3f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_REQ_QP_OFFSET); + dev_cap->max_requester_per_qp = 1 << (field & 0x3f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_RES_QP_OFFSET); + dev_cap->max_responder_per_qp = 1 << (field & 0x3f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_RDMA_OFFSET); + dev_cap->max_rdma_global = 1 << (field & 0x3f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_ACK_DELAY_OFFSET); + dev_cap->local_ca_ack_delay = field & 0x1f; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MTU_WIDTH_OFFSET); + dev_cap->max_mtu = field >> 4; + dev_cap->max_port_width = field & 0xf; + MLX4_GET(field, outbox, QUERY_DEV_CAP_VL_PORT_OFFSET); + dev_cap->max_vl = field >> 4; + dev_cap->num_ports = field & 0xf; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_GID_OFFSET); + dev_cap->max_gids = 1 << (field & 0xf); + MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); + dev_cap->stat_rate_support = stat_rate; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PKEY_OFFSET); + dev_cap->max_pkeys = 1 << (field & 0xf); + MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); + dev_cap->reserved_uars = field >> 4; + MLX4_GET(field, outbox, QUERY_DEV_CAP_UAR_SZ_OFFSET); + dev_cap->uar_size = 1 << ((field & 0x3f) + 20); + MLX4_GET(field, outbox, QUERY_DEV_CAP_PAGE_SZ_OFFSET); + dev_cap->min_page_sz = 1 << field; + + MLX4_GET(field, outbox, QUERY_DEV_CAP_BF_OFFSET); + if (field & 0x80) { + MLX4_GET(field, outbox, QUERY_DEV_CAP_LOG_BF_REG_SZ_OFFSET); + dev_cap->bf_reg_size = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_LOG_MAX_BF_REGS_PER_PAGE_OFFSET); + dev_cap->bf_regs_per_page = 1 << (field & 0x3f); + mlx4_dbg(dev, "BlueFlame available (reg size %d, regs/page %d)\n", + dev_cap->bf_reg_size, dev_cap->bf_regs_per_page); + } else { + dev_cap->bf_reg_size = 0; + mlx4_dbg(dev, "BlueFlame not available\n"); + } + + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_SG_SQ_OFFSET); + dev_cap->max_sq_sg = field; + MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET); + dev_cap->max_sq_desc_sz = size; + + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_QP_MCG_OFFSET); + dev_cap->max_qp_per_mcg = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_MCG_OFFSET); + dev_cap->reserved_mgms = field & 0xf; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MCG_OFFSET); + dev_cap->max_mcgs = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_PD_OFFSET); + dev_cap->reserved_pds = field >> 4; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_PD_OFFSET); + dev_cap->max_pds = 1 << (field & 0x3f); + + MLX4_GET(size, outbox, QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET); + dev_cap->rdmarc_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET); + dev_cap->qpc_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET); + dev_cap->aux_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_ALTC_ENTRY_SZ_OFFSET); + dev_cap->altc_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_EQC_ENTRY_SZ_OFFSET); + dev_cap->eqc_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_CQC_ENTRY_SZ_OFFSET); + dev_cap->cqc_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_SRQ_ENTRY_SZ_OFFSET); + dev_cap->srq_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET); + dev_cap->cmpt_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET); + dev_cap->mtt_entry_sz = size; + MLX4_GET(size, outbox, QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET); + dev_cap->dmpt_entry_sz = size; + + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_SRQ_SZ_OFFSET); + dev_cap->max_srq_sz = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_QP_SZ_OFFSET); + dev_cap->max_qp_sz = 1 << field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_RSZ_SRQ_OFFSET); + dev_cap->resize_srq = field & 1; + MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_SG_RQ_OFFSET); + dev_cap->max_rq_sg = field; + MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET); + dev_cap->max_rq_desc_sz = size; + + MLX4_GET(dev_cap->bmme_flags, outbox, + QUERY_DEV_CAP_BMME_FLAGS_OFFSET); + MLX4_GET(dev_cap->reserved_lkey, outbox, + QUERY_DEV_CAP_RSVD_LKEY_OFFSET); + MLX4_GET(dev_cap->max_icm_sz, outbox, + QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET); + + if (dev_cap->bmme_flags & 1) + mlx4_dbg(dev, "Base MM extensions: yes " + "(flags %d, rsvd L_Key %08x)\n", + dev_cap->bmme_flags, dev_cap->reserved_lkey); + else + mlx4_dbg(dev, "Base MM extensions: no\n"); + + /* + * Each UAR has 4 EQ doorbells; so if a UAR is reserved, then + * we can't use any EQs whose doorbell falls on that page, + * even if the EQ itself isn't reserved. + */ + dev_cap->reserved_eqs = max(dev_cap->reserved_uars * 4, + dev_cap->reserved_eqs); + + mlx4_dbg(dev, "Max ICM size %lld MB\n", + (unsigned long long) dev_cap->max_icm_sz >> 20); + mlx4_dbg(dev, "Max QPs: %d, reserved QPs: %d, entry size: %d\n", + dev_cap->max_qps, dev_cap->reserved_qps, dev_cap->qpc_entry_sz); + mlx4_dbg(dev, "Max SRQs: %d, reserved SRQs: %d, entry size: %d\n", + dev_cap->max_srqs, dev_cap->reserved_srqs, dev_cap->srq_entry_sz); + mlx4_dbg(dev, "Max CQs: %d, reserved CQs: %d, entry size: %d\n", + dev_cap->max_cqs, dev_cap->reserved_cqs, dev_cap->cqc_entry_sz); + mlx4_dbg(dev, "Max EQs: %d, reserved EQs: %d, entry size: %d\n", + dev_cap->max_eqs, dev_cap->reserved_eqs, dev_cap->eqc_entry_sz); + mlx4_dbg(dev, "reserved MPTs: %d, reserved MTTs: %d\n", + dev_cap->reserved_mrws, dev_cap->reserved_mtts); + mlx4_dbg(dev, "Max PDs: %d, reserved PDs: %d, reserved UARs: %d\n", + dev_cap->max_pds, dev_cap->reserved_pds, dev_cap->reserved_uars); + mlx4_dbg(dev, "Max QP/MCG: %d, reserved MGMs: %d\n", + dev_cap->max_pds, dev_cap->reserved_mgms); + mlx4_dbg(dev, "Max CQEs: %d, max WQEs: %d, max SRQ WQEs: %d\n", + dev_cap->max_cq_sz, dev_cap->max_qp_sz, dev_cap->max_srq_sz); + mlx4_dbg(dev, "Local CA ACK delay: %d, max MTU: %d, port width cap: %d\n", + dev_cap->local_ca_ack_delay, 128 << dev_cap->max_mtu, + dev_cap->max_port_width); + mlx4_dbg(dev, "Max SQ desc size: %d, max SQ S/G: %d\n", + dev_cap->max_sq_desc_sz, dev_cap->max_sq_sg); + mlx4_dbg(dev, "Max RQ desc size: %d, max RQ S/G: %d\n", + dev_cap->max_rq_desc_sz, dev_cap->max_rq_sg); + + dump_dev_cap_flags(dev, dev_cap->flags); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + +int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_icm_iter iter; + __be64 *pages; + int lg; + int nent = 0; + int i; + int err = 0; + int ts = 0, tc = 0; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE); + pages = mailbox->buf; + + for (mlx4_icm_first(icm, &iter); + !mlx4_icm_last(&iter); + mlx4_icm_next(&iter)) { + /* + * We have to pass pages that are aligned to their + * size, so find the least significant 1 in the + * address or size and use that as our log2 size. + */ + lg = ffs(mlx4_icm_addr(&iter) | mlx4_icm_size(&iter)) - 1; + if (lg < MLX4_ICM_PAGE_SHIFT) { + mlx4_warn(dev, "Got FW area not aligned to %d (%llx/%lx).\n", + MLX4_ICM_PAGE_SIZE, + (unsigned long long) mlx4_icm_addr(&iter), + mlx4_icm_size(&iter)); + err = -EINVAL; + goto out; + } + + for (i = 0; i < mlx4_icm_size(&iter) >> lg; ++i) { + if (virt != -1) { + pages[nent * 2] = cpu_to_be64(virt); + virt += 1 << lg; + } + + pages[nent * 2 + 1] = + cpu_to_be64((mlx4_icm_addr(&iter) + (i << lg)) | + (lg - MLX4_ICM_PAGE_SHIFT)); + ts += 1 << (lg - 10); + ++tc; + + if (++nent == MLX4_MAILBOX_SIZE / 16) { + err = mlx4_cmd(dev, mailbox->dma, nent, 0, op, + MLX4_CMD_TIME_CLASS_B); + if (err) + goto out; + nent = 0; + } + } + } + + if (nent) + err = mlx4_cmd(dev, mailbox->dma, nent, 0, op, MLX4_CMD_TIME_CLASS_B); + if (err) + goto out; + + switch (op) { + case MLX4_CMD_MAP_FA: + mlx4_dbg(dev, "Mapped %d chunks/%d KB for FW.\n", tc, ts); + break; + case MLX4_CMD_MAP_ICM_AUX: + mlx4_dbg(dev, "Mapped %d chunks/%d KB for ICM aux.\n", tc, ts); + break; + case MLX4_CMD_MAP_ICM: + mlx4_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM.\n", + tc, ts, (unsigned long long) virt - (ts << 10)); + break; + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + +int mlx4_MAP_FA(struct mlx4_dev *dev, struct mlx4_icm *icm) +{ + return mlx4_map_cmd(dev, MLX4_CMD_MAP_FA, icm, -1); +} + +int mlx4_UNMAP_FA(struct mlx4_dev *dev) +{ + return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_UNMAP_FA, MLX4_CMD_TIME_CLASS_B); +} + + +int mlx4_RUN_FW(struct mlx4_dev *dev) +{ + return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_RUN_FW, MLX4_CMD_TIME_CLASS_A); +} + +int mlx4_QUERY_FW(struct mlx4_dev *dev) +{ + struct mlx4_fw *fw = &mlx4_priv(dev)->fw; + struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; + struct mlx4_cmd_mailbox *mailbox; + u32 *outbox; + int err = 0; + u64 fw_ver; + u8 lg; + +#define QUERY_FW_OUT_SIZE 0x100 +#define QUERY_FW_VER_OFFSET 0x00 +#define QUERY_FW_MAX_CMD_OFFSET 0x0f +#define QUERY_FW_ERR_START_OFFSET 0x30 +#define QUERY_FW_ERR_SIZE_OFFSET 0x38 +#define QUERY_FW_ERR_BAR_OFFSET 0x3c + +#define QUERY_FW_SIZE_OFFSET 0x00 +#define QUERY_FW_CLR_INT_BASE_OFFSET 0x20 +#define QUERY_FW_CLR_INT_BAR_OFFSET 0x28 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, MLX4_CMD_QUERY_FW, + MLX4_CMD_TIME_CLASS_A); + if (err) + goto out; + + MLX4_GET(fw_ver, outbox, QUERY_FW_VER_OFFSET); + /* + * FW subminor version is at more signifant bits than minor + * version, so swap here. + */ + dev->caps.fw_ver = (fw_ver & 0xffff00000000ull) | + ((fw_ver & 0xffff0000ull) >> 16) | + ((fw_ver & 0x0000ffffull) << 16); + + MLX4_GET(lg, outbox, QUERY_FW_MAX_CMD_OFFSET); + cmd->max_cmds = 1 << lg; + + mlx4_dbg(dev, "FW version %d.%d.%03d, max commands %d\n", + (int) (dev->caps.fw_ver >> 32), + (int) (dev->caps.fw_ver >> 16) & 0xffff, + (int) dev->caps.fw_ver & 0xffff, + cmd->max_cmds); + + MLX4_GET(fw->catas_offset, outbox, QUERY_FW_ERR_START_OFFSET); + MLX4_GET(fw->catas_size, outbox, QUERY_FW_ERR_SIZE_OFFSET); + MLX4_GET(fw->catas_bar, outbox, QUERY_FW_ERR_BAR_OFFSET); + fw->catas_bar = (fw->catas_bar >> 6) * 2; + + mlx4_dbg(dev, "Catastrophic error buffer at 0x%llx, size 0x%x, BAR %d\n", + (unsigned long long) fw->catas_offset, fw->catas_size, fw->catas_bar); + + MLX4_GET(fw->fw_pages, outbox, QUERY_FW_SIZE_OFFSET); + MLX4_GET(fw->clr_int_base, outbox, QUERY_FW_CLR_INT_BASE_OFFSET); + MLX4_GET(fw->clr_int_bar, outbox, QUERY_FW_CLR_INT_BAR_OFFSET); + fw->clr_int_bar = (fw->clr_int_bar >> 6) * 2; + + mlx4_dbg(dev, "FW size %d KB\n", fw->fw_pages >> 2); + + /* + * Round up number of system pages needed in case + * MLX4_ICM_PAGE_SIZE < PAGE_SIZE. + */ + fw->fw_pages = + ALIGN(fw->fw_pages, PAGE_SIZE / MLX4_ICM_PAGE_SIZE) >> + (PAGE_SHIFT - MLX4_ICM_PAGE_SHIFT); + + mlx4_dbg(dev, "Clear int @ %llx, BAR %d\n", + (unsigned long long) fw->clr_int_base, fw->clr_int_bar); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + +static void get_board_id(void *vsd, char *board_id) +{ + int i; + +#define VSD_OFFSET_SIG1 0x00 +#define VSD_OFFSET_SIG2 0xde +#define VSD_OFFSET_MLX_BOARD_ID 0xd0 +#define VSD_OFFSET_TS_BOARD_ID 0x20 + +#define VSD_SIGNATURE_TOPSPIN 0x5ad + + memset(board_id, 0, MLX4_BOARD_ID_LEN); + + if (be16_to_cpup(vsd + VSD_OFFSET_SIG1) == VSD_SIGNATURE_TOPSPIN && + be16_to_cpup(vsd + VSD_OFFSET_SIG2) == VSD_SIGNATURE_TOPSPIN) { + strlcpy(board_id, vsd + VSD_OFFSET_TS_BOARD_ID, MLX4_BOARD_ID_LEN); + } else { + /* + * The board ID is a string but the firmware byte + * swaps each 4-byte word before passing it back to + * us. Therefore we need to swab it before printing. + */ + for (i = 0; i < 4; ++i) + ((u32 *) board_id)[i] = + swab32(*(u32 *) (vsd + VSD_OFFSET_MLX_BOARD_ID + i * 4)); + } +} + +int mlx4_QUERY_ADAPTER(struct mlx4_dev *dev, struct mlx4_adapter *adapter) +{ + struct mlx4_cmd_mailbox *mailbox; + u32 *outbox; + int err; + +#define QUERY_ADAPTER_OUT_SIZE 0x100 +#define QUERY_ADAPTER_VENDOR_ID_OFFSET 0x00 +#define QUERY_ADAPTER_DEVICE_ID_OFFSET 0x04 +#define QUERY_ADAPTER_REVISION_ID_OFFSET 0x08 +#define QUERY_ADAPTER_INTA_PIN_OFFSET 0x10 +#define QUERY_ADAPTER_VSD_OFFSET 0x20 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, MLX4_CMD_QUERY_ADAPTER, + MLX4_CMD_TIME_CLASS_A); + if (err) + goto out; + + MLX4_GET(adapter->vendor_id, outbox, QUERY_ADAPTER_VENDOR_ID_OFFSET); + MLX4_GET(adapter->device_id, outbox, QUERY_ADAPTER_DEVICE_ID_OFFSET); + MLX4_GET(adapter->revision_id, outbox, QUERY_ADAPTER_REVISION_ID_OFFSET); + MLX4_GET(adapter->inta_pin, outbox, QUERY_ADAPTER_INTA_PIN_OFFSET); + + get_board_id(outbox + QUERY_ADAPTER_VSD_OFFSET / 4, + adapter->board_id); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + +int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) +{ + struct mlx4_cmd_mailbox *mailbox; + __be32 *inbox; + int err; + +#define INIT_HCA_IN_SIZE 0x200 +#define INIT_HCA_VERSION_OFFSET 0x000 +#define INIT_HCA_VERSION 2 +#define INIT_HCA_FLAGS_OFFSET 0x014 +#define INIT_HCA_QPC_OFFSET 0x020 +#define INIT_HCA_QPC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x10) +#define INIT_HCA_LOG_QP_OFFSET (INIT_HCA_QPC_OFFSET + 0x17) +#define INIT_HCA_SRQC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x28) +#define INIT_HCA_LOG_SRQ_OFFSET (INIT_HCA_QPC_OFFSET + 0x2f) +#define INIT_HCA_CQC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x30) +#define INIT_HCA_LOG_CQ_OFFSET (INIT_HCA_QPC_OFFSET + 0x37) +#define INIT_HCA_ALTC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x40) +#define INIT_HCA_AUXC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x50) +#define INIT_HCA_EQC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x60) +#define INIT_HCA_LOG_EQ_OFFSET (INIT_HCA_QPC_OFFSET + 0x67) +#define INIT_HCA_RDMARC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x70) +#define INIT_HCA_LOG_RD_OFFSET (INIT_HCA_QPC_OFFSET + 0x77) +#define INIT_HCA_MCAST_OFFSET 0x0c0 +#define INIT_HCA_MC_BASE_OFFSET (INIT_HCA_MCAST_OFFSET + 0x00) +#define INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x12) +#define INIT_HCA_LOG_MC_HASH_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x16) +#define INIT_HCA_LOG_MC_TABLE_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x1b) +#define INIT_HCA_TPT_OFFSET 0x0f0 +#define INIT_HCA_DMPT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x00) +#define INIT_HCA_LOG_MPT_SZ_OFFSET (INIT_HCA_TPT_OFFSET + 0x0b) +#define INIT_HCA_MTT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x10) +#define INIT_HCA_CMPT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x18) +#define INIT_HCA_UAR_OFFSET 0x120 +#define INIT_HCA_LOG_UAR_SZ_OFFSET (INIT_HCA_UAR_OFFSET + 0x0a) +#define INIT_HCA_UAR_PAGE_SZ_OFFSET (INIT_HCA_UAR_OFFSET + 0x0b) + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; + + memset(inbox, 0, INIT_HCA_IN_SIZE); + + *((u8 *) mailbox->buf + INIT_HCA_VERSION_OFFSET) = INIT_HCA_VERSION; + +#if defined(__LITTLE_ENDIAN) + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) &= ~cpu_to_be32(1 << 1); +#elif defined(__BIG_ENDIAN) + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 1); +#else +#error Host endianness not defined +#endif + /* Check port for UD address vector: */ + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1); + + /* QPC/EEC/CQC/EQC/RDMARC attributes */ + + MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_num_qps, INIT_HCA_LOG_QP_OFFSET); + MLX4_PUT(inbox, param->srqc_base, INIT_HCA_SRQC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_num_srqs, INIT_HCA_LOG_SRQ_OFFSET); + MLX4_PUT(inbox, param->cqc_base, INIT_HCA_CQC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_num_cqs, INIT_HCA_LOG_CQ_OFFSET); + MLX4_PUT(inbox, param->altc_base, INIT_HCA_ALTC_BASE_OFFSET); + MLX4_PUT(inbox, param->auxc_base, INIT_HCA_AUXC_BASE_OFFSET); + MLX4_PUT(inbox, param->eqc_base, INIT_HCA_EQC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_num_eqs, INIT_HCA_LOG_EQ_OFFSET); + MLX4_PUT(inbox, param->rdmarc_base, INIT_HCA_RDMARC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_rd_per_qp, INIT_HCA_LOG_RD_OFFSET); + + /* multicast attributes */ + + MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + + /* TPT attributes */ + + MLX4_PUT(inbox, param->dmpt_base, INIT_HCA_DMPT_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mpt_sz, INIT_HCA_LOG_MPT_SZ_OFFSET); + MLX4_PUT(inbox, param->mtt_base, INIT_HCA_MTT_BASE_OFFSET); + MLX4_PUT(inbox, param->cmpt_base, INIT_HCA_CMPT_BASE_OFFSET); + + /* UAR attributes */ + + MLX4_PUT(inbox, (u8) (PAGE_SHIFT - 12), INIT_HCA_UAR_PAGE_SZ_OFFSET); + MLX4_PUT(inbox, param->log_uar_sz, INIT_HCA_LOG_UAR_SZ_OFFSET); + + err = mlx4_cmd(dev, mailbox->dma, 0, 0, MLX4_CMD_INIT_HCA, 1000); + + if (err) + mlx4_err(dev, "INIT_HCA returns %d\n", err); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + +int mlx4_INIT_PORT(struct mlx4_dev *dev, struct mlx4_init_port_param *param, int port) +{ + struct mlx4_cmd_mailbox *mailbox; + u32 *inbox; + int err; + u32 flags; + +#define INIT_PORT_IN_SIZE 256 +#define INIT_PORT_FLAGS_OFFSET 0x00 +#define INIT_PORT_FLAG_SIG (1 << 18) +#define INIT_PORT_FLAG_NG (1 << 17) +#define INIT_PORT_FLAG_G0 (1 << 16) +#define INIT_PORT_VL_SHIFT 4 +#define INIT_PORT_PORT_WIDTH_SHIFT 8 +#define INIT_PORT_MTU_OFFSET 0x04 +#define INIT_PORT_MAX_GID_OFFSET 0x06 +#define INIT_PORT_MAX_PKEY_OFFSET 0x0a +#define INIT_PORT_GUID0_OFFSET 0x10 +#define INIT_PORT_NODE_GUID_OFFSET 0x18 +#define INIT_PORT_SI_GUID_OFFSET 0x20 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; + + memset(inbox, 0, INIT_PORT_IN_SIZE); + + flags = 0; + flags |= param->set_guid0 ? INIT_PORT_FLAG_G0 : 0; + flags |= param->set_node_guid ? INIT_PORT_FLAG_NG : 0; + flags |= param->set_si_guid ? INIT_PORT_FLAG_SIG : 0; + flags |= (param->vl_cap & 0xf) << INIT_PORT_VL_SHIFT; + flags |= (param->port_width_cap & 0xf) << INIT_PORT_PORT_WIDTH_SHIFT; + MLX4_PUT(inbox, flags, INIT_PORT_FLAGS_OFFSET); + + MLX4_PUT(inbox, param->mtu, INIT_PORT_MTU_OFFSET); + MLX4_PUT(inbox, param->max_gid, INIT_PORT_MAX_GID_OFFSET); + MLX4_PUT(inbox, param->max_pkey, INIT_PORT_MAX_PKEY_OFFSET); + MLX4_PUT(inbox, param->guid0, INIT_PORT_GUID0_OFFSET); + MLX4_PUT(inbox, param->node_guid, INIT_PORT_NODE_GUID_OFFSET); + MLX4_PUT(inbox, param->si_guid, INIT_PORT_SI_GUID_OFFSET); + + err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_INIT_PORT, + MLX4_CMD_TIME_CLASS_A); + + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL_GPL(mlx4_INIT_PORT); + +int mlx4_CLOSE_PORT(struct mlx4_dev *dev, int port) +{ + return mlx4_cmd(dev, 0, port, 0, MLX4_CMD_CLOSE_PORT, 1000); +} +EXPORT_SYMBOL_GPL(mlx4_CLOSE_PORT); + +int mlx4_CLOSE_HCA(struct mlx4_dev *dev, int panic) +{ + return mlx4_cmd(dev, 0, 0, panic, MLX4_CMD_CLOSE_HCA, 1000); +} + +int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages) +{ + int ret = mlx4_cmd_imm(dev, icm_size, aux_pages, 0, 0, + MLX4_CMD_SET_ICM_SIZE, + MLX4_CMD_TIME_CLASS_A); + if (ret) + return ret; + + /* + * Round up number of system pages needed in case + * MLX4_ICM_PAGE_SIZE < PAGE_SIZE. + */ + *aux_pages = ALIGN(*aux_pages, PAGE_SIZE / MLX4_ICM_PAGE_SIZE) >> + (PAGE_SHIFT - MLX4_ICM_PAGE_SHIFT); + + return 0; +} + +int mlx4_NOP(struct mlx4_dev *dev) +{ + /* Input modifier of 0x1f means "finish as soon as possible." */ + return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100); +} diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h new file mode 100644 index 00000000000..2616fa53d4d --- /dev/null +++ b/drivers/net/mlx4/fw.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_FW_H +#define MLX4_FW_H + +#include "mlx4.h" +#include "icm.h" + +struct mlx4_dev_cap { + int max_srq_sz; + int max_qp_sz; + int reserved_qps; + int max_qps; + int reserved_srqs; + int max_srqs; + int max_cq_sz; + int reserved_cqs; + int max_cqs; + int max_mpts; + int reserved_eqs; + int max_eqs; + int reserved_mtts; + int max_mrw_sz; + int reserved_mrws; + int max_mtt_seg; + int max_requester_per_qp; + int max_responder_per_qp; + int max_rdma_global; + int local_ca_ack_delay; + int max_mtu; + int max_port_width; + int max_vl; + int num_ports; + int max_gids; + u16 stat_rate_support; + int max_pkeys; + u32 flags; + int reserved_uars; + int uar_size; + int min_page_sz; + int bf_reg_size; + int bf_regs_per_page; + int max_sq_sg; + int max_sq_desc_sz; + int max_rq_sg; + int max_rq_desc_sz; + int max_qp_per_mcg; + int reserved_mgms; + int max_mcgs; + int reserved_pds; + int max_pds; + int qpc_entry_sz; + int rdmarc_entry_sz; + int altc_entry_sz; + int aux_entry_sz; + int srq_entry_sz; + int cqc_entry_sz; + int eqc_entry_sz; + int dmpt_entry_sz; + int cmpt_entry_sz; + int mtt_entry_sz; + int resize_srq; + u8 bmme_flags; + u32 reserved_lkey; + u64 max_icm_sz; +}; + +struct mlx4_adapter { + u32 vendor_id; + u32 device_id; + u32 revision_id; + char board_id[MLX4_BOARD_ID_LEN]; + u8 inta_pin; +}; + +struct mlx4_init_hca_param { + u64 qpc_base; + u64 rdmarc_base; + u64 auxc_base; + u64 altc_base; + u64 srqc_base; + u64 cqc_base; + u64 eqc_base; + u64 mc_base; + u64 dmpt_base; + u64 cmpt_base; + u64 mtt_base; + u16 log_mc_entry_sz; + u16 log_mc_hash_sz; + u8 log_num_qps; + u8 log_num_srqs; + u8 log_num_cqs; + u8 log_num_eqs; + u8 log_rd_per_qp; + u8 log_mc_table_sz; + u8 log_mpt_sz; + u8 log_uar_sz; +}; + +struct mlx4_init_ib_param { + int port_width; + int vl_cap; + int mtu_cap; + u16 gid_cap; + u16 pkey_cap; + int set_guid0; + u64 guid0; + int set_node_guid; + u64 node_guid; + int set_si_guid; + u64 si_guid; +}; + +struct mlx4_set_ib_param { + int set_si_guid; + int reset_qkey_viol; + u64 si_guid; + u32 cap_mask; +}; + +int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap); +int mlx4_MAP_FA(struct mlx4_dev *dev, struct mlx4_icm *icm); +int mlx4_UNMAP_FA(struct mlx4_dev *dev); +int mlx4_RUN_FW(struct mlx4_dev *dev); +int mlx4_QUERY_FW(struct mlx4_dev *dev); +int mlx4_QUERY_ADAPTER(struct mlx4_dev *dev, struct mlx4_adapter *adapter); +int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param); +int mlx4_CLOSE_HCA(struct mlx4_dev *dev, int panic); +int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt); +int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages); +int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); +int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev); +int mlx4_NOP(struct mlx4_dev *dev); + +#endif /* MLX4_FW_H */ diff --git a/drivers/net/mlx4/icm.c b/drivers/net/mlx4/icm.c new file mode 100644 index 00000000000..e96feaed6ed --- /dev/null +++ b/drivers/net/mlx4/icm.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4.h" +#include "icm.h" +#include "fw.h" + +/* + * We allocate in as big chunks as we can, up to a maximum of 256 KB + * per chunk. + */ +enum { + MLX4_ICM_ALLOC_SIZE = 1 << 18, + MLX4_TABLE_CHUNK_SIZE = 1 << 18 +}; + +void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm) +{ + struct mlx4_icm_chunk *chunk, *tmp; + int i; + + list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) { + if (chunk->nsg > 0) + pci_unmap_sg(dev->pdev, chunk->mem, chunk->npages, + PCI_DMA_BIDIRECTIONAL); + + for (i = 0; i < chunk->npages; ++i) + __free_pages(chunk->mem[i].page, + get_order(chunk->mem[i].length)); + + kfree(chunk); + } + + kfree(icm); +} + +struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, + gfp_t gfp_mask) +{ + struct mlx4_icm *icm; + struct mlx4_icm_chunk *chunk = NULL; + int cur_order; + + icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); + if (!icm) + return icm; + + icm->refcount = 0; + INIT_LIST_HEAD(&icm->chunk_list); + + cur_order = get_order(MLX4_ICM_ALLOC_SIZE); + + while (npages > 0) { + if (!chunk) { + chunk = kmalloc(sizeof *chunk, + gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); + if (!chunk) + goto fail; + + chunk->npages = 0; + chunk->nsg = 0; + list_add_tail(&chunk->list, &icm->chunk_list); + } + + while (1 << cur_order > npages) + --cur_order; + + chunk->mem[chunk->npages].page = alloc_pages(gfp_mask, cur_order); + if (chunk->mem[chunk->npages].page) { + chunk->mem[chunk->npages].length = PAGE_SIZE << cur_order; + chunk->mem[chunk->npages].offset = 0; + + if (++chunk->npages == MLX4_ICM_CHUNK_LEN) { + chunk->nsg = pci_map_sg(dev->pdev, chunk->mem, + chunk->npages, + PCI_DMA_BIDIRECTIONAL); + + if (chunk->nsg <= 0) + goto fail; + + chunk = NULL; + } + + npages -= 1 << cur_order; + } else { + --cur_order; + if (cur_order < 0) + goto fail; + } + } + + if (chunk) { + chunk->nsg = pci_map_sg(dev->pdev, chunk->mem, + chunk->npages, + PCI_DMA_BIDIRECTIONAL); + + if (chunk->nsg <= 0) + goto fail; + } + + return icm; + +fail: + mlx4_free_icm(dev, icm); + return NULL; +} + +static int mlx4_MAP_ICM(struct mlx4_dev *dev, struct mlx4_icm *icm, u64 virt) +{ + return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM, icm, virt); +} + +int mlx4_UNMAP_ICM(struct mlx4_dev *dev, u64 virt, u32 page_count) +{ + return mlx4_cmd(dev, virt, page_count, 0, MLX4_CMD_UNMAP_ICM, + MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_MAP_ICM_page(struct mlx4_dev *dev, u64 dma_addr, u64 virt) +{ + struct mlx4_cmd_mailbox *mailbox; + __be64 *inbox; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; + + inbox[0] = cpu_to_be64(virt); + inbox[1] = cpu_to_be64(dma_addr); + + err = mlx4_cmd(dev, mailbox->dma, 1, 0, MLX4_CMD_MAP_ICM, + MLX4_CMD_TIME_CLASS_B); + + mlx4_free_cmd_mailbox(dev, mailbox); + + if (!err) + mlx4_dbg(dev, "Mapped page at %llx to %llx for ICM.\n", + (unsigned long long) dma_addr, (unsigned long long) virt); + + return err; +} + +int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm) +{ + return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM_AUX, icm, -1); +} + +int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev) +{ + return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_UNMAP_ICM_AUX, MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj) +{ + int i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size); + int ret = 0; + + mutex_lock(&table->mutex); + + if (table->icm[i]) { + ++table->icm[i]->refcount; + goto out; + } + + table->icm[i] = mlx4_alloc_icm(dev, MLX4_TABLE_CHUNK_SIZE >> PAGE_SHIFT, + (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) | + __GFP_NOWARN); + if (!table->icm[i]) { + ret = -ENOMEM; + goto out; + } + + if (mlx4_MAP_ICM(dev, table->icm[i], table->virt + + (u64) i * MLX4_TABLE_CHUNK_SIZE)) { + mlx4_free_icm(dev, table->icm[i]); + table->icm[i] = NULL; + ret = -ENOMEM; + goto out; + } + + ++table->icm[i]->refcount; + +out: + mutex_unlock(&table->mutex); + return ret; +} + +void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj) +{ + int i; + + i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size); + + mutex_lock(&table->mutex); + + if (--table->icm[i]->refcount == 0) { + mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE, + MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); + mlx4_free_icm(dev, table->icm[i]); + table->icm[i] = NULL; + } + + mutex_unlock(&table->mutex); +} + +void *mlx4_table_find(struct mlx4_icm_table *table, int obj) +{ + int idx, offset, i; + struct mlx4_icm_chunk *chunk; + struct mlx4_icm *icm; + struct page *page = NULL; + + if (!table->lowmem) + return NULL; + + mutex_lock(&table->mutex); + + idx = obj & (table->num_obj - 1); + icm = table->icm[idx / (MLX4_TABLE_CHUNK_SIZE / table->obj_size)]; + offset = idx % (MLX4_TABLE_CHUNK_SIZE / table->obj_size); + + if (!icm) + goto out; + + list_for_each_entry(chunk, &icm->chunk_list, list) { + for (i = 0; i < chunk->npages; ++i) { + if (chunk->mem[i].length > offset) { + page = chunk->mem[i].page; + goto out; + } + offset -= chunk->mem[i].length; + } + } + +out: + mutex_unlock(&table->mutex); + return page ? lowmem_page_address(page) + offset : NULL; +} + +int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end) +{ + int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size; + int i, err; + + for (i = start; i <= end; i += inc) { + err = mlx4_table_get(dev, table, i); + if (err) + goto fail; + } + + return 0; + +fail: + while (i > start) { + i -= inc; + mlx4_table_put(dev, table, i); + } + + return err; +} + +void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end) +{ + int i; + + for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size) + mlx4_table_put(dev, table, i); +} + +int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, + u64 virt, int obj_size, int nobj, int reserved, + int use_lowmem) +{ + int obj_per_chunk; + int num_icm; + unsigned chunk_size; + int i; + + obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size; + num_icm = (nobj + obj_per_chunk - 1) / obj_per_chunk; + + table->icm = kcalloc(num_icm, sizeof *table->icm, GFP_KERNEL); + if (!table->icm) + return -ENOMEM; + table->virt = virt; + table->num_icm = num_icm; + table->num_obj = nobj; + table->obj_size = obj_size; + table->lowmem = use_lowmem; + mutex_init(&table->mutex); + + for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) { + chunk_size = MLX4_TABLE_CHUNK_SIZE; + if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > nobj * obj_size) + chunk_size = PAGE_ALIGN(nobj * obj_size - i * MLX4_TABLE_CHUNK_SIZE); + + table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT, + (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) | + __GFP_NOWARN); + if (!table->icm[i]) + goto err; + if (mlx4_MAP_ICM(dev, table->icm[i], virt + i * MLX4_TABLE_CHUNK_SIZE)) { + mlx4_free_icm(dev, table->icm[i]); + table->icm[i] = NULL; + goto err; + } + + /* + * Add a reference to this ICM chunk so that it never + * gets freed (since it contains reserved firmware objects). + */ + ++table->icm[i]->refcount; + } + + return 0; + +err: + for (i = 0; i < num_icm; ++i) + if (table->icm[i]) { + mlx4_UNMAP_ICM(dev, virt + i * MLX4_TABLE_CHUNK_SIZE, + MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); + mlx4_free_icm(dev, table->icm[i]); + } + + return -ENOMEM; +} + +void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table) +{ + int i; + + for (i = 0; i < table->num_icm; ++i) + if (table->icm[i]) { + mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE, + MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); + mlx4_free_icm(dev, table->icm[i]); + } + + kfree(table->icm); +} diff --git a/drivers/net/mlx4/icm.h b/drivers/net/mlx4/icm.h new file mode 100644 index 00000000000..bea223d879a --- /dev/null +++ b/drivers/net/mlx4/icm.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_ICM_H +#define MLX4_ICM_H + +#include +#include +#include + +#define MLX4_ICM_CHUNK_LEN \ + ((256 - sizeof (struct list_head) - 2 * sizeof (int)) / \ + (sizeof (struct scatterlist))) + +enum { + MLX4_ICM_PAGE_SHIFT = 12, + MLX4_ICM_PAGE_SIZE = 1 << MLX4_ICM_PAGE_SHIFT, +}; + +struct mlx4_icm_chunk { + struct list_head list; + int npages; + int nsg; + struct scatterlist mem[MLX4_ICM_CHUNK_LEN]; +}; + +struct mlx4_icm { + struct list_head chunk_list; + int refcount; +}; + +struct mlx4_icm_iter { + struct mlx4_icm *icm; + struct mlx4_icm_chunk *chunk; + int page_idx; +}; + +struct mlx4_dev; + +struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, gfp_t gfp_mask); +void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm); + +int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj); +void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj); +int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end); +void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end); +int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, + u64 virt, int obj_size, int nobj, int reserved, + int use_lowmem); +void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table); +int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj); +void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj); +void *mlx4_table_find(struct mlx4_icm_table *table, int obj); +int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end); +void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, + int start, int end); + +static inline void mlx4_icm_first(struct mlx4_icm *icm, + struct mlx4_icm_iter *iter) +{ + iter->icm = icm; + iter->chunk = list_empty(&icm->chunk_list) ? + NULL : list_entry(icm->chunk_list.next, + struct mlx4_icm_chunk, list); + iter->page_idx = 0; +} + +static inline int mlx4_icm_last(struct mlx4_icm_iter *iter) +{ + return !iter->chunk; +} + +static inline void mlx4_icm_next(struct mlx4_icm_iter *iter) +{ + if (++iter->page_idx >= iter->chunk->nsg) { + if (iter->chunk->list.next == &iter->icm->chunk_list) { + iter->chunk = NULL; + return; + } + + iter->chunk = list_entry(iter->chunk->list.next, + struct mlx4_icm_chunk, list); + iter->page_idx = 0; + } +} + +static inline dma_addr_t mlx4_icm_addr(struct mlx4_icm_iter *iter) +{ + return sg_dma_address(&iter->chunk->mem[iter->page_idx]); +} + +static inline unsigned long mlx4_icm_size(struct mlx4_icm_iter *iter) +{ + return sg_dma_len(&iter->chunk->mem[iter->page_idx]); +} + +int mlx4_UNMAP_ICM(struct mlx4_dev *dev, u64 virt, u32 page_count); +int mlx4_MAP_ICM_page(struct mlx4_dev *dev, u64 dma_addr, u64 virt); +int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); +int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev); + +#endif /* MLX4_ICM_H */ diff --git a/drivers/net/mlx4/intf.c b/drivers/net/mlx4/intf.c new file mode 100644 index 00000000000..65854f9e9c7 --- /dev/null +++ b/drivers/net/mlx4/intf.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "mlx4.h" + +struct mlx4_device_context { + struct list_head list; + struct mlx4_interface *intf; + void *context; +}; + +static LIST_HEAD(intf_list); +static LIST_HEAD(dev_list); +static DEFINE_MUTEX(intf_mutex); + +static void mlx4_add_device(struct mlx4_interface *intf, struct mlx4_priv *priv) +{ + struct mlx4_device_context *dev_ctx; + + dev_ctx = kmalloc(sizeof *dev_ctx, GFP_KERNEL); + if (!dev_ctx) + return; + + dev_ctx->intf = intf; + dev_ctx->context = intf->add(&priv->dev); + + if (dev_ctx->context) { + spin_lock_irq(&priv->ctx_lock); + list_add_tail(&dev_ctx->list, &priv->ctx_list); + spin_unlock_irq(&priv->ctx_lock); + } else + kfree(dev_ctx); +} + +static void mlx4_remove_device(struct mlx4_interface *intf, struct mlx4_priv *priv) +{ + struct mlx4_device_context *dev_ctx; + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf == intf) { + spin_lock_irq(&priv->ctx_lock); + list_del(&dev_ctx->list); + spin_unlock_irq(&priv->ctx_lock); + + intf->remove(&priv->dev, dev_ctx->context); + kfree(dev_ctx); + return; + } +} + +int mlx4_register_interface(struct mlx4_interface *intf) +{ + struct mlx4_priv *priv; + + if (!intf->add || !intf->remove) + return -EINVAL; + + mutex_lock(&intf_mutex); + + list_add_tail(&intf->list, &intf_list); + list_for_each_entry(priv, &dev_list, dev_list) + mlx4_add_device(intf, priv); + + mutex_unlock(&intf_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_register_interface); + +void mlx4_unregister_interface(struct mlx4_interface *intf) +{ + struct mlx4_priv *priv; + + mutex_lock(&intf_mutex); + + list_for_each_entry(priv, &dev_list, dev_list) + mlx4_remove_device(intf, priv); + + list_del(&intf->list); + + mutex_unlock(&intf_mutex); +} +EXPORT_SYMBOL_GPL(mlx4_unregister_interface); + +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_event type, + int subtype, int port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_device_context *dev_ctx; + unsigned long flags; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf->event) + dev_ctx->intf->event(dev, dev_ctx->context, type, + subtype, port); + + spin_unlock_irqrestore(&priv->ctx_lock, flags); +} + +int mlx4_register_device(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_interface *intf; + + INIT_LIST_HEAD(&priv->ctx_list); + spin_lock_init(&priv->ctx_lock); + + mutex_lock(&intf_mutex); + + list_add_tail(&priv->dev_list, &dev_list); + list_for_each_entry(intf, &intf_list, list) + mlx4_add_device(intf, priv); + + mutex_unlock(&intf_mutex); + + return 0; +} + +void mlx4_unregister_device(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_interface *intf; + + mutex_lock(&intf_mutex); + + list_for_each_entry(intf, &intf_list, list) + mlx4_remove_device(intf, priv); + + list_del(&priv->dev_list); + + mutex_unlock(&intf_mutex); +} diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c new file mode 100644 index 00000000000..4debb024eaf --- /dev/null +++ b/drivers/net/mlx4/main.c @@ -0,0 +1,936 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "mlx4.h" +#include "fw.h" +#include "icm.h" + +MODULE_AUTHOR("Roland Dreier"); +MODULE_DESCRIPTION("Mellanox ConnectX HCA low-level driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); + +#ifdef CONFIG_MLX4_DEBUG + +int mlx4_debug_level = 0; +module_param_named(debug_level, mlx4_debug_level, int, 0644); +MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0"); + +#endif /* CONFIG_MLX4_DEBUG */ + +#ifdef CONFIG_PCI_MSI + +static int msi_x; +module_param(msi_x, int, 0444); +MODULE_PARM_DESC(msi_x, "attempt to use MSI-X if nonzero"); + +#else /* CONFIG_PCI_MSI */ + +#define msi_x (0) + +#endif /* CONFIG_PCI_MSI */ + +static const char mlx4_version[] __devinitdata = + DRV_NAME ": Mellanox ConnectX core driver v" + DRV_VERSION " (" DRV_RELDATE ")\n"; + +static struct mlx4_profile default_profile = { + .num_qp = 1 << 16, + .num_srq = 1 << 16, + .rdmarc_per_qp = 4, + .num_cq = 1 << 16, + .num_mcg = 1 << 13, + .num_mpt = 1 << 17, + .num_mtt = 1 << 20, +}; + +static int __devinit mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) +{ + int err; + + err = mlx4_QUERY_DEV_CAP(dev, dev_cap); + if (err) { + mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n"); + return err; + } + + if (dev_cap->min_page_sz > PAGE_SIZE) { + mlx4_err(dev, "HCA minimum page size of %d bigger than " + "kernel PAGE_SIZE of %ld, aborting.\n", + dev_cap->min_page_sz, PAGE_SIZE); + return -ENODEV; + } + if (dev_cap->num_ports > MLX4_MAX_PORTS) { + mlx4_err(dev, "HCA has %d ports, but we only support %d, " + "aborting.\n", + dev_cap->num_ports, MLX4_MAX_PORTS); + return -ENODEV; + } + + if (dev_cap->uar_size > pci_resource_len(dev->pdev, 2)) { + mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than " + "PCI resource 2 size of 0x%llx, aborting.\n", + dev_cap->uar_size, + (unsigned long long) pci_resource_len(dev->pdev, 2)); + return -ENODEV; + } + + dev->caps.num_ports = dev_cap->num_ports; + dev->caps.num_uars = dev_cap->uar_size / PAGE_SIZE; + dev->caps.vl_cap = dev_cap->max_vl; + dev->caps.mtu_cap = dev_cap->max_mtu; + dev->caps.gid_table_len = dev_cap->max_gids; + dev->caps.pkey_table_len = dev_cap->max_pkeys; + dev->caps.local_ca_ack_delay = dev_cap->local_ca_ack_delay; + dev->caps.bf_reg_size = dev_cap->bf_reg_size; + dev->caps.bf_regs_per_page = dev_cap->bf_regs_per_page; + dev->caps.max_sq_sg = dev_cap->max_sq_sg; + dev->caps.max_rq_sg = dev_cap->max_rq_sg; + dev->caps.max_wqes = dev_cap->max_qp_sz; + dev->caps.max_qp_init_rdma = dev_cap->max_requester_per_qp; + dev->caps.reserved_qps = dev_cap->reserved_qps; + dev->caps.max_srq_wqes = dev_cap->max_srq_sz; + dev->caps.max_srq_sge = dev_cap->max_rq_sg - 1; + dev->caps.reserved_srqs = dev_cap->reserved_srqs; + dev->caps.max_sq_desc_sz = dev_cap->max_sq_desc_sz; + dev->caps.max_rq_desc_sz = dev_cap->max_rq_desc_sz; + dev->caps.num_qp_per_mgm = MLX4_QP_PER_MGM; + /* + * Subtract 1 from the limit because we need to allocate a + * spare CQE so the HCA HW can tell the difference between an + * empty CQ and a full CQ. + */ + dev->caps.max_cqes = dev_cap->max_cq_sz - 1; + dev->caps.reserved_cqs = dev_cap->reserved_cqs; + dev->caps.reserved_eqs = dev_cap->reserved_eqs; + dev->caps.reserved_mtts = dev_cap->reserved_mtts; + dev->caps.reserved_mrws = dev_cap->reserved_mrws; + dev->caps.reserved_uars = dev_cap->reserved_uars; + dev->caps.reserved_pds = dev_cap->reserved_pds; + dev->caps.port_width_cap = dev_cap->max_port_width; + dev->caps.mtt_entry_sz = MLX4_MTT_ENTRY_PER_SEG * dev_cap->mtt_entry_sz; + dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1); + dev->caps.flags = dev_cap->flags; + dev->caps.stat_rate_support = dev_cap->stat_rate_support; + + return 0; +} + +static int __devinit mlx4_load_fw(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + priv->fw.fw_icm = mlx4_alloc_icm(dev, priv->fw.fw_pages, + GFP_HIGHUSER | __GFP_NOWARN); + if (!priv->fw.fw_icm) { + mlx4_err(dev, "Couldn't allocate FW area, aborting.\n"); + return -ENOMEM; + } + + err = mlx4_MAP_FA(dev, priv->fw.fw_icm); + if (err) { + mlx4_err(dev, "MAP_FA command failed, aborting.\n"); + goto err_free; + } + + err = mlx4_RUN_FW(dev); + if (err) { + mlx4_err(dev, "RUN_FW command failed, aborting.\n"); + goto err_unmap_fa; + } + + return 0; + +err_unmap_fa: + mlx4_UNMAP_FA(dev); + +err_free: + mlx4_free_icm(dev, priv->fw.fw_icm); + return err; +} + +static int __devinit mlx4_init_cmpt_table(struct mlx4_dev *dev, u64 cmpt_base, + int cmpt_entry_sz) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + err = mlx4_init_icm_table(dev, &priv->qp_table.cmpt_table, + cmpt_base + + ((u64) (MLX4_CMPT_TYPE_QP * + cmpt_entry_sz) << MLX4_CMPT_SHIFT), + cmpt_entry_sz, dev->caps.num_qps, + dev->caps.reserved_qps, 0); + if (err) + goto err; + + err = mlx4_init_icm_table(dev, &priv->srq_table.cmpt_table, + cmpt_base + + ((u64) (MLX4_CMPT_TYPE_SRQ * + cmpt_entry_sz) << MLX4_CMPT_SHIFT), + cmpt_entry_sz, dev->caps.num_srqs, + dev->caps.reserved_srqs, 0); + if (err) + goto err_qp; + + err = mlx4_init_icm_table(dev, &priv->cq_table.cmpt_table, + cmpt_base + + ((u64) (MLX4_CMPT_TYPE_CQ * + cmpt_entry_sz) << MLX4_CMPT_SHIFT), + cmpt_entry_sz, dev->caps.num_cqs, + dev->caps.reserved_cqs, 0); + if (err) + goto err_srq; + + err = mlx4_init_icm_table(dev, &priv->eq_table.cmpt_table, + cmpt_base + + ((u64) (MLX4_CMPT_TYPE_EQ * + cmpt_entry_sz) << MLX4_CMPT_SHIFT), + cmpt_entry_sz, + roundup_pow_of_two(MLX4_NUM_EQ + + dev->caps.reserved_eqs), + MLX4_NUM_EQ + dev->caps.reserved_eqs, 0); + if (err) + goto err_cq; + + return 0; + +err_cq: + mlx4_cleanup_icm_table(dev, &priv->cq_table.cmpt_table); + +err_srq: + mlx4_cleanup_icm_table(dev, &priv->srq_table.cmpt_table); + +err_qp: + mlx4_cleanup_icm_table(dev, &priv->qp_table.cmpt_table); + +err: + return err; +} + +static int __devinit mlx4_init_icm(struct mlx4_dev *dev, + struct mlx4_dev_cap *dev_cap, + struct mlx4_init_hca_param *init_hca, + u64 icm_size) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + u64 aux_pages; + int err; + + err = mlx4_SET_ICM_SIZE(dev, icm_size, &aux_pages); + if (err) { + mlx4_err(dev, "SET_ICM_SIZE command failed, aborting.\n"); + return err; + } + + mlx4_dbg(dev, "%lld KB of HCA context requires %lld KB aux memory.\n", + (unsigned long long) icm_size >> 10, + (unsigned long long) aux_pages << 2); + + priv->fw.aux_icm = mlx4_alloc_icm(dev, aux_pages, + GFP_HIGHUSER | __GFP_NOWARN); + if (!priv->fw.aux_icm) { + mlx4_err(dev, "Couldn't allocate aux memory, aborting.\n"); + return -ENOMEM; + } + + err = mlx4_MAP_ICM_AUX(dev, priv->fw.aux_icm); + if (err) { + mlx4_err(dev, "MAP_ICM_AUX command failed, aborting.\n"); + goto err_free_aux; + } + + err = mlx4_init_cmpt_table(dev, init_hca->cmpt_base, dev_cap->cmpt_entry_sz); + if (err) { + mlx4_err(dev, "Failed to map cMPT context memory, aborting.\n"); + goto err_unmap_aux; + } + + err = mlx4_map_eq_icm(dev, init_hca->eqc_base); + if (err) { + mlx4_err(dev, "Failed to map EQ context memory, aborting.\n"); + goto err_unmap_cmpt; + } + + err = mlx4_init_icm_table(dev, &priv->mr_table.mtt_table, + init_hca->mtt_base, + dev->caps.mtt_entry_sz, + dev->caps.num_mtt_segs, + dev->caps.reserved_mtts, 1); + if (err) { + mlx4_err(dev, "Failed to map MTT context memory, aborting.\n"); + goto err_unmap_eq; + } + + err = mlx4_init_icm_table(dev, &priv->mr_table.dmpt_table, + init_hca->dmpt_base, + dev_cap->dmpt_entry_sz, + dev->caps.num_mpts, + dev->caps.reserved_mrws, 1); + if (err) { + mlx4_err(dev, "Failed to map dMPT context memory, aborting.\n"); + goto err_unmap_mtt; + } + + err = mlx4_init_icm_table(dev, &priv->qp_table.qp_table, + init_hca->qpc_base, + dev_cap->qpc_entry_sz, + dev->caps.num_qps, + dev->caps.reserved_qps, 0); + if (err) { + mlx4_err(dev, "Failed to map QP context memory, aborting.\n"); + goto err_unmap_dmpt; + } + + err = mlx4_init_icm_table(dev, &priv->qp_table.auxc_table, + init_hca->auxc_base, + dev_cap->aux_entry_sz, + dev->caps.num_qps, + dev->caps.reserved_qps, 0); + if (err) { + mlx4_err(dev, "Failed to map AUXC context memory, aborting.\n"); + goto err_unmap_qp; + } + + err = mlx4_init_icm_table(dev, &priv->qp_table.altc_table, + init_hca->altc_base, + dev_cap->altc_entry_sz, + dev->caps.num_qps, + dev->caps.reserved_qps, 0); + if (err) { + mlx4_err(dev, "Failed to map ALTC context memory, aborting.\n"); + goto err_unmap_auxc; + } + + err = mlx4_init_icm_table(dev, &priv->qp_table.rdmarc_table, + init_hca->rdmarc_base, + dev_cap->rdmarc_entry_sz << priv->qp_table.rdmarc_shift, + dev->caps.num_qps, + dev->caps.reserved_qps, 0); + if (err) { + mlx4_err(dev, "Failed to map RDMARC context memory, aborting\n"); + goto err_unmap_altc; + } + + err = mlx4_init_icm_table(dev, &priv->cq_table.table, + init_hca->cqc_base, + dev_cap->cqc_entry_sz, + dev->caps.num_cqs, + dev->caps.reserved_cqs, 0); + if (err) { + mlx4_err(dev, "Failed to map CQ context memory, aborting.\n"); + goto err_unmap_rdmarc; + } + + err = mlx4_init_icm_table(dev, &priv->srq_table.table, + init_hca->srqc_base, + dev_cap->srq_entry_sz, + dev->caps.num_srqs, + dev->caps.reserved_srqs, 0); + if (err) { + mlx4_err(dev, "Failed to map SRQ context memory, aborting.\n"); + goto err_unmap_cq; + } + + /* + * It's not strictly required, but for simplicity just map the + * whole multicast group table now. The table isn't very big + * and it's a lot easier than trying to track ref counts. + */ + err = mlx4_init_icm_table(dev, &priv->mcg_table.table, + init_hca->mc_base, MLX4_MGM_ENTRY_SIZE, + dev->caps.num_mgms + dev->caps.num_amgms, + dev->caps.num_mgms + dev->caps.num_amgms, + 0); + if (err) { + mlx4_err(dev, "Failed to map MCG context memory, aborting.\n"); + goto err_unmap_srq; + } + + return 0; + +err_unmap_srq: + mlx4_cleanup_icm_table(dev, &priv->srq_table.table); + +err_unmap_cq: + mlx4_cleanup_icm_table(dev, &priv->cq_table.table); + +err_unmap_rdmarc: + mlx4_cleanup_icm_table(dev, &priv->qp_table.rdmarc_table); + +err_unmap_altc: + mlx4_cleanup_icm_table(dev, &priv->qp_table.altc_table); + +err_unmap_auxc: + mlx4_cleanup_icm_table(dev, &priv->qp_table.auxc_table); + +err_unmap_qp: + mlx4_cleanup_icm_table(dev, &priv->qp_table.qp_table); + +err_unmap_dmpt: + mlx4_cleanup_icm_table(dev, &priv->mr_table.dmpt_table); + +err_unmap_mtt: + mlx4_cleanup_icm_table(dev, &priv->mr_table.mtt_table); + +err_unmap_eq: + mlx4_unmap_eq_icm(dev); + +err_unmap_cmpt: + 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); + +err_unmap_aux: + mlx4_UNMAP_ICM_AUX(dev); + +err_free_aux: + mlx4_free_icm(dev, priv->fw.aux_icm); + + return err; +} + +static void mlx4_free_icms(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + mlx4_cleanup_icm_table(dev, &priv->mcg_table.table); + mlx4_cleanup_icm_table(dev, &priv->srq_table.table); + mlx4_cleanup_icm_table(dev, &priv->cq_table.table); + mlx4_cleanup_icm_table(dev, &priv->qp_table.rdmarc_table); + mlx4_cleanup_icm_table(dev, &priv->qp_table.altc_table); + mlx4_cleanup_icm_table(dev, &priv->qp_table.auxc_table); + 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.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); +} + +static void mlx4_close_hca(struct mlx4_dev *dev) +{ + mlx4_CLOSE_HCA(dev, 0); + mlx4_free_icms(dev); + mlx4_UNMAP_FA(dev); + mlx4_free_icm(dev, mlx4_priv(dev)->fw.fw_icm); +} + +static int __devinit mlx4_init_hca(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_adapter adapter; + struct mlx4_dev_cap dev_cap; + struct mlx4_profile profile; + struct mlx4_init_hca_param init_hca; + u64 icm_size; + int err; + + err = mlx4_QUERY_FW(dev); + if (err) { + mlx4_err(dev, "QUERY_FW command failed, aborting.\n"); + return err; + } + + err = mlx4_load_fw(dev); + if (err) { + mlx4_err(dev, "Failed to start FW, aborting.\n"); + return err; + } + + err = mlx4_dev_cap(dev, &dev_cap); + if (err) { + mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n"); + goto err_stop_fw; + } + + profile = default_profile; + + icm_size = mlx4_make_profile(dev, &profile, &dev_cap, &init_hca); + if ((long long) icm_size < 0) { + err = icm_size; + goto err_stop_fw; + } + + init_hca.log_uar_sz = ilog2(dev->caps.num_uars); + + err = mlx4_init_icm(dev, &dev_cap, &init_hca, icm_size); + if (err) + goto err_stop_fw; + + err = mlx4_INIT_HCA(dev, &init_hca); + if (err) { + mlx4_err(dev, "INIT_HCA command failed, aborting.\n"); + goto err_free_icm; + } + + err = mlx4_QUERY_ADAPTER(dev, &adapter); + if (err) { + mlx4_err(dev, "QUERY_ADAPTER command failed, aborting.\n"); + goto err_close; + } + + priv->eq_table.inta_pin = adapter.inta_pin; + priv->rev_id = adapter.revision_id; + memcpy(priv->board_id, adapter.board_id, sizeof priv->board_id); + + return 0; + +err_close: + mlx4_close_hca(dev); + +err_free_icm: + mlx4_free_icms(dev); + +err_stop_fw: + mlx4_UNMAP_FA(dev); + mlx4_free_icm(dev, priv->fw.fw_icm); + + return err; +} + +static int __devinit mlx4_setup_hca(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + MLX4_INIT_DOORBELL_LOCK(&priv->doorbell_lock); + + err = mlx4_init_uar_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "user access region table, aborting.\n"); + return err; + } + + err = mlx4_uar_alloc(dev, &priv->driver_uar); + if (err) { + mlx4_err(dev, "Failed to allocate driver access region, " + "aborting.\n"); + goto err_uar_table_free; + } + + priv->kar = ioremap(priv->driver_uar.pfn << PAGE_SHIFT, PAGE_SIZE); + if (!priv->kar) { + mlx4_err(dev, "Couldn't map kernel access region, " + "aborting.\n"); + err = -ENOMEM; + goto err_uar_free; + } + + err = mlx4_init_pd_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "protection domain table, aborting.\n"); + goto err_kar_unmap; + } + + err = mlx4_init_mr_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "memory region table, aborting.\n"); + goto err_pd_table_free; + } + + mlx4_map_catas_buf(dev); + + err = mlx4_init_eq_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "event queue table, aborting.\n"); + goto err_catas_buf; + } + + err = mlx4_cmd_use_events(dev); + if (err) { + mlx4_err(dev, "Failed to switch to event-driven " + "firmware commands, aborting.\n"); + goto err_eq_table_free; + } + + err = mlx4_NOP(dev); + if (err) { + mlx4_err(dev, "NOP command failed to generate interrupt " + "(IRQ %d), aborting.\n", + priv->eq_table.eq[MLX4_EQ_ASYNC].irq); + if (dev->flags & MLX4_FLAG_MSI_X) + mlx4_err(dev, "Try again with MSI-X disabled.\n"); + else + mlx4_err(dev, "BIOS or ACPI interrupt routing problem?\n"); + + goto err_cmd_poll; + } + + mlx4_dbg(dev, "NOP command IRQ test passed\n"); + + err = mlx4_init_cq_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "completion queue table, aborting.\n"); + goto err_cmd_poll; + } + + err = mlx4_init_srq_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "shared receive queue table, aborting.\n"); + goto err_cq_table_free; + } + + err = mlx4_init_qp_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "queue pair table, aborting.\n"); + goto err_srq_table_free; + } + + err = mlx4_init_mcg_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize " + "multicast group table, aborting.\n"); + goto err_qp_table_free; + } + + return 0; + +err_qp_table_free: + mlx4_cleanup_qp_table(dev); + +err_srq_table_free: + mlx4_cleanup_srq_table(dev); + +err_cq_table_free: + mlx4_cleanup_cq_table(dev); + +err_cmd_poll: + mlx4_cmd_use_polling(dev); + +err_eq_table_free: + mlx4_cleanup_eq_table(dev); + +err_catas_buf: + mlx4_unmap_catas_buf(dev); + mlx4_cleanup_mr_table(dev); + +err_pd_table_free: + mlx4_cleanup_pd_table(dev); + +err_kar_unmap: + iounmap(priv->kar); + +err_uar_free: + mlx4_uar_free(dev, &priv->driver_uar); + +err_uar_table_free: + mlx4_cleanup_uar_table(dev); + return err; +} + +static void __devinit mlx4_enable_msi_x(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct msix_entry entries[MLX4_NUM_EQ]; + int err; + int i; + + if (msi_x) { + for (i = 0; i < MLX4_NUM_EQ; ++i) + entries[i].entry = i; + + err = pci_enable_msix(dev->pdev, entries, ARRAY_SIZE(entries)); + if (err) { + if (err > 0) + mlx4_info(dev, "Only %d MSI-X vectors available, " + "not using MSI-X\n", err); + goto no_msi; + } + + for (i = 0; i < MLX4_NUM_EQ; ++i) + priv->eq_table.eq[i].irq = entries[i].vector; + + dev->flags |= MLX4_FLAG_MSI_X; + return; + } + +no_msi: + for (i = 0; i < MLX4_NUM_EQ; ++i) + priv->eq_table.eq[i].irq = dev->pdev->irq; +} + +static int __devinit mlx4_init_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + static int mlx4_version_printed; + struct mlx4_priv *priv; + struct mlx4_dev *dev; + int err; + + if (!mlx4_version_printed) { + printk(KERN_INFO "%s", mlx4_version); + ++mlx4_version_printed; + } + + printk(KERN_INFO PFX "Initializing %s\n", + pci_name(pdev)); + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Cannot enable PCI device, " + "aborting.\n"); + return err; + } + + /* + * Check for BARs. We expect 0: 1MB, 2: 8MB, 4: DDR (may not + * be present) + */ + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || + pci_resource_len(pdev, 0) != 1 << 20) { + dev_err(&pdev->dev, "Missing DCS, aborting.\n"); + err = -ENODEV; + goto err_disable_pdev; + } + if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "Missing UAR, aborting.\n"); + err = -ENODEV; + goto err_disable_pdev; + } + + err = pci_request_region(pdev, 0, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "Cannot request control region, 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_64BIT_MASK); + if (err) { + dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n"); + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n"); + goto err_release_bar2; + } + } + err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (err) { + dev_warn(&pdev->dev, "Warning: couldn't set 64-bit " + "consistent PCI DMA mask.\n"); + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, " + "aborting.\n"); + goto err_release_bar2; + } + } + + priv = kzalloc(sizeof *priv, GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "Device struct alloc failed, " + "aborting.\n"); + err = -ENOMEM; + goto err_release_bar2; + } + + dev = &priv->dev; + dev->pdev = pdev; + + /* + * Now reset the HCA before we touch the PCI capabilities or + * attempt a firmware command, since a boot ROM may have left + * the HCA in an undefined state. + */ + err = mlx4_reset(dev); + if (err) { + mlx4_err(dev, "Failed to reset HCA, aborting.\n"); + goto err_free_dev; + } + + mlx4_enable_msi_x(dev); + + if (mlx4_cmd_init(dev)) { + mlx4_err(dev, "Failed to init command interface, aborting.\n"); + goto err_free_dev; + } + + err = mlx4_init_hca(dev); + if (err) + goto err_cmd; + + err = mlx4_setup_hca(dev); + if (err) + goto err_close; + + err = mlx4_register_device(dev); + if (err) + goto err_cleanup; + + pci_set_drvdata(pdev, dev); + + return 0; + +err_cleanup: + mlx4_cleanup_mcg_table(dev); + mlx4_cleanup_qp_table(dev); + mlx4_cleanup_srq_table(dev); + mlx4_cleanup_cq_table(dev); + mlx4_cmd_use_polling(dev); + mlx4_cleanup_eq_table(dev); + + mlx4_unmap_catas_buf(dev); + + mlx4_cleanup_mr_table(dev); + mlx4_cleanup_pd_table(dev); + mlx4_cleanup_uar_table(dev); + +err_close: + mlx4_close_hca(dev); + +err_cmd: + mlx4_cmd_cleanup(dev); + +err_free_dev: + if (dev->flags & MLX4_FLAG_MSI_X) + pci_disable_msix(pdev); + + kfree(priv); + +err_release_bar2: + pci_release_region(pdev, 2); + +err_release_bar0: + pci_release_region(pdev, 0); + +err_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void __devexit mlx4_remove_one(struct pci_dev *pdev) +{ + struct mlx4_dev *dev = pci_get_drvdata(pdev); + struct mlx4_priv *priv = mlx4_priv(dev); + int p; + + if (dev) { + mlx4_unregister_device(dev); + + for (p = 1; p <= dev->caps.num_ports; ++p) + mlx4_CLOSE_PORT(dev, p); + + mlx4_cleanup_mcg_table(dev); + mlx4_cleanup_qp_table(dev); + mlx4_cleanup_srq_table(dev); + mlx4_cleanup_cq_table(dev); + mlx4_cmd_use_polling(dev); + mlx4_cleanup_eq_table(dev); + + mlx4_unmap_catas_buf(dev); + + mlx4_cleanup_mr_table(dev); + mlx4_cleanup_pd_table(dev); + + iounmap(priv->kar); + mlx4_uar_free(dev, &priv->driver_uar); + mlx4_cleanup_uar_table(dev); + mlx4_close_hca(dev); + mlx4_cmd_cleanup(dev); + + if (dev->flags & MLX4_FLAG_MSI_X) + pci_disable_msix(pdev); + + kfree(priv); + pci_release_region(pdev, 2); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + } +} + +static struct pci_device_id mlx4_pci_table[] = { + { PCI_VDEVICE(MELLANOX, 0x6340) }, /* MT25408 "Hermon" SDR */ + { PCI_VDEVICE(MELLANOX, 0x634a) }, /* MT25408 "Hermon" DDR */ + { PCI_VDEVICE(MELLANOX, 0x6354) }, /* MT25408 "Hermon" QDR */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, mlx4_pci_table); + +static struct pci_driver mlx4_driver = { + .name = DRV_NAME, + .id_table = mlx4_pci_table, + .probe = mlx4_init_one, + .remove = __devexit_p(mlx4_remove_one) +}; + +static int __init mlx4_init(void) +{ + int ret; + + ret = pci_register_driver(&mlx4_driver); + return ret < 0 ? ret : 0; +} + +static void __exit mlx4_cleanup(void) +{ + pci_unregister_driver(&mlx4_driver); +} + +module_init(mlx4_init); +module_exit(mlx4_cleanup); diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c new file mode 100644 index 00000000000..672024a0ee7 --- /dev/null +++ b/drivers/net/mlx4/mcg.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "mlx4.h" + +struct mlx4_mgm { + __be32 next_gid_index; + __be32 members_count; + u32 reserved[2]; + u8 gid[16]; + __be32 qp[MLX4_QP_PER_MGM]; +}; + +static const u8 zero_gid[16]; /* automatically initialized to 0 */ + +static int mlx4_READ_MCG(struct mlx4_dev *dev, int index, + struct mlx4_cmd_mailbox *mailbox) +{ + return mlx4_cmd_box(dev, 0, mailbox->dma, index, 0, MLX4_CMD_READ_MCG, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_WRITE_MCG(struct mlx4_dev *dev, int index, + struct mlx4_cmd_mailbox *mailbox) +{ + return mlx4_cmd(dev, mailbox->dma, index, 0, MLX4_CMD_WRITE_MCG, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_MGID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + u16 *hash) +{ + u64 imm; + int err; + + err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, 0, MLX4_CMD_MGID_HASH, + MLX4_CMD_TIME_CLASS_A); + + if (!err) + *hash = imm; + + return err; +} + +/* + * Caller must hold MCG table semaphore. gid and mgm parameters must + * be properly aligned for command interface. + * + * Returns 0 unless a firmware command error occurs. + * + * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1 + * and *mgm holds MGM entry. + * + * if GID is found in AMGM, *index = index in AMGM, *prev = index of + * previous entry in hash chain and *mgm holds AMGM entry. + * + * If no AMGM exists for given gid, *index = -1, *prev = index of last + * entry in hash chain and *mgm holds end of hash chain. + */ +static int find_mgm(struct mlx4_dev *dev, + u8 *gid, struct mlx4_cmd_mailbox *mgm_mailbox, + u16 *hash, int *prev, int *index) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm = mgm_mailbox->buf; + u8 *mgid; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return -ENOMEM; + mgid = mailbox->buf; + + memcpy(mgid, gid, 16); + + err = mlx4_MGID_HASH(dev, mailbox, hash); + mlx4_free_cmd_mailbox(dev, mailbox); + if (err) + return err; + + if (0) + mlx4_dbg(dev, "Hash for %04x:%04x:%04x:%04x:" + "%04x:%04x:%04x:%04x is %04x\n", + be16_to_cpu(((__be16 *) gid)[0]), + be16_to_cpu(((__be16 *) gid)[1]), + be16_to_cpu(((__be16 *) gid)[2]), + be16_to_cpu(((__be16 *) gid)[3]), + be16_to_cpu(((__be16 *) gid)[4]), + be16_to_cpu(((__be16 *) gid)[5]), + be16_to_cpu(((__be16 *) gid)[6]), + be16_to_cpu(((__be16 *) gid)[7]), + *hash); + + *index = *hash; + *prev = -1; + + do { + err = mlx4_READ_MCG(dev, *index, mgm_mailbox); + if (err) + return err; + + if (!memcmp(mgm->gid, zero_gid, 16)) { + if (*index != *hash) { + mlx4_err(dev, "Found zero MGID in AMGM.\n"); + err = -EINVAL; + } + return err; + } + + if (!memcmp(mgm->gid, gid, 16)) + return err; + + *prev = *index; + *index = be32_to_cpu(mgm->next_gid_index) >> 6; + } while (*index); + + *index = -1; + return err; +} + +int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + u32 members_count; + u16 hash; + int index, prev; + int link = 0; + int i; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + mgm = mailbox->buf; + + mutex_lock(&priv->mcg_table.mutex); + + err = find_mgm(dev, gid, mailbox, &hash, &prev, &index); + if (err) + goto out; + + if (index != -1) { + if (!memcmp(mgm->gid, zero_gid, 16)) + memcpy(mgm->gid, gid, 16); + } else { + link = 1; + + index = mlx4_bitmap_alloc(&priv->mcg_table.bitmap); + if (index == -1) { + mlx4_err(dev, "No AMGM entries left\n"); + err = -ENOMEM; + goto out; + } + index += dev->caps.num_mgms; + + err = mlx4_READ_MCG(dev, index, mailbox); + if (err) + goto out; + + memset(mgm, 0, sizeof *mgm); + memcpy(mgm->gid, gid, 16); + } + + members_count = be32_to_cpu(mgm->members_count); + if (members_count == MLX4_QP_PER_MGM) { + mlx4_err(dev, "MGM at index %x is full.\n", index); + err = -ENOMEM; + goto out; + } + + for (i = 0; i < members_count; ++i) + if (mgm->qp[i] == cpu_to_be32(qp->qpn)) { + mlx4_dbg(dev, "QP %06x already a member of MGM\n", qp->qpn); + err = 0; + goto out; + } + + mgm->qp[members_count++] = cpu_to_be32(qp->qpn); + mgm->members_count = cpu_to_be32(members_count); + + err = mlx4_WRITE_MCG(dev, index, mailbox); + if (err) + goto out; + + if (!link) + goto out; + + err = mlx4_READ_MCG(dev, prev, mailbox); + if (err) + goto out; + + mgm->next_gid_index = cpu_to_be32(index << 6); + + err = mlx4_WRITE_MCG(dev, prev, mailbox); + if (err) + goto out; + +out: + if (err && link && index != -1) { + if (index < dev->caps.num_mgms) + mlx4_warn(dev, "Got AMGM index %d < %d", + index, dev->caps.num_mgms); + else + mlx4_bitmap_free(&priv->mcg_table.bitmap, + index - dev->caps.num_mgms); + } + mutex_unlock(&priv->mcg_table.mutex); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_multicast_attach); + +int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + u32 members_count; + u16 hash; + int prev, index; + int i, loc; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + mgm = mailbox->buf; + + mutex_lock(&priv->mcg_table.mutex); + + err = find_mgm(dev, gid, mailbox, &hash, &prev, &index); + if (err) + goto out; + + if (index == -1) { + mlx4_err(dev, "MGID %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " + "not found\n", + be16_to_cpu(((__be16 *) gid)[0]), + be16_to_cpu(((__be16 *) gid)[1]), + be16_to_cpu(((__be16 *) gid)[2]), + be16_to_cpu(((__be16 *) gid)[3]), + be16_to_cpu(((__be16 *) gid)[4]), + be16_to_cpu(((__be16 *) gid)[5]), + be16_to_cpu(((__be16 *) gid)[6]), + be16_to_cpu(((__be16 *) gid)[7])); + err = -EINVAL; + goto out; + } + + members_count = be32_to_cpu(mgm->members_count); + for (loc = -1, i = 0; i < members_count; ++i) + if (mgm->qp[i] == cpu_to_be32(qp->qpn)) + loc = i; + + if (loc == -1) { + mlx4_err(dev, "QP %06x not found in MGM\n", qp->qpn); + err = -EINVAL; + goto out; + } + + + mgm->members_count = cpu_to_be32(--members_count); + mgm->qp[loc] = mgm->qp[i - 1]; + mgm->qp[i - 1] = 0; + + err = mlx4_WRITE_MCG(dev, index, mailbox); + if (err) + goto out; + + if (i != 1) + goto out; + + if (prev == -1) { + /* Remove entry from MGM */ + int amgm_index = be32_to_cpu(mgm->next_gid_index) >> 6; + if (amgm_index) { + err = mlx4_READ_MCG(dev, amgm_index, mailbox); + if (err) + goto out; + } else + memset(mgm->gid, 0, 16); + + err = mlx4_WRITE_MCG(dev, index, mailbox); + if (err) + goto out; + + if (amgm_index) { + if (amgm_index < dev->caps.num_mgms) + mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d", + index, amgm_index, dev->caps.num_mgms); + else + mlx4_bitmap_free(&priv->mcg_table.bitmap, + amgm_index - dev->caps.num_mgms); + } + } else { + /* Remove entry from AMGM */ + int cur_next_index = be32_to_cpu(mgm->next_gid_index) >> 6; + err = mlx4_READ_MCG(dev, prev, mailbox); + if (err) + goto out; + + mgm->next_gid_index = cpu_to_be32(cur_next_index << 6); + + err = mlx4_WRITE_MCG(dev, prev, mailbox); + if (err) + goto out; + + if (index < dev->caps.num_mgms) + mlx4_warn(dev, "entry %d had next AMGM index %d < %d", + prev, index, dev->caps.num_mgms); + else + mlx4_bitmap_free(&priv->mcg_table.bitmap, + index - dev->caps.num_mgms); + } + +out: + mutex_unlock(&priv->mcg_table.mutex); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_multicast_detach); + +int __devinit mlx4_init_mcg_table(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + err = mlx4_bitmap_init(&priv->mcg_table.bitmap, + dev->caps.num_amgms, dev->caps.num_amgms - 1, 0); + if (err) + return err; + + mutex_init(&priv->mcg_table.mutex); + + return 0; +} + +void mlx4_cleanup_mcg_table(struct mlx4_dev *dev) +{ + mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); +} diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h new file mode 100644 index 00000000000..9befbae3d19 --- /dev/null +++ b/drivers/net/mlx4/mlx4.h @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2004 Voltaire, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_H +#define MLX4_H + +#include + +#include +#include + +#define DRV_NAME "mlx4_core" +#define PFX DRV_NAME ": " +#define DRV_VERSION "0.01" +#define DRV_RELDATE "May 1, 2007" + +enum { + MLX4_HCR_BASE = 0x80680, + MLX4_HCR_SIZE = 0x0001c, + MLX4_CLR_INT_SIZE = 0x00008 +}; + +enum { + MLX4_BOARD_ID_LEN = 64 +}; + +enum { + MLX4_MGM_ENTRY_SIZE = 0x40, + MLX4_QP_PER_MGM = 4 * (MLX4_MGM_ENTRY_SIZE / 16 - 2), + MLX4_MTT_ENTRY_PER_SEG = 8 +}; + +enum { + MLX4_EQ_ASYNC, + MLX4_EQ_COMP, + MLX4_EQ_CATAS, + MLX4_NUM_EQ +}; + +enum { + MLX4_NUM_PDS = 1 << 15 +}; + +enum { + MLX4_CMPT_TYPE_QP = 0, + MLX4_CMPT_TYPE_SRQ = 1, + MLX4_CMPT_TYPE_CQ = 2, + MLX4_CMPT_TYPE_EQ = 3, + MLX4_CMPT_NUM_TYPE +}; + +enum { + MLX4_CMPT_SHIFT = 24, + MLX4_NUM_CMPTS = MLX4_CMPT_NUM_TYPE << MLX4_CMPT_SHIFT +}; + +#ifdef CONFIG_MLX4_DEBUG +extern int mlx4_debug_level; + +#define mlx4_dbg(mdev, format, arg...) \ + do { \ + if (mlx4_debug_level) \ + dev_printk(KERN_DEBUG, &mdev->pdev->dev, format, ## arg); \ + } while (0) + +#else /* CONFIG_MLX4_DEBUG */ + +#define mlx4_dbg(mdev, format, arg...) do { (void) mdev; } while (0) + +#endif /* CONFIG_MLX4_DEBUG */ + +#define mlx4_err(mdev, format, arg...) \ + dev_err(&mdev->pdev->dev, format, ## arg) +#define mlx4_info(mdev, format, arg...) \ + dev_info(&mdev->pdev->dev, format, ## arg) +#define mlx4_warn(mdev, format, arg...) \ + dev_warn(&mdev->pdev->dev, format, ## arg) + +struct mlx4_bitmap { + u32 last; + u32 top; + u32 max; + u32 mask; + spinlock_t lock; + unsigned long *table; +}; + +struct mlx4_buddy { + unsigned long **bits; + int max_order; + spinlock_t lock; +}; + +struct mlx4_icm; + +struct mlx4_icm_table { + u64 virt; + int num_icm; + int num_obj; + int obj_size; + int lowmem; + struct mutex mutex; + struct mlx4_icm **icm; +}; + +struct mlx4_eq { + struct mlx4_dev *dev; + void __iomem *doorbell; + int eqn; + u32 cons_index; + u16 irq; + u16 have_irq; + int nent; + struct mlx4_buf_list *page_list; + struct mlx4_mtt mtt; +}; + +struct mlx4_profile { + int num_qp; + int rdmarc_per_qp; + int num_srq; + int num_cq; + int num_mcg; + int num_mpt; + int num_mtt; +}; + +struct mlx4_fw { + u64 clr_int_base; + u64 catas_offset; + struct mlx4_icm *fw_icm; + struct mlx4_icm *aux_icm; + u32 catas_size; + u16 fw_pages; + u8 clr_int_bar; + u8 catas_bar; +}; + +struct mlx4_cmd { + struct pci_pool *pool; + void __iomem *hcr; + struct mutex hcr_mutex; + struct semaphore poll_sem; + struct semaphore event_sem; + int max_cmds; + spinlock_t context_lock; + int free_head; + struct mlx4_cmd_context *context; + u16 token_mask; + u8 use_events; + u8 toggle; +}; + +struct mlx4_uar_table { + struct mlx4_bitmap bitmap; +}; + +struct mlx4_mr_table { + struct mlx4_bitmap mpt_bitmap; + struct mlx4_buddy mtt_buddy; + u64 mtt_base; + u64 mpt_base; + struct mlx4_icm_table mtt_table; + struct mlx4_icm_table dmpt_table; +}; + +struct mlx4_cq_table { + struct mlx4_bitmap bitmap; + spinlock_t lock; + struct radix_tree_root tree; + struct mlx4_icm_table table; + struct mlx4_icm_table cmpt_table; +}; + +struct mlx4_eq_table { + struct mlx4_bitmap bitmap; + void __iomem *clr_int; + void __iomem *uar_map[(MLX4_NUM_EQ + 6) / 4]; + u32 clr_mask; + struct mlx4_eq eq[MLX4_NUM_EQ]; + u64 icm_virt; + struct page *icm_page; + dma_addr_t icm_dma; + struct mlx4_icm_table cmpt_table; + int have_irq; + u8 inta_pin; +}; + +struct mlx4_srq_table { + struct mlx4_bitmap bitmap; + spinlock_t lock; + struct radix_tree_root tree; + struct mlx4_icm_table table; + struct mlx4_icm_table cmpt_table; +}; + +struct mlx4_qp_table { + struct mlx4_bitmap bitmap; + u32 rdmarc_base; + int rdmarc_shift; + spinlock_t lock; + struct mlx4_icm_table qp_table; + struct mlx4_icm_table auxc_table; + struct mlx4_icm_table altc_table; + struct mlx4_icm_table rdmarc_table; + struct mlx4_icm_table cmpt_table; +}; + +struct mlx4_mcg_table { + struct mutex mutex; + struct mlx4_bitmap bitmap; + struct mlx4_icm_table table; +}; + +struct mlx4_catas_err { + u32 __iomem *map; + int size; +}; + +struct mlx4_priv { + struct mlx4_dev dev; + + struct list_head dev_list; + struct list_head ctx_list; + spinlock_t ctx_lock; + + struct mlx4_fw fw; + struct mlx4_cmd cmd; + + struct mlx4_bitmap pd_bitmap; + struct mlx4_uar_table uar_table; + struct mlx4_mr_table mr_table; + struct mlx4_cq_table cq_table; + struct mlx4_eq_table eq_table; + struct mlx4_srq_table srq_table; + struct mlx4_qp_table qp_table; + struct mlx4_mcg_table mcg_table; + + struct mlx4_catas_err catas_err; + + void __iomem *clr_base; + + struct mlx4_uar driver_uar; + void __iomem *kar; + MLX4_DECLARE_DOORBELL_LOCK(doorbell_lock) + + u32 rev_id; + char board_id[MLX4_BOARD_ID_LEN]; +}; + +static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) +{ + return container_of(dev, struct mlx4_priv, dev); +} + +u32 mlx4_bitmap_alloc(struct mlx4_bitmap *bitmap); +void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj); +int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, u32 reserved); +void mlx4_bitmap_cleanup(struct mlx4_bitmap *bitmap); + +int mlx4_reset(struct mlx4_dev *dev); + +int mlx4_init_pd_table(struct mlx4_dev *dev); +int mlx4_init_uar_table(struct mlx4_dev *dev); +int mlx4_init_mr_table(struct mlx4_dev *dev); +int mlx4_init_eq_table(struct mlx4_dev *dev); +int mlx4_init_cq_table(struct mlx4_dev *dev); +int mlx4_init_qp_table(struct mlx4_dev *dev); +int mlx4_init_srq_table(struct mlx4_dev *dev); +int mlx4_init_mcg_table(struct mlx4_dev *dev); + +void mlx4_cleanup_pd_table(struct mlx4_dev *dev); +void mlx4_cleanup_uar_table(struct mlx4_dev *dev); +void mlx4_cleanup_mr_table(struct mlx4_dev *dev); +void mlx4_cleanup_eq_table(struct mlx4_dev *dev); +void mlx4_cleanup_cq_table(struct mlx4_dev *dev); +void mlx4_cleanup_qp_table(struct mlx4_dev *dev); +void mlx4_cleanup_srq_table(struct mlx4_dev *dev); +void mlx4_cleanup_mcg_table(struct mlx4_dev *dev); + +void mlx4_map_catas_buf(struct mlx4_dev *dev); +void mlx4_unmap_catas_buf(struct mlx4_dev *dev); + +int mlx4_register_device(struct mlx4_dev *dev); +void mlx4_unregister_device(struct mlx4_dev *dev); +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_event type, + int subtype, int port); + +struct mlx4_dev_cap; +struct mlx4_init_hca_param; + +u64 mlx4_make_profile(struct mlx4_dev *dev, + struct mlx4_profile *request, + 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); +int mlx4_cmd_use_events(struct mlx4_dev *dev); +void mlx4_cmd_use_polling(struct mlx4_dev *dev); + +void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn); +void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type); + +void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type); + +void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type); + +void mlx4_handle_catas_err(struct mlx4_dev *dev); + +#endif /* MLX4_H */ diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c new file mode 100644 index 00000000000..b33864dab17 --- /dev/null +++ b/drivers/net/mlx4/mr.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4.h" +#include "icm.h" + +/* + * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits. + */ +struct mlx4_mpt_entry { + __be32 flags; + __be32 qpn; + __be32 key; + __be32 pd; + __be64 start; + __be64 length; + __be32 lkey; + __be32 win_cnt; + u8 reserved1[3]; + u8 mtt_rep; + __be64 mtt_seg; + __be32 mtt_sz; + __be32 entity_size; + __be32 first_byte_offset; +} __attribute__((packed)); + +#define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) +#define MLX4_MPT_FLAG_MIO (1 << 17) +#define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) +#define MLX4_MPT_FLAG_PHYSICAL (1 << 9) +#define MLX4_MPT_FLAG_REGION (1 << 8) + +#define MLX4_MTT_FLAG_PRESENT 1 + +static u32 mlx4_buddy_alloc(struct mlx4_buddy *buddy, int order) +{ + int o; + int m; + u32 seg; + + spin_lock(&buddy->lock); + + for (o = order; o <= buddy->max_order; ++o) { + m = 1 << (buddy->max_order - o); + seg = find_first_bit(buddy->bits[o], m); + if (seg < m) + goto found; + } + + spin_unlock(&buddy->lock); + return -1; + + found: + clear_bit(seg, buddy->bits[o]); + + while (o > order) { + --o; + seg <<= 1; + set_bit(seg ^ 1, buddy->bits[o]); + } + + spin_unlock(&buddy->lock); + + seg <<= order; + + return seg; +} + +static void mlx4_buddy_free(struct mlx4_buddy *buddy, u32 seg, int order) +{ + seg >>= order; + + spin_lock(&buddy->lock); + + while (test_bit(seg ^ 1, buddy->bits[order])) { + clear_bit(seg ^ 1, buddy->bits[order]); + seg >>= 1; + ++order; + } + + set_bit(seg, buddy->bits[order]); + + spin_unlock(&buddy->lock); +} + +static int __devinit mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) +{ + int i, s; + + buddy->max_order = max_order; + spin_lock_init(&buddy->lock); + + buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), + GFP_KERNEL); + if (!buddy->bits) + goto err_out; + + for (i = 0; i <= buddy->max_order; ++i) { + s = BITS_TO_LONGS(1 << (buddy->max_order - i)); + buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL); + if (!buddy->bits[i]) + goto err_out_free; + bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i)); + } + + set_bit(0, buddy->bits[buddy->max_order]); + + return 0; + +err_out_free: + for (i = 0; i <= buddy->max_order; ++i) + kfree(buddy->bits[i]); + + kfree(buddy->bits); + +err_out: + return -ENOMEM; +} + +static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy) +{ + int i; + + for (i = 0; i <= buddy->max_order; ++i) + kfree(buddy->bits[i]); + + kfree(buddy->bits); +} + +static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order) +{ + struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; + u32 seg; + + seg = mlx4_buddy_alloc(&mr_table->mtt_buddy, order); + if (seg == -1) + return -1; + + if (mlx4_table_get_range(dev, &mr_table->mtt_table, seg, + seg + (1 << order) - 1)) { + mlx4_buddy_free(&mr_table->mtt_buddy, seg, order); + return -1; + } + + return seg; +} + +int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift, + struct mlx4_mtt *mtt) +{ + int i; + + if (!npages) { + mtt->order = -1; + mtt->page_shift = MLX4_ICM_PAGE_SHIFT; + return 0; + } else + mtt->page_shift = page_shift; + + for (mtt->order = 0, i = MLX4_MTT_ENTRY_PER_SEG; i < npages; i <<= 1) + ++mtt->order; + + mtt->first_seg = mlx4_alloc_mtt_range(dev, mtt->order); + if (mtt->first_seg == -1) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_mtt_init); + +void mlx4_mtt_cleanup(struct mlx4_dev *dev, struct mlx4_mtt *mtt) +{ + struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; + + if (mtt->order < 0) + return; + + mlx4_buddy_free(&mr_table->mtt_buddy, mtt->first_seg, mtt->order); + mlx4_table_put_range(dev, &mr_table->mtt_table, mtt->first_seg, + mtt->first_seg + (1 << mtt->order) - 1); +} +EXPORT_SYMBOL_GPL(mlx4_mtt_cleanup); + +u64 mlx4_mtt_addr(struct mlx4_dev *dev, struct mlx4_mtt *mtt) +{ + return (u64) mtt->first_seg * dev->caps.mtt_entry_sz; +} +EXPORT_SYMBOL_GPL(mlx4_mtt_addr); + +static u32 hw_index_to_key(u32 ind) +{ + return (ind >> 24) | (ind << 8); +} + +static u32 key_to_hw_index(u32 key) +{ + return (key << 24) | (key >> 8); +} + +static int mlx4_SW2HW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int mpt_index) +{ + return mlx4_cmd(dev, mailbox->dma, mpt_index, 0, MLX4_CMD_SW2HW_MPT, + MLX4_CMD_TIME_CLASS_B); +} + +static int mlx4_HW2SW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int mpt_index) +{ + return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, mpt_index, + !mailbox, MLX4_CMD_HW2SW_MPT, MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_mr_alloc(struct mlx4_dev *dev, u32 pd, u64 iova, u64 size, u32 access, + int npages, int page_shift, struct mlx4_mr *mr) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + u32 index; + int err; + + index = mlx4_bitmap_alloc(&priv->mr_table.mpt_bitmap); + if (index == -1) { + err = -ENOMEM; + goto err; + } + + mr->iova = iova; + mr->size = size; + mr->pd = pd; + mr->access = access; + mr->enabled = 0; + mr->key = hw_index_to_key(index); + + err = mlx4_mtt_init(dev, npages, page_shift, &mr->mtt); + if (err) + goto err_index; + + return 0; + +err_index: + mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, index); + +err: + kfree(mr); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_mr_alloc); + +void mlx4_mr_free(struct mlx4_dev *dev, struct mlx4_mr *mr) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + if (mr->enabled) { + err = mlx4_HW2SW_MPT(dev, NULL, + key_to_hw_index(mr->key) & + (dev->caps.num_mpts - 1)); + if (err) + mlx4_warn(dev, "HW2SW_MPT failed (%d)\n", err); + } + + mlx4_mtt_cleanup(dev, &mr->mtt); + mlx4_bitmap_free(&priv->mr_table.mpt_bitmap, key_to_hw_index(mr->key)); +} +EXPORT_SYMBOL_GPL(mlx4_mr_free); + +int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) +{ + struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mpt_entry *mpt_entry; + int err; + + err = mlx4_table_get(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); + if (err) + return err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = PTR_ERR(mailbox); + goto err_table; + } + mpt_entry = mailbox->buf; + + memset(mpt_entry, 0, sizeof *mpt_entry); + + mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS | + MLX4_MPT_FLAG_MIO | + MLX4_MPT_FLAG_REGION | + mr->access); + if (mr->mtt.order < 0) + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); + + mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); + mpt_entry->pd = cpu_to_be32(mr->pd); + mpt_entry->start = cpu_to_be64(mr->iova); + mpt_entry->length = cpu_to_be64(mr->size); + mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); + mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); + + err = mlx4_SW2HW_MPT(dev, mailbox, + key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); + if (err) { + mlx4_warn(dev, "SW2HW_MPT failed (%d)\n", err); + goto err_cmd; + } + + mr->enabled = 1; + + mlx4_free_cmd_mailbox(dev, mailbox); + + return 0; + +err_cmd: + mlx4_free_cmd_mailbox(dev, mailbox); + +err_table: + mlx4_table_put(dev, &mr_table->dmpt_table, key_to_hw_index(mr->key)); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_mr_enable); + +static int mlx4_WRITE_MTT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int num_mtt) +{ + return mlx4_cmd(dev, mailbox->dma, num_mtt, 0, MLX4_CMD_WRITE_MTT, + MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + int start_index, int npages, u64 *page_list) +{ + struct mlx4_cmd_mailbox *mailbox; + __be64 *mtt_entry; + int i; + int err = 0; + + if (mtt->order < 0) + return -EINVAL; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + mtt_entry = mailbox->buf; + + while (npages > 0) { + mtt_entry[0] = cpu_to_be64(mlx4_mtt_addr(dev, mtt) + start_index * 8); + mtt_entry[1] = 0; + + for (i = 0; i < npages && i < MLX4_MAILBOX_SIZE / 8 - 2; ++i) + mtt_entry[i + 2] = cpu_to_be64(page_list[i] | + MLX4_MTT_FLAG_PRESENT); + + /* + * If we have an odd number of entries to write, add + * one more dummy entry for firmware efficiency. + */ + if (i & 1) + mtt_entry[i + 2] = 0; + + err = mlx4_WRITE_MTT(dev, mailbox, (i + 1) & ~1); + if (err) + goto out; + + npages -= i; + start_index += i; + page_list += i; + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL_GPL(mlx4_write_mtt); + +int mlx4_buf_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + struct mlx4_buf *buf) +{ + u64 *page_list; + int err; + int i; + + page_list = kmalloc(buf->npages * sizeof *page_list, GFP_KERNEL); + if (!page_list) + return -ENOMEM; + + for (i = 0; i < buf->npages; ++i) + if (buf->nbufs == 1) + page_list[i] = buf->u.direct.map + (i << buf->page_shift); + else + page_list[i] = buf->u.page_list[i].map; + + err = mlx4_write_mtt(dev, mtt, 0, buf->npages, page_list); + + kfree(page_list); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_buf_write_mtt); + +int __devinit mlx4_init_mr_table(struct mlx4_dev *dev) +{ + struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; + int err; + + err = mlx4_bitmap_init(&mr_table->mpt_bitmap, dev->caps.num_mpts, + ~0, dev->caps.reserved_mrws); + if (err) + return err; + + err = mlx4_buddy_init(&mr_table->mtt_buddy, + ilog2(dev->caps.num_mtt_segs)); + if (err) + goto err_buddy; + + if (dev->caps.reserved_mtts) { + if (mlx4_alloc_mtt_range(dev, ilog2(dev->caps.reserved_mtts)) == -1) { + mlx4_warn(dev, "MTT table of order %d is too small.\n", + mr_table->mtt_buddy.max_order); + err = -ENOMEM; + goto err_reserve_mtts; + } + } + + return 0; + +err_reserve_mtts: + mlx4_buddy_cleanup(&mr_table->mtt_buddy); + +err_buddy: + mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); + + return err; +} + +void mlx4_cleanup_mr_table(struct mlx4_dev *dev) +{ + struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; + + mlx4_buddy_cleanup(&mr_table->mtt_buddy); + mlx4_bitmap_cleanup(&mr_table->mpt_bitmap); +} diff --git a/drivers/net/mlx4/pd.c b/drivers/net/mlx4/pd.c new file mode 100644 index 00000000000..23dea1ee775 --- /dev/null +++ b/drivers/net/mlx4/pd.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "mlx4.h" +#include "icm.h" + +int mlx4_pd_alloc(struct mlx4_dev *dev, u32 *pdn) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + *pdn = mlx4_bitmap_alloc(&priv->pd_bitmap); + if (*pdn == -1) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_pd_alloc); + +void mlx4_pd_free(struct mlx4_dev *dev, u32 pdn) +{ + mlx4_bitmap_free(&mlx4_priv(dev)->pd_bitmap, pdn); +} +EXPORT_SYMBOL_GPL(mlx4_pd_free); + +int __devinit mlx4_init_pd_table(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + return mlx4_bitmap_init(&priv->pd_bitmap, dev->caps.num_pds, + (1 << 24) - 1, dev->caps.reserved_pds); +} + +void mlx4_cleanup_pd_table(struct mlx4_dev *dev) +{ + mlx4_bitmap_cleanup(&mlx4_priv(dev)->pd_bitmap); +} + + +int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar) +{ + uar->index = mlx4_bitmap_alloc(&mlx4_priv(dev)->uar_table.bitmap); + if (uar->index == -1) + return -ENOMEM; + + uar->pfn = (pci_resource_start(dev->pdev, 2) >> PAGE_SHIFT) + uar->index; + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_uar_alloc); + +void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar) +{ + mlx4_bitmap_free(&mlx4_priv(dev)->uar_table.bitmap, uar->index); +} +EXPORT_SYMBOL_GPL(mlx4_uar_free); + +int mlx4_init_uar_table(struct mlx4_dev *dev) +{ + return mlx4_bitmap_init(&mlx4_priv(dev)->uar_table.bitmap, + dev->caps.num_uars, dev->caps.num_uars - 1, + max(128, dev->caps.reserved_uars)); +} + +void mlx4_cleanup_uar_table(struct mlx4_dev *dev) +{ + mlx4_bitmap_cleanup(&mlx4_priv(dev)->uar_table.bitmap); +} diff --git a/drivers/net/mlx4/profile.c b/drivers/net/mlx4/profile.c new file mode 100644 index 00000000000..9ca42b213d5 --- /dev/null +++ b/drivers/net/mlx4/profile.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "mlx4.h" +#include "fw.h" + +enum { + MLX4_RES_QP, + MLX4_RES_RDMARC, + MLX4_RES_ALTC, + MLX4_RES_AUXC, + MLX4_RES_SRQ, + MLX4_RES_CQ, + MLX4_RES_EQ, + MLX4_RES_DMPT, + MLX4_RES_CMPT, + MLX4_RES_MTT, + MLX4_RES_MCG, + MLX4_RES_NUM +}; + +static const char *res_name[] = { + [MLX4_RES_QP] = "QP", + [MLX4_RES_RDMARC] = "RDMARC", + [MLX4_RES_ALTC] = "ALTC", + [MLX4_RES_AUXC] = "AUXC", + [MLX4_RES_SRQ] = "SRQ", + [MLX4_RES_CQ] = "CQ", + [MLX4_RES_EQ] = "EQ", + [MLX4_RES_DMPT] = "DMPT", + [MLX4_RES_CMPT] = "CMPT", + [MLX4_RES_MTT] = "MTT", + [MLX4_RES_MCG] = "MCG", +}; + +u64 mlx4_make_profile(struct mlx4_dev *dev, + struct mlx4_profile *request, + struct mlx4_dev_cap *dev_cap, + struct mlx4_init_hca_param *init_hca) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource { + u64 size; + u64 start; + int type; + int num; + int log_num; + }; + + u64 total_size = 0; + struct mlx4_resource *profile; + struct mlx4_resource tmp; + int i, j; + + profile = kzalloc(MLX4_RES_NUM * sizeof *profile, GFP_KERNEL); + if (!profile) + return -ENOMEM; + + profile[MLX4_RES_QP].size = dev_cap->qpc_entry_sz; + profile[MLX4_RES_RDMARC].size = dev_cap->rdmarc_entry_sz; + profile[MLX4_RES_ALTC].size = dev_cap->altc_entry_sz; + profile[MLX4_RES_AUXC].size = dev_cap->aux_entry_sz; + profile[MLX4_RES_SRQ].size = dev_cap->srq_entry_sz; + profile[MLX4_RES_CQ].size = dev_cap->cqc_entry_sz; + profile[MLX4_RES_EQ].size = dev_cap->eqc_entry_sz; + profile[MLX4_RES_DMPT].size = dev_cap->dmpt_entry_sz; + profile[MLX4_RES_CMPT].size = dev_cap->cmpt_entry_sz; + profile[MLX4_RES_MTT].size = MLX4_MTT_ENTRY_PER_SEG * dev_cap->mtt_entry_sz; + profile[MLX4_RES_MCG].size = MLX4_MGM_ENTRY_SIZE; + + profile[MLX4_RES_QP].num = request->num_qp; + profile[MLX4_RES_RDMARC].num = request->num_qp * request->rdmarc_per_qp; + profile[MLX4_RES_ALTC].num = request->num_qp; + profile[MLX4_RES_AUXC].num = request->num_qp; + profile[MLX4_RES_SRQ].num = request->num_srq; + profile[MLX4_RES_CQ].num = request->num_cq; + profile[MLX4_RES_EQ].num = MLX4_NUM_EQ + dev_cap->reserved_eqs; + profile[MLX4_RES_DMPT].num = request->num_mpt; + profile[MLX4_RES_CMPT].num = MLX4_NUM_CMPTS; + profile[MLX4_RES_MTT].num = request->num_mtt; + profile[MLX4_RES_MCG].num = request->num_mcg; + + for (i = 0; i < MLX4_RES_NUM; ++i) { + profile[i].type = i; + profile[i].num = roundup_pow_of_two(profile[i].num); + profile[i].log_num = ilog2(profile[i].num); + profile[i].size *= profile[i].num; + profile[i].size = max(profile[i].size, (u64) PAGE_SIZE); + } + + /* + * Sort the resources in decreasing order of size. Since they + * all have sizes that are powers of 2, we'll be able to keep + * resources aligned to their size and pack them without gaps + * using the sorted order. + */ + for (i = MLX4_RES_NUM; i > 0; --i) + for (j = 1; j < i; ++j) { + if (profile[j].size > profile[j - 1].size) { + tmp = profile[j]; + profile[j] = profile[j - 1]; + profile[j - 1] = tmp; + } + } + + for (i = 0; i < MLX4_RES_NUM; ++i) { + if (profile[i].size) { + profile[i].start = total_size; + total_size += profile[i].size; + } + + if (total_size > dev_cap->max_icm_sz) { + mlx4_err(dev, "Profile requires 0x%llx bytes; " + "won't fit in 0x%llx bytes of context memory.\n", + (unsigned long long) total_size, + (unsigned long long) dev_cap->max_icm_sz); + kfree(profile); + return -ENOMEM; + } + + if (profile[i].size) + mlx4_dbg(dev, " profile[%2d] (%6s): 2^%02d entries @ 0x%10llx, " + "size 0x%10llx\n", + i, res_name[profile[i].type], profile[i].log_num, + (unsigned long long) profile[i].start, + (unsigned long long) profile[i].size); + } + + mlx4_dbg(dev, "HCA context memory: reserving %d KB\n", + (int) (total_size >> 10)); + + for (i = 0; i < MLX4_RES_NUM; ++i) { + switch (profile[i].type) { + case MLX4_RES_QP: + dev->caps.num_qps = profile[i].num; + init_hca->qpc_base = profile[i].start; + init_hca->log_num_qps = profile[i].log_num; + break; + case MLX4_RES_RDMARC: + for (priv->qp_table.rdmarc_shift = 0; + request->num_qp << priv->qp_table.rdmarc_shift < profile[i].num; + ++priv->qp_table.rdmarc_shift) + ; /* nothing */ + dev->caps.max_qp_dest_rdma = 1 << priv->qp_table.rdmarc_shift; + priv->qp_table.rdmarc_base = (u32) profile[i].start; + init_hca->rdmarc_base = profile[i].start; + init_hca->log_rd_per_qp = priv->qp_table.rdmarc_shift; + break; + case MLX4_RES_ALTC: + init_hca->altc_base = profile[i].start; + break; + case MLX4_RES_AUXC: + init_hca->auxc_base = profile[i].start; + break; + case MLX4_RES_SRQ: + dev->caps.num_srqs = profile[i].num; + init_hca->srqc_base = profile[i].start; + init_hca->log_num_srqs = profile[i].log_num; + break; + case MLX4_RES_CQ: + dev->caps.num_cqs = profile[i].num; + init_hca->cqc_base = profile[i].start; + init_hca->log_num_cqs = profile[i].log_num; + break; + case MLX4_RES_EQ: + dev->caps.num_eqs = profile[i].num; + init_hca->eqc_base = profile[i].start; + init_hca->log_num_eqs = profile[i].log_num; + break; + case MLX4_RES_DMPT: + dev->caps.num_mpts = profile[i].num; + priv->mr_table.mpt_base = profile[i].start; + init_hca->dmpt_base = profile[i].start; + init_hca->log_mpt_sz = profile[i].log_num; + break; + case MLX4_RES_CMPT: + init_hca->cmpt_base = profile[i].start; + break; + case MLX4_RES_MTT: + dev->caps.num_mtt_segs = profile[i].num; + priv->mr_table.mtt_base = profile[i].start; + init_hca->mtt_base = profile[i].start; + break; + case MLX4_RES_MCG: + dev->caps.num_mgms = profile[i].num >> 1; + dev->caps.num_amgms = profile[i].num >> 1; + init_hca->mc_base = profile[i].start; + init_hca->log_mc_entry_sz = ilog2(MLX4_MGM_ENTRY_SIZE); + init_hca->log_mc_table_sz = profile[i].log_num; + init_hca->log_mc_hash_sz = profile[i].log_num - 1; + break; + default: + break; + } + } + + /* + * PDs don't take any HCA memory, but we assign them as part + * of the HCA profile anyway. + */ + dev->caps.num_pds = MLX4_NUM_PDS; + + kfree(profile); + return total_size; +} diff --git a/drivers/net/mlx4/qp.c b/drivers/net/mlx4/qp.c new file mode 100644 index 00000000000..7f8b7d55b6e --- /dev/null +++ b/drivers/net/mlx4/qp.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2004 Voltaire, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include +#include + +#include "mlx4.h" +#include "icm.h" + +void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type) +{ + struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; + struct mlx4_qp *qp; + + spin_lock(&qp_table->lock); + + qp = __mlx4_qp_lookup(dev, qpn); + if (qp) + atomic_inc(&qp->refcount); + + spin_unlock(&qp_table->lock); + + if (!qp) { + mlx4_warn(dev, "Async event for bogus QP %08x\n", qpn); + return; + } + + qp->event(qp, event_type); + + if (atomic_dec_and_test(&qp->refcount)) + complete(&qp->free); +} + +int mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + enum mlx4_qp_state cur_state, enum mlx4_qp_state new_state, + struct mlx4_qp_context *context, enum mlx4_qp_optpar optpar, + int sqd_event, struct mlx4_qp *qp) +{ + static const u16 op[MLX4_QP_NUM_STATE][MLX4_QP_NUM_STATE] = { + [MLX4_QP_STATE_RST] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_INIT] = MLX4_CMD_RST2INIT_QP, + }, + [MLX4_QP_STATE_INIT] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_INIT] = MLX4_CMD_INIT2INIT_QP, + [MLX4_QP_STATE_RTR] = MLX4_CMD_INIT2RTR_QP, + }, + [MLX4_QP_STATE_RTR] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_RTS] = MLX4_CMD_RTR2RTS_QP, + }, + [MLX4_QP_STATE_RTS] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_RTS] = MLX4_CMD_RTS2RTS_QP, + [MLX4_QP_STATE_SQD] = MLX4_CMD_RTS2SQD_QP, + }, + [MLX4_QP_STATE_SQD] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_RTS] = MLX4_CMD_SQD2RTS_QP, + [MLX4_QP_STATE_SQD] = MLX4_CMD_SQD2SQD_QP, + }, + [MLX4_QP_STATE_SQER] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + [MLX4_QP_STATE_RTS] = MLX4_CMD_SQERR2RTS_QP, + }, + [MLX4_QP_STATE_ERR] = { + [MLX4_QP_STATE_RST] = MLX4_CMD_2RST_QP, + [MLX4_QP_STATE_ERR] = MLX4_CMD_2ERR_QP, + } + }; + + struct mlx4_cmd_mailbox *mailbox; + int ret = 0; + + if (cur_state < 0 || cur_state >= MLX4_QP_NUM_STATE || + new_state < 0 || cur_state >= MLX4_QP_NUM_STATE || + !op[cur_state][new_state]) + return -EINVAL; + + if (op[cur_state][new_state] == MLX4_CMD_2RST_QP) + return mlx4_cmd(dev, 0, qp->qpn, 2, + MLX4_CMD_2RST_QP, MLX4_CMD_TIME_CLASS_A); + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + if (cur_state == MLX4_QP_STATE_RST && new_state == MLX4_QP_STATE_INIT) { + u64 mtt_addr = mlx4_mtt_addr(dev, mtt); + context->mtt_base_addr_h = mtt_addr >> 32; + context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff); + context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT; + } + + *(__be32 *) mailbox->buf = cpu_to_be32(optpar); + memcpy(mailbox->buf + 8, context, sizeof *context); + + ((struct mlx4_qp_context *) (mailbox->buf + 8))->local_qpn = + cpu_to_be32(qp->qpn); + + ret = mlx4_cmd(dev, mailbox->dma, qp->qpn | (!!sqd_event << 31), + new_state == MLX4_QP_STATE_RST ? 2 : 0, + op[cur_state][new_state], MLX4_CMD_TIME_CLASS_C); + + mlx4_free_cmd_mailbox(dev, mailbox); + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_qp_modify); + +int mlx4_qp_alloc(struct mlx4_dev *dev, int sqpn, struct mlx4_qp *qp) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_qp_table *qp_table = &priv->qp_table; + int err; + + if (sqpn) + qp->qpn = sqpn; + else { + qp->qpn = mlx4_bitmap_alloc(&qp_table->bitmap); + if (qp->qpn == -1) + return -ENOMEM; + } + + err = mlx4_table_get(dev, &qp_table->qp_table, qp->qpn); + if (err) + goto err_out; + + err = mlx4_table_get(dev, &qp_table->auxc_table, qp->qpn); + if (err) + goto err_put_qp; + + err = mlx4_table_get(dev, &qp_table->altc_table, qp->qpn); + if (err) + goto err_put_auxc; + + err = mlx4_table_get(dev, &qp_table->rdmarc_table, qp->qpn); + if (err) + goto err_put_altc; + + err = mlx4_table_get(dev, &qp_table->cmpt_table, qp->qpn); + if (err) + goto err_put_rdmarc; + + spin_lock_irq(&qp_table->lock); + err = radix_tree_insert(&dev->qp_table_tree, qp->qpn & (dev->caps.num_qps - 1), qp); + spin_unlock_irq(&qp_table->lock); + if (err) + goto err_put_cmpt; + + atomic_set(&qp->refcount, 1); + init_completion(&qp->free); + + return 0; + +err_put_cmpt: + mlx4_table_put(dev, &qp_table->cmpt_table, qp->qpn); + +err_put_rdmarc: + mlx4_table_put(dev, &qp_table->rdmarc_table, qp->qpn); + +err_put_altc: + mlx4_table_put(dev, &qp_table->altc_table, qp->qpn); + +err_put_auxc: + mlx4_table_put(dev, &qp_table->auxc_table, qp->qpn); + +err_put_qp: + mlx4_table_put(dev, &qp_table->qp_table, qp->qpn); + +err_out: + if (!sqpn) + mlx4_bitmap_free(&qp_table->bitmap, qp->qpn); + + return err; +} +EXPORT_SYMBOL_GPL(mlx4_qp_alloc); + +void mlx4_qp_remove(struct mlx4_dev *dev, struct mlx4_qp *qp) +{ + struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; + unsigned long flags; + + spin_lock_irqsave(&qp_table->lock, flags); + radix_tree_delete(&dev->qp_table_tree, qp->qpn & (dev->caps.num_qps - 1)); + spin_unlock_irqrestore(&qp_table->lock, flags); +} +EXPORT_SYMBOL_GPL(mlx4_qp_remove); + +void mlx4_qp_free(struct mlx4_dev *dev, struct mlx4_qp *qp) +{ + struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; + + if (atomic_dec_and_test(&qp->refcount)) + complete(&qp->free); + wait_for_completion(&qp->free); + + mlx4_table_put(dev, &qp_table->cmpt_table, qp->qpn); + mlx4_table_put(dev, &qp_table->rdmarc_table, qp->qpn); + mlx4_table_put(dev, &qp_table->altc_table, qp->qpn); + mlx4_table_put(dev, &qp_table->auxc_table, qp->qpn); + mlx4_table_put(dev, &qp_table->qp_table, qp->qpn); + + mlx4_bitmap_free(&qp_table->bitmap, qp->qpn); +} +EXPORT_SYMBOL_GPL(mlx4_qp_free); + +static int mlx4_CONF_SPECIAL_QP(struct mlx4_dev *dev, u32 base_qpn) +{ + return mlx4_cmd(dev, 0, base_qpn, 0, MLX4_CMD_CONF_SPECIAL_QP, + MLX4_CMD_TIME_CLASS_B); +} + +int __devinit mlx4_init_qp_table(struct mlx4_dev *dev) +{ + struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; + int err; + + spin_lock_init(&qp_table->lock); + INIT_RADIX_TREE(&dev->qp_table_tree, GFP_ATOMIC); + + /* + * We reserve 2 extra QPs per port for the special QPs. The + * block of special QPs must be aligned to a multiple of 8, so + * round up. + */ + dev->caps.sqp_start = ALIGN(dev->caps.reserved_qps, 8); + err = mlx4_bitmap_init(&qp_table->bitmap, dev->caps.num_qps, + (1 << 24) - 1, dev->caps.sqp_start + 8); + if (err) + return err; + + return mlx4_CONF_SPECIAL_QP(dev, dev->caps.sqp_start); +} + +void mlx4_cleanup_qp_table(struct mlx4_dev *dev) +{ + mlx4_CONF_SPECIAL_QP(dev, 0); + mlx4_bitmap_cleanup(&mlx4_priv(dev)->qp_table.bitmap); +} diff --git a/drivers/net/mlx4/reset.c b/drivers/net/mlx4/reset.c new file mode 100644 index 00000000000..51eef8492e9 --- /dev/null +++ b/drivers/net/mlx4/reset.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "mlx4.h" + +int mlx4_reset(struct mlx4_dev *dev) +{ + void __iomem *reset; + u32 *hca_header = NULL; + int pcie_cap; + u16 devctl; + u16 linkctl; + u16 vendor; + unsigned long end; + u32 sem; + int i; + int err = 0; + +#define MLX4_RESET_BASE 0xf0000 +#define MLX4_RESET_SIZE 0x400 +#define MLX4_SEM_OFFSET 0x3fc +#define MLX4_RESET_OFFSET 0x10 +#define MLX4_RESET_VALUE swab32(1) + +#define MLX4_SEM_TIMEOUT_JIFFIES (10 * HZ) +#define MLX4_RESET_TIMEOUT_JIFFIES (2 * HZ) + + /* + * Reset the chip. This is somewhat ugly because we have to + * save off the PCI header before reset and then restore it + * after the chip reboots. We skip config space offsets 22 + * and 23 since those have a special meaning. + */ + + /* Do we need to save off the full 4K PCI Express header?? */ + hca_header = kmalloc(256, GFP_KERNEL); + if (!hca_header) { + err = -ENOMEM; + mlx4_err(dev, "Couldn't allocate memory to save HCA " + "PCI header, aborting.\n"); + goto out; + } + + pcie_cap = pci_find_capability(dev->pdev, PCI_CAP_ID_EXP); + + for (i = 0; i < 64; ++i) { + if (i == 22 || i == 23) + continue; + if (pci_read_config_dword(dev->pdev, i * 4, hca_header + i)) { + err = -ENODEV; + mlx4_err(dev, "Couldn't save HCA " + "PCI header, aborting.\n"); + goto out; + } + } + + reset = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_RESET_BASE, + MLX4_RESET_SIZE); + if (!reset) { + err = -ENOMEM; + mlx4_err(dev, "Couldn't map HCA reset register, aborting.\n"); + goto out; + } + + /* grab HW semaphore to lock out flash updates */ + end = jiffies + MLX4_SEM_TIMEOUT_JIFFIES; + do { + sem = readl(reset + MLX4_SEM_OFFSET); + if (!sem) + break; + + msleep(1); + } while (time_before(jiffies, end)); + + if (sem) { + mlx4_err(dev, "Failed to obtain HW semaphore, aborting\n"); + err = -EAGAIN; + iounmap(reset); + goto out; + } + + /* actually hit reset */ + writel(MLX4_RESET_VALUE, reset + MLX4_RESET_OFFSET); + iounmap(reset); + + end = jiffies + MLX4_RESET_TIMEOUT_JIFFIES; + do { + if (!pci_read_config_word(dev->pdev, PCI_VENDOR_ID, &vendor) && + vendor != 0xffff) + break; + + msleep(1); + } while (time_before(jiffies, end)); + + if (vendor == 0xffff) { + err = -ENODEV; + mlx4_err(dev, "PCI device did not come back after reset, " + "aborting.\n"); + goto out; + } + + /* Now restore the PCI headers */ + if (pcie_cap) { + devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4]; + if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_DEVCTL, + devctl)) { + err = -ENODEV; + mlx4_err(dev, "Couldn't restore HCA PCI Express " + "Device Control register, aborting.\n"); + goto out; + } + linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4]; + if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_LNKCTL, + linkctl)) { + err = -ENODEV; + mlx4_err(dev, "Couldn't restore HCA PCI Express " + "Link control register, aborting.\n"); + goto out; + } + } + + for (i = 0; i < 16; ++i) { + if (i * 4 == PCI_COMMAND) + continue; + + if (pci_write_config_dword(dev->pdev, i * 4, hca_header[i])) { + err = -ENODEV; + mlx4_err(dev, "Couldn't restore HCA reg %x, " + "aborting.\n", i); + goto out; + } + } + + if (pci_write_config_dword(dev->pdev, PCI_COMMAND, + hca_header[PCI_COMMAND / 4])) { + err = -ENODEV; + mlx4_err(dev, "Couldn't restore HCA COMMAND, " + "aborting.\n"); + goto out; + } + +out: + kfree(hca_header); + + return err; +} diff --git a/drivers/net/mlx4/srq.c b/drivers/net/mlx4/srq.c new file mode 100644 index 00000000000..2134f83aed8 --- /dev/null +++ b/drivers/net/mlx4/srq.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include + +#include "mlx4.h" +#include "icm.h" + +struct mlx4_srq_context { + __be32 state_logsize_srqn; + u8 logstride; + u8 reserved1[3]; + u8 pg_offset; + u8 reserved2[3]; + u32 reserved3; + u8 log_page_size; + u8 reserved4[2]; + u8 mtt_base_addr_h; + __be32 mtt_base_addr_l; + __be32 pd; + __be16 limit_watermark; + __be16 wqe_cnt; + u16 reserved5; + __be16 wqe_counter; + u32 reserved6; + __be64 db_rec_addr; +}; + +void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) +{ + struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; + struct mlx4_srq *srq; + + spin_lock(&srq_table->lock); + + srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1)); + if (srq) + atomic_inc(&srq->refcount); + + spin_unlock(&srq_table->lock); + + if (!srq) { + mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn); + return; + } + + srq->event(srq, event_type); + + if (atomic_dec_and_test(&srq->refcount)) + complete(&srq->free); +} + +static int mlx4_SW2HW_SRQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int srq_num) +{ + return mlx4_cmd(dev, mailbox->dma, srq_num, 0, MLX4_CMD_SW2HW_SRQ, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_HW2SW_SRQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + int srq_num) +{ + return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, srq_num, + mailbox ? 0 : 1, MLX4_CMD_HW2SW_SRQ, + MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_ARM_SRQ(struct mlx4_dev *dev, int srq_num, int limit_watermark) +{ + return mlx4_cmd(dev, limit_watermark, srq_num, 0, MLX4_CMD_ARM_SRQ, + MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt, + u64 db_rec, struct mlx4_srq *srq) +{ + struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_srq_context *srq_context; + u64 mtt_addr; + int err; + + srq->srqn = mlx4_bitmap_alloc(&srq_table->bitmap); + if (srq->srqn == -1) + return -ENOMEM; + + err = mlx4_table_get(dev, &srq_table->table, srq->srqn); + if (err) + goto err_out; + + err = mlx4_table_get(dev, &srq_table->cmpt_table, srq->srqn); + if (err) + goto err_put; + + spin_lock_irq(&srq_table->lock); + err = radix_tree_insert(&srq_table->tree, srq->srqn, srq); + spin_unlock_irq(&srq_table->lock); + if (err) + goto err_cmpt_put; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = PTR_ERR(mailbox); + goto err_radix; + } + + srq_context = mailbox->buf; + memset(srq_context, 0, sizeof *srq_context); + + srq_context->state_logsize_srqn = cpu_to_be32((ilog2(srq->max) << 24) | + srq->srqn); + srq_context->logstride = srq->wqe_shift - 4; + srq_context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT; + + mtt_addr = mlx4_mtt_addr(dev, mtt); + srq_context->mtt_base_addr_h = mtt_addr >> 32; + srq_context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff); + srq_context->pd = cpu_to_be32(pdn); + srq_context->db_rec_addr = cpu_to_be64(db_rec); + + err = mlx4_SW2HW_SRQ(dev, mailbox, srq->srqn); + mlx4_free_cmd_mailbox(dev, mailbox); + if (err) + goto err_radix; + + atomic_set(&srq->refcount, 1); + init_completion(&srq->free); + + return 0; + +err_radix: + spin_lock_irq(&srq_table->lock); + radix_tree_delete(&srq_table->tree, srq->srqn); + spin_unlock_irq(&srq_table->lock); + +err_cmpt_put: + mlx4_table_put(dev, &srq_table->cmpt_table, srq->srqn); + +err_put: + mlx4_table_put(dev, &srq_table->table, srq->srqn); + +err_out: + mlx4_bitmap_free(&srq_table->bitmap, srq->srqn); + + return err; +} +EXPORT_SYMBOL_GPL(mlx4_srq_alloc); + +void mlx4_srq_free(struct mlx4_dev *dev, struct mlx4_srq *srq) +{ + struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; + int err; + + err = mlx4_HW2SW_SRQ(dev, NULL, srq->srqn); + if (err) + mlx4_warn(dev, "HW2SW_SRQ failed (%d) for SRQN %06x\n", err, srq->srqn); + + spin_lock_irq(&srq_table->lock); + radix_tree_delete(&srq_table->tree, srq->srqn); + spin_unlock_irq(&srq_table->lock); + + if (atomic_dec_and_test(&srq->refcount)) + complete(&srq->free); + wait_for_completion(&srq->free); + + mlx4_table_put(dev, &srq_table->table, srq->srqn); + mlx4_bitmap_free(&srq_table->bitmap, srq->srqn); +} +EXPORT_SYMBOL_GPL(mlx4_srq_free); + +int mlx4_srq_arm(struct mlx4_dev *dev, struct mlx4_srq *srq, int limit_watermark) +{ + return mlx4_ARM_SRQ(dev, srq->srqn, limit_watermark); +} +EXPORT_SYMBOL_GPL(mlx4_srq_arm); + +int __devinit mlx4_init_srq_table(struct mlx4_dev *dev) +{ + struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; + int err; + + spin_lock_init(&srq_table->lock); + INIT_RADIX_TREE(&srq_table->tree, GFP_ATOMIC); + + err = mlx4_bitmap_init(&srq_table->bitmap, dev->caps.num_srqs, + dev->caps.num_srqs - 1, dev->caps.reserved_srqs); + if (err) + return err; + + return 0; +} + +void mlx4_cleanup_srq_table(struct mlx4_dev *dev) +{ + mlx4_bitmap_cleanup(&mlx4_priv(dev)->srq_table.bitmap); +} diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h new file mode 100644 index 00000000000..4fb552d12f7 --- /dev/null +++ b/include/linux/mlx4/cmd.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_CMD_H +#define MLX4_CMD_H + +#include + +enum { + /* initialization and general commands */ + MLX4_CMD_SYS_EN = 0x1, + MLX4_CMD_SYS_DIS = 0x2, + MLX4_CMD_MAP_FA = 0xfff, + MLX4_CMD_UNMAP_FA = 0xffe, + MLX4_CMD_RUN_FW = 0xff6, + MLX4_CMD_MOD_STAT_CFG = 0x34, + MLX4_CMD_QUERY_DEV_CAP = 0x3, + MLX4_CMD_QUERY_FW = 0x4, + MLX4_CMD_ENABLE_LAM = 0xff8, + MLX4_CMD_DISABLE_LAM = 0xff7, + MLX4_CMD_QUERY_DDR = 0x5, + MLX4_CMD_QUERY_ADAPTER = 0x6, + MLX4_CMD_INIT_HCA = 0x7, + MLX4_CMD_CLOSE_HCA = 0x8, + MLX4_CMD_INIT_PORT = 0x9, + MLX4_CMD_CLOSE_PORT = 0xa, + MLX4_CMD_QUERY_HCA = 0xb, + MLX4_CMD_SET_PORT = 0xc, + MLX4_CMD_ACCESS_DDR = 0x2e, + MLX4_CMD_MAP_ICM = 0xffa, + MLX4_CMD_UNMAP_ICM = 0xff9, + MLX4_CMD_MAP_ICM_AUX = 0xffc, + MLX4_CMD_UNMAP_ICM_AUX = 0xffb, + MLX4_CMD_SET_ICM_SIZE = 0xffd, + + /* TPT commands */ + MLX4_CMD_SW2HW_MPT = 0xd, + MLX4_CMD_QUERY_MPT = 0xe, + MLX4_CMD_HW2SW_MPT = 0xf, + MLX4_CMD_READ_MTT = 0x10, + MLX4_CMD_WRITE_MTT = 0x11, + MLX4_CMD_SYNC_TPT = 0x2f, + + /* EQ commands */ + MLX4_CMD_MAP_EQ = 0x12, + MLX4_CMD_SW2HW_EQ = 0x13, + MLX4_CMD_HW2SW_EQ = 0x14, + MLX4_CMD_QUERY_EQ = 0x15, + + /* CQ commands */ + MLX4_CMD_SW2HW_CQ = 0x16, + MLX4_CMD_HW2SW_CQ = 0x17, + MLX4_CMD_QUERY_CQ = 0x18, + MLX4_CMD_RESIZE_CQ = 0x2c, + + /* SRQ commands */ + MLX4_CMD_SW2HW_SRQ = 0x35, + MLX4_CMD_HW2SW_SRQ = 0x36, + MLX4_CMD_QUERY_SRQ = 0x37, + MLX4_CMD_ARM_SRQ = 0x40, + + /* QP/EE commands */ + MLX4_CMD_RST2INIT_QP = 0x19, + MLX4_CMD_INIT2RTR_QP = 0x1a, + MLX4_CMD_RTR2RTS_QP = 0x1b, + MLX4_CMD_RTS2RTS_QP = 0x1c, + MLX4_CMD_SQERR2RTS_QP = 0x1d, + MLX4_CMD_2ERR_QP = 0x1e, + MLX4_CMD_RTS2SQD_QP = 0x1f, + MLX4_CMD_SQD2SQD_QP = 0x38, + MLX4_CMD_SQD2RTS_QP = 0x20, + MLX4_CMD_2RST_QP = 0x21, + MLX4_CMD_QUERY_QP = 0x22, + MLX4_CMD_INIT2INIT_QP = 0x2d, + MLX4_CMD_SUSPEND_QP = 0x32, + MLX4_CMD_UNSUSPEND_QP = 0x33, + /* special QP and management commands */ + MLX4_CMD_CONF_SPECIAL_QP = 0x23, + MLX4_CMD_MAD_IFC = 0x24, + + /* multicast commands */ + MLX4_CMD_READ_MCG = 0x25, + MLX4_CMD_WRITE_MCG = 0x26, + MLX4_CMD_MGID_HASH = 0x27, + + /* miscellaneous commands */ + MLX4_CMD_DIAG_RPRT = 0x30, + MLX4_CMD_NOP = 0x31, + + /* debug commands */ + MLX4_CMD_QUERY_DEBUG_MSG = 0x2a, + MLX4_CMD_SET_DEBUG_MSG = 0x2b, +}; + +enum { + MLX4_CMD_TIME_CLASS_A = 10000, + MLX4_CMD_TIME_CLASS_B = 10000, + MLX4_CMD_TIME_CLASS_C = 10000, +}; + +enum { + MLX4_MAILBOX_SIZE = 4096 +}; + +struct mlx4_dev; + +struct mlx4_cmd_mailbox { + void *buf; + dma_addr_t dma; +}; + +int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + int out_is_imm, u32 in_modifier, u8 op_modifier, + u16 op, unsigned long timeout); + +/* Invoke a command with no output parameter */ +static inline int mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u32 in_modifier, + u8 op_modifier, u16 op, unsigned long timeout) +{ + return __mlx4_cmd(dev, in_param, NULL, 0, in_modifier, + op_modifier, op, timeout); +} + +/* Invoke a command with an output mailbox */ +static inline int mlx4_cmd_box(struct mlx4_dev *dev, u64 in_param, u64 out_param, + u32 in_modifier, u8 op_modifier, u16 op, + unsigned long timeout) +{ + return __mlx4_cmd(dev, in_param, &out_param, 0, in_modifier, + op_modifier, op, timeout); +} + +/* + * Invoke a command with an immediate output parameter (and copy the + * output into the caller's out_param pointer after the command + * executes). + */ +static inline int mlx4_cmd_imm(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + u32 in_modifier, u8 op_modifier, u16 op, + unsigned long timeout) +{ + return __mlx4_cmd(dev, in_param, out_param, 1, in_modifier, + op_modifier, op, timeout); +} + +struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev); +void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox); + +#endif /* MLX4_CMD_H */ diff --git a/include/linux/mlx4/cq.h b/include/linux/mlx4/cq.h new file mode 100644 index 00000000000..0181e0a57cb --- /dev/null +++ b/include/linux/mlx4/cq.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_CQ_H +#define MLX4_CQ_H + +#include + +#include +#include + +struct mlx4_cqe { + __be32 my_qpn; + __be32 immed_rss_invalid; + __be32 g_mlpath_rqpn; + u8 sl; + u8 reserved1; + __be16 rlid; + u32 reserved2; + __be32 byte_cnt; + __be16 wqe_index; + __be16 checksum; + u8 reserved3[3]; + u8 owner_sr_opcode; +}; + +struct mlx4_err_cqe { + __be32 my_qpn; + u32 reserved1[5]; + __be16 wqe_index; + u8 vendor_err_syndrome; + u8 syndrome; + u8 reserved2[3]; + u8 owner_sr_opcode; +}; + +enum { + MLX4_CQE_OWNER_MASK = 0x80, + MLX4_CQE_IS_SEND_MASK = 0x40, + MLX4_CQE_OPCODE_MASK = 0x1f +}; + +enum { + MLX4_CQE_SYNDROME_LOCAL_LENGTH_ERR = 0x01, + MLX4_CQE_SYNDROME_LOCAL_QP_OP_ERR = 0x02, + MLX4_CQE_SYNDROME_LOCAL_PROT_ERR = 0x04, + MLX4_CQE_SYNDROME_WR_FLUSH_ERR = 0x05, + MLX4_CQE_SYNDROME_MW_BIND_ERR = 0x06, + MLX4_CQE_SYNDROME_BAD_RESP_ERR = 0x10, + MLX4_CQE_SYNDROME_LOCAL_ACCESS_ERR = 0x11, + MLX4_CQE_SYNDROME_REMOTE_INVAL_REQ_ERR = 0x12, + MLX4_CQE_SYNDROME_REMOTE_ACCESS_ERR = 0x13, + MLX4_CQE_SYNDROME_REMOTE_OP_ERR = 0x14, + MLX4_CQE_SYNDROME_TRANSPORT_RETRY_EXC_ERR = 0x15, + MLX4_CQE_SYNDROME_RNR_RETRY_EXC_ERR = 0x16, + MLX4_CQE_SYNDROME_REMOTE_ABORTED_ERR = 0x22, +}; + +static inline void mlx4_cq_arm(struct mlx4_cq *cq, u32 cmd, + void __iomem *uar_page, + spinlock_t *doorbell_lock) +{ + __be32 doorbell[2]; + u32 sn; + u32 ci; + + sn = cq->arm_sn & 3; + ci = cq->cons_index & 0xffffff; + + *cq->arm_db = cpu_to_be32(sn << 28 | cmd | ci); + + /* + * Make sure that the doorbell record in host memory is + * written before ringing the doorbell via PCI MMIO. + */ + wmb(); + + doorbell[0] = cpu_to_be32(sn << 28 | cmd | cq->cqn); + doorbell[1] = cpu_to_be32(ci); + + mlx4_write64(doorbell, uar_page + MLX4_CQ_DOORBELL, doorbell_lock); +} + +static inline void mlx4_cq_set_ci(struct mlx4_cq *cq) +{ + *cq->set_ci_db = cpu_to_be32(cq->cons_index & 0xffffff); +} + +enum { + MLX4_CQ_DB_REQ_NOT_SOL = 1 << 24, + MLX4_CQ_DB_REQ_NOT = 2 << 24 +}; + +#endif /* MLX4_CQ_H */ diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h new file mode 100644 index 00000000000..8c5f8fd8684 --- /dev/null +++ b/include/linux/mlx4/device.h @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2006, 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_DEVICE_H +#define MLX4_DEVICE_H + +#include +#include +#include + +#include + +enum { + MLX4_FLAG_MSI_X = 1 << 0, +}; + +enum { + MLX4_MAX_PORTS = 2 +}; + +enum { + MLX4_DEV_CAP_FLAG_RC = 1 << 0, + MLX4_DEV_CAP_FLAG_UC = 1 << 1, + MLX4_DEV_CAP_FLAG_UD = 1 << 2, + MLX4_DEV_CAP_FLAG_SRQ = 1 << 6, + MLX4_DEV_CAP_FLAG_IPOIB_CSUM = 1 << 7, + MLX4_DEV_CAP_FLAG_BAD_PKEY_CNTR = 1 << 8, + MLX4_DEV_CAP_FLAG_BAD_QKEY_CNTR = 1 << 9, + MLX4_DEV_CAP_FLAG_MEM_WINDOW = 1 << 16, + MLX4_DEV_CAP_FLAG_APM = 1 << 17, + MLX4_DEV_CAP_FLAG_ATOMIC = 1 << 18, + MLX4_DEV_CAP_FLAG_RAW_MCAST = 1 << 19, + MLX4_DEV_CAP_FLAG_UD_AV_PORT = 1 << 20, + MLX4_DEV_CAP_FLAG_UD_MCAST = 1 << 21 +}; + +enum mlx4_event { + MLX4_EVENT_TYPE_COMP = 0x00, + MLX4_EVENT_TYPE_PATH_MIG = 0x01, + MLX4_EVENT_TYPE_COMM_EST = 0x02, + MLX4_EVENT_TYPE_SQ_DRAINED = 0x03, + MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE = 0x13, + MLX4_EVENT_TYPE_SRQ_LIMIT = 0x14, + MLX4_EVENT_TYPE_CQ_ERROR = 0x04, + MLX4_EVENT_TYPE_WQ_CATAS_ERROR = 0x05, + MLX4_EVENT_TYPE_EEC_CATAS_ERROR = 0x06, + MLX4_EVENT_TYPE_PATH_MIG_FAILED = 0x07, + MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10, + MLX4_EVENT_TYPE_WQ_ACCESS_ERROR = 0x11, + MLX4_EVENT_TYPE_SRQ_CATAS_ERROR = 0x12, + MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR = 0x08, + MLX4_EVENT_TYPE_PORT_CHANGE = 0x09, + MLX4_EVENT_TYPE_EQ_OVERFLOW = 0x0f, + MLX4_EVENT_TYPE_ECC_DETECT = 0x0e, + MLX4_EVENT_TYPE_CMD = 0x0a +}; + +enum { + MLX4_PORT_CHANGE_SUBTYPE_DOWN = 1, + MLX4_PORT_CHANGE_SUBTYPE_ACTIVE = 4 +}; + +enum { + MLX4_PERM_LOCAL_READ = 1 << 10, + MLX4_PERM_LOCAL_WRITE = 1 << 11, + MLX4_PERM_REMOTE_READ = 1 << 12, + MLX4_PERM_REMOTE_WRITE = 1 << 13, + MLX4_PERM_ATOMIC = 1 << 14 +}; + +enum { + MLX4_OPCODE_NOP = 0x00, + MLX4_OPCODE_SEND_INVAL = 0x01, + MLX4_OPCODE_RDMA_WRITE = 0x08, + MLX4_OPCODE_RDMA_WRITE_IMM = 0x09, + MLX4_OPCODE_SEND = 0x0a, + MLX4_OPCODE_SEND_IMM = 0x0b, + MLX4_OPCODE_LSO = 0x0e, + MLX4_OPCODE_RDMA_READ = 0x10, + MLX4_OPCODE_ATOMIC_CS = 0x11, + MLX4_OPCODE_ATOMIC_FA = 0x12, + MLX4_OPCODE_ATOMIC_MASK_CS = 0x14, + MLX4_OPCODE_ATOMIC_MASK_FA = 0x15, + MLX4_OPCODE_BIND_MW = 0x18, + MLX4_OPCODE_FMR = 0x19, + MLX4_OPCODE_LOCAL_INVAL = 0x1b, + MLX4_OPCODE_CONFIG_CMD = 0x1f, + + MLX4_RECV_OPCODE_RDMA_WRITE_IMM = 0x00, + MLX4_RECV_OPCODE_SEND = 0x01, + MLX4_RECV_OPCODE_SEND_IMM = 0x02, + MLX4_RECV_OPCODE_SEND_INVAL = 0x03, + + MLX4_CQE_OPCODE_ERROR = 0x1e, + MLX4_CQE_OPCODE_RESIZE = 0x16, +}; + +enum { + MLX4_STAT_RATE_OFFSET = 5 +}; + +struct mlx4_caps { + u64 fw_ver; + int num_ports; + int vl_cap; + int mtu_cap; + int gid_table_len; + int pkey_table_len; + int local_ca_ack_delay; + int num_uars; + int bf_reg_size; + int bf_regs_per_page; + int max_sq_sg; + int max_rq_sg; + int num_qps; + int max_wqes; + int max_sq_desc_sz; + int max_rq_desc_sz; + int max_qp_init_rdma; + int max_qp_dest_rdma; + int reserved_qps; + int sqp_start; + int num_srqs; + int max_srq_wqes; + int max_srq_sge; + int reserved_srqs; + int num_cqs; + int max_cqes; + int reserved_cqs; + int num_eqs; + int reserved_eqs; + int num_mpts; + int num_mtt_segs; + int fmr_reserved_mtts; + int reserved_mtts; + int reserved_mrws; + int reserved_uars; + int num_mgms; + int num_amgms; + int reserved_mcgs; + int num_qp_per_mgm; + int num_pds; + int reserved_pds; + int mtt_entry_sz; + u32 page_size_cap; + u32 flags; + u16 stat_rate_support; + u8 port_width_cap; +}; + +struct mlx4_buf_list { + void *buf; + dma_addr_t map; +}; + +struct mlx4_buf { + union { + struct mlx4_buf_list direct; + struct mlx4_buf_list *page_list; + } u; + int nbufs; + int npages; + int page_shift; +}; + +struct mlx4_mtt { + u32 first_seg; + int order; + int page_shift; +}; + +struct mlx4_mr { + struct mlx4_mtt mtt; + u64 iova; + u64 size; + u32 key; + u32 pd; + u32 access; + int enabled; +}; + +struct mlx4_uar { + unsigned long pfn; + int index; +}; + +struct mlx4_cq { + void (*comp) (struct mlx4_cq *); + void (*event) (struct mlx4_cq *, enum mlx4_event); + + struct mlx4_uar *uar; + + u32 cons_index; + + __be32 *set_ci_db; + __be32 *arm_db; + int arm_sn; + + int cqn; + + atomic_t refcount; + struct completion free; +}; + +struct mlx4_qp { + void (*event) (struct mlx4_qp *, enum mlx4_event); + + int qpn; + + atomic_t refcount; + struct completion free; +}; + +struct mlx4_srq { + void (*event) (struct mlx4_srq *, enum mlx4_event); + + int srqn; + int max; + int max_gs; + int wqe_shift; + + atomic_t refcount; + struct completion free; +}; + +struct mlx4_av { + __be32 port_pd; + u8 reserved1; + u8 g_slid; + __be16 dlid; + u8 reserved2; + u8 gid_index; + u8 stat_rate; + u8 hop_limit; + __be32 sl_tclass_flowlabel; + u8 dgid[16]; +}; + +struct mlx4_dev { + struct pci_dev *pdev; + unsigned long flags; + struct mlx4_caps caps; + struct radix_tree_root qp_table_tree; +}; + +struct mlx4_init_port_param { + int set_guid0; + int set_node_guid; + int set_si_guid; + u16 mtu; + int port_width_cap; + u16 vl_cap; + u16 max_gid; + u16 max_pkey; + u64 guid0; + u64 node_guid; + u64 si_guid; +}; + +int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, + struct mlx4_buf *buf); +void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf); + +int mlx4_pd_alloc(struct mlx4_dev *dev, u32 *pdn); +void mlx4_pd_free(struct mlx4_dev *dev, u32 pdn); + +int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar); +void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar); + +int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift, + struct mlx4_mtt *mtt); +void mlx4_mtt_cleanup(struct mlx4_dev *dev, struct mlx4_mtt *mtt); +u64 mlx4_mtt_addr(struct mlx4_dev *dev, struct mlx4_mtt *mtt); + +int mlx4_mr_alloc(struct mlx4_dev *dev, u32 pd, u64 iova, u64 size, u32 access, + int npages, int page_shift, struct mlx4_mr *mr); +void mlx4_mr_free(struct mlx4_dev *dev, struct mlx4_mr *mr); +int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr); +int mlx4_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + int start_index, int npages, u64 *page_list); +int mlx4_buf_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + struct mlx4_buf *buf); + +int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt, + struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq); +void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq); + +int mlx4_qp_alloc(struct mlx4_dev *dev, int sqpn, struct mlx4_qp *qp); +void mlx4_qp_free(struct mlx4_dev *dev, struct mlx4_qp *qp); + +int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, struct mlx4_mtt *mtt, + u64 db_rec, struct mlx4_srq *srq); +void mlx4_srq_free(struct mlx4_dev *dev, struct mlx4_srq *srq); +int mlx4_srq_arm(struct mlx4_dev *dev, struct mlx4_srq *srq, int limit_watermark); + +int mlx4_INIT_PORT(struct mlx4_dev *dev, struct mlx4_init_port_param *param, int port); +int mlx4_CLOSE_PORT(struct mlx4_dev *dev, int port); + +int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]); +int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16]); + +#endif /* MLX4_DEVICE_H */ diff --git a/include/linux/mlx4/doorbell.h b/include/linux/mlx4/doorbell.h new file mode 100644 index 00000000000..3f2da442d7c --- /dev/null +++ b/include/linux/mlx4/doorbell.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2005 Mellanox Technologies. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_DOORBELL_H +#define MLX4_DOORBELL_H + +#include +#include + +#define MLX4_SEND_DOORBELL 0x14 +#define MLX4_CQ_DOORBELL 0x20 + +#if BITS_PER_LONG == 64 +/* + * Assume that we can just write a 64-bit doorbell atomically. s390 + * actually doesn't have writeq() but S/390 systems don't even have + * PCI so we won't worry about it. + */ + +#define MLX4_DECLARE_DOORBELL_LOCK(name) +#define MLX4_INIT_DOORBELL_LOCK(ptr) do { } while (0) +#define MLX4_GET_DOORBELL_LOCK(ptr) (NULL) + +static inline void mlx4_write64_raw(__be64 val, void __iomem *dest) +{ + __raw_writeq((__force u64) val, dest); +} + +static inline void mlx4_write64(__be32 val[2], void __iomem *dest, + spinlock_t *doorbell_lock) +{ + __raw_writeq(*(u64 *) val, dest); +} + +#else + +/* + * Just fall back to a spinlock to protect the doorbell if + * BITS_PER_LONG is 32 -- there's no portable way to do atomic 64-bit + * MMIO writes. + */ + +#define MLX4_DECLARE_DOORBELL_LOCK(name) spinlock_t name; +#define MLX4_INIT_DOORBELL_LOCK(ptr) spin_lock_init(ptr) +#define MLX4_GET_DOORBELL_LOCK(ptr) (ptr) + +static inline void mlx4_write64_raw(__be64 val, void __iomem *dest) +{ + __raw_writel(((__force u32 *) &val)[0], dest); + __raw_writel(((__force u32 *) &val)[1], dest + 4); +} + +static inline void mlx4_write64(__be32 val[2], void __iomem *dest, + spinlock_t *doorbell_lock) +{ + unsigned long flags; + + spin_lock_irqsave(doorbell_lock, flags); + __raw_writel((__force u32) val[0], dest); + __raw_writel((__force u32) val[1], dest + 4); + spin_unlock_irqrestore(doorbell_lock, flags); +} + +#endif + +#endif /* MLX4_DOORBELL_H */ diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h new file mode 100644 index 00000000000..1b835ca49df --- /dev/null +++ b/include/linux/mlx4/driver.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_DRIVER_H +#define MLX4_DRIVER_H + +#include + +struct mlx4_dev; + +enum mlx4_dev_event { + MLX4_DEV_EVENT_CATASTROPHIC_ERROR, + MLX4_DEV_EVENT_PORT_UP, + MLX4_DEV_EVENT_PORT_DOWN, + MLX4_DEV_EVENT_PORT_REINIT, +}; + +struct mlx4_interface { + void * (*add) (struct mlx4_dev *dev); + void (*remove)(struct mlx4_dev *dev, void *context); + void (*event) (struct mlx4_dev *dev, void *context, + enum mlx4_dev_event event, int subtype, + int port); + struct list_head list; +}; + +int mlx4_register_interface(struct mlx4_interface *intf); +void mlx4_unregister_interface(struct mlx4_interface *intf); + +#endif /* MLX4_DRIVER_H */ diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h new file mode 100644 index 00000000000..9eeb61adf6a --- /dev/null +++ b/include/linux/mlx4/qp.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_QP_H +#define MLX4_QP_H + +#include + +#include + +#define MLX4_INVALID_LKEY 0x100 + +enum mlx4_qp_optpar { + MLX4_QP_OPTPAR_ALT_ADDR_PATH = 1 << 0, + MLX4_QP_OPTPAR_RRE = 1 << 1, + MLX4_QP_OPTPAR_RAE = 1 << 2, + MLX4_QP_OPTPAR_RWE = 1 << 3, + MLX4_QP_OPTPAR_PKEY_INDEX = 1 << 4, + MLX4_QP_OPTPAR_Q_KEY = 1 << 5, + MLX4_QP_OPTPAR_RNR_TIMEOUT = 1 << 6, + MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH = 1 << 7, + MLX4_QP_OPTPAR_SRA_MAX = 1 << 8, + MLX4_QP_OPTPAR_RRA_MAX = 1 << 9, + MLX4_QP_OPTPAR_PM_STATE = 1 << 10, + MLX4_QP_OPTPAR_RETRY_COUNT = 1 << 12, + MLX4_QP_OPTPAR_RNR_RETRY = 1 << 13, + MLX4_QP_OPTPAR_ACK_TIMEOUT = 1 << 14, + MLX4_QP_OPTPAR_SCHED_QUEUE = 1 << 16 +}; + +enum mlx4_qp_state { + MLX4_QP_STATE_RST = 0, + MLX4_QP_STATE_INIT = 1, + MLX4_QP_STATE_RTR = 2, + MLX4_QP_STATE_RTS = 3, + MLX4_QP_STATE_SQER = 4, + MLX4_QP_STATE_SQD = 5, + MLX4_QP_STATE_ERR = 6, + MLX4_QP_STATE_SQ_DRAINING = 7, + MLX4_QP_NUM_STATE +}; + +enum { + MLX4_QP_ST_RC = 0x0, + MLX4_QP_ST_UC = 0x1, + MLX4_QP_ST_RD = 0x2, + MLX4_QP_ST_UD = 0x3, + MLX4_QP_ST_MLX = 0x7 +}; + +enum { + MLX4_QP_PM_MIGRATED = 0x3, + MLX4_QP_PM_ARMED = 0x0, + MLX4_QP_PM_REARM = 0x1 +}; + +enum { + /* params1 */ + MLX4_QP_BIT_SRE = 1 << 15, + MLX4_QP_BIT_SWE = 1 << 14, + MLX4_QP_BIT_SAE = 1 << 13, + /* params2 */ + MLX4_QP_BIT_RRE = 1 << 15, + MLX4_QP_BIT_RWE = 1 << 14, + MLX4_QP_BIT_RAE = 1 << 13, + MLX4_QP_BIT_RIC = 1 << 4, +}; + +struct mlx4_qp_path { + u8 fl; + u8 reserved1[2]; + u8 pkey_index; + u8 reserved2; + u8 grh_mylmc; + __be16 rlid; + u8 ackto; + u8 mgid_index; + u8 static_rate; + u8 hop_limit; + __be32 tclass_flowlabel; + u8 rgid[16]; + u8 sched_queue; + u8 snooper_flags; + u8 reserved3[2]; + u8 counter_index; + u8 reserved4[7]; +}; + +struct mlx4_qp_context { + __be32 flags; + __be32 pd; + u8 mtu_msgmax; + u8 rq_size_stride; + u8 sq_size_stride; + u8 rlkey; + __be32 usr_page; + __be32 local_qpn; + __be32 remote_qpn; + struct mlx4_qp_path pri_path; + struct mlx4_qp_path alt_path; + __be32 params1; + u32 reserved1; + __be32 next_send_psn; + __be32 cqn_send; + u32 reserved2[2]; + __be32 last_acked_psn; + __be32 ssn; + __be32 params2; + __be32 rnr_nextrecvpsn; + __be32 srcd; + __be32 cqn_recv; + __be64 db_rec_addr; + __be32 qkey; + __be32 srqn; + __be32 msn; + __be16 rq_wqe_counter; + __be16 sq_wqe_counter; + u32 reserved3[2]; + __be32 param3; + __be32 nummmcpeers_basemkey; + u8 log_page_size; + u8 reserved4[2]; + u8 mtt_base_addr_h; + __be32 mtt_base_addr_l; + u32 reserved5[10]; +}; + +enum { + MLX4_WQE_CTRL_FENCE = 1 << 6, + MLX4_WQE_CTRL_CQ_UPDATE = 3 << 2, + MLX4_WQE_CTRL_SOLICITED = 1 << 1, +}; + +struct mlx4_wqe_ctrl_seg { + __be32 owner_opcode; + u8 reserved2[3]; + u8 fence_size; + /* + * High 24 bits are SRC remote buffer; low 8 bits are flags: + * [7] SO (strong ordering) + * [5] TCP/UDP checksum + * [4] IP checksum + * [3:2] C (generate completion queue entry) + * [1] SE (solicited event) + */ + __be32 srcrb_flags; + /* + * imm is immediate data for send/RDMA write w/ immediate; + * also invalidation key for send with invalidate; input + * modifier for WQEs on CCQs. + */ + __be32 imm; +}; + +enum { + MLX4_WQE_MLX_VL15 = 1 << 17, + MLX4_WQE_MLX_SLR = 1 << 16 +}; + +struct mlx4_wqe_mlx_seg { + u8 owner; + u8 reserved1[2]; + u8 opcode; + u8 reserved2[3]; + u8 size; + /* + * [17] VL15 + * [16] SLR + * [15:12] static rate + * [11:8] SL + * [4] ICRC + * [3:2] C + * [0] FL (force loopback) + */ + __be32 flags; + __be16 rlid; + u16 reserved3; +}; + +struct mlx4_wqe_datagram_seg { + __be32 av[8]; + __be32 dqpn; + __be32 qkey; + __be32 reservd[2]; +}; + +struct mlx4_wqe_bind_seg { + __be32 flags1; + __be32 flags2; + __be32 new_rkey; + __be32 lkey; + __be64 addr; + __be64 length; +}; + +struct mlx4_wqe_fmr_seg { + __be32 flags; + __be32 mem_key; + __be64 buf_list; + __be64 start_addr; + __be64 reg_len; + __be32 offset; + __be32 page_size; + u32 reserved[2]; +}; + +struct mlx4_wqe_fmr_ext_seg { + u8 flags; + u8 reserved; + __be16 app_mask; + __be16 wire_app_tag; + __be16 mem_app_tag; + __be32 wire_ref_tag_base; + __be32 mem_ref_tag_base; +}; + +struct mlx4_wqe_local_inval_seg { + u8 flags; + u8 reserved1[3]; + __be32 mem_key; + u8 reserved2[3]; + u8 guest_id; + __be64 pa; +}; + +struct mlx4_wqe_raddr_seg { + __be64 raddr; + __be32 rkey; + u32 reserved; +}; + +struct mlx4_wqe_atomic_seg { + __be64 swap_add; + __be64 compare; +}; + +struct mlx4_wqe_data_seg { + __be32 byte_count; + __be32 lkey; + __be64 addr; +}; + +struct mlx4_wqe_inline_seg { + __be32 byte_count; +}; + +int mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, + enum mlx4_qp_state cur_state, enum mlx4_qp_state new_state, + struct mlx4_qp_context *context, enum mlx4_qp_optpar optpar, + int sqd_event, struct mlx4_qp *qp); + +static inline struct mlx4_qp *__mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn) +{ + return radix_tree_lookup(&dev->qp_table_tree, qpn & (dev->caps.num_qps - 1)); +} + +void mlx4_qp_remove(struct mlx4_dev *dev, struct mlx4_qp *qp); + +#endif /* MLX4_QP_H */ diff --git a/include/linux/mlx4/srq.h b/include/linux/mlx4/srq.h new file mode 100644 index 00000000000..799a0697a38 --- /dev/null +++ b/include/linux/mlx4/srq.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2007 Cisco Systems, Inc. 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_SRQ_H +#define MLX4_SRQ_H + +struct mlx4_wqe_srq_next_seg { + u16 reserved1; + __be16 next_wqe_index; + u32 reserved2[3]; +}; + +#endif /* MLX4_SRQ_H */ -- cgit v1.2.3 From 5f8c9908f200b775a3d6c345bc6f3e928e2426a9 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 11:55:21 +0900 Subject: sh: generic quicklist support. This moves SH over to the generic quicklists. As per x86_64, we have special mappings for the PGDs, so these go on their own list.. Signed-off-by: Paul Mundt --- arch/sh/kernel/process.c | 2 ++ arch/sh/mm/Kconfig | 3 +++ arch/sh/mm/init.c | 2 ++ include/asm-sh/pgalloc.h | 44 ++++++++++++++++++++++++++++---------------- mm/Kconfig | 2 +- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 329b3f3051d..4688b890aef 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -18,6 +18,7 @@ #include #include #include +#include #include static int hlt_counter; @@ -64,6 +65,7 @@ void cpu_idle(void) preempt_enable_no_resched(); schedule(); preempt_disable(); + check_pgt_cache(); } } diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index 12f3d394dc2..b773361892c 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -218,6 +218,9 @@ endmenu menu "Memory management options" +config QUICKLIST + def_bool y + config MMU bool "Support for memory management hardware" depends on !CPU_SH2 diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 4d030988b36..8fe223a890e 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -67,6 +67,8 @@ void show_mem(void) printk("%d slab pages\n", slab); printk("%d pages shared\n", shared); printk("%d pages swap cached\n", cached); + printk(KERN_INFO "Total of %ld pages in page table cache\n", + quicklist_total_size()); } #ifdef CONFIG_MMU diff --git a/include/asm-sh/pgalloc.h b/include/asm-sh/pgalloc.h index 888e4529e6f..26e493f39db 100644 --- a/include/asm-sh/pgalloc.h +++ b/include/asm-sh/pgalloc.h @@ -1,6 +1,12 @@ #ifndef __ASM_SH_PGALLOC_H #define __ASM_SH_PGALLOC_H +#include +#include + +#define QUICK_PGD 0 /* We preserve special mappings over free */ +#define QUICK_PT 1 /* Other page table pages that are zero on free */ + static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) { @@ -13,48 +19,49 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, set_pmd(pmd, __pmd((unsigned long)page_address(pte))); } +static inline void pgd_ctor(void *x) +{ + pgd_t *pgd = x; + + memcpy(pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); +} + /* * Allocate and free page tables. */ static inline pgd_t *pgd_alloc(struct mm_struct *mm) { - pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT); - - if (pgd) { - memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); - memcpy(pgd + USER_PTRS_PER_PGD, - swapper_pg_dir + USER_PTRS_PER_PGD, - (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); - } - - return pgd; + return quicklist_alloc(QUICK_PGD, GFP_KERNEL | __GFP_REPEAT, pgd_ctor); } static inline void pgd_free(pgd_t *pgd) { - free_page((unsigned long)pgd); + quicklist_free(QUICK_PGD, NULL, pgd); } static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO); + return quicklist_alloc(QUICK_PT, GFP_KERNEL, NULL); } static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) { - return alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO); + void *pg = quicklist_alloc(QUICK_PT, GFP_KERNEL, NULL); + return pg ? virt_to_page(pg) : NULL; } static inline void pte_free_kernel(pte_t *pte) { - free_page((unsigned long)pte); + quicklist_free(QUICK_PT, NULL, pte); } static inline void pte_free(struct page *pte) { - __free_page(pte); + quicklist_free_page(QUICK_PT, NULL, pte); } #define __pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte)) @@ -66,6 +73,11 @@ static inline void pte_free(struct page *pte) #define pmd_free(x) do { } while (0) #define __pmd_free_tlb(tlb,x) do { } while (0) -#define check_pgt_cache() do { } while (0) + +static inline void check_pgt_cache(void) +{ + quicklist_trim(QUICK_PGD, NULL, 25, 16); + quicklist_trim(QUICK_PT, NULL, 25, 16); +} #endif /* __ASM_SH_PGALLOC_H */ diff --git a/mm/Kconfig b/mm/Kconfig index 1ac718f636e..a17da8bafe6 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -166,5 +166,5 @@ config ZONE_DMA_FLAG config NR_QUICK int depends on QUICKLIST + default "2" if SUPERH default "1" - -- cgit v1.2.3 From 435c55d1ef3ec5460fab8c332a693ef5fad18454 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 11:56:27 +0900 Subject: rtc: rtc-sh: Fix up dev_dbg() warnings. Signed-off-by: Paul Mundt --- drivers/rtc/rtc-sh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 6abf4811958..bde3d34cb00 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -341,7 +341,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_sec--; #endif - dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, -- cgit v1.2.3 From 1039b9a9d830e906efa770db75db8a3e11c47c1a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 12:07:21 +0900 Subject: sh: __GFP_REPEAT for pte allocations, too. This got dropped in the quicklist conversion, add it back in.. Signed-off-by: Paul Mundt --- include/asm-sh/pgalloc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/asm-sh/pgalloc.h b/include/asm-sh/pgalloc.h index 26e493f39db..18b613c57cf 100644 --- a/include/asm-sh/pgalloc.h +++ b/include/asm-sh/pgalloc.h @@ -44,13 +44,13 @@ static inline void pgd_free(pgd_t *pgd) static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - return quicklist_alloc(QUICK_PT, GFP_KERNEL, NULL); + return quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL); } static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) { - void *pg = quicklist_alloc(QUICK_PT, GFP_KERNEL, NULL); + void *pg = quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL); return pg ? virt_to_page(pg) : NULL; } -- cgit v1.2.3 From 44530c696b3fc2c8a45bdc798af85528e065ed80 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 12:14:54 +0900 Subject: sh: Always define TRAPA_BUG_OPCODE. Previously this was only set when CONFIG_BUG=y. While we rely on that for handle_BUG() dispatch, we still want to hand the opcode off to the die chain notifier for determining the trap value. Signed-off-by: Paul Mundt --- include/asm-sh/bug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/asm-sh/bug.h b/include/asm-sh/bug.h index 794c36daf06..46f925c815a 100644 --- a/include/asm-sh/bug.h +++ b/include/asm-sh/bug.h @@ -1,12 +1,12 @@ #ifndef __ASM_SH_BUG_H #define __ASM_SH_BUG_H +#define TRAPA_BUG_OPCODE 0xc33e /* trapa #0x3e */ + #ifdef CONFIG_BUG #define HAVE_ARCH_BUG #define HAVE_ARCH_WARN_ON -#define TRAPA_BUG_OPCODE 0xc33e /* trapa #0x3e */ - /** * _EMIT_BUG_ENTRY * %1 - __FILE__ -- cgit v1.2.3 From 00bf0610e4cdbb9fd79ce4718e12b24cd7876dd7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 13:01:37 +0900 Subject: sh: Share exception vector table for SH-3/4. The only difference between these at the moment are the FPU exceptions, and these are hidden away under CONFIG_SH_FPU (which is only set for the SH-4 case anyways..). This consolidates the two tables, and updates SH-4 to use the updated copy. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh3/ex.S | 13 +++++---- arch/sh/kernel/cpu/sh4/Makefile | 6 ++-- arch/sh/kernel/cpu/sh4/ex.S | 62 ----------------------------------------- 3 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 arch/sh/kernel/cpu/sh4/ex.S diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index ba3082d640b..2b2a9e02fb7 100644 --- a/arch/sh/kernel/cpu/sh3/ex.S +++ b/arch/sh/kernel/cpu/sh3/ex.S @@ -1,7 +1,7 @@ /* * arch/sh/kernel/cpu/sh3/ex.S * - * The SH-3 exception vector table. + * The SH-3 and SH-4 exception vector table. * Copyright (C) 1999, 2000, 2002 Niibe Yutaka * Copyright (C) 2003 - 2006 Paul Mundt @@ -9,7 +9,6 @@ * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. - * */ #include @@ -36,8 +35,12 @@ ENTRY(exception_handling_table) .long exception_error ! address error load .long exception_error ! address error store /* 100 */ #endif - .long exception_error ! fpu_exception /* 120 */ - .long exception_error /* 140 */ +#if defined(CONFIG_SH_FPU) + .long do_fpu_error /* 120 */ +#else + .long exception_error /* 120 */ +#endif + .long exception_error /* 140 */ .long system_call ! Unconditional Trap /* 160 */ .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ @@ -55,4 +58,4 @@ ENTRY(user_break_point_trap) * away offsets can be manually inserted in to their appropriate * location via set_exception_table_{evt,vec}(). */ - .balign 4096,0,4096 + .balign 4096,0,4096 diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index 19ca68c7188..8add10bd826 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -2,10 +2,10 @@ # Makefile for the Linux/SuperH SH-4 backends. # -obj-y := ex.o probe.o common.o -common-y += $(addprefix ../sh3/, entry.o) +obj-y := probe.o common.o +common-y += $(addprefix ../sh3/, entry.o ex.o) -obj-$(CONFIG_SH_FPU) += fpu.o +obj-$(CONFIG_SH_FPU) += fpu.o obj-$(CONFIG_SH_STORE_QUEUES) += sq.o # CPU subtype setup diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S deleted file mode 100644 index ac8ab57413c..00000000000 --- a/arch/sh/kernel/cpu/sh4/ex.S +++ /dev/null @@ -1,62 +0,0 @@ -/* - * arch/sh/kernel/cpu/sh4/ex.S - * - * The SH-4 exception vector table. - - * Copyright (C) 1999, 2000, 2002 Niibe Yutaka - * Copyright (C) 2003 - 2006 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - */ -#include - - .align 2 - .data - -ENTRY(exception_handling_table) - .long exception_error /* 000 */ - .long exception_error -#if defined(CONFIG_MMU) - .long tlb_miss_load /* 040 */ - .long tlb_miss_store - .long initial_page_write - .long tlb_protection_violation_load - .long tlb_protection_violation_store - .long address_error_load - .long address_error_store /* 100 */ -#else - .long exception_error ! tlb miss load /* 040 */ - .long exception_error ! tlb miss store - .long exception_error ! initial page write - .long exception_error ! tlb prot violation load - .long exception_error ! tlb prot violation store - .long exception_error ! address error load - .long exception_error ! address error store /* 100 */ -#endif -#if defined(CONFIG_SH_FPU) - .long do_fpu_error /* 120 */ -#else - .long exception_error /* 120 */ -#endif - .long exception_error /* 140 */ - .long system_call ! Unconditional Trap /* 160 */ - .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ - .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ -ENTRY(nmi_slot) -#if defined (CONFIG_KGDB_NMI) - .long debug_enter /* 1C0 */ ! Allow trap to debugger -#else - .long exception_none /* 1C0 */ ! Not implemented yet -#endif -ENTRY(user_break_point_trap) - .long break_point_trap /* 1E0 */ - - /* - * Pad the remainder of the table out, exceptions residing in far - * away offsets can be manually inserted in to their appropriate - * location via set_exception_table_{evt,vec}(). - */ - .balign 4096,0,4096 -- cgit v1.2.3 From 51c8b856f5edfa45d956721aa6d6ebaa15699062 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 13:16:57 +0900 Subject: sh: Kill off redundant __div64_32 symbol export. modpost noticed that __div64_32 was being exported twice: WARNING: lib/built-in: '__div64_32' exported twice. Previous export was in arch/sh/kernel/built-in.ko kill off the duplicate. Signed-off-by: Paul Mundt --- arch/sh/kernel/sh_ksyms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/sh/kernel/sh_ksyms.c b/arch/sh/kernel/sh_ksyms.c index fa91641c1f6..c1cfcb9f047 100644 --- a/arch/sh/kernel/sh_ksyms.c +++ b/arch/sh/kernel/sh_ksyms.c @@ -58,8 +58,6 @@ EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(__const_udelay); -EXPORT_SYMBOL(__div64_32); - #define DECLARE_EXPORT(name) extern void name(void);EXPORT_SYMBOL(name) /* These symbols are generated by the compiler itself */ -- cgit v1.2.3 From bd0799977cb9b68aa6a39e9630aeea4778a58385 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 14:50:59 +0900 Subject: sh: Support for SH-2A 32-bit opcodes. SH-2A supports both 16 and 32-bit instructions, add a simple helper for figuring out the instruction size in the places where there are hardcoded 16-bit assumptions. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh2a/Makefile | 5 ++- arch/sh/kernel/cpu/sh2a/opcode_helper.c | 55 +++++++++++++++++++++++++++++++++ arch/sh/kernel/kgdb_stub.c | 2 +- arch/sh/kernel/process.c | 5 +-- arch/sh/kernel/signal.c | 8 ++--- include/asm-sh/system.h | 9 ++++++ 6 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 arch/sh/kernel/cpu/sh2a/opcode_helper.c diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile index 350972ae941..965fa2572b2 100644 --- a/arch/sh/kernel/cpu/sh2a/Makefile +++ b/arch/sh/kernel/cpu/sh2a/Makefile @@ -2,9 +2,8 @@ # Makefile for the Linux/SuperH SH-2A backends. # -obj-y := common.o probe.o +obj-y := common.o probe.o opcode_helper.o -common-y += $(addprefix ../sh2/, ex.o) -common-y += $(addprefix ../sh2/, entry.o) +common-y += $(addprefix ../sh2/, ex.o entry.o) obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o diff --git a/arch/sh/kernel/cpu/sh2a/opcode_helper.c b/arch/sh/kernel/cpu/sh2a/opcode_helper.c new file mode 100644 index 00000000000..9704b7926d8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/opcode_helper.c @@ -0,0 +1,55 @@ +/* + * arch/sh/kernel/cpu/sh2a/opcode_helper.c + * + * Helper for the SH-2A 32-bit opcodes. + * + * Copyright (C) 2007 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include + +/* + * Instructions on SH are generally fixed at 16-bits, however, SH-2A + * introduces some 32-bit instructions. Since there are no real + * constraints on their use (and they can be mixed and matched), we need + * to check the instruction encoding to work out if it's a true 32-bit + * instruction or not. + * + * Presently, 32-bit opcodes have only slight variations in what the + * actual encoding looks like in the first-half of the instruction, which + * makes it fairly straightforward to differentiate from the 16-bit ones. + * + * First 16-bits of encoding Used by + * + * 0011nnnnmmmm0001 mov.b, mov.w, mov.l, fmov.d, + * fmov.s, movu.b, movu.w + * + * 0011nnnn0iii1001 bclr.b, bld.b, bset.b, bst.b, band.b, + * bandnot.b, bldnot.b, bor.b, bornot.b, + * bxor.b + * + * 0000nnnniiii0000 movi20 + * 0000nnnniiii0001 movi20s + */ +unsigned int instruction_size(unsigned int insn) +{ + /* Look for the common cases */ + switch ((insn & 0xf00f)) { + case 0x0000: /* movi20 */ + case 0x0001: /* movi20s */ + case 0x3001: /* 32-bit mov/fmov/movu variants */ + return 4; + } + + /* And the special cases.. */ + switch ((insn & 0xf08f)) { + case 0x3009: /* 32-bit b*.b bit operations */ + return 4; + } + + return 2; +} diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index a5323364cbc..ffe3e3ee580 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c @@ -867,7 +867,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) trap 0xff, since that indicates a compiled-in breakpoint which will not be replaced (and we would retake the trap forever) */ if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) - trap_registers.pc -= 2; + trap_registers.pc -= instruction_size(trap_registers.pc); /* Undo any stepping we may have done */ undo_single_step(); diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 4688b890aef..209cc9b4218 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -19,6 +19,7 @@ #include #include #include +#include #include static int hlt_counter; @@ -497,7 +498,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) @@ -516,7 +517,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index eb0191c374b..d7d98d691c6 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -23,7 +23,7 @@ #include #include #include - +#include #include #include #include @@ -500,7 +500,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, } /* fallthrough */ case -ERESTARTNOINTR: - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); } } else { /* gUSA handling */ @@ -600,9 +600,9 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0) regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); regs->regs[3] = __NR_restart_syscall; } } diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index e7e96ee0c8a..82f3e229e62 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -255,6 +255,15 @@ static inline void *set_exception_table_evt(unsigned int evt, void *handler) return set_exception_table_vec(evt >> 5, handler); } +/* + * SH-2A has both 16 and 32-bit opcodes, do lame encoding checks. + */ +#ifdef CONFIG_CPU_SH2A +extern unsigned int instruction_size(unsigned int insn); +#else +#define instruction_size(insn) (2) +#endif + /* XXX * disable hlt during certain critical i/o operations */ -- cgit v1.2.3 From 53f983a90d7908bcece51f86180c7c9b575a1e4d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 15:31:48 +0900 Subject: sh: Fix PC adjustments for varying opcode length. There are a few different cases for figuring out how to size the instruction. We read in the instruction located at regs->pc - 4 when rewinding the opcode to figure out if there's a 32-bit opcode before the faulting instruction, with a default of a - 2 adjustment on a mismatch. In practice this works for the cases where pc - 4 is just another 16-bit opcode, or we happen to have a 32-bit and a 16-bit immediately preceeding the pc value. In the cases where we aren't rewinding, this is much less ugly.. We also don't bother fixing up the places where we're explicitly dealing with 16-bit instructions, since this might lead to confusion regarding the encoding size possibilities on other CPU variants. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh4/fpu.c | 3 ++- arch/sh/kernel/kgdb_stub.c | 2 +- arch/sh/kernel/process.c | 4 ++-- arch/sh/kernel/signal.c | 11 +++++++---- arch/sh/kernel/traps.c | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c index 7624677f662..d61dd599169 100644 --- a/arch/sh/kernel/cpu/sh4/fpu.c +++ b/arch/sh/kernel/cpu/sh4/fpu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* The PR (precision) bit in the FP Status Register must be clear when @@ -265,7 +266,7 @@ ieee_fpe_handler (struct pt_regs *regs) nextpc = regs->pr; finsn = *(unsigned short *) (regs->pc + 2); } else { - nextpc = regs->pc + 2; + nextpc = regs->pc + instruction_size(insn); finsn = insn; } diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index ffe3e3ee580..a5323364cbc 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c @@ -867,7 +867,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) trap 0xff, since that indicates a compiled-in breakpoint which will not be replaced (and we would retake the trap forever) */ if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) - trap_registers.pc -= instruction_size(trap_registers.pc); + trap_registers.pc -= 2; /* Undo any stepping we may have done */ undo_single_step(); diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 209cc9b4218..9005b2f12aa 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -498,7 +498,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) @@ -517,7 +517,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index d7d98d691c6..b32c35a7c0a 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -500,7 +500,9 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, } /* fallthrough */ case -ERESTARTNOINTR: - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size( + ctrl_inw(regs->pc - 4)); + break; } } else { /* gUSA handling */ @@ -516,7 +518,8 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, regs->regs[15] = regs->regs[1]; if (regs->pc < regs->regs[0]) /* Go to rewind point #1 */ - regs->pc = regs->regs[0] + offset - 2; + regs->pc = regs->regs[0] + offset - + instruction_size(ctrl_inw(regs->pc-4)); } #ifdef CONFIG_PREEMPT local_irq_restore(flags); @@ -600,9 +603,9 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0) regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); regs->regs[3] = __NR_restart_syscall; } } diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 7b40f0ff3df..1446d12ba22 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -505,7 +505,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs) simple: ret = handle_unaligned_ins(instruction,regs); if (ret==0) - regs->pc += 2; + regs->pc += instruction_size(instruction); return ret; } #endif /* CONFIG_CPU_SH2A */ @@ -682,7 +682,7 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, err = do_fpu_inst(inst, regs); if (!err) { - regs->pc += 2; + regs->pc += instruction_size(inst); return; } /* not a FPU inst. */ -- cgit v1.2.3 From 074f98df0547b7d15f78db9a17e985da0c22af28 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 15:45:33 +0900 Subject: sh: Add 32-bit opcode feature CPU flag. Add a CPU flag for the CPUs that support 32-bit opcodes, which gets passed down to userspace. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh2a/probe.c | 1 + arch/sh/kernel/setup.c | 2 +- include/asm-sh/cpu-features.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c index 426f6db01fc..f455c350978 100644 --- a/arch/sh/kernel/cpu/sh2a/probe.c +++ b/arch/sh/kernel/cpu/sh2a/probe.c @@ -18,6 +18,7 @@ int __init detect_cpu_and_cache_system(void) { /* Just SH7206 for now .. */ current_cpu_data.type = CPU_SH7206; + current_cpu_data.flags |= CPU_HAS_OP32; current_cpu_data.dcache.ways = 4; current_cpu_data.dcache.way_incr = (1 << 11); diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 477d2a854fc..c2772913593 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -431,7 +431,7 @@ const char *get_cpu_subtype(struct sh_cpuinfo *c) /* Symbolic CPU flags, keep in sync with asm/cpu-features.h */ static const char *cpu_flags[] = { "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr", - "ptea", "llsc", "l2", NULL + "ptea", "llsc", "l2", "op32", NULL }; static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c) diff --git a/include/asm-sh/cpu-features.h b/include/asm-sh/cpu-features.h index 4bccd7c032f..86308aa3973 100644 --- a/include/asm-sh/cpu-features.h +++ b/include/asm-sh/cpu-features.h @@ -20,5 +20,6 @@ #define CPU_HAS_PTEA 0x0020 /* PTEA register */ #define CPU_HAS_LLSC 0x0040 /* movli.l/movco.l */ #define CPU_HAS_L2_CACHE 0x0080 /* Secondary cache / URAM */ +#define CPU_HAS_OP32 0x0100 /* 32-bit instruction support */ #endif /* __ASM_SH_CPU_FEATURES_H */ -- cgit v1.2.3 From c71861e65e2898850478a7ac6c4b8cc9f7007e9e Mon Sep 17 00:00:00 2001 From: kogiidena Date: Tue, 8 May 2007 20:45:46 +0900 Subject: sh: Fixup ndelay() xloops calculation for alternate HZ. Currently the xloops calculation in ndelay() gets set to 0 when calculated with HZ=250, fix up how we do the HZ factoring in order to get this right for differing values. Signed-off-by: kogiidena Signed-off-by: Paul Mundt --- arch/sh/lib/delay.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/sh/lib/delay.c b/arch/sh/lib/delay.c index 351714694d6..f3ddd2133e6 100644 --- a/arch/sh/lib/delay.c +++ b/arch/sh/lib/delay.c @@ -24,9 +24,10 @@ inline void __const_udelay(unsigned long xloops) __asm__("dmulu.l %0, %2\n\t" "sts mach, %0" : "=r" (xloops) - : "0" (xloops), "r" (cpu_data[raw_smp_processor_id()].loops_per_jiffy) + : "0" (xloops), + "r" (HZ * cpu_data[raw_smp_processor_id()].loops_per_jiffy) : "macl", "mach"); - __delay(xloops * HZ); + __delay(xloops); } void __udelay(unsigned long usecs) -- cgit v1.2.3 From f6a3c1ca00ce9a43942364a0cc15601b291df281 Mon Sep 17 00:00:00 2001 From: kogiidena Date: Tue, 8 May 2007 20:49:46 +0900 Subject: sh: landisk mv_nr_irqs definition. Fix up the landisk build. When NR_IRQS was removed, landisk got missed in the updates. Update the machvec for the landisk IRQs to get it working again. Signed-off-by: kogiidena Signed-off-by: Paul Mundt --- arch/sh/boards/landisk/setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/boards/landisk/setup.c b/arch/sh/boards/landisk/setup.c index a83a5d9587b..4058b4f50d4 100644 --- a/arch/sh/boards/landisk/setup.c +++ b/arch/sh/boards/landisk/setup.c @@ -93,6 +93,7 @@ static void __init landisk_setup(char **cmdline_p) */ struct sh_machine_vector mv_landisk __initmv = { .mv_name = "LANDISK", + .mv_nr_irqs = 72, .mv_setup = landisk_setup, .mv_init_irq = init_landisk_IRQ, }; -- cgit v1.2.3 From 21ec4c6453790cb9bfa996ab8f27f22dd47c516a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 10:42:48 +0900 Subject: sh: Wire up utimensat syscall. Signed-off-by: Paul Mundt --- arch/sh/kernel/syscalls.S | 1 + include/asm-sh/unistd.h | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/sh/kernel/syscalls.S b/arch/sh/kernel/syscalls.S index 38fc8cd3ea3..4357d1a6358 100644 --- a/arch/sh/kernel/syscalls.S +++ b/arch/sh/kernel/syscalls.S @@ -354,3 +354,4 @@ ENTRY(sys_call_table) .long sys_move_pages .long sys_getcpu .long sys_epoll_pwait + .long sys_utimensat /* 320 */ diff --git a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h index 49be50a36b7..af71e379a5e 100644 --- a/include/asm-sh/unistd.h +++ b/include/asm-sh/unistd.h @@ -85,7 +85,7 @@ #define __NR_sigpending 73 #define __NR_sethostname 74 #define __NR_setrlimit 75 -#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ #define __NR_getrusage 77 #define __NR_gettimeofday 78 #define __NR_settimeofday 79 @@ -328,8 +328,9 @@ #define __NR_move_pages 317 #define __NR_getcpu 318 #define __NR_epoll_pwait 319 +#define __NR_utimensat 320 -#define NR_syscalls 320 +#define NR_syscalls 321 #ifdef __KERNEL__ -- cgit v1.2.3 From b118ca572db5af832c6fc1af7b301105378d1a08 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 10:55:38 +0900 Subject: sh: Convert to common die chain. This went in immediately after SH added the die chain notifiers, so move over to that instead.. Signed-off-by: Paul Mundt --- arch/sh/kernel/process.c | 6 +++--- arch/sh/kernel/traps.c | 16 +--------------- arch/sh/mm/fault.c | 2 +- include/asm-sh/kdebug.h | 25 ++----------------------- 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 9005b2f12aa..d755589ba8b 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -500,7 +500,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, /* Rewind */ regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); - if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, + if (notify_die(DIE_TRAP, "debug trap", regs, 0, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) return; @@ -519,7 +519,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, /* Rewind */ regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); - if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, + if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) return; diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 1446d12ba22..3a197649cd8 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include #ifdef CONFIG_SH_KGDB #include @@ -76,20 +76,6 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) } } -ATOMIC_NOTIFIER_HEAD(shdie_chain); - -int register_die_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&shdie_chain, nb); -} -EXPORT_SYMBOL(register_die_notifier); - -int unregister_die_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&shdie_chain, nb); -} -EXPORT_SYMBOL(unregister_die_notifier); - static DEFINE_SPINLOCK(die_lock); void die(const char * str, struct pt_regs * regs, long err) diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 0ecc117cade..9207da67ff8 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/asm-sh/kdebug.h b/include/asm-sh/kdebug.h index 493c2062974..16578b7c9da 100644 --- a/include/asm-sh/kdebug.h +++ b/include/asm-sh/kdebug.h @@ -2,20 +2,6 @@ #define __ASM_SH_KDEBUG_H #include -#include - -struct pt_regs; - -struct die_args { - struct pt_regs *regs; - int trapnr; -}; - -int register_die_notifier(struct notifier_block *nb); -int unregister_die_notifier(struct notifier_block *nb); -int register_page_fault_notifier(struct notifier_block *nb); -int unregister_page_fault_notifier(struct notifier_block *nb); -extern struct atomic_notifier_head shdie_chain; /* Grossly misnamed. */ enum die_val { @@ -23,14 +9,7 @@ enum die_val { DIE_PAGE_FAULT, }; -static inline int notify_die(enum die_val val, struct pt_regs *regs, - int trap, int sig) -{ - struct die_args args = { - .regs = regs, - .trapnr = trap, - }; +int register_page_fault_notifier(struct notifier_block *nb); +int unregister_page_fault_notifier(struct notifier_block *nb); - return atomic_notifier_call_chain(&shdie_chain, val, &args); -} #endif /* __ASM_SH_KDEBUG_H */ -- cgit v1.2.3 From a361a68bc510b91b50462433cfa65c6b620a80c3 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 12:37:53 +0900 Subject: rtc: rtc-sh: Fix rtc_dev pointer for rtc_update_irq(). When the rtc_update_irq() callsites stopped passing in the class_dev, the rtc_dev references weren't fixed. Fix it up, so we pass in the proper pointer. Signed-off-by: Paul Mundt --- drivers/rtc/rtc-sh.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index bde3d34cb00..e0f91dfce0f 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -104,7 +104,7 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) writeb(tmp, rtc->regbase + RCR1); - rtc_update_irq(&rtc->rtc_dev, 1, events); + rtc_update_irq(rtc->rtc_dev, 1, events); spin_unlock(&rtc->lock); @@ -139,7 +139,7 @@ static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) rtc->rearm_aie = 1; - rtc_update_irq(&rtc->rtc_dev, 1, events); + rtc_update_irq(rtc->rtc_dev, 1, events); } spin_unlock(&rtc->lock); @@ -153,7 +153,7 @@ static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) spin_lock(&rtc->lock); - rtc_update_irq(&rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); + rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); spin_unlock(&rtc->lock); -- cgit v1.2.3 From 1ce7ddd5f4cc754b6afe9eec5cee89ede75348ea Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 13:20:52 +0900 Subject: sh: Truncate MAX_ACTIVE_REGIONS for the common case. Most SH platforms aren't going to need more than a single active region, ones that need more can pad this out as necessary. Signed-off-by: Paul Mundt --- arch/sh/mm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index b773361892c..253346d7b31 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -303,6 +303,10 @@ config NODES_SHIFT config ARCH_FLATMEM_ENABLE def_bool y +config MAX_ACTIVE_REGIONS + int + default "1" + config ARCH_POPULATES_NODE_MAP def_bool y -- cgit v1.2.3 From 0f8952c2fa981c75dbbb363ac83b320068fffa69 Mon Sep 17 00:00:00 2001 From: Ronni Nielsen Date: Wed, 9 May 2007 06:44:57 +0200 Subject: fs/libfs.c: >80 columns line break fix Signed-off-by: Ronni Nielsen Signed-off-by: Adrian Bunk --- fs/libfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/libfs.c b/fs/libfs.c index 1247ee90253..5294de1f40c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -159,7 +159,10 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) continue; spin_unlock(&dcache_lock); - if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0) + if (filldir(dirent, next->d_name.name, + next->d_name.len, filp->f_pos, + next->d_inode->i_ino, + dt_type(next->d_inode)) < 0) return 0; spin_lock(&dcache_lock); /* next is still alive */ -- cgit v1.2.3 From d3e6975e0f25044c4c86f5a42c9917090973636b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 9 May 2007 07:02:59 +0200 Subject: devres: kernel-doc and DocBook Make devres.c ready for adding to DocBook. Add devres.c to DocBook. Signed-off-by: Randy Dunlap Signed-off-by: Adrian Bunk --- Documentation/DocBook/kernel-api.tmpl | 4 ++++ drivers/base/devres.c | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index a2b2b4d187c..38f88b6ae40 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -84,6 +84,10 @@ X!Iinclude/linux/kobject.h !Ekernel/rcupdate.c + Device Resource Management +!Edrivers/base/devres.c + + diff --git a/drivers/base/devres.c b/drivers/base/devres.c index e177c9533b6..e1c0730a3b9 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -101,19 +101,6 @@ static void add_dr(struct device *dev, struct devres_node *node) list_add_tail(&node->entry, &dev->devres_head); } -/** - * devres_alloc - Allocate device resource data - * @release: Release function devres will be associated with - * @size: Allocation size - * @gfp: Allocation flags - * - * allocate devres of @size bytes. The allocated area is zeroed, then - * associated with @release. The returned pointer can be passed to - * other devres_*() functions. - * - * RETURNS: - * Pointer to allocated devres on success, NULL on failure. - */ #ifdef CONFIG_DEBUG_DEVRES void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp, const char *name) @@ -128,6 +115,19 @@ void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp, } EXPORT_SYMBOL_GPL(__devres_alloc); #else +/** + * devres_alloc - Allocate device resource data + * @release: Release function devres will be associated with + * @size: Allocation size + * @gfp: Allocation flags + * + * Allocate devres of @size bytes. The allocated area is zeroed, then + * associated with @release. The returned pointer can be passed to + * other devres_*() functions. + * + * RETURNS: + * Pointer to allocated devres on success, NULL on failure. + */ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) { struct devres *dr; @@ -416,7 +416,7 @@ static int release_nodes(struct device *dev, struct list_head *first, } /** - * devres_release_all - Release all resources + * devres_release_all - Release all managed resources * @dev: Device to release resources for * * Release all resources associated with @dev. This function is @@ -600,7 +600,7 @@ static int devm_kzalloc_match(struct device *dev, void *res, void *data) } /** - * devm_kzalloc - Managed kzalloc + * devm_kzalloc - Resource-managed kzalloc * @dev: Device to allocate memory for * @size: Allocation size * @gfp: Allocation gfp flags @@ -628,7 +628,7 @@ void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp) EXPORT_SYMBOL_GPL(devm_kzalloc); /** - * devm_kfree - Managed kfree + * devm_kfree - Resource-managed kfree * @dev: Device this memory belongs to * @p: Memory to free * -- cgit v1.2.3 From ccf6780dc3d228f380e17b6858b93fc48e40afd4 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 9 May 2007 07:10:02 +0200 Subject: Style fix in fs/select.c Signed-off-by: WANG Cong Signed-off-by: Adrian Bunk --- fs/select.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/select.c b/fs/select.c index d86224154de..a974082b082 100644 --- a/fs/select.c +++ b/fs/select.c @@ -64,7 +64,7 @@ EXPORT_SYMBOL(poll_initwait); static void free_poll_entry(struct poll_table_entry *entry) { - remove_wait_queue(entry->wait_address,&entry->wait); + remove_wait_queue(entry->wait_address, &entry->wait); fput(entry->filp); } @@ -128,7 +128,7 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, entry->filp = filp; entry->wait_address = wait_address; init_waitqueue_entry(&entry->wait, current); - add_wait_queue(wait_address,&entry->wait); + add_wait_queue(wait_address, &entry->wait); } #define FDS_IN(fds, n) (fds->in + n) -- cgit v1.2.3 From 3dde6ad8fc3939d345a3768464ecff43c91d511a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 9 May 2007 07:12:20 +0200 Subject: Fix trivial typos in Kconfig* files Fix several typos in help text in Kconfig* files. Signed-off-by: David Sterba Signed-off-by: Adrian Bunk --- arch/cris/arch-v32/drivers/Kconfig | 2 +- arch/h8300/Kconfig.debug | 4 ++-- arch/i386/Kconfig.cpu | 4 ++-- arch/ia64/Kconfig | 2 +- arch/m68knommu/Kconfig.debug | 2 +- arch/mips/Kconfig | 6 +++--- arch/powerpc/Kconfig.debug | 2 +- arch/s390/crypto/Kconfig | 2 +- crypto/Kconfig | 2 +- drivers/ata/Kconfig | 2 +- drivers/char/tpm/Kconfig | 2 +- drivers/crypto/Kconfig | 2 +- drivers/macintosh/Kconfig | 2 +- drivers/rtc/Kconfig | 2 +- drivers/usb/serial/Kconfig | 2 +- init/Kconfig | 2 +- kernel/Kconfig.preempt | 4 ++-- net/ipv4/Kconfig | 4 ++-- net/ipv6/netfilter/Kconfig | 2 +- net/netfilter/Kconfig | 2 +- security/selinux/Kconfig | 2 +- 21 files changed, 27 insertions(+), 27 deletions(-) diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index f64624fc450..1d859c16931 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -603,7 +603,7 @@ config ETRAX_CARDBUS select HOTPLUG select PCCARD_NONSTATIC help - Enabled the ETRAX Carbus driver. + Enabled the ETRAX Cardbus driver. config PCI bool diff --git a/arch/h8300/Kconfig.debug b/arch/h8300/Kconfig.debug index e0e9bcb015a..554efe604a0 100644 --- a/arch/h8300/Kconfig.debug +++ b/arch/h8300/Kconfig.debug @@ -21,12 +21,12 @@ config GDB_MAGICPRINT bool "Message Output for GDB MagicPrint service" depends on (H8300H_SIM || H8S_SIM) help - kernel messages output useing MagicPrint service from GDB + kernel messages output using MagicPrint service from GDB config SYSCALL_PRINT bool "SystemCall trace print" help - outout history of systemcall + output history of systemcall config GDB_DEBUG bool "Use gdb stub" diff --git a/arch/i386/Kconfig.cpu b/arch/i386/Kconfig.cpu index dce6124cb84..d7f6fb0b30f 100644 --- a/arch/i386/Kconfig.cpu +++ b/arch/i386/Kconfig.cpu @@ -108,7 +108,7 @@ config MCORE2 bool "Core 2/newer Xeon" help Select this for Intel Core 2 and newer Core 2 Xeons (Xeon 51xx and 53xx) - CPUs. You can distingush newer from older Xeons by the CPU family + CPUs. You can distinguish newer from older Xeons by the CPU family in /proc/cpuinfo. Newer ones have 6. config MPENTIUM4 @@ -172,7 +172,7 @@ config MWINCHIP3D help Select this for an IDT Winchip-2A or 3. Linux and GCC treat this chip as a 586TSC with some extended instructions - and alignment reqirements. Also enable out of order memory + and alignment requirements. Also enable out of order memory stores for this CPU, which can increase performance of some operations. diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index e23af4b6ae8..6e41471449c 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -468,7 +468,7 @@ config KEXEC help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot - but it is indepedent of the system firmware. And like a reboot + but it is independent of the system firmware. And like a reboot you can start any kernel with it, not just Linux. The name comes from the similiarity to the exec system call. diff --git a/arch/m68knommu/Kconfig.debug b/arch/m68knommu/Kconfig.debug index 763c9aa0b4f..9ff47bd09ae 100644 --- a/arch/m68knommu/Kconfig.debug +++ b/arch/m68knommu/Kconfig.debug @@ -5,7 +5,7 @@ source "lib/Kconfig.debug" config FULLDEBUG bool "Full Symbolic/Source Debugging support" help - Enable debuging symbols on kernel build. + Enable debugging symbols on kernel build. config HIGHPROFILE bool "Use fast second timer for profiling" diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index b7cb048bc77..16ecea3c081 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -956,7 +956,7 @@ choice byte order. These modes require different kernels and a different Linux distribution. In general there is one preferred byteorder for a particular system but some systems are just as commonly used in the - one or the other endianess. + one or the other endianness. config CPU_BIG_ENDIAN bool "Big endian" @@ -1750,7 +1750,7 @@ config ARCH_DISCONTIGMEM_ENABLE bool default y if SGI_IP27 help - Say Y to upport efficient handling of discontiguous physical memory, + Say Y to support efficient handling of discontiguous physical memory, for architectures which are either NUMA (Non-Uniform Memory Access) or have huge holes in the physical address space for other reasons. See for more. @@ -1938,7 +1938,7 @@ config KEXEC help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot - but it is indepedent of the system firmware. And like a reboot + but it is independent of the system firmware. And like a reboot you can start any kernel with it, not just Linux. The name comes from the similiarity to the exec system call. diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index f70e795c262..346cd3befe1 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -32,7 +32,7 @@ config HCALL_STATS depends on PPC_PSERIES && DEBUG_FS help Adds code to keep track of the number of hypervisor calls made and - the amount of time spent in hypervisor callsr. Wall time spent in + the amount of time spent in hypervisor calls. Wall time spent in each call is always calculated, and if available CPU cycles spent are also calculated. A directory named hcall_inst is added at the root of the debugfs filesystem. Within the hcall_inst directory diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig index 99ff9f08e4d..d1defbbfcd8 100644 --- a/arch/s390/crypto/Kconfig +++ b/arch/s390/crypto/Kconfig @@ -54,7 +54,7 @@ config S390_PRNG default "m" help Select this option if you want to use the s390 pseudo random number - generator. The PRNG is part of the cryptograhic processor functions + generator. The PRNG is part of the cryptographic processor functions and uses triple-DES to generate secure random numbers like the ANSI X9.17 standard. The PRNG is usable via the char device /dev/prandom. diff --git a/crypto/Kconfig b/crypto/Kconfig index 620e14cabdc..4ca0ab3448d 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -271,7 +271,7 @@ config CRYPTO_SERPENT Keys are allowed to be from 0 to 256 bits in length, in steps of 8 bits. Also includes the 'Tnepres' algorithm, a reversed - variant of Serpent for compatibility with old kerneli code. + variant of Serpent for compatibility with old kerneli.org code. See also: diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 45dbdc14915..c7219663f2b 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -435,7 +435,7 @@ config PATA_OPTIDMA help This option enables DMA/PIO support for the later OPTi controllers found on some old motherboards and in some - latops + laptops. If unsure, say N. diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index fe00c7dfb64..11089be0691 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -33,7 +33,7 @@ config TCG_NSC tristate "National Semiconductor TPM Interface" depends on TCG_TPM && PNPACPI ---help--- - If you have a TPM security chip from National Semicondutor + If you have a TPM security chip from National Semiconductor say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_nsc. diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index f21fe66c9ee..f4c634504d1 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -51,7 +51,7 @@ config CRYPTO_DEV_GEODE default m help Say 'Y' here to use the AMD Geode LX processor on-board AES - engine for the CryptoAPI AES alogrithm. + engine for the CryptoAPI AES algorithm. To compile this driver as a module, choose M here: the module will be called geode-aes. diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index a32c91e27b3..58926da0ae1 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -237,7 +237,7 @@ config PMAC_RACKMETER tristate "Support for Apple XServe front panel LEDs" depends on PPC_PMAC help - This driver procides some support to control the front panel + This driver provides some support to control the front panel blue LEDs "vu-meter" of the XServer macs. endif # MACINTOSH_DRIVERS diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5e439836db2..76422eded36 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -98,7 +98,7 @@ config RTC_INTF_DEV_UIE_EMUL bool "RTC UIE emulation on dev interface" depends on RTC_INTF_DEV help - Provides an emulation for RTC_UIE if the underlaying rtc chip + Provides an emulation for RTC_UIE if the underlying rtc chip driver does not expose RTC_UIE ioctls. Those requests generate once-per-second update interrupts, used for synchronization. diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index ba5d1dc0303..3efe67092f1 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -558,7 +558,7 @@ config USB_SERIAL_DEBUG tristate "USB Debugging Device" depends on USB_SERIAL help - Say Y here if you have a USB debugging device used to recieve + Say Y here if you have a USB debugging device used to receive debugging data from another machine. The most common of these devices is the NetChip TurboCONNECT device. diff --git a/init/Kconfig b/init/Kconfig index d0edf42f4db..f34270087e7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -308,7 +308,7 @@ config SYSFS_DEPRECATED releases. If enabled, this option will also move any device structures - that belong to a class, back into the /sys/class heirachy, in + that belong to a class, back into the /sys/class hierarchy, in order to support older versions of udev. If you are using a distro that was released in 2006 or later, diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt index 0b46a5dff4c..c64ce9c1420 100644 --- a/kernel/Kconfig.preempt +++ b/kernel/Kconfig.preempt @@ -23,7 +23,7 @@ config PREEMPT_VOLUNTARY "explicit preemption points" to the kernel code. These new preemption points have been selected to reduce the maximum latency of rescheduling, providing faster application reactions, - at the cost of slighly lower throughput. + at the cost of slightly lower throughput. This allows reaction to interactive events by allowing a low priority process to voluntarily preempt itself even if it @@ -43,7 +43,7 @@ config PREEMPT even if it is in kernel mode executing a system call and would otherwise not be about to reach a natural preemption point. This allows applications to run more 'smoothly' even when the - system is under load, at the cost of slighly lower throughput + system is under load, at the cost of slightly lower throughput and a slight runtime overhead to kernel code. Select this if you are building a kernel for a desktop or diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index e62aee0ec4c..c68196cc56a 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -130,7 +130,7 @@ config IP_ROUTE_MULTIPATH_RR tristate "MULTIPATH: round robin algorithm" depends on IP_ROUTE_MULTIPATH_CACHED help - Mulitpath routes are chosen according to Round Robin + Multipath routes are chosen according to Round Robin config IP_ROUTE_MULTIPATH_RANDOM tristate "MULTIPATH: random algorithm" @@ -651,7 +651,7 @@ config TCP_MD5SIG select CRYPTO select CRYPTO_MD5 ---help--- - RFC2385 specifices a method of giving MD5 protection to TCP sessions. + RFC2385 specifies a method of giving MD5 protection to TCP sessions. Its main (only?) use is to protect BGP sessions between core routers on the Internet. diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index bbe99f842f9..838b8ddee8c 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -28,7 +28,7 @@ config IP6_NF_QUEUE packets which enables users to receive the filtered packets with QUEUE target using libipq. - THis option enables the old IPv6-only "ip6_queue" implementation + This option enables the old IPv6-only "ip6_queue" implementation which has been obsoleted by the new "nfnetlink_queue" code (see CONFIG_NETFILTER_NETLINK_QUEUE). diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ea6211cade0..a567dae8e5f 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -197,7 +197,7 @@ config NF_CONNTRACK_PPTP Please note that not all PPTP modes of operation are supported yet. Specifically these limitations exist: - - Blindy assumes that control connections are always established + - Blindly assumes that control connections are always established in PNS->PAC direction. This is a violation of RFC2637. - Only supports a single call within each session diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 23b51047494..b32a459c068 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -137,7 +137,7 @@ config SECURITY_SELINUX_POLICYDB_VERSION_MAX Examples: For the Fedora Core 3 or 4 Linux distributions, enable this option - and set the value via the next option. For Fedore Core 5 and later, + and set the value via the next option. For Fedora Core 5 and later, do not enable this option. If you are unsure how to answer this question, answer N. -- cgit v1.2.3 From beb7dd86a101263bf63a78c7c6d4da3849b35bd6 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:14:03 +0200 Subject: Fix misspellings collected by members of KJ list. Fix the misspellings of "propogate", "writting" and (oh, the shame :-) "kenrel" in the source tree. Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- Documentation/sysctl/kernel.txt | 2 +- arch/powerpc/oprofile/op_model_cell.c | 2 +- arch/x86_64/kernel/io_apic.c | 2 +- drivers/char/tty_io.c | 8 ++++---- drivers/media/dvb/frontends/dib7000m.c | 2 +- drivers/media/dvb/frontends/dib7000p.c | 2 +- drivers/media/video/em28xx/em28xx-i2c.c | 2 +- drivers/net/irda/donauboe.h | 2 +- drivers/net/wireless/prism54/islpci_dev.c | 2 +- drivers/net/wireless/wavelan_cs.c | 4 ++-- drivers/net/wireless/wavelan_cs.p.h | 2 +- drivers/sbus/char/bpp.c | 2 +- drivers/usb/serial/aircable.c | 4 ++-- drivers/usb/serial/io_edgeport.c | 4 ++-- drivers/video/matrox/matroxfb_Ti3026.c | 2 +- drivers/video/matrox/matroxfb_accel.c | 2 +- drivers/video/matrox/matroxfb_base.c | 2 +- drivers/video/matrox/matroxfb_misc.c | 2 +- fs/direct-io.c | 2 +- fs/reiserfs/journal.c | 4 ++-- include/asm-generic/bitops/atomic.h | 2 +- include/asm-i386/bitops.h | 2 +- include/asm-i386/boot.h | 2 +- include/asm-i386/sync_bitops.h | 2 +- include/asm-m32r/system.h | 2 +- include/asm-mips/bootinfo.h | 2 +- include/linux/mount.h | 2 +- sound/pci/ice1712/delta.h | 4 ++-- sound/sparc/dbri.c | 2 +- 29 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 5922e84d913..d3d3c255ddb 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -228,7 +228,7 @@ Controls the kernel's behaviour when an oops or BUG is encountered. pid_max: -PID allocation wrap value. When the kenrel's next PID value +PID allocation wrap value. When the kernel's next PID value reaches this value, it wraps back to a minimum PID value. PIDs of value pid_max or larger are not allocated. diff --git a/arch/powerpc/oprofile/op_model_cell.c b/arch/powerpc/oprofile/op_model_cell.c index 626b29f3830..c29293befba 100644 --- a/arch/powerpc/oprofile/op_model_cell.c +++ b/arch/powerpc/oprofile/op_model_cell.c @@ -747,7 +747,7 @@ cell_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr) * counter value etc.) are not copied to the actual registers * until the performance monitor is enabled. In order to get * this to work as desired, the permormance monitor needs to - * be disabled while writting to the latches. This is a + * be disabled while writing to the latches. This is a * HW design issue. */ cbe_enable_pm(cpu); diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 4d582589fa8..d8bfe315471 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c @@ -1413,7 +1413,7 @@ static void ack_apic_level(unsigned int irq) /* * We must acknowledge the irq before we move it or the acknowledge will - * not propogate properly. + * not propagate properly. */ ack_APIC_irq(); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 7710a6a77d9..f6ac1d316ea 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1573,11 +1573,11 @@ void no_tty(void) /** - * stop_tty - propogate flow control + * stop_tty - propagate flow control * @tty: tty to stop * * Perform flow control to the driver. For PTY/TTY pairs we - * must also propogate the TIOCKPKT status. May be called + * must also propagate the TIOCKPKT status. May be called * on an already stopped device and will not re-call the driver * method. * @@ -1607,11 +1607,11 @@ void stop_tty(struct tty_struct *tty) EXPORT_SYMBOL(stop_tty); /** - * start_tty - propogate flow control + * start_tty - propagate flow control * @tty: tty to start * * Start a tty that has been stopped if at all possible. Perform - * any neccessary wakeups and propogate the TIOCPKT status. If this + * any neccessary wakeups and propagate the TIOCPKT status. If this * is the tty was previous stopped and is being started then the * driver start method is invoked and the line discipline woken. * diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c index f5d40aa3d27..f64546c6aeb 100644 --- a/drivers/media/dvb/frontends/dib7000m.c +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -266,7 +266,7 @@ static int dib7000m_sad_calib(struct dib7000m_state *state) { /* internal */ -// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth +// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is written in set_bandwidth dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 0349a4b5da3..aece458cfe1 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -223,7 +223,7 @@ static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx) static int dib7000p_sad_calib(struct dib7000p_state *state) { /* internal */ -// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth +// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is written in set_bandwidth dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096 diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 563a8319e60..54ccc6e1f92 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -70,7 +70,7 @@ static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { - em28xx_warn("writting to i2c device failed (error=%i)\n", ret); + em28xx_warn("writing to i2c device failed (error=%i)\n", ret); return -EIO; } for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h index 2ab173d9a0e..1e67720f106 100644 --- a/drivers/net/irda/donauboe.h +++ b/drivers/net/irda/donauboe.h @@ -113,7 +113,7 @@ /* RxOver overflow in Recv FIFO */ /* SipRcv received serial gap (or other condition you set) */ /* Interrupts are enabled by writing a one to the IER register */ -/* Interrupts are cleared by writting a one to the ISR register */ +/* Interrupts are cleared by writing a one to the ISR register */ /* */ /* 6. The remaining registers: 0x6 and 0x3 appear to be */ /* reserved parts of 16 or 32 bit registersthe remainder */ diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index a037b11dac9..084795355b7 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -115,7 +115,7 @@ isl_upload_firmware(islpci_private *priv) ISL38XX_MEMORY_WINDOW_SIZE : fw_len; u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN; - /* set the cards base address for writting the data */ + /* set the card's base address for writing the data */ isl38xx_w32_flush(device_base, reg, ISL38XX_DIR_MEM_BASE_REG); wmb(); /* be paranoid */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 67b867f837c..5740d4d4267 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -176,7 +176,7 @@ psa_write(struct net_device * dev, volatile u_char __iomem *verify = lp->mem + PSA_ADDR + (psaoff(0, psa_comp_number) << 1); - /* Authorize writting to PSA */ + /* Authorize writing to PSA */ hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN); while(n-- > 0) @@ -1676,7 +1676,7 @@ wv_set_frequency(u_long base, /* i/o port of the card */ fee_write(base, 0x60, dac, 2); - /* We now should verify here that the EEprom writting was ok */ + /* We now should verify here that the EEprom writing was ok */ /* ReRead the first area */ fee_read(base, 0x00, diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h index 4d1c4905c74..4b9de0093a7 100644 --- a/drivers/net/wireless/wavelan_cs.p.h +++ b/drivers/net/wireless/wavelan_cs.p.h @@ -120,7 +120,7 @@ * the Wavelan itself (NCR -> AT&T -> Lucent). * * All started with Anders Klemets , - * writting a Wavelan ISA driver for the MACH microkernel. Girish + * writing a Wavelan ISA driver for the MACH microkernel. Girish * Welling had also worked on it. * Keith Moore modify this for the Pcmcia hardware. * diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index 74b999d77bb..4fab0c23814 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -156,7 +156,7 @@ static unsigned short get_pins(unsigned minor) #define BPP_ICR 0x18 #define BPP_SIZE 0x1A -/* BPP_CSR. Bits of type RW1 are cleared with writting '1'. */ +/* BPP_CSR. Bits of type RW1 are cleared with writing '1'. */ #define P_DEV_ID_MASK 0xf0000000 /* R */ #define P_DEV_ID_ZEBRA 0x40000000 #define P_DEV_ID_L64854 0xa0000000 /* == NCR 89C100+89C105. Pity. */ diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index b675735bfbe..fbc8c27d5d9 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -9,7 +9,7 @@ * The device works as an standard CDC device, it has 2 interfaces, the first * one is for firmware access and the second is the serial one. * The protocol is very simply, there are two posibilities reading or writing. - * When writting the first urb must have a Header that starts with 0x20 0x29 the + * When writing the first urb must have a Header that starts with 0x20 0x29 the * next two bytes must say how much data will be sended. * When reading the process is almost equal except that the header starts with * 0x00 0x20. @@ -18,7 +18,7 @@ * buffer: The First and Second byte is used for a Header, the Third and Fourth * tells the device the amount of information the package holds. * Packages are 60 bytes long Header Stuff. - * When writting to the device the first two bytes of the header are 0x20 0x29 + * When writing to the device the first two bytes of the header are 0x20 0x29 * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange * situation, when too much data arrives to the device because it sends the data * but with out the header. I will use a simply hack to override this situation, diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 18f74ac7656..4807f960150 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2465,7 +2465,7 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r ((edge_serial->is_epic) && (!edge_serial->epic_descriptor.Supports.IOSPWriteMCR) && (regNum == MCR))) { - dbg("SendCmdWriteUartReg - Not writting to MCR Register"); + dbg("SendCmdWriteUartReg - Not writing to MCR Register"); return 0; } @@ -2473,7 +2473,7 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r ((edge_serial->is_epic) && (!edge_serial->epic_descriptor.Supports.IOSPWriteLCR) && (regNum == LCR))) { - dbg ("SendCmdWriteUartReg - Not writting to LCR Register"); + dbg ("SendCmdWriteUartReg - Not writing to LCR Register"); return 0; } diff --git a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c index a5690a5f29d..9445cdb759b 100644 --- a/drivers/video/matrox/matroxfb_Ti3026.c +++ b/drivers/video/matrox/matroxfb_Ti3026.c @@ -72,7 +72,7 @@ * (c) 1998 Gerd Knorr * * (following author is not in any relation with this code, but his ideas - * were used when writting this driver) + * were used when writing this driver) * * FreeVBE/AF (Matrox), "Shawn Hargreaves" * diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c index a5c825d9946..c57aaadf410 100644 --- a/drivers/video/matrox/matroxfb_accel.c +++ b/drivers/video/matrox/matroxfb_accel.c @@ -70,7 +70,7 @@ * (c) 1998 Gerd Knorr * * (following author is not in any relation with this code, but his ideas - * were used when writting this driver) + * were used when writing this driver) * * FreeVBE/AF (Matrox), "Shawn Hargreaves" * diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index cb2aa402ddf..c8559a756b7 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -93,7 +93,7 @@ * (c) 1998 Gerd Knorr * * (following author is not in any relation with this code, but his ideas - * were used when writting this driver) + * were used when writing this driver) * * FreeVBE/AF (Matrox), "Shawn Hargreaves" * diff --git a/drivers/video/matrox/matroxfb_misc.c b/drivers/video/matrox/matroxfb_misc.c index 18886b629cb..5948e54b9ef 100644 --- a/drivers/video/matrox/matroxfb_misc.c +++ b/drivers/video/matrox/matroxfb_misc.c @@ -78,7 +78,7 @@ * (c) 1998 Gerd Knorr * * (following author is not in any relation with this code, but his ideas - * were used when writting this driver) + * were used when writing this driver) * * FreeVBE/AF (Matrox), "Shawn Hargreaves" * diff --git a/fs/direct-io.c b/fs/direct-io.c index d9d0833444f..1e88d8d1d2a 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -439,7 +439,7 @@ static int dio_bio_complete(struct dio *dio, struct bio *bio) * Wait on and process all in-flight BIOs. This must only be called once * all bios have been issued so that the refcount can only decrease. * This just waits for all bios to make it through dio_bio_complete. IO - * errors are propogated through dio->io_error and should be propogated via + * errors are propagated through dio->io_error and should be propagated via * dio_complete(). */ static void dio_await_completion(struct dio *dio) diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index e073fd86cf6..f25086aeef5 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -1110,7 +1110,7 @@ static int flush_commit_list(struct super_block *s, if (!barrier) { /* If there was a write error in the journal - we can't commit * this transaction - it will be invalid and, if successful, - * will just end up propogating the write error out to + * will just end up propagating the write error out to * the file system. */ if (likely(!retval && !reiserfs_is_journal_aborted (journal))) { if (buffer_dirty(jl->j_commit_bh)) @@ -1125,7 +1125,7 @@ static int flush_commit_list(struct super_block *s, /* If there was a write error in the journal - we can't commit this * transaction - it will be invalid and, if successful, will just end - * up propogating the write error out to the filesystem. */ + * up propagating the write error out to the filesystem. */ if (unlikely(!buffer_uptodate(jl->j_commit_bh))) { #ifdef CONFIG_REISERFS_CHECK reiserfs_warning(s, "journal-615: buffer write failed"); diff --git a/include/asm-generic/bitops/atomic.h b/include/asm-generic/bitops/atomic.h index 78339319ba0..cd8a9641bd6 100644 --- a/include/asm-generic/bitops/atomic.h +++ b/include/asm-generic/bitops/atomic.h @@ -58,7 +58,7 @@ extern raw_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned; * if you do not require the atomic guarantees. * * Note: there are no guarantees that this function will not be reordered - * on non x86 architectures, so if you are writting portable code, + * on non x86 architectures, so if you are writing portable code, * make sure not to rely on its reordering guarantees. * * Note that @nr may be almost arbitrarily large; this function is not diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h index 273b5062935..a20fe9822f6 100644 --- a/include/asm-i386/bitops.h +++ b/include/asm-i386/bitops.h @@ -27,7 +27,7 @@ * if you do not require the atomic guarantees. * * Note: there are no guarantees that this function will not be reordered - * on non x86 architectures, so if you are writting portable code, + * on non x86 architectures, so if you are writing portable code, * make sure not to rely on its reordering guarantees. * * Note that @nr may be almost arbitrarily large; this function is not diff --git a/include/asm-i386/boot.h b/include/asm-i386/boot.h index e7686d0a841..bd024ab4fe5 100644 --- a/include/asm-i386/boot.h +++ b/include/asm-i386/boot.h @@ -12,7 +12,7 @@ #define EXTENDED_VGA 0xfffe /* 80x50 mode */ #define ASK_VGA 0xfffd /* ask for it at bootup */ -/* Physical address where kenrel should be loaded. */ +/* Physical address where kernel should be loaded. */ #define LOAD_PHYSICAL_ADDR ((CONFIG_PHYSICAL_START \ + (CONFIG_PHYSICAL_ALIGN - 1)) \ & ~(CONFIG_PHYSICAL_ALIGN - 1)) diff --git a/include/asm-i386/sync_bitops.h b/include/asm-i386/sync_bitops.h index 7d72351bea7..cbce08a2d13 100644 --- a/include/asm-i386/sync_bitops.h +++ b/include/asm-i386/sync_bitops.h @@ -24,7 +24,7 @@ * if you do not require the atomic guarantees. * * Note: there are no guarantees that this function will not be reordered - * on non x86 architectures, so if you are writting portable code, + * on non-x86 architectures, so if you are writing portable code, * make sure not to rely on its reordering guarantees. * * Note that @nr may be almost arbitrarily large; this function is not diff --git a/include/asm-m32r/system.h b/include/asm-m32r/system.h index 06cdece3586..f62f5c9abba 100644 --- a/include/asm-m32r/system.h +++ b/include/asm-m32r/system.h @@ -136,7 +136,7 @@ extern void __xchg_called_with_bad_pointer(void); "add3 "reg0", "addr", #0x2000; \n\t" \ "ld "reg0", @"reg0"; \n\t" \ "unlock "reg0", @"reg1"; \n\t" - /* FIXME: This workaround code cannot handle kenrel modules + /* FIXME: This workaround code cannot handle kernel modules * correctly under SMP environment. */ #else /* CONFIG_CHIP_M32700_TS1 */ diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h index c7c945baf1e..dbf834f4dac 100644 --- a/include/asm-mips/bootinfo.h +++ b/include/asm-mips/bootinfo.h @@ -254,7 +254,7 @@ extern void free_init_pages(const char *what, extern char arcs_cmdline[CL_SIZE]; /* - * Registers a0, a1, a3 and a4 as passed to the kenrel entry by firmware + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware */ extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; diff --git a/include/linux/mount.h b/include/linux/mount.h index dab69afee2f..6d3047d8c91 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -33,7 +33,7 @@ struct mnt_namespace; #define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */ #define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */ -#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */ +#define MNT_PNODE_MASK 0x3000 /* propagation flag mask */ struct vfsmount { struct list_head mnt_hash; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index e65d669af63..e47861ccd6e 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -63,7 +63,7 @@ extern const struct snd_ice1712_card_info snd_ice1712_delta_cards[]; /* look to CS8414 datasheet */ #define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04 /* S/PDIF output status clock */ - /* (writting on rising edge - 0->1) */ + /* (writing on rising edge - 0->1) */ /* all except Delta44 */ /* look to CS8404A datasheet */ #define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08 @@ -100,7 +100,7 @@ extern const struct snd_ice1712_card_info snd_ice1712_delta_cards[]; /* AKM4524 serial data */ #define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20 /* AKM4524 serial clock */ - /* (writting on rising edge - 0->1 */ + /* (writing on rising edge - 0->1 */ #define ICE1712_DELTA_CODEC_CHIP_A 0x40 #define ICE1712_DELTA_CODEC_CHIP_B 0x80 /* 1 - select chip A or B */ diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 25a2a733300..e07085a7cfc 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -673,7 +673,7 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len) } /* - * Send prepared cmd string. It works by writting a JUMP cmd into + * Send prepared cmd string. It works by writing a JUMP cmd into * the last WAIT cmd and force DBRI to reread the cmd. * The JUMP cmd points to the new cmd string. * It also releases the cmdlock spinlock. -- cgit v1.2.3 From 1591275cb57bc5df7ff59774b85c8af84bc45e87 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:18:01 +0200 Subject: Fix "deprecated" typoes. Fix remaining misspellings of "depreciated" to "deprecated." Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- Documentation/arm/Interrupts | 2 +- drivers/net/hamradio/Kconfig | 2 +- drivers/net/wireless/prism54/isl_ioctl.c | 2 +- include/asm-arm26/io.h | 2 +- include/asm-arm26/memory.h | 4 ++-- include/asm-arm26/setup.h | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/arm/Interrupts b/Documentation/arm/Interrupts index 72c93de8cd4..0d3dbf1099b 100644 --- a/Documentation/arm/Interrupts +++ b/Documentation/arm/Interrupts @@ -149,7 +149,7 @@ So, what's changed? 3. set_GPIO_IRQ_edge() is obsolete, and should be replaced by set_irq_type. -4. Direct access to SA1111 INTPOL is depreciated. Use set_irq_type instead. +4. Direct access to SA1111 INTPOL is deprecated. Use set_irq_type instead. 5. A handler is expected to perform any necessary acknowledgement of the parent IRQ via the correct chip specific function. For instance, if diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig index 6e90619b3b4..36d2c7d4f4d 100644 --- a/drivers/net/hamradio/Kconfig +++ b/drivers/net/hamradio/Kconfig @@ -140,7 +140,7 @@ config BAYCOM_SER_HDX modems that connect to a serial interface. The driver supports the ser12 design in half-duplex mode. This is the old driver. It is still provided in case your serial interface chip does not work with - the full-duplex driver. This driver is depreciated. To configure + the full-duplex driver. This driver is deprecated. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on the modems, see and diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 841b3c136ad..283be4a7052 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -3054,7 +3054,7 @@ static const iw_handler prism54_handler[] = { (iw_handler) prism54_set_wap, /* SIOCSIWAP */ (iw_handler) prism54_get_wap, /* SIOCGIWAP */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST depreciated */ + (iw_handler) NULL, /* SIOCGIWAPLIST deprecated */ (iw_handler) prism54_set_scan, /* SIOCSIWSCAN */ (iw_handler) prism54_get_scan, /* SIOCGIWSCAN */ (iw_handler) prism54_set_essid, /* SIOCSIWESSID */ diff --git a/include/asm-arm26/io.h b/include/asm-arm26/io.h index 2aa033bd067..a5a7a4d5e09 100644 --- a/include/asm-arm26/io.h +++ b/include/asm-arm26/io.h @@ -321,7 +321,7 @@ DECLARE_IO(int,l,"") #define mmiowb() -/* the following macro is depreciated */ +/* the following macro is deprecated */ #define ioaddr(port) __ioaddr((port)) /* diff --git a/include/asm-arm26/memory.h b/include/asm-arm26/memory.h index a65f10b80df..7c1e5be3906 100644 --- a/include/asm-arm26/memory.h +++ b/include/asm-arm26/memory.h @@ -60,7 +60,7 @@ static inline void *phys_to_virt(unsigned long x) /* * Virtual <-> DMA view memory address translations * Again, these are *only* valid on the kernel direct mapped RAM - * memory. Use of these is *depreciated*. + * memory. Use of these is *deprecated*. */ #define virt_to_bus(x) ((unsigned long)(x)) #define bus_to_virt(x) ((void *)((unsigned long)(x))) @@ -93,7 +93,7 @@ static inline void *phys_to_virt(unsigned long x) #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) /* - * We should really eliminate virt_to_bus() here - it's depreciated. + * We should really eliminate virt_to_bus() here - it's deprecated. */ #define page_to_bus(page) (page_address(page)) diff --git a/include/asm-arm26/setup.h b/include/asm-arm26/setup.h index 1a867b4e8d5..10fd07c7666 100644 --- a/include/asm-arm26/setup.h +++ b/include/asm-arm26/setup.h @@ -70,7 +70,7 @@ struct tag_ramdisk { /* describes where the compressed ramdisk image lives */ /* * this one accidentally used virtual addresses - as such, - * its depreciated. + * it's deprecated. */ #define ATAG_INITRD 0x54410005 -- cgit v1.2.3 From 8b60756a628a73bc8bf8b59d8716cb3f09b7e7eb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 9 May 2007 07:19:14 +0200 Subject: Fix more "deprecated" spellos. Signed-off-by: Randy Dunlap Signed-off-by: Adrian Bunk --- Documentation/fb/aty128fb.txt | 4 ++-- Documentation/filesystems/proc.txt | 2 +- drivers/pci/pci-driver.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/fb/aty128fb.txt b/Documentation/fb/aty128fb.txt index 069262fb619..b605204fcfe 100644 --- a/Documentation/fb/aty128fb.txt +++ b/Documentation/fb/aty128fb.txt @@ -54,8 +54,8 @@ Accepted options: noaccel - do not use acceleration engine. It is default. accel - use acceleration engine. Not finished. -vmode:x - chooses PowerMacintosh video mode . Depreciated. -cmode:x - chooses PowerMacintosh colour mode . Depreciated. +vmode:x - chooses PowerMacintosh video mode . Deprecated. +cmode:x - chooses PowerMacintosh colour mode . Deprecated. - selects startup videomode. See modedb.txt for detailed explanation. Default is 640x480x8bpp. diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 4f3e84c520a..8756a07f4dc 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -229,7 +229,7 @@ Table 1-3: Kernel info in /proc mounts Mounted filesystems net Networking info (see text) partitions Table of partitions known to the system - pci Depreciated info of PCI bus (new way -> /proc/bus/pci/, + pci Deprecated info of PCI bus (new way -> /proc/bus/pci/, decoupled by lspci (2.4) rtc Real time clock scsi SCSI info (see text) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3bb7739d26a..8e58ea3d95c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -119,7 +119,7 @@ static inline int pci_create_newid_file(struct pci_driver *drv) * system is in its list of supported devices. Returns the matching * pci_device_id structure or %NULL if there is no match. * - * Depreciated, don't use this as it will not catch any dynamic ids + * Deprecated, don't use this as it will not catch any dynamic ids * that a driver might want to check for. */ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, -- cgit v1.2.3 From c685ce059d42ebda7822786498dfd366695457f6 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:21:11 +0200 Subject: Correct comments in genrtc.c to refer to correct /proc file. Correct documentation in genrtc.c to refer to the correct /proc file that's used to export information: /proc/driver/rtc. Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- drivers/char/genrtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c index 49f914e7921..9e1fc02967f 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -12,7 +12,7 @@ * * This driver allows use of the real time clock (built into * nearly all computers) from user space. It exports the /dev/rtc - * interface supporting various ioctl() and also the /proc/dev/rtc + * interface supporting various ioctl() and also the /proc/driver/rtc * pseudo-file for status information. * * The ioctls can be used to set the interrupt behaviour where @@ -377,7 +377,7 @@ static int gen_rtc_release(struct inode *inode, struct file *file) #ifdef CONFIG_PROC_FS /* - * Info exported via "/proc/rtc". + * Info exported via "/proc/driver/rtc". */ static int gen_rtc_proc_output(char *buf) -- cgit v1.2.3 From fd2dbc92e318ee9d306c4c3f393eef7294a5a30c Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:23:41 +0200 Subject: Kconfig: A couple of grammatical fixes in arch/i386/Kconfig Fix a couple grammatical errors in arch/i386/Kconfig. Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- arch/i386/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 64ad10f984a..30944ee2e61 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -858,9 +858,9 @@ config RELOCATABLE bool "Build a relocatable kernel(EXPERIMENTAL)" depends on EXPERIMENTAL help - This build a kernel image that retains relocation information + This builds a kernel image that retains relocation information so it can be loaded someplace besides the default 1MB. - The relocations tend to the kernel binary about 10% larger, + The relocations tend to make the kernel binary about 10% larger, but are discarded at runtime. One use is for the kexec on panic case where the recovery kernel -- cgit v1.2.3 From b0e37650404b2302416ad3dbe649fdecba8d35d4 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:25:13 +0200 Subject: Kconfig: Remove reference to external mqueue library Remove the reference to an external mqueue library since that was merged into glibc in 2004. Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- init/Kconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index f34270087e7..a7e48796d57 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -143,9 +143,7 @@ config POSIX_MQUEUE queues every message has a priority which decides about succession of receiving it by a process. If you want to compile and run programs written e.g. for Solaris with use of its POSIX message - queues (functions mq_*) say Y here. To use this feature you will - also need mqueue library, available from - + queues (functions mq_*) say Y here. POSIX message queues are visible as a filesystem called 'mqueue' and can be mounted somewhere if you want to do filesystem -- cgit v1.2.3 From 02a3e59a088749c08b0293ee1535f5bf48f5926c Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 9 May 2007 07:26:28 +0200 Subject: Fix minor typoes in kernel/module.c Fix minor (comment) typoes in kernel/module.c. Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- kernel/module.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index d36e45477fa..9bd93de01f4 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -96,9 +96,9 @@ static inline void add_taint_module(struct module *mod, unsigned flag) mod->taints |= flag; } -/* A thread that wants to hold a reference to a module only while it - * is running can call ths to safely exit. - * nfsd and lockd use this. +/* + * A thread that wants to hold a reference to a module only while it + * is running can call this to safely exit. nfsd and lockd use this. */ void __module_put_and_exit(struct module *mod, long code) { @@ -1199,7 +1199,7 @@ static int __unlink_module(void *_mod) return 0; } -/* Free a module, remove from lists, etc (must hold module mutex). */ +/* Free a module, remove from lists, etc (must hold module_mutex). */ static void free_module(struct module *mod) { /* Delete from various lists */ @@ -1246,7 +1246,7 @@ EXPORT_SYMBOL_GPL(__symbol_get); /* * Ensure that an exported symbol [global namespace] does not already exist - * in the Kernel or in some other modules exported symbol table. + * in the kernel or in some other module's exported symbol table. */ static int verify_export_symbols(struct module *mod) { -- cgit v1.2.3 From c19aa754f32ff1dd7cb8e96454f5eba95c20585e Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 30 Apr 2007 17:15:46 +0200 Subject: [AVR32] Use correct config symbol when setting cpuflags As Robert P. J. Day pointed out, the CONFIG_CPU_AT32AP7000 symbol wasn't used for anything. It should have been used to select the correct -mcpu= options for CFLAGS. -mcpu=ap7000 is the default anyway, so this patch shouldn't really make any difference, but it's always nice to do things correctly. Signed-off-by: Haavard Skinnemoen --- arch/avr32/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/avr32/Makefile b/arch/avr32/Makefile index 6115fc1f0cf..dc6bc01f232 100644 --- a/arch/avr32/Makefile +++ b/arch/avr32/Makefile @@ -16,7 +16,7 @@ AFLAGS += -mrelax -mno-pic CFLAGS_MODULE += -mno-relax LDFLAGS_vmlinux += --relax -cpuflags-$(CONFIG_CPU_AP7000) += -mcpu=ap7000 +cpuflags-$(CONFIG_CPU_AT32AP7000) += -mcpu=ap7000 CFLAGS += $(cpuflags-y) AFLAGS += $(cpuflags-y) -- cgit v1.2.3 From e89b064a4fd18b9c57b7aecbe7101d782759cf81 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 29 Apr 2007 16:10:34 +0000 Subject: AVR32: Spinlock initializer cleanup Signed-off-by: Thomas Gleixner Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/traps.c | 2 +- arch/avr32/mach-at32ap/clock.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c index 4de9edf96ed..86d107511dd 100644 --- a/arch/avr32/kernel/traps.c +++ b/arch/avr32/kernel/traps.c @@ -123,7 +123,7 @@ asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) /* This way of handling undefined instructions is stolen from ARM */ static LIST_HEAD(undef_hook); -static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(undef_lock); void register_undef_hook(struct undef_hook *hook) { diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c index 00c435452d7..0f8c89c9f83 100644 --- a/arch/avr32/mach-at32ap/clock.c +++ b/arch/avr32/mach-at32ap/clock.c @@ -18,7 +18,7 @@ #include "clock.h" -static spinlock_t clk_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(clk_lock); struct clk *clk_get(struct device *dev, const char *id) { -- cgit v1.2.3 From a492dbb9e3d04db138f2841648d1904d38a5295d Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 20 Mar 2007 14:41:13 +0100 Subject: [AVR32] Implement dma_{alloc,free}_writecombine() Implement dma_alloc_writecombine() and its dma_free_writecombine() counterpart. These will do basically the same thing as dma_alloc_coherent() except that the virtual mapping will allow write buffering, causing better performance for certain use cases like frame buffers. The same API is already available on ARM. Signed-off-by: Haavard Skinnemoen --- arch/avr32/mm/dma-coherent.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c index b68d669f823..099212d4567 100644 --- a/arch/avr32/mm/dma-coherent.c +++ b/arch/avr32/mm/dma-coherent.c @@ -112,16 +112,21 @@ void dma_free_coherent(struct device *dev, size_t size, } EXPORT_SYMBOL(dma_free_coherent); -#if 0 void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) { struct page *page; + dma_addr_t phys; page = __dma_alloc(dev, size, handle, gfp); + if (!page) + return NULL; + + phys = page_to_phys(page); + *handle = phys; /* Now, map the page into P3 with write-combining turned on */ - return __ioremap(page_to_phys(page), size, _PAGE_BUFFER); + return __ioremap(phys, size, _PAGE_BUFFER); } EXPORT_SYMBOL(dma_alloc_writecombine); @@ -132,8 +137,7 @@ void dma_free_writecombine(struct device *dev, size_t size, iounmap(cpu_addr); - page = bus_to_page(handle); + page = phys_to_page(handle); __dma_free(dev, size, page, handle); } EXPORT_SYMBOL(dma_free_writecombine); -#endif -- cgit v1.2.3 From 59c51591a0ac7568824f541f57de967e88adaa07 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 9 May 2007 08:57:56 +0200 Subject: Fix occurrences of "the the " Signed-off-by: Michael Opdenacker Signed-off-by: Adrian Bunk --- Documentation/ABI/removed/devfs | 2 +- Documentation/driver-model/platform.txt | 2 +- Documentation/netlabel/introduction.txt | 2 +- Documentation/pci.txt | 2 +- Documentation/powerpc/booting-without-of.txt | 2 +- arch/mips/Makefile | 2 +- arch/mips/pci/fixup-sb1250.c | 4 ++-- arch/powerpc/platforms/cell/io-workarounds.c | 2 +- arch/v850/kernel/entry.S | 2 +- block/ll_rw_blk.c | 2 +- drivers/block/rd.c | 2 +- drivers/char/drm/drm_dma.c | 2 +- drivers/char/drm/drm_vm.c | 2 +- drivers/char/drm/r300_reg.h | 2 +- drivers/char/pcmcia/cm4000_cs.c | 2 +- drivers/ide/pci/siimage.c | 2 +- drivers/ieee1394/nodemgr.c | 2 +- drivers/isdn/hardware/eicon/divasync.h | 2 +- drivers/isdn/hisax/hfc_usb.c | 4 ++-- drivers/media/dvb/dvb-usb/dvb-usb-remote.c | 2 +- drivers/media/dvb/frontends/tda10021.c | 2 +- drivers/media/dvb/frontends/ves1x93.c | 2 +- drivers/media/video/em28xx/em28xx-video.c | 2 +- drivers/media/video/usbvideo/vicam.c | 2 +- drivers/message/fusion/mptbase.c | 2 +- drivers/mtd/maps/nettel.c | 2 +- drivers/mtd/onenand/onenand_base.c | 2 +- drivers/net/bonding/bond_main.c | 2 +- drivers/net/eepro.c | 2 +- drivers/net/ixgb/ixgb_ee.c | 2 +- drivers/net/meth.h | 2 +- drivers/net/tulip/interrupt.c | 2 +- drivers/net/tulip/winbond-840.c | 2 +- drivers/net/tulip/xircom_cb.c | 2 +- drivers/net/typhoon.c | 2 +- drivers/net/wireless/airport.c | 2 +- drivers/s390/char/sclp_rw.c | 2 +- drivers/s390/net/qeth_main.c | 2 +- drivers/s390/scsi/zfcp_qdio.c | 2 +- drivers/scsi/aic7xxx/aic79xx_pci.c | 2 +- drivers/scsi/aic94xx/Makefile | 2 +- drivers/scsi/dc395x.c | 6 +++--- drivers/scsi/scsi_lib.c | 2 +- drivers/usb/misc/auerswald.c | 2 +- drivers/usb/net/usbnet.h | 2 +- drivers/video/i810/i810_main.c | 2 +- drivers/video/skeletonfb.c | 2 +- fs/jfs/jfs_dmap.c | 2 +- fs/jfs/jfs_imap.c | 4 ++-- fs/jfs/jfs_logmgr.c | 2 +- fs/xfs/xfs_itable.c | 2 +- include/asm-arm/dma-mapping.h | 2 +- include/asm-powerpc/ppc-pci.h | 2 +- include/linux/ext3_fs_i.h | 2 +- include/linux/ext4_fs_i.h | 2 +- include/linux/radix-tree.h | 4 ++-- include/linux/security.h | 2 +- include/linux/usb.h | 2 +- kernel/relay.c | 2 +- kernel/wait.c | 2 +- mm/mmap.c | 2 +- net/decnet/af_decnet.c | 2 +- net/ipv4/cipso_ipv4.c | 2 +- net/ipv4/ipvs/ip_vs_sed.c | 2 +- net/ipv4/udp.c | 2 +- net/llc/af_llc.c | 2 +- net/netfilter/nf_conntrack_expect.c | 2 +- net/sctp/chunk.c | 2 +- net/sctp/socket.c | 4 ++-- net/sunrpc/svcauth.c | 2 +- scripts/basic/docproc.c | 2 +- sound/pci/ac97/ac97_codec.c | 2 +- 72 files changed, 79 insertions(+), 79 deletions(-) diff --git a/Documentation/ABI/removed/devfs b/Documentation/ABI/removed/devfs index 8195c4e0d0a..8ffd28bf659 100644 --- a/Documentation/ABI/removed/devfs +++ b/Documentation/ABI/removed/devfs @@ -6,7 +6,7 @@ Description: races, contains a naming policy within the kernel that is against the LSB, and can be replaced by using udev. The files fs/devfs/*, include/linux/devfs_fs*.h were removed, - along with the the assorted devfs function calls throughout the + along with the assorted devfs function calls throughout the kernel tree. Users: diff --git a/Documentation/driver-model/platform.txt b/Documentation/driver-model/platform.txt index f7c9262b2dc..c5491c22086 100644 --- a/Documentation/driver-model/platform.txt +++ b/Documentation/driver-model/platform.txt @@ -125,7 +125,7 @@ three different ways to find such a match: usually register later during booting, or by module loading. - Registering a driver using platform_driver_probe() works just like - using platform_driver_register(), except that the the driver won't + using platform_driver_register(), except that the driver won't be probed later if another device registers. (Which is OK, since this interface is only for use with non-hotpluggable devices.) diff --git a/Documentation/netlabel/introduction.txt b/Documentation/netlabel/introduction.txt index a4ffba1694c..5ecd8d1dcf4 100644 --- a/Documentation/netlabel/introduction.txt +++ b/Documentation/netlabel/introduction.txt @@ -30,7 +30,7 @@ The communication layer exists to allow NetLabel configuration and monitoring from user space. The NetLabel communication layer uses a message based protocol built on top of the Generic NETLINK transport mechanism. The exact formatting of these NetLabel messages as well as the Generic NETLINK family -names can be found in the the 'net/netlabel/' directory as comments in the +names can be found in the 'net/netlabel/' directory as comments in the header files as well as in 'include/net/netlabel.h'. * Security Module API diff --git a/Documentation/pci.txt b/Documentation/pci.txt index e2c9d0a0c43..d38261b6790 100644 --- a/Documentation/pci.txt +++ b/Documentation/pci.txt @@ -373,7 +373,7 @@ E.g. clearing pending interrupts. 3.6 Register IRQ handler ~~~~~~~~~~~~~~~~~~~~~~~~ -While calling request_irq() is the the last step described here, +While calling request_irq() is the last step described here, this is often just another intermediate step to initialize a device. This step can often be deferred until the device is opened for use. diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index d4bfae75c94..c141f238847 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -1444,7 +1444,7 @@ platforms are moved over to use the flattened-device-tree model. Basically, it is a bus of devices, that could act more or less as a complete entity (UCC, USB etc ). All of them should be siblings on the "root" qe node, using the common properties from there. - The description below applies to the the qe of MPC8360 and + The description below applies to the qe of MPC8360 and more nodes and properties would be extended in the future. i) Root QE device diff --git a/arch/mips/Makefile b/arch/mips/Makefile index f2f742df32c..4892db88a86 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -92,7 +92,7 @@ cflags-y += -ffreestanding # when fed the toolchain default! # # Certain gcc versions upto gcc 4.1.1 (probably 4.2-subversion as of -# 2006-10-10 don't properly change the the predefined symbols if -EB / -EL +# 2006-10-10 don't properly change the predefined symbols if -EB / -EL # are used, so we kludge that here. A bug has been filed at # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29413. # diff --git a/arch/mips/pci/fixup-sb1250.c b/arch/mips/pci/fixup-sb1250.c index 7a7444874e8..0ad39e53f7b 100644 --- a/arch/mips/pci/fixup-sb1250.c +++ b/arch/mips/pci/fixup-sb1250.c @@ -14,7 +14,7 @@ #include /* - * Set the the BCM1250, etc. PCI host bridge's TRDY timeout + * Set the BCM1250, etc. PCI host bridge's TRDY timeout * to the finite max. */ static void __init quirk_sb1250_pci(struct pci_dev *dev) @@ -35,7 +35,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_HT, quirk_sb1250_ht); /* - * Set the the SP1011 HT/PCI bridge's TRDY timeout to the finite max. + * Set the SP1011 HT/PCI bridge's TRDY timeout to the finite max. */ static void __init quirk_sp1011(struct pci_dev *dev) { diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index d68d920eb2c..7fb92f23f38 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -74,7 +74,7 @@ static void spider_io_flush(const volatile void __iomem *addr) /* Fast path if we have a non-0 token, it indicates which bus we * are on. * - * If the token is 0, that means either the the ioremap was done + * If the token is 0, that means either that the ioremap was done * before we initialized this layer, or it's a PIO operation. We * fallback to a low path in this case. Hopefully, internal devices * which are ioremap'ed early should use in_XX/out_XX functions diff --git a/arch/v850/kernel/entry.S b/arch/v850/kernel/entry.S index 8bc521ca081..e4327a8d6bc 100644 --- a/arch/v850/kernel/entry.S +++ b/arch/v850/kernel/entry.S @@ -523,7 +523,7 @@ END(ret_from_trap) /* This the initial entry point for a new child thread, with an appropriate - stack in place that makes it look the the child is in the middle of an + stack in place that makes it look that the child is in the middle of an syscall. This function is actually `returned to' from switch_thread (copy_thread makes ret_from_fork the return address in each new thread's saved context). */ diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index d99d402953a..f294f1538f1 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -1704,7 +1704,7 @@ EXPORT_SYMBOL(blk_stop_queue); * on a queue, such as calling the unplug function after a timeout. * A block device may call blk_sync_queue to ensure that any * such activity is cancelled, thus allowing it to release resources - * the the callbacks might use. The caller must already have made sure + * that the callbacks might use. The caller must already have made sure * that its ->make_request_fn will not re-add plugging prior to calling * this function. * diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 43d4ebcb3b4..a1512da3241 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -151,7 +151,7 @@ static int ramdisk_commit_write(struct file *file, struct page *page, } /* - * ->writepage to the the blockdev's mapping has to redirty the page so that the + * ->writepage to the blockdev's mapping has to redirty the page so that the * VM doesn't go and steal it. We return AOP_WRITEPAGE_ACTIVATE so that the VM * won't try to (pointlessly) write the page again for a while. * diff --git a/drivers/char/drm/drm_dma.c b/drivers/char/drm/drm_dma.c index 892db709698..32ed19c9ec1 100644 --- a/drivers/char/drm/drm_dma.c +++ b/drivers/char/drm/drm_dma.c @@ -65,7 +65,7 @@ int drm_dma_setup(drm_device_t * dev) * \param dev DRM device. * * Free all pages associated with DMA buffers, the buffers and pages lists, and - * finally the the drm_device::dma structure itself. + * finally the drm_device::dma structure itself. */ void drm_dma_takedown(drm_device_t * dev) { diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c index 35540cfb43d..b5c5b9fa84c 100644 --- a/drivers/char/drm/drm_vm.c +++ b/drivers/char/drm/drm_vm.c @@ -157,7 +157,7 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, * \param address access address. * \return pointer to the page structure. * - * Get the the mapping, find the real physical page to map, get the page, and + * Get the mapping, find the real physical page to map, get the page, and * return it. */ static __inline__ struct page *drm_do_vm_shm_nopage(struct vm_area_struct *vma, diff --git a/drivers/char/drm/r300_reg.h b/drivers/char/drm/r300_reg.h index a881f96c983..ecda760ae8c 100644 --- a/drivers/char/drm/r300_reg.h +++ b/drivers/char/drm/r300_reg.h @@ -293,7 +293,7 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_PVS_CNTL_1_PROGRAM_START_SHIFT 0 # define R300_PVS_CNTL_1_POS_END_SHIFT 10 # define R300_PVS_CNTL_1_PROGRAM_END_SHIFT 20 -/* Addresses are relative the the vertex program parameters area. */ +/* Addresses are relative to the vertex program parameters area. */ #define R300_VAP_PVS_CNTL_2 0x22D4 # define R300_PVS_CNTL_2_PARAM_OFFSET_SHIFT 0 # define R300_PVS_CNTL_2_PARAM_COUNT_SHIFT 16 diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index e91b43a014b..4ea587983ae 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1114,7 +1114,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf, /* * wait for atr to become valid. * note: it is important to lock this code. if we dont, the monitor - * could be run between test_bit and the the call the sleep on the + * could be run between test_bit and the call to sleep on the * atr-queue. if *then* the monitor detects atr valid, it will wake up * any process on the atr-queue, *but* since we have been interrupted, * we do not yet sleep on this queue. this would result in a missed diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index c0188de3cc6..79cec50a242 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -831,7 +831,7 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) /* * Now set up the hw. We have to do this ourselves as - * the MMIO layout isnt the same as the the standard port + * the MMIO layout isnt the same as the standard port * based I/O */ diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 6a1a0572275..835937e3852 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -1702,7 +1702,7 @@ static int nodemgr_host_thread(void *__hi) generation = get_hpsb_generation(host); /* If we get a reset before we are done waiting, then - * start the the waiting over again */ + * start the waiting over again */ if (generation != g) g = generation, i = 0; } diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h index af3eb9e795b..85784a7ffb2 100644 --- a/drivers/isdn/hardware/eicon/divasync.h +++ b/drivers/isdn/hardware/eicon/divasync.h @@ -216,7 +216,7 @@ typedef struct #define SERIAL_HOOK_RING 0x85 #define SERIAL_HOOK_DETACH 0x8f unsigned char Flags; /* function refinements */ - /* parameters passed by the the ATTACH request */ + /* parameters passed by the ATTACH request */ SERIAL_INT_CB InterruptHandler; /* called on each interrupt */ SERIAL_DPC_CB DeferredHandler; /* called on hook state changes */ void *HandlerContext; /* context for both handlers */ diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index 99e70d4103b..1f18f199338 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -1217,11 +1217,11 @@ usb_init(hfcusb_data * hfc) /* aux = output, reset off */ write_usb(hfc, HFCUSB_CIRM, 0x10); - /* set USB_SIZE to match the the wMaxPacketSize for INT or BULK transfers */ + /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ write_usb(hfc, HFCUSB_USB_SIZE, (hfc->packet_size / 8) | ((hfc->packet_size / 8) << 4)); - /* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */ + /* set USB_SIZE_I to match the wMaxPacketSize for ISO transfers */ write_usb(hfc, HFCUSB_USB_SIZE_I, hfc->iso_packet_size); /* enable PCM/GCI master mode */ diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 68ed3a78808..9200a30dd1b 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -3,7 +3,7 @@ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) * see dvb-usb-init.c for copyright information. * - * This file contains functions for initializing the the input-device and for handling remote-control-queries. + * This file contains functions for initializing the input-device and for handling remote-control-queries. */ #include "dvb-usb-common.h" #include diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index 110536843e8..e725f612a6b 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -1,6 +1,6 @@ /* TDA10021 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards + used on the Siemens DVB-C cards Copyright (C) 1999 Convergence Integrated Media GmbH Copyright (C) 2004 Markus Schulz diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c index 54d7b07571b..23fd0303c91 100644 --- a/drivers/media/dvb/frontends/ves1x93.c +++ b/drivers/media/dvb/frontends/ves1x93.c @@ -306,7 +306,7 @@ static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) * The ves1893 sometimes returns sync values that make no sense, * because, e.g., the SIGNAL bit is 0, while some of the higher * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?). - * Tests showed that the the VITERBI and SYNC bits are returned + * Tests showed that the VITERBI and SYNC bits are returned * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong. * If such a case occurs, we read the value again, until we get a * valid value. diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index bec67609500..2c7b158ce7e 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1729,7 +1729,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, endpoint = &interface->cur_altsetting->endpoint[1].desc; - /* check if the the device has the iso in endpoint at the correct place */ + /* check if the device has the iso in endpoint at the correct place */ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 876fd276824..982b115193f 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -28,7 +28,7 @@ * * Portions of this code were also copied from usbvideo.c * - * Special thanks to the the whole team at Sourceforge for help making + * Special thanks to the whole team at Sourceforge for help making * this driver become a reality. Notably: * Andy Armstrong who reverse engineered the color encoding and * Pavel Machek and Chris Cheney who worked on reverse engineering the diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 97471af4309..5021d1a2a1d 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -3585,7 +3585,7 @@ initChainBuffers(MPT_ADAPTER *ioc) * index = chain_idx * * Calculate the number of chain buffers needed(plus 1) per I/O - * then multiply the the maximum number of simultaneous cmds + * then multiply the maximum number of simultaneous cmds * * num_sge = num sge in request frame + last chain buffer * scale = num sge per chain buffer if no chain element diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 9f53c655af3..7b96cd02f82 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -358,7 +358,7 @@ int __init nettel_init(void) /* Turn other PAR off so the first probe doesn't find it */ *intel1par = 0; - /* Probe for the the size of the first Intel flash */ + /* Probe for the size of the first Intel flash */ nettel_intel_map.size = maxsize; nettel_intel_map.phys = intel0addr; nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 000794c6caf..0537fac8de7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2192,7 +2192,7 @@ static int onenand_check_maf(int manuf) * @param mtd MTD device structure * * OneNAND detection method: - * Compare the the values from command with ones from register + * Compare the values from command with ones from register */ static int onenand_probe(struct mtd_info *mtd) { diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 724bce51f93..223517dcbcf 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3461,7 +3461,7 @@ void bond_unregister_arp(struct bonding *bond) /*---------------------------- Hashing Policies -----------------------------*/ /* - * Hash for the the output device based upon layer 3 and layer 4 data. If + * Hash for the output device based upon layer 3 and layer 4 data. If * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is * altogether not IP, mimic bond_xmit_hash_policy_l2() */ diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index 39654e1e2be..47680237f78 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1126,7 +1126,7 @@ static void eepro_tx_timeout (struct net_device *dev) printk (KERN_ERR "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); /* This is not a duplicate. One message for the console, - one for the the log file */ + one for the log file */ printk (KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); eepro_complete_selreset(ioaddr); diff --git a/drivers/net/ixgb/ixgb_ee.c b/drivers/net/ixgb/ixgb_ee.c index f15aebde7b9..52c99d01d56 100644 --- a/drivers/net/ixgb/ixgb_ee.c +++ b/drivers/net/ixgb/ixgb_ee.c @@ -315,7 +315,7 @@ ixgb_wait_eeprom_command(struct ixgb_hw *hw) * hw - Struct containing variables accessed by shared code * * Reads the first 64 16 bit words of the EEPROM and sums the values read. - * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * If the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is * valid. * * Returns: diff --git a/drivers/net/meth.h b/drivers/net/meth.h index 84960dae2a2..ea3b8fc86d1 100644 --- a/drivers/net/meth.h +++ b/drivers/net/meth.h @@ -126,7 +126,7 @@ typedef struct rx_packet { /* Note: when loopback is set this bit becomes collision control. Setting this bit will */ /* cause a collision to be reported. */ - /* Bits 5 and 6 are used to determine the the Destination address filter mode */ + /* Bits 5 and 6 are used to determine the Destination address filter mode */ #define METH_ACCEPT_MY 0 /* 00: Accept PHY address only */ #define METH_ACCEPT_MCAST 0x20 /* 01: Accept physical, broadcast, and multicast filter matches only */ #define METH_ACCEPT_AMCAST 0x40 /* 10: Accept physical, broadcast, and all multicast packets */ diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 9b08afbd1f6..ea896777bca 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -269,7 +269,7 @@ done: This would turn on IM for devices that is not contributing to backlog congestion with unnecessary latency. - We monitor the the device RX-ring and have: + We monitor the device RX-ring and have: HW Interrupt Mitigation either ON or OFF. diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index fa440706fb4..38f3b99716b 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -1021,7 +1021,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) np->tx_ring[entry].length |= DescEndRing; /* Now acquire the irq spinlock. - * The difficult race is the the ordering between + * The difficult race is the ordering between * increasing np->cur_tx and setting DescOwned: * - if np->cur_tx is increased first the interrupt * handler could consider the packet as transmitted diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 985a1810ca5..2470b1ee33c 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -1043,7 +1043,7 @@ static int enable_promisc(struct xircom_private *card) /* -link_status() checks the the links status and will return 0 for no link, 10 for 10mbit link and 100 for.. guess what. +link_status() checks the links status and will return 0 for no link, 10 for 10mbit link and 100 for.. guess what. Must be called in locked state with interrupts disabled */ diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index f2dd7763cd0..f7257359412 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -639,7 +639,7 @@ typhoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd, typhoon_inc_cmd_index(&ring->lastWrite, num_cmd); - /* "I feel a presence... another warrior is on the the mesa." + /* "I feel a presence... another warrior is on the mesa." */ wmb(); iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY); diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c index 38fac3bbcd8..7d5b8c2cc61 100644 --- a/drivers/net/wireless/airport.c +++ b/drivers/net/wireless/airport.c @@ -149,7 +149,7 @@ static int airport_hard_reset(struct orinoco_private *priv) /* Vitally important. If we don't do this it seems we get an * interrupt somewhere during the power cycle, since * hw_unavailable is already set it doesn't get ACKed, we get - * into an interrupt loop and the the PMU decides to turn us + * into an interrupt loop and the PMU decides to turn us * off. */ disable_irq(dev->irq); diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index bbd5b8b66f4..d6b06ab8118 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -23,7 +23,7 @@ /* * The room for the SCCB (only for writing) is not equal to a pages size - * (as it is specified as the maximum size in the the SCLP documentation) + * (as it is specified as the maximum size in the SCLP documentation) * because of the additional data structure described above. */ #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 29d176036e5..0b96d49dd63 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -2860,7 +2860,7 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, if (!atomic_read(&queue->set_pci_flags_count)){ /* * there's no outstanding PCI any more, so we - * have to request a PCI to be sure the the PCI + * have to request a PCI to be sure that the PCI * will wake at some time in the future then we * can flush packed buffers that might still be * hanging around, which can happen if no diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index cb08ca3cc0f..bdf5782b8a7 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -222,7 +222,7 @@ zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status, * Since we have been using this adapter, it is save to assume * that it is not failed but recoverable. The card seems to * report link-up events by self-initiated queue shutdown. - * That is why we need to clear the the link-down flag + * That is why we need to clear the link-down flag * which is set again in case we have missed by a mile. */ zfcp_erp_adapter_reopen( diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c index 8d72bbae96a..0bada0028aa 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_pci.c @@ -966,7 +966,7 @@ ahd_aic790X_setup(struct ahd_softc *ahd) | AHD_BUSFREEREV_BUG; ahd->bugs |= AHD_LQOOVERRUN_BUG|AHD_EARLY_REQ_BUG; - /* If the user requested the the SLOWCRC bit to be set. */ + /* If the user requested that the SLOWCRC bit to be set. */ if (aic79xx_slowcrc) ahd->features |= AHD_AIC79XXB_SLOWCRC; diff --git a/drivers/scsi/aic94xx/Makefile b/drivers/scsi/aic94xx/Makefile index e6b70123940..e78ce0fa44d 100644 --- a/drivers/scsi/aic94xx/Makefile +++ b/drivers/scsi/aic94xx/Makefile @@ -6,7 +6,7 @@ # # This file is licensed under GPLv2. # -# This file is part of the the aic94xx driver. +# This file is part of the aic94xx driver. # # The aic94xx driver is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index a965ed3548d..564ea90ed3a 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -541,7 +541,7 @@ static struct ParameterData __devinitdata cfg_data[] = { /* - * Safe settings. If set to zero the the BIOS/default values with + * Safe settings. If set to zero the BIOS/default values with * command line overrides will be used. If set to 1 then safe and * slow settings will be used. */ @@ -617,7 +617,7 @@ static void __devinit fix_settings(void) /* * Mapping from the eeprom delay index value (index into this array) - * to the the number of actual seconds that the delay should be for. + * to the number of actual seconds that the delay should be for. */ static char __devinitdata eeprom_index_to_delay_map[] = { 1, 3, 5, 10, 16, 30, 60, 120 }; @@ -4136,7 +4136,7 @@ static void __devinit trms1040_write_all(struct NvRamType *eeprom, unsigned long * @io_port: base I/O address * @addr: offset into SEEPROM * - * Returns the the byte read. + * Returns the byte read. **/ static u8 __devinit trms1040_get_data(unsigned long io_port, u8 addr) { diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 61fbcdcbb00..1f5a07bf2a7 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -173,7 +173,7 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * @retries: number of times to retry request * @flags: or into request flags; * - * returns the req->errors value which is the the scsi_cmnd result + * returns the req->errors value which is the scsi_cmnd result * field. **/ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index b5332e679c4..88fb56d5db8 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -1307,7 +1307,7 @@ static int auerswald_addservice (pauerswald_t cp, pauerscon_t scp) } -/* remove a service from the the device +/* remove a service from the device scp->id must be set! */ static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp) { diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h index cbb53e065d6..82db5a8e528 100644 --- a/drivers/usb/net/usbnet.h +++ b/drivers/usb/net/usbnet.h @@ -129,7 +129,7 @@ extern void usbnet_disconnect(struct usb_interface *); /* Drivers that reuse some of the standard USB CDC infrastructure - * (notably, using multiple interfaces according to the the CDC + * (notably, using multiple interfaces according to the CDC * union descriptor) get some helper code. */ struct cdc_state { diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index 7e760197cf2..1a7d7789d87 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -1717,7 +1717,7 @@ static int __devinit i810_alloc_agp_mem(struct fb_info *info) * @info: pointer to device specific info structure * * DESCRIPTION: - * Sets the the user monitor's horizontal and vertical + * Sets the user monitor's horizontal and vertical * frequency limits */ static void __devinit i810_init_monspecs(struct fb_info *info) diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 842b5cd054c..836a612af97 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -14,7 +14,7 @@ * of it. * * First the roles of struct fb_info and struct display have changed. Struct - * display will go away. The way the the new framebuffer console code will + * display will go away. The way the new framebuffer console code will * work is that it will act to translate data about the tty/console in * struct vc_data to data in a device independent way in struct fb_info. Then * various functions in struct fb_ops will be called to store the device diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 82b0544bd76..f3b1ebb2228 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -1507,7 +1507,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) if (l2nb < budmin) { /* search the lower level dmap control pages to get - * the starting block number of the the dmap that + * the starting block number of the dmap that * contains or starts off the free space. */ if ((rc = diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index c465607be99..c6530227cda 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -386,7 +386,7 @@ int diRead(struct inode *ip) return -EIO; } - /* locate the the disk inode requested */ + /* locate the disk inode requested */ dp = (struct dinode *) mp->data; dp += rel_inode; @@ -1407,7 +1407,7 @@ int diAlloc(struct inode *pip, bool dir, struct inode *ip) inum = pip->i_ino + 1; ino = inum & (INOSPERIAG - 1); - /* back off the the hint if it is outside of the iag */ + /* back off the hint if it is outside of the iag */ if (ino == 0) inum = pip->i_ino; diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index 6a3f00dc8c8..44a2f33cb98 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1960,7 +1960,7 @@ static void lbmfree(struct lbuf * bp) /* * NAME: lbmRedrive * - * FUNCTION: add a log buffer to the the log redrive list + * FUNCTION: add a log buffer to the log redrive list * * PARAMETER: * bp - log buffer diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 7775ddc0b3c..e725ddd3de5 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -809,7 +809,7 @@ xfs_inumbers( xfs_buf_relse(agbp); agbp = NULL; /* - * Move up the the last inode in the current + * Move up the last inode in the current * chunk. The lookup_ge will always get * us the first inode in the next chunk. */ diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index abfb75b654c..c8b5d0db0cf 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -445,7 +445,7 @@ extern void dmabounce_unregister_dev(struct device *); * * The dmabounce routines call this function whenever a dma-mapping * is requested to determine whether a given buffer needs to be bounced - * or not. The function must return 0 if the the buffer is OK for + * or not. The function must return 0 if the buffer is OK for * DMA access and 1 if the buffer needs to be bounced. * */ diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index d74b2965bb8..6dcd7a811fe 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h @@ -64,7 +64,7 @@ struct pci_dev *pci_get_device_by_addr(unsigned long addr); * eeh_slot_error_detail -- record and EEH error condition to the log * @severity: 1 if temporary, 2 if permanent failure. * - * Obtains the the EEH error details from the RTAS subsystem, + * Obtains the EEH error details from the RTAS subsystem, * and then logs these details with the RTAS error log system. */ void eeh_slot_error_detail (struct pci_dn *pdn, int severity); diff --git a/include/linux/ext3_fs_i.h b/include/linux/ext3_fs_i.h index 4395e520674..7894dd0f3b7 100644 --- a/include/linux/ext3_fs_i.h +++ b/include/linux/ext3_fs_i.h @@ -54,7 +54,7 @@ struct ext3_block_alloc_info { /* * Was i_next_alloc_goal in ext3_inode_info * is the *physical* companion to i_next_alloc_block. - * it the the physical block number of the block which was most-recentl + * it the physical block number of the block which was most-recentl * allocated to this file. This give us the goal (target) for the next * allocation when we detect linearly ascending requests. */ diff --git a/include/linux/ext4_fs_i.h b/include/linux/ext4_fs_i.h index bb42379cb7f..d5b177e5b39 100644 --- a/include/linux/ext4_fs_i.h +++ b/include/linux/ext4_fs_i.h @@ -52,7 +52,7 @@ struct ext4_block_alloc_info { /* * Was i_next_alloc_goal in ext4_inode_info * is the *physical* companion to i_next_alloc_block. - * it the the physical block number of the block which was most-recentl + * it the physical block number of the block which was most-recentl * allocated to this file. This give us the goal (target) for the next * allocation when we detect linearly ascending requests. */ diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 0deb842541a..f9e77d2ee32 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -87,10 +87,10 @@ do { \ * management of their lifetimes must be completely managed by API users. * * For API usage, in general, - * - any function _modifying_ the the tree or tags (inserting or deleting + * - any function _modifying_ the tree or tags (inserting or deleting * items, setting or clearing tags must exclude other modifications, and * exclude any functions reading the tree. - * - any function _reading_ the the tree or tags (looking up items or tags, + * - any function _reading_ the tree or tags (looking up items or tags, * gang lookups) must exclude modifications to the tree, but may occur * concurrently with other readers. * diff --git a/include/linux/security.h b/include/linux/security.h index 47e82c120f9..9eb9e0fe033 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -322,7 +322,7 @@ struct request_sock; * @dir contains the inode structure of parent of the new file. * @dentry contains the dentry structure of the new file. * @mode contains the mode of the new file. - * @dev contains the the device number. + * @dev contains the device number. * Return 0 if permission is granted. * @inode_rename: * Check for permission to rename a file or directory. diff --git a/include/linux/usb.h b/include/linux/usb.h index cfbd2bb8fa2..94bd38a6d94 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -126,7 +126,7 @@ enum usb_interface_condition { * Each interface may have alternate settings. The initial configuration * of a device sets altsetting 0, but the device driver can change * that setting using usb_set_interface(). Alternate settings are often - * used to control the the use of periodic endpoints, such as by having + * used to control the use of periodic endpoints, such as by having * different endpoints use different amounts of reserved USB bandwidth. * All standards-conformant USB devices that use isochronous endpoints * will use them in non-default settings. diff --git a/kernel/relay.c b/kernel/relay.c index 577f251c7e2..d24395e8b6e 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -310,7 +310,7 @@ static struct rchan_callbacks default_channel_callbacks = { /** * wakeup_readers - wake up readers waiting on a channel - * @work: work struct that contains the the channel buffer + * @work: work struct that contains the channel buffer * * This is the work function used to defer reader waking. The * reason waking is deferred is that calling directly from write diff --git a/kernel/wait.c b/kernel/wait.c index 59a82f63275..444ddbfaefc 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -61,7 +61,7 @@ EXPORT_SYMBOL(remove_wait_queue); * The spin_unlock() itself is semi-permeable and only protects * one way (it only protects stuff inside the critical region and * stops them from bleeding out - it would still allow subsequent - * loads to move into the the critical region). + * loads to move into the critical region). */ void fastcall prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) diff --git a/mm/mmap.c b/mm/mmap.c index cc1f543eb1b..68b9ad2ef1d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1720,7 +1720,7 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma, /* * Split a vma into two pieces at address 'addr', a new vma is allocated - * either for the first part or the the tail. + * either for the first part or the tail. */ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr, int new_below) diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 9fbe87c9380..bfa910b6ad2 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1839,7 +1839,7 @@ static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *que } /* - * The DECnet spec requires the the "routing layer" accepts packets which + * The DECnet spec requires that the "routing layer" accepts packets which * are at least 230 bytes in size. This excludes any headers which the NSP * layer might add, so we always assume that we'll be using the maximal * length header on data packets. The variation in length is due to the diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index e1f18489db1..86a2b52aad3 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -629,7 +629,7 @@ doi_walk_return: * @domain: the domain to add * * Description: - * Adds the @domain to the the DOI specified by @doi_def, this function + * Adds the @domain to the DOI specified by @doi_def, this function * should only be called by external functions (i.e. NetLabel). This function * does allocate memory. Returns zero on success, negative values on failure. * diff --git a/net/ipv4/ipvs/ip_vs_sed.c b/net/ipv4/ipvs/ip_vs_sed.c index ff366f7390d..dd7c128f9db 100644 --- a/net/ipv4/ipvs/ip_vs_sed.c +++ b/net/ipv4/ipvs/ip_vs_sed.c @@ -18,7 +18,7 @@ * The SED algorithm attempts to minimize each job's expected delay until * completion. The expected delay that the job will experience is * (Ci + 1) / Ui if sent to the ith server, in which Ci is the number of - * jobs on the the ith server and Ui is the fixed service rate (weight) of + * jobs on the ith server and Ui is the fixed service rate (weight) of * the ith server. The SED algorithm adopts a greedy policy that each does * what is in its own best interest, i.e. to join the queue which would * minimize its expected delay of completion. diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 113e0c4c8a9..66026df1cc7 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -983,7 +983,7 @@ int udp_disconnect(struct sock *sk, int flags) } /* return: - * 1 if the the UDP system should process it + * 1 if the UDP system should process it * 0 if we should drop this packet * -1 if it should get processed by xfrm4_rcv_encap */ diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 7d9fa38b6a7..6b8a103cf9e 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -324,7 +324,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) memset(&laddr, 0, sizeof(laddr)); memset(&daddr, 0, sizeof(daddr)); /* - * FIXME: check if the the address is multicast, + * FIXME: check if the address is multicast, * only SOCK_DGRAM can do this. */ memcpy(laddr.mac, addr->sllc_mac, IFHWADDRLEN); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index c31af29a443..117cbfdb910 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -177,7 +177,7 @@ void nf_conntrack_unexpect_related(struct nf_conntrack_expect *exp) struct nf_conntrack_expect *i; write_lock_bh(&nf_conntrack_lock); - /* choose the the oldest expectation to evict */ + /* choose the oldest expectation to evict */ list_for_each_entry_reverse(i, &nf_conntrack_expect_list, list) { if (expect_matches(i, exp) && del_timer(&i->timeout)) { nf_ct_unlink_expect(i); diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 83ef411772f..77fb7b06a9c 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -3,7 +3,7 @@ * * This file is part of the SCTP kernel reference Implementation * - * This file contains the code relating the the chunk abstraction. + * This file contains the code relating the chunk abstraction. * * The SCTP reference implementation is free software; * you can redistribute it and/or modify it under the terms of diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9f1a908776d..83a76ba9d7b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2586,7 +2586,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt * * 7.1.2 SCTP_ASSOCINFO * - * This option is used to tune the the maximum retransmission attempts + * This option is used to tune the maximum retransmission attempts * of the association. * Returns an error if the new association retransmission value is * greater than the sum of the retransmission value of the peer. @@ -4547,7 +4547,7 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len, * * 7.1.2 SCTP_ASSOCINFO * - * This option is used to tune the the maximum retransmission attempts + * This option is used to tune the maximum retransmission attempts * of the association. * Returns an error if the new association retransmission value is * greater than the sum of the retransmission value of the peer. diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index f5c3808bf85..af7c5f05c6e 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -64,7 +64,7 @@ int svc_set_client(struct svc_rqst *rqstp) } /* A request, which was authenticated, has now executed. - * Time to finalise the the credentials and verifier + * Time to finalise the credentials and verifier * and release and resources */ int svc_authorise(struct svc_rqst *rqstp) diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c index d6071cbf13d..f4d2f68452b 100644 --- a/scripts/basic/docproc.c +++ b/scripts/basic/docproc.c @@ -211,7 +211,7 @@ void find_export_symbols(char * filename) * Document all external or internal functions in a file. * Call kernel-doc with following parameters: * kernel-doc -docbook -nofunction function_name1 filename - * function names are obtained from all the the src files + * function names are obtained from all the src files * by find_export_symbols. * intfunc uses -nofunction * extfunc uses -function diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index a9eec2a2357..3bfb2102fc5 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1083,7 +1083,7 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha unsigned short val; snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); /* Do the read twice due to buffers on some ac97 codecs. - * e.g. The STAC9704 returns exactly what you wrote the the register + * e.g. The STAC9704 returns exactly what you wrote to the register * if you read it immediately. This causes the detect routine to fail. */ val = snd_ac97_read(ac97, reg); -- cgit v1.2.3 From 148e423f909e14564d8af13239c5d060f7df273e Mon Sep 17 00:00:00 2001 From: "Alexander E. Patrakov" Date: Wed, 9 May 2007 07:31:54 +0200 Subject: Remove obsolete fat_cvf help text The text removed by the following patch refers to functionality that never worked, to non-existing documentation file, and to mount options marked as obsolete in the module. Signed-off-by: Alexander E. Patrakov Signed-off-by: Adrian Bunk --- fs/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/Kconfig b/fs/Kconfig index 4622dabb225..0fa0c1193e8 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -724,10 +724,6 @@ config FAT_FS file system and use GNU tar's M option. GNU tar is a program available for Unix and DOS ("man tar" or "info tar"). - It is now also becoming possible to read and write compressed FAT - file systems; read for - details. - The FAT support will enlarge your kernel by about 37 KB. If unsure, say Y. -- cgit v1.2.3 From a982ac06b069f6ee9ea1b64f4ce68cdf2e138742 Mon Sep 17 00:00:00 2001 From: Matt LaPlante Date: Wed, 9 May 2007 07:35:06 +0200 Subject: misc doc and kconfig typos Fix various typos in kernel docs and Kconfigs, 2.6.21-rc4. Signed-off-by: Matt LaPlante Signed-off-by: Adrian Bunk --- Documentation/MSI-HOWTO.txt | 6 +++--- Documentation/block/ioprio.txt | 8 ++++---- Documentation/cpu-freq/cpufreq-stats.txt | 2 +- Documentation/driver-model/platform.txt | 2 +- Documentation/fb/imacfb.txt | 2 +- Documentation/filesystems/hpfs.txt | 2 +- Documentation/filesystems/ntfs.txt | 2 +- Documentation/filesystems/relay.txt | 2 +- Documentation/filesystems/xip.txt | 2 +- Documentation/fujitsu/frv/gdbstub.txt | 2 +- Documentation/input/atarikbd.txt | 4 ++-- Documentation/input/xpad.txt | 6 +++--- Documentation/networking/NAPI_HOWTO.txt | 2 +- Documentation/networking/udplite.txt | 2 +- Documentation/networking/wan-router.txt | 4 ++-- Documentation/pnp.txt | 2 +- Documentation/powerpc/booting-without-of.txt | 2 +- Documentation/scsi/aic7xxx.txt | 2 +- Documentation/scsi/aic7xxx_old.txt | 2 +- Documentation/scsi/ncr53c8xx.txt | 2 +- Documentation/scsi/sym53c8xx_2.txt | 2 +- Documentation/sysctl/kernel.txt | 2 +- arch/arm/mach-at91/Kconfig | 2 +- arch/arm/mach-omap1/Kconfig | 2 +- drivers/isdn/capi/Kconfig | 2 +- drivers/media/video/pwc/philips.txt | 6 +++--- drivers/message/fusion/lsi/mpi_history.txt | 2 +- drivers/mtd/maps/Kconfig | 2 +- sound/core/Kconfig | 2 +- 29 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Documentation/MSI-HOWTO.txt b/Documentation/MSI-HOWTO.txt index d389388c733..0d8240774fc 100644 --- a/Documentation/MSI-HOWTO.txt +++ b/Documentation/MSI-HOWTO.txt @@ -480,8 +480,8 @@ The PCI stack provides 3 possible levels of MSI disabling: 6.1. Disabling MSI on a single device -Under some circumstances, it might be required to disable MSI on a -single device, It may be achived by either not calling pci_enable_msi() +Under some circumstances it might be required to disable MSI on a +single device. This may be achieved by either not calling pci_enable_msi() or all, or setting the pci_dev->no_msi flag before (most of the time in a quirk). @@ -492,7 +492,7 @@ being able to route MSI between busses. In this case, MSI have to be disabled on all devices behind this bridge. It is achieves by setting the PCI_BUS_FLAGS_NO_MSI flag in the pci_bus->bus_flags of the bridge subordinate bus. There is no need to set the same flag on bridges that -are below the broken brigde. When pci_enable_msi() is called to enable +are below the broken bridge. When pci_enable_msi() is called to enable MSI on a device, pci_msi_supported() takes care of checking the NO_MSI flag in all parent busses of the device. diff --git a/Documentation/block/ioprio.txt b/Documentation/block/ioprio.txt index 96ccf681075..1b930ef5a07 100644 --- a/Documentation/block/ioprio.txt +++ b/Documentation/block/ioprio.txt @@ -6,10 +6,10 @@ Intro ----- With the introduction of cfq v3 (aka cfq-ts or time sliced cfq), basic io -priorities is supported for reads on files. This enables users to io nice -processes or process groups, similar to what has been possible to cpu -scheduling for ages. This document mainly details the current possibilites -with cfq, other io schedulers do not support io priorities so far. +priorities are supported for reads on files. This enables users to io nice +processes or process groups, similar to what has been possible with cpu +scheduling for ages. This document mainly details the current possibilities +with cfq; other io schedulers do not support io priorities thus far. Scheduling classes ------------------ diff --git a/Documentation/cpu-freq/cpufreq-stats.txt b/Documentation/cpu-freq/cpufreq-stats.txt index 53d62c1e1c9..fc647492e94 100644 --- a/Documentation/cpu-freq/cpufreq-stats.txt +++ b/Documentation/cpu-freq/cpufreq-stats.txt @@ -17,7 +17,7 @@ Contents 1. Introduction -cpufreq-stats is a driver that provices CPU frequency statistics for each CPU. +cpufreq-stats is a driver that provides CPU frequency statistics for each CPU. These statistics are provided in /sysfs as a bunch of read_only interfaces. This interface (when configured) will appear in a separate directory under cpufreq in /sysfs (/devices/system/cpu/cpuX/cpufreq/stats/) for each CPU. diff --git a/Documentation/driver-model/platform.txt b/Documentation/driver-model/platform.txt index c5491c22086..19c4a6e1367 100644 --- a/Documentation/driver-model/platform.txt +++ b/Documentation/driver-model/platform.txt @@ -16,7 +16,7 @@ host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of some other kind of bus; but its -registers will still be directly addressible. +registers will still be directly addressable. Platform devices are given a name, used in driver binding, and a list of resources such as addresses and IRQs. diff --git a/Documentation/fb/imacfb.txt b/Documentation/fb/imacfb.txt index 759028545a7..316ec9bb7de 100644 --- a/Documentation/fb/imacfb.txt +++ b/Documentation/fb/imacfb.txt @@ -17,7 +17,7 @@ How to use it? ============== Imacfb does not have any kind of autodetection of your machine. -You have to add the fillowing kernel parameters in your elilo.conf: +You have to add the following kernel parameters in your elilo.conf: Macbook : video=imacfb:macbook MacMini : diff --git a/Documentation/filesystems/hpfs.txt b/Documentation/filesystems/hpfs.txt index 38aba03efc5..fa45c3baed9 100644 --- a/Documentation/filesystems/hpfs.txt +++ b/Documentation/filesystems/hpfs.txt @@ -290,7 +290,7 @@ History 2.07 More fixes for Warp Server. Now it really works 2.08 Creating new files is not so slow on large disks An attempt to sync deleted file does not generate filesystem error -2.09 Fixed error on extremly fragmented files +2.09 Fixed error on extremely fragmented files vim: set textwidth=80: diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index 81779068b09..8ee10ec8829 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -349,7 +349,7 @@ end of the line. Note the "Should sync?" parameter "nosync" means that the two mirrors are already in sync which will be the case on a clean shutdown of Windows. If the mirrors are not clean, you can specify the "sync" option instead of "nosync" -and the Device-Mapper driver will then copy the entirey of the "Source Device" +and the Device-Mapper driver will then copy the entirety of the "Source Device" to the "Target Device" or if you specified multipled target devices to all of them. diff --git a/Documentation/filesystems/relay.txt b/Documentation/filesystems/relay.txt index 7fbb6ffe576..18d23f9a18c 100644 --- a/Documentation/filesystems/relay.txt +++ b/Documentation/filesystems/relay.txt @@ -351,7 +351,7 @@ If the current buffer is full, i.e. all sub-buffers remain unconsumed, the callback returns 0 to indicate that the buffer switch should not occur yet, i.e. until the consumer has had a chance to read the current set of ready sub-buffers. For the relay_buf_full() function -to make sense, the consumer is reponsible for notifying the relay +to make sense, the consumer is responsible for notifying the relay interface when sub-buffers have been consumed via relay_subbufs_consumed(). Any subsequent attempts to write into the buffer will again invoke the subbuf_start() callback with the same diff --git a/Documentation/filesystems/xip.txt b/Documentation/filesystems/xip.txt index 6c0cef10eb4..3cc4010521a 100644 --- a/Documentation/filesystems/xip.txt +++ b/Documentation/filesystems/xip.txt @@ -19,7 +19,7 @@ completely. With execute-in-place, read&write type operations are performed directly from/to the memory backed storage device. For file mappings, the storage device itself is mapped directly into userspace. -This implementation was initialy written for shared memory segments between +This implementation was initially written for shared memory segments between different virtual machines on s390 hardware to allow multiple machines to share the same binaries and libraries. diff --git a/Documentation/fujitsu/frv/gdbstub.txt b/Documentation/fujitsu/frv/gdbstub.txt index 9304fb36ae8..b92bfd902a4 100644 --- a/Documentation/fujitsu/frv/gdbstub.txt +++ b/Documentation/fujitsu/frv/gdbstub.txt @@ -126,5 +126,5 @@ GDB stub and the debugger: Furthermore, the GDB stub will intercept a number of exceptions automatically if they are caused by kernel execution. It will also intercept BUG() macro -invokation. +invocation. diff --git a/Documentation/input/atarikbd.txt b/Documentation/input/atarikbd.txt index 668f4d0d97d..ab050621e20 100644 --- a/Documentation/input/atarikbd.txt +++ b/Documentation/input/atarikbd.txt @@ -179,9 +179,9 @@ reporting mode for joystick 1, with both buttons being logically assigned to the mouse. After any joystick command, the ikbd assumes that joysticks are connected to both Joystick0 and Joystick1. Any mouse command (except MOUSE DISABLE) then causes port 0 to again be scanned as if it were a mouse, and -both buttons are logically connected to it. If a mouse diable command is +both buttons are logically connected to it. If a mouse disable command is received while port 0 is presumed to be a mouse, the button is logically -assigned to Joystick1 ( until the mouse is reenabled by another mouse command). +assigned to Joystick1 (until the mouse is reenabled by another mouse command). 9. ikbd Command Set diff --git a/Documentation/input/xpad.txt b/Documentation/input/xpad.txt index 5427bdf225e..aae0d404c56 100644 --- a/Documentation/input/xpad.txt +++ b/Documentation/input/xpad.txt @@ -65,15 +65,15 @@ of buttons, see section 0.3 - Unknown Controllers I've tested this with Stepmania, and it works quite well. -0.3 Unkown Controllers +0.3 Unknown Controllers ---------------------- -If you have an unkown xbox controller, it should work just fine with +If you have an unknown xbox controller, it should work just fine with the default settings. HOWEVER if you have an unknown dance pad not listed below, it will not work UNLESS you set "dpad_to_buttons" to 1 in the module configuration. -PLEASE if you have an unkown controller, email Dom with +PLEASE, if you have an unknown controller, email Dom with a dump from /proc/bus/usb and a description of the pad (manufacturer, country, whether it is a dance pad or normal controller) so that we can add your pad to the list of supported devices, ensuring that it will work out of the diff --git a/Documentation/networking/NAPI_HOWTO.txt b/Documentation/networking/NAPI_HOWTO.txt index fb8dc6422a5..7907435a661 100644 --- a/Documentation/networking/NAPI_HOWTO.txt +++ b/Documentation/networking/NAPI_HOWTO.txt @@ -160,7 +160,7 @@ on current cpu. This primitive is called by dev->poll(), when it completes its work. The device cannot be out of poll list at this call, if it is then clearly it is a BUG(). You'll know ;-> -All these above nethods are used below. So keep reading for clarity. +All of the above methods are used below, so keep reading for clarity. Device driver changes to be made when porting NAPI ================================================== diff --git a/Documentation/networking/udplite.txt b/Documentation/networking/udplite.txt index dd6f46b83da..6be09ba24a3 100644 --- a/Documentation/networking/udplite.txt +++ b/Documentation/networking/udplite.txt @@ -139,7 +139,7 @@ 3) Disabling the Checksum Computation On both sender and receiver, checksumming will always be performed - and can not be disabled using SO_NO_CHECK. Thus + and cannot be disabled using SO_NO_CHECK. Thus setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, ... ); diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt index 07dd6d9930a..bc2ab419a74 100644 --- a/Documentation/networking/wan-router.txt +++ b/Documentation/networking/wan-router.txt @@ -335,7 +335,7 @@ REVISION HISTORY creating applications using BiSync streaming. -2.0.5 Aug 04, 1999 CHDLC initializatin bug fix. +2.0.5 Aug 04, 1999 CHDLC initialization bug fix. PPP interrupt driven driver: Fix to the PPP line hangup problem. New PPP firmware @@ -372,7 +372,7 @@ REVISION HISTORY o cfgft1 GUI csu/dsu configurator o wancfg GUI configuration file configurator. - o Architectual directory changes. + o Architectural directory changes. beta-2.1.4 Jul 2000 o Dynamic interface configuration: Network interfaces reflect the state diff --git a/Documentation/pnp.txt b/Documentation/pnp.txt index 28037aa1846..481faf515d5 100644 --- a/Documentation/pnp.txt +++ b/Documentation/pnp.txt @@ -140,7 +140,7 @@ Plug and Play but it is planned to be in the near future. Requirements for a Linux PnP protocol: 1.) the protocol must use EISA IDs 2.) the protocol must inform the PnP Layer of a devices current configuration -- the ability to set resources is optional but prefered. +- the ability to set resources is optional but preferred. The following are PnP protocol related functions: diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index c141f238847..b49ce169a63 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -1633,7 +1633,7 @@ platforms are moved over to use the flattened-device-tree model. - assignment : function number of the pin according to the Pin Assignment tables in User Manual. Each pin can have up to 4 possible functions in QE and two options for CPM. - - has_irq : indicates if the pin is used as source of exteral + - has_irq : indicates if the pin is used as source of external interrupts. Example: diff --git a/Documentation/scsi/aic7xxx.txt b/Documentation/scsi/aic7xxx.txt index 9b894f116d9..5f34d2ba69b 100644 --- a/Documentation/scsi/aic7xxx.txt +++ b/Documentation/scsi/aic7xxx.txt @@ -40,7 +40,7 @@ The following information is available in this file: 2. Multi-function Twin Channel Device - Two controllers on one chip. 3. Command Channel Secondary DMA Engine - Allows scatter gather list and SCB prefetch. - 4. 64 Byte SCB Support - Allows disconnected, unttagged request table + 4. 64 Byte SCB Support - Allows disconnected, untagged request table for all possible target/lun combinations. 5. Block Move Instruction Support - Doubles the speed of certain sequencer operations. diff --git a/Documentation/scsi/aic7xxx_old.txt b/Documentation/scsi/aic7xxx_old.txt index 05667e7308d..7bd210ab45a 100644 --- a/Documentation/scsi/aic7xxx_old.txt +++ b/Documentation/scsi/aic7xxx_old.txt @@ -356,7 +356,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD or enable Tagged Command Queueing (TCQ) on specific devices. As of driver version 5.1.11, TCQ is now either on or off by default according to the setting you choose during the make config process. - In order to en/disable TCQ for certian devices at boot time, a user + In order to en/disable TCQ for certain devices at boot time, a user may use this boot param. The driver will then parse this message out and en/disable the specific device entries that are present based upon the value given. The param line is parsed in the following manner: diff --git a/Documentation/scsi/ncr53c8xx.txt b/Documentation/scsi/ncr53c8xx.txt index 88ef88b949f..39d409a8efe 100644 --- a/Documentation/scsi/ncr53c8xx.txt +++ b/Documentation/scsi/ncr53c8xx.txt @@ -1260,7 +1260,7 @@ then the request of the IRQ obviously will not succeed for all the drivers. 15.1 Problem tracking Most SCSI problems are due to a non conformant SCSI bus or to buggy -devices. If infortunately you have SCSI problems, you can check the +devices. If unfortunately you have SCSI problems, you can check the following things: - SCSI bus cables diff --git a/Documentation/scsi/sym53c8xx_2.txt b/Documentation/scsi/sym53c8xx_2.txt index 2c1745a9df0..3d9f06bb3d0 100644 --- a/Documentation/scsi/sym53c8xx_2.txt +++ b/Documentation/scsi/sym53c8xx_2.txt @@ -587,7 +587,7 @@ devices, ... may cause a SCSI signal to be wrong when te driver reads it. 15.1 Problem tracking Most SCSI problems are due to a non conformant SCSI bus or too buggy -devices. If infortunately you have SCSI problems, you can check the +devices. If unfortunately you have SCSI problems, you can check the following things: - SCSI bus cables diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index d3d3c255ddb..111fd28727e 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -221,7 +221,7 @@ Controls the kernel's behaviour when an oops or BUG is encountered. 0: try to continue operation -1: panic immediatly. If the `panic' sysctl is also non-zero then the +1: panic immediately. If the `panic' sysctl is also non-zero then the machine will be rebooted. ============================================================== diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index e238ad8cfd8..018d637f87f 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -107,7 +107,7 @@ config ARCH_AT91SAM9260_SAM9XE depends on ARCH_AT91SAM9260 help Select this if you are using Atmel's AT91SAM9XE System-on-Chip. - They are basicaly AT91SAM9260s with various sizes of embedded Flash. + They are basically AT91SAM9260s with various sizes of embedded Flash. comment "AT91SAM9260 / AT91SAM9XE Board Type" diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 8781aaeb576..c7e229b00f6 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -84,7 +84,7 @@ config MACH_OMAP_PALMTE Support for the Palm Tungsten E PDA. Currently only the LCD panel is supported. To boot the kernel, you'll need a PalmOS compatible bootloader; check out http://palmtelinux.sourceforge.net for more - informations. + information. Say Y here if you have such a PDA, say NO otherwise. config MACH_NOKIA770 diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig index c921d6c522f..c92f9d764fc 100644 --- a/drivers/isdn/capi/Kconfig +++ b/drivers/isdn/capi/Kconfig @@ -17,7 +17,7 @@ config CAPI_TRACE help If you say Y here, the kernelcapi driver can make verbose traces of CAPI messages. This feature can be enabled/disabled via IOCTL for - every controler (default disabled). + every controller (default disabled). This will increase the size of the kernelcapi module by 20 KB. If unsure, say Y. diff --git a/drivers/media/video/pwc/philips.txt b/drivers/media/video/pwc/philips.txt index f5e84841031..f9f3584281d 100644 --- a/drivers/media/video/pwc/philips.txt +++ b/drivers/media/video/pwc/philips.txt @@ -54,9 +54,9 @@ fps Specifies the desired framerate. Is an integer in the range of 4-30. fbufs - This paramter specifies the number of internal buffers to use for storing + This parameter specifies the number of internal buffers to use for storing frames from the cam. This will help if the process that reads images from - the cam is a bit slow or momentarely busy. However, on slow machines it + the cam is a bit slow or momentarily busy. However, on slow machines it only introduces lag, so choose carefully. The default is 3, which is reasonable. You can set it between 2 and 5. @@ -209,7 +209,7 @@ trace 128 0x80 PWCX debugging Off - For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, + For example, to trace the open() & read() functions, sum 8 + 4 = 12, so you would supply trace=12 during insmod or modprobe. If you want to turn the initialization and probing tracing off, set trace=0. The default value for trace is 35 (0x23). diff --git a/drivers/message/fusion/lsi/mpi_history.txt b/drivers/message/fusion/lsi/mpi_history.txt index d6b4c607453..ddc7ae029dd 100644 --- a/drivers/message/fusion/lsi/mpi_history.txt +++ b/drivers/message/fusion/lsi/mpi_history.txt @@ -571,7 +571,7 @@ mpi_fc.h * 11-02-00 01.01.01 Original release for post 1.0 work * 12-04-00 01.01.02 Added messages for Common Transport Send and * Primitive Send. - * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * 01-09-01 01.01.03 Modified some of the new flags to have an MPI prefix * and modified the FcPrimitiveSend flags. * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger * field. diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index d990d8141ef..5fcd8b3631b 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -60,7 +60,7 @@ config MTD_PHYSMAP_BANKWIDTH (i.e., run-time calling physmap_configure()). config MTD_PHYSMAP_OF - tristate "Flash device in physical memory map based on OF descirption" + tristate "Flash device in physical memory map based on OF description" depends on PPC_OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) help This provides a 'mapping' driver which allows the NOR Flash and diff --git a/sound/core/Kconfig b/sound/core/Kconfig index b2927523d79..829ca38b595 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -146,7 +146,7 @@ config SND_VERBOSE_PROCFS default y help Say Y here to include code for verbose procfs contents (provides - usefull information to developers when a problem occurs). On the + useful information to developers when a problem occurs). On the other side, it makes the ALSA subsystem larger. config SND_VERBOSE_PRINTK -- cgit v1.2.3 From 01afd80626e98c2347bc25be92ee4a3faf314514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Wed, 9 May 2007 07:48:05 +0200 Subject: drivers/base/platform.c: fix small typo in doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typo: iwithout -> without. Signed-off-by: Márton Németh Signed-off-by: Adrian Bunk --- drivers/base/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index eb84d9d4464..869ff8c0014 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -360,7 +360,7 @@ EXPORT_SYMBOL_GPL(platform_device_unregister); * This function creates a simple platform device that requires minimal * resource and memory management. Canned release function freeing * memory allocated for the device allows drivers using such devices - * to be unloaded iwithout waiting for the last reference to the device + * to be unloaded without waiting for the last reference to the device * to be dropped. * * This interface is primarily intended for use with legacy drivers -- cgit v1.2.3 From 5886269962f94fa9185c32db3ec936c612503235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 May 2007 07:51:49 +0200 Subject: fix file specification in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many files include the filename at the beginning, serveral used a wrong one. Signed-off-by: Uwe Kleine-König Signed-off-by: Adrian Bunk --- arch/arm/mach-s3c2410/sleep.S | 2 +- arch/arm/plat-s3c24xx/sleep.S | 2 +- arch/i386/kernel/cpu/mcheck/therm_throt.c | 2 +- arch/powerpc/platforms/8xx/mpc86xads_setup.c | 2 +- arch/powerpc/platforms/8xx/mpc885ads_setup.c | 2 +- arch/ppc/platforms/mpc866ads_setup.c | 2 +- arch/ppc/syslib/ipic.c | 2 +- arch/sh/boards/se/7751/setup.c | 2 +- arch/sh/kernel/cpu/sh3/entry.S | 2 +- arch/sh/kernel/cpu/sh4a/clock-sh73180.c | 2 +- arch/sh/kernel/cpu/sh4a/clock-sh7343.c | 2 +- arch/sh/kernel/cpu/sh4a/clock-sh7770.c | 2 +- arch/sh/kernel/cpu/sh4a/clock-sh7780.c | 2 +- arch/sh/kernel/vsyscall/vsyscall.c | 2 +- arch/um/include/sysdep-i386/archsetjmp.h | 2 +- arch/um/include/sysdep-x86_64/archsetjmp.h | 2 +- arch/xtensa/kernel/pci-dma.c | 2 +- drivers/leds/leds-h1940.c | 2 +- drivers/video/console/softcursor.c | 2 +- fs/jbd/checkpoint.c | 2 +- fs/jbd/recovery.c | 2 +- fs/jbd/revoke.c | 2 +- fs/jbd/transaction.c | 2 +- fs/jbd2/checkpoint.c | 2 +- fs/jbd2/recovery.c | 2 +- fs/jbd2/revoke.c | 2 +- fs/jbd2/transaction.c | 2 +- include/asm-arm/arch-iop32x/glantank.h | 2 +- include/asm-arm/arch-iop32x/n2100.h | 2 +- include/asm-arm/arch-s3c2410/regs-power.h | 2 +- include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h | 2 +- include/asm-arm/arch-s3c2410/regs-watchdog.h | 2 +- include/asm-arm/arch-s3c2410/udc.h | 2 +- include/asm-sh/edosk7705.h | 2 +- include/asm-sh/snapgear.h | 2 +- include/asm-xtensa/platform-iss/simcall.h | 2 +- include/linux/generic_acl.h | 2 +- sound/soc/pxa/pxa2xx-ac97.h | 2 +- sound/soc/pxa/pxa2xx-i2s.h | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S index 637aaba6539..d1eeed2ad47 100644 --- a/arch/arm/mach-s3c2410/sleep.S +++ b/arch/arm/mach-s3c2410/sleep.S @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-sleep.S +/* linux/arch/arm/mach-s3c2410/sleep.S * * Copyright (c) 2004 Simtec Electronics * Ben Dooks diff --git a/arch/arm/plat-s3c24xx/sleep.S b/arch/arm/plat-s3c24xx/sleep.S index 435349dc324..7b7ae790b00 100644 --- a/arch/arm/plat-s3c24xx/sleep.S +++ b/arch/arm/plat-s3c24xx/sleep.S @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/sleep.S +/* linux/arch/arm/plat-s3c24xx/sleep.S * * Copyright (c) 2004 Simtec Electronics * Ben Dooks diff --git a/arch/i386/kernel/cpu/mcheck/therm_throt.c b/arch/i386/kernel/cpu/mcheck/therm_throt.c index 065005c3f16..2f28540caae 100644 --- a/arch/i386/kernel/cpu/mcheck/therm_throt.c +++ b/arch/i386/kernel/cpu/mcheck/therm_throt.c @@ -1,5 +1,5 @@ /* - * linux/arch/i386/kerne/cpu/mcheck/therm_throt.c + * linux/arch/i386/kernel/cpu/mcheck/therm_throt.c * * Thermal throttle event support code (such as syslog messaging and rate * limiting) that was factored out from x86_64 (mce_intel.c) and i386 (p4.c). diff --git a/arch/powerpc/platforms/8xx/mpc86xads_setup.c b/arch/powerpc/platforms/8xx/mpc86xads_setup.c index a35315af5c5..cf0e7bc8c2e 100644 --- a/arch/powerpc/platforms/8xx/mpc86xads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc86xads_setup.c @@ -1,4 +1,4 @@ -/*arch/ppc/platforms/mpc86xads-setup.c +/*arch/powerpc/platforms/8xx/mpc86xads_setup.c * * Platform setup for the Freescale mpc86xads board * diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index a57b57785ac..c36e475d93d 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -1,4 +1,4 @@ -/*arch/ppc/platforms/mpc885ads-setup.c +/*arch/powerpc/platforms/8xx/mpc885ads_setup.c * * Platform setup for the Freescale mpc885ads board * diff --git a/arch/ppc/platforms/mpc866ads_setup.c b/arch/ppc/platforms/mpc866ads_setup.c index 7ce5364fdb3..bf72204125c 100644 --- a/arch/ppc/platforms/mpc866ads_setup.c +++ b/arch/ppc/platforms/mpc866ads_setup.c @@ -1,4 +1,4 @@ -/*arch/ppc/platforms/mpc866ads-setup.c +/*arch/ppc/platforms/mpc866ads_setup.c * * Platform setup for the Freescale mpc866ads board * diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c index 10659c24b1b..9192777d0f7 100644 --- a/arch/ppc/syslib/ipic.c +++ b/arch/ppc/syslib/ipic.c @@ -1,5 +1,5 @@ /* - * include/asm-ppc/ipic.c + * arch/ppc/syslib/ipic.c * * IPIC routines implementations. * diff --git a/arch/sh/boards/se/7751/setup.c b/arch/sh/boards/se/7751/setup.c index 770defed9c4..52c7bfa57c2 100644 --- a/arch/sh/boards/se/7751/setup.c +++ b/arch/sh/boards/se/7751/setup.c @@ -1,5 +1,5 @@ /* - * linux/arch/sh/kernel/setup_7751se.c + * linux/arch/sh/boards/se/7751/setup.c * * Copyright (C) 2000 Kazumoto Kojima * diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index f3e827f29a4..832c0b4a1e6 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/entry.S + * arch/sh/kernel/cpu/sh3/entry.S * * Copyright (C) 1999, 2000, 2002 Niibe Yutaka * Copyright (C) 2003 - 2006 Paul Mundt diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh73180.c b/arch/sh/kernel/cpu/sh4a/clock-sh73180.c index 2fa5cb2ae68..6d5ba373a75 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh73180.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh73180.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/cpu/sh4/clock-sh73180.c + * arch/sh/kernel/cpu/sh4a/clock-sh73180.c * * SH73180 support for the clock framework * diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c index 1707a213f0c..7adc4f16e95 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/cpu/sh4/clock-sh7343.c + * arch/sh/kernel/cpu/sh4a/clock-sh7343.c * * SH7343/SH7722 support for the clock framework * diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7770.c b/arch/sh/kernel/cpu/sh4a/clock-sh7770.c index c8694bac647..8e236062c72 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7770.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7770.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/cpu/sh4/clock-sh7770.c + * arch/sh/kernel/cpu/sh4a/clock-sh7770.c * * SH7770 support for the clock framework * diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7780.c b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c index 9e6a216750c..01f3da619d3 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/cpu/sh4/clock-sh7780.c + * arch/sh/kernel/cpu/sh4a/clock-sh7780.c * * SH7780 support for the clock framework * diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c index 7b0f66f0331..e146bafcd14 100644 --- a/arch/sh/kernel/vsyscall/vsyscall.c +++ b/arch/sh/kernel/vsyscall/vsyscall.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/vsyscall.c + * arch/sh/kernel/vsyscall/vsyscall.c * * Copyright (C) 2006 Paul Mundt * diff --git a/arch/um/include/sysdep-i386/archsetjmp.h b/arch/um/include/sysdep-i386/archsetjmp.h index 11bafab669e..0f312085ce1 100644 --- a/arch/um/include/sysdep-i386/archsetjmp.h +++ b/arch/um/include/sysdep-i386/archsetjmp.h @@ -1,5 +1,5 @@ /* - * arch/i386/include/klibc/archsetjmp.h + * arch/um/include/sysdep-i386/archsetjmp.h */ #ifndef _KLIBC_ARCHSETJMP_H diff --git a/arch/um/include/sysdep-x86_64/archsetjmp.h b/arch/um/include/sysdep-x86_64/archsetjmp.h index 9a5e1a6ec80..2af8f12ca16 100644 --- a/arch/um/include/sysdep-x86_64/archsetjmp.h +++ b/arch/um/include/sysdep-x86_64/archsetjmp.h @@ -1,5 +1,5 @@ /* - * arch/x86_64/include/klibc/archsetjmp.h + * arch/um/include/sysdep-x86_64/archsetjmp.h */ #ifndef _KLIBC_ARCHSETJMP_H diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c index ca76f071666..f5319d78c87 100644 --- a/arch/xtensa/kernel/pci-dma.c +++ b/arch/xtensa/kernel/pci-dma.c @@ -1,5 +1,5 @@ /* - * arch/xtensa/pci-dma.c + * arch/xtensa/kernel/pci-dma.c * * DMA coherent memory allocation. * diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c index 1d49d2ade55..677c99325be 100644 --- a/drivers/leds/leds-h1940.c +++ b/drivers/leds/leds-h1940.c @@ -1,5 +1,5 @@ /* - * drivers/leds/h1940-leds.c + * drivers/leds/leds-h1940.c * Copyright (c) Arnaud Patard * * This file is subject to the terms and conditions of the GNU General Public diff --git a/drivers/video/console/softcursor.c b/drivers/video/console/softcursor.c index f577bd80e02..03cfb7ac573 100644 --- a/drivers/video/console/softcursor.c +++ b/drivers/video/console/softcursor.c @@ -1,5 +1,5 @@ /* - * linux/drivers/video/softcursor.c + * linux/drivers/video/console/softcursor.c * * Generic software cursor for frame buffer devices * diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index 0208cc7ac5d..47552d4a632 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c @@ -1,5 +1,5 @@ /* - * linux/fs/checkpoint.c + * linux/fs/jbd/checkpoint.c * * Written by Stephen C. Tweedie , 1999 * diff --git a/fs/jbd/recovery.c b/fs/jbd/recovery.c index 11563fe2a52..2a5f4b833e3 100644 --- a/fs/jbd/recovery.c +++ b/fs/jbd/recovery.c @@ -1,5 +1,5 @@ /* - * linux/fs/recovery.c + * linux/fs/jbd/recovery.c * * Written by Stephen C. Tweedie , 1999 * diff --git a/fs/jbd/revoke.c b/fs/jbd/revoke.c index a68cbb60502..824e3b7d4ec 100644 --- a/fs/jbd/revoke.c +++ b/fs/jbd/revoke.c @@ -1,5 +1,5 @@ /* - * linux/fs/revoke.c + * linux/fs/jbd/revoke.c * * Written by Stephen C. Tweedie , 2000 * diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index f9822fc0785..772b6531a2a 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1,5 +1,5 @@ /* - * linux/fs/transaction.c + * linux/fs/jbd/transaction.c * * Written by Stephen C. Tweedie , 1998 * diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 68039fa9a56..3fccde7ba00 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -1,5 +1,5 @@ /* - * linux/fs/checkpoint.c + * linux/fs/jbd2/checkpoint.c * * Written by Stephen C. Tweedie , 1999 * diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 9f10acafaf7..395c92a04ac 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -1,5 +1,5 @@ /* - * linux/fs/recovery.c + * linux/fs/jbd2/recovery.c * * Written by Stephen C. Tweedie , 1999 * diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 1e864dcc49e..9246e763da7 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -1,5 +1,5 @@ /* - * linux/fs/revoke.c + * linux/fs/jbd2/revoke.c * * Written by Stephen C. Tweedie , 2000 * diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index e347d8c078b..7946ff43fc4 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1,5 +1,5 @@ /* - * linux/fs/transaction.c + * linux/fs/jbd2/transaction.c * * Written by Stephen C. Tweedie , 1998 * diff --git a/include/asm-arm/arch-iop32x/glantank.h b/include/asm-arm/arch-iop32x/glantank.h index 3b065618dd0..bf0665acc1c 100644 --- a/include/asm-arm/arch-iop32x/glantank.h +++ b/include/asm-arm/arch-iop32x/glantank.h @@ -1,5 +1,5 @@ /* - * include/asm/arch-iop32x/glantank.h + * include/asm-arm/arch-iop32x/glantank.h * * IO-Data GLAN Tank board registers */ diff --git a/include/asm-arm/arch-iop32x/n2100.h b/include/asm-arm/arch-iop32x/n2100.h index fed31a64842..77a8af47662 100644 --- a/include/asm-arm/arch-iop32x/n2100.h +++ b/include/asm-arm/arch-iop32x/n2100.h @@ -1,5 +1,5 @@ /* - * include/asm/arch-iop32x/n2100.h + * include/asm-arm/arch-iop32x/n2100.h * * Thecus N2100 board registers */ diff --git a/include/asm-arm/arch-s3c2410/regs-power.h b/include/asm-arm/arch-s3c2410/regs-power.h index 6c319ea2afa..94ff96505b6 100644 --- a/include/asm-arm/arch-s3c2410/regs-power.h +++ b/include/asm-arm/arch-s3c2410/regs-power.h @@ -1,4 +1,4 @@ -/* linux/include/asm/arch-s3c2410/regs-power.h +/* linux/include/asm-arm/arch-s3c2410/regs-power.h * * Copyright (c) 2003,2004,2005,2006 Simtec Electronics * http://armlinux.simtec.co.uk/ diff --git a/include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h b/include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h index ff0536d2de4..cd9e26568f8 100644 --- a/include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h +++ b/include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h @@ -1,4 +1,4 @@ -/* linux/include/asm-arm/arch-s3c2410/regs-clock.h +/* linux/include/asm-arm/arch-s3c2410/regs-s3c2443-clock.h * * Copyright (c) 2007 Simtec Electronics * Ben Dooks diff --git a/include/asm-arm/arch-s3c2410/regs-watchdog.h b/include/asm-arm/arch-s3c2410/regs-watchdog.h index f4fff448c7b..a9c5d491bdb 100644 --- a/include/asm-arm/arch-s3c2410/regs-watchdog.h +++ b/include/asm-arm/arch-s3c2410/regs-watchdog.h @@ -1,4 +1,4 @@ -/* linux/include/asm/arch-s3c2410/regs-watchdog.h +/* linux/include/asm-arm/arch-s3c2410/regs-watchdog.h * * Copyright (c) 2003 Simtec Electronics * http://www.simtec.co.uk/products/SWLINUX/ diff --git a/include/asm-arm/arch-s3c2410/udc.h b/include/asm-arm/arch-s3c2410/udc.h index e59ec339d61..b8aa6cb69b5 100644 --- a/include/asm-arm/arch-s3c2410/udc.h +++ b/include/asm-arm/arch-s3c2410/udc.h @@ -1,4 +1,4 @@ -/* linux/include/asm/arch-s3c2410/udc.h +/* linux/include/asm-arm/arch-s3c2410/udc.h * * Copyright (c) 2005 Arnaud Patard * diff --git a/include/asm-sh/edosk7705.h b/include/asm-sh/edosk7705.h index a1089a65bc3..5bdc9d9be3d 100644 --- a/include/asm-sh/edosk7705.h +++ b/include/asm-sh/edosk7705.h @@ -1,5 +1,5 @@ /* - * include/asm-sh/edosk7705/io.h + * include/asm-sh/edosk7705.h * * Modified version of io_se.h for the EDOSK7705 specific functions. * diff --git a/include/asm-sh/snapgear.h b/include/asm-sh/snapgear.h index 6b5e4ddc073..2d712e72c9e 100644 --- a/include/asm-sh/snapgear.h +++ b/include/asm-sh/snapgear.h @@ -1,5 +1,5 @@ /* - * include/asm-sh/snapgear/io.h + * include/asm-sh/snapgear.h * * Modified version of io_se.h for the snapgear-specific functions. * diff --git a/include/asm-xtensa/platform-iss/simcall.h b/include/asm-xtensa/platform-iss/simcall.h index 6acb572759a..b7952c06a2b 100644 --- a/include/asm-xtensa/platform-iss/simcall.h +++ b/include/asm-xtensa/platform-iss/simcall.h @@ -1,5 +1,5 @@ /* - * include/asm-xtensa/platform-iss/hardware.h + * include/asm-xtensa/platform-iss/simcall.h * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive diff --git a/include/linux/generic_acl.h b/include/linux/generic_acl.h index 80764f40be7..886f5faa08c 100644 --- a/include/linux/generic_acl.h +++ b/include/linux/generic_acl.h @@ -1,5 +1,5 @@ /* - * fs/generic_acl.c + * include/linux/generic_acl.h * * (C) 2005 Andreas Gruenbacher * diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h index 4c4b882316a..b8ccfee095c 100644 --- a/sound/soc/pxa/pxa2xx-ac97.h +++ b/sound/soc/pxa/pxa2xx-ac97.h @@ -1,5 +1,5 @@ /* - * linux/sound/arm/pxa2xx-ac97.h + * linux/sound/soc/pxa/pxa2xx-ac97.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h index a2484f0881f..4435bd9f884 100644 --- a/sound/soc/pxa/pxa2xx-i2s.h +++ b/sound/soc/pxa/pxa2xx-i2s.h @@ -1,5 +1,5 @@ /* - * linux/sound/arm/pxa2xx-i2s.h + * linux/sound/soc/pxa/pxa2xx-i2s.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as -- cgit v1.2.3 From a7bc02f4f47fd0e7860c6589f0ad000d1476f7a3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 9 May 2007 07:53:16 +0200 Subject: trivial: s/i_sem /i_mutex/ This patch substitutes i_sem by i_mutex in Documentation/filesystems/Locking. The patch also removes a couple of trailing white-spaces. Signed-off-by: Artem Bityutskiy Signed-off-by: Adrian Bunk --- Documentation/filesystems/Locking | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 59c14159cc4..d866551be03 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -54,7 +54,7 @@ ata *); locking rules: all may block, none have BKL - i_sem(inode) + i_mutex(inode) lookup: yes create: yes link: yes (both) @@ -74,7 +74,7 @@ setxattr: yes getxattr: no listxattr: no removexattr: yes - Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_sem on + Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on victim. cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem. ->truncate() is never called directly - it's a callback, not a @@ -461,7 +461,7 @@ doesn't take the BKL. ->read on directories probably must go away - we should just enforce -EISDIR in sys_read() and friends. -->fsync() has i_sem on inode. +->fsync() has i_mutex on inode. --------------------------- dquot_operations ------------------------------- prototypes: -- cgit v1.2.3 From b96687768a9ac0fdd005c7700093ebb24b93450f Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Wed, 9 May 2007 07:54:49 +0200 Subject: Magic number prefix consistency change to Documentation/magic-number.txt Signed-off-by: Jesper Juhl Signed-off-by: Adrian Bunk --- Documentation/magic-number.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/magic-number.txt b/Documentation/magic-number.txt index 0e740c812d1..bd450e79755 100644 --- a/Documentation/magic-number.txt +++ b/Documentation/magic-number.txt @@ -129,7 +129,7 @@ SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c GDA_MAGIC 0x58464552 gda include/asm-mips64/sn/gda.h RED_MAGIC1 0x5a2cf071 (any) mm/slab.c STL_PORTMAGIC 0x5a7182c9 stlport include/linux/stallion.h -EEPROM_MAGIC_VALUE 0X5ab478d2 lanai_dev drivers/atm/lanai.c +EEPROM_MAGIC_VALUE 0x5ab478d2 lanai_dev drivers/atm/lanai.c HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state include/linux/hdlcdrv.h EPCA_MAGIC 0x5c6df104 channel include/linux/epca.h PCXX_MAGIC 0x5c6df104 channel drivers/char/pcxx.h -- cgit v1.2.3 From 2c2a8c531e953c753b06605c8ad6a9161ca527fa Mon Sep 17 00:00:00 2001 From: Markus Dahms Date: Wed, 9 May 2007 07:58:10 +0200 Subject: remove broken URLs from net drivers' output Remove broken URLs (www.scyld.com) from network drivers' logging output. URLs in comments and other strings are left intact. Signed-off-by: Markus Dahms Acked-by: Jesper Juhl Acked-by: Alan Cox igned-off-by: Adrian Bunk --- drivers/net/3c509.c | 5 ++--- drivers/net/3c59x.c | 2 +- drivers/net/atp.c | 8 +++----- drivers/net/eepro100.c | 2 +- drivers/net/epic100.c | 10 ++++------ drivers/net/natsemi.c | 1 - drivers/net/ne2k-pci.c | 3 +-- drivers/net/sundance.c | 3 +-- drivers/net/yellowfin.c | 1 - 9 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index 9588da3a30e..127f60841b1 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -95,8 +95,7 @@ static int max_interrupt_work = 10; #include #include -static char versionA[] __initdata = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE " becker@scyld.com\n"; -static char versionB[] __initdata = "http://www.scyld.com/network/3c509.html\n"; +static char version[] __initdata = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE " becker@scyld.com\n"; #if defined(CONFIG_PM) && (defined(CONFIG_MCA) || defined(CONFIG_EISA)) #define EL3_SUSPEND @@ -360,7 +359,7 @@ static int __init el3_common_init(struct net_device *dev) printk(", IRQ %d.\n", dev->irq); if (el3_debug > 0) - printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + printk(KERN_INFO "%s", version); return 0; } diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 80924f76dee..f26ca331615 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -103,7 +103,7 @@ static int vortex_debug = 1; static char version[] __devinitdata = -DRV_NAME ": Donald Becker and others. www.scyld.com/network/vortex.html\n"; +DRV_NAME ": Donald Becker and others.\n"; MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("3Com 3c59x/3c9xx ethernet driver "); diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 18aba838c1f..82d78ff8399 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -31,10 +31,8 @@ */ -static const char versionA[] = +static const char version[] = "atp.c:v1.09=ac 2002/10/01 Donald Becker \n"; -static const char versionB[] = -" http://www.scyld.com/network/atp.html\n"; /* The user-configurable values. These may be modified when a driver module is loaded.*/ @@ -324,7 +322,7 @@ static int __init atp_probe1(long ioaddr) #ifndef MODULE if (net_debug) - printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + printk(KERN_INFO "%s", version); #endif printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM " @@ -926,7 +924,7 @@ static void set_rx_mode_8012(struct net_device *dev) static int __init atp_init_module(void) { if (debug) /* Emit version even if no cards detected. */ - printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + printk(KERN_INFO "%s", version); return atp_init(); } diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 6c267c38df9..9800341956a 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -28,7 +28,7 @@ */ static const char * const version = -"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://www.scyld.com/network/eepro100.html\n" +"eepro100.c:v1.09j-t 9/29/99 Donald Becker\n" "eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin and others\n"; /* A few user-configurable values that apply to all boards. diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 4e3f14c9c71..5e517946f46 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -93,8 +93,6 @@ static int rx_copybreak; static char version[] __devinitdata = DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker \n"; static char version2[] __devinitdata = -" http://www.scyld.com/network/epic100.html\n"; -static char version3[] __devinitdata = " (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n"; MODULE_AUTHOR("Donald Becker "); @@ -323,8 +321,8 @@ static int __devinit epic_init_one (struct pci_dev *pdev, #ifndef MODULE static int printed_version; if (!printed_version++) - printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s", - version, version2, version3); + printk (KERN_INFO "%s" KERN_INFO "%s", + version, version2); #endif card_idx++; @@ -1596,8 +1594,8 @@ static int __init epic_init (void) { /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE - printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s", - version, version2, version3); + printk (KERN_INFO "%s" KERN_INFO "%s", + version, version2); #endif return pci_register_driver(&epic_driver); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 223e0e6264b..4cf0d3fcb51 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -131,7 +131,6 @@ static const char version[] __devinitdata = KERN_INFO DRV_NAME " dp8381x driver, version " DRV_VERSION ", " DRV_RELDATE "\n" KERN_INFO " originally by Donald Becker \n" - KERN_INFO " http://www.scyld.com/network/natsemi.html\n" KERN_INFO " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n"; MODULE_AUTHOR("Donald Becker "); diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index 589785d1e76..995c0a5d406 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -63,8 +63,7 @@ static int options[MAX_UNITS]; /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " D. Becker/P. Gortmaker\n" -KERN_INFO " http://www.scyld.com/network/ne2k-pci.html\n"; +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " D. Becker/P. Gortmaker\n"; #if defined(__powerpc__) #define inl_le(addr) le32_to_cpu(inl(addr)) diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index f51ba31970a..e1f912d0404 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -110,8 +110,7 @@ static char *media[MAX_UNITS]; /* These identify the driver base version and may not be removed. */ static char version[] = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n" -KERN_INFO " http://www.scyld.com/network/sundance.html\n"; +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n"; MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("Sundance Alta Ethernet driver"); diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 3f4a7cf9efe..f2a90a7fa2d 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -109,7 +109,6 @@ static int gx_fix; /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = KERN_INFO DRV_NAME ".c:v1.05 1/09/2001 Written by Donald Becker \n" -KERN_INFO " http://www.scyld.com/network/yellowfin.html\n" KERN_INFO " (unofficial 2.4.x port, " DRV_VERSION ", " DRV_RELDATE ")\n"; MODULE_AUTHOR("Donald Becker "); -- cgit v1.2.3 From dd4ef01c4004410e6515d9dd0d92dec133c60b9b Mon Sep 17 00:00:00 2001 From: John Anthony Kazos Jr Date: Wed, 9 May 2007 08:06:37 +0200 Subject: Convert the toplevel files CREDITS and MAINTAINERS to UTF-8. Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Adrian Bunk --- CREDITS | 44 ++++++++++++++++++++++---------------------- MAINTAINERS | 4 ++-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CREDITS b/CREDITS index 80e241304b8..6829e91a88d 100644 --- a/CREDITS +++ b/CREDITS @@ -380,7 +380,7 @@ S: FutureTV Labs Ltd S: Brunswick House, 61-69 Newmarket Rd, Cambridge CB5 8EG S: United Kingdom -N: Thomas Bogendrfer +N: Thomas Bogendörfer E: tsbogend@alpha.franken.de D: PCnet32 driver, SONIC driver, JAZZ_ESP driver D: newport abscon driver, g364 framebuffer driver @@ -400,7 +400,7 @@ W: http://math-www.uni-paderborn.de/~axel/ D: Configuration help text support D: Linux CD and Support Giveaway List -N: Erik Inge Bols +N: Erik Inge Bolsø E: knan@mo.himolde.no D: Misc kernel hacks @@ -428,7 +428,7 @@ D: Various fixes (mostly networking) S: Montreal, Quebec S: Canada -N: Zoltn Bszrmnyi +N: Zoltán Böszörményi E: zboszor@mail.externet.hu D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support @@ -1029,11 +1029,11 @@ D: Future Domain TMC-16x0 SCSI driver (author) D: APM driver (early port) D: DRM drivers (author of several) -N: Jnos Farkas +N: János Farkas E: chexum@shadow.banki.hu D: romfs, various (mostly networking) fixes P: 1024/F81FB2E1 41 B7 E4 E6 3E D4 A6 71 6D 9C F3 9F F2 BF DF 6E -S: Madarsz Viktor utca 25 +S: Madarász Viktor utca 25 S: 1131 Budapest S: Hungary @@ -1044,10 +1044,10 @@ D: UDF filesystem S: (ask for current address) S: USA -N: Jrgen Fischer -E: fischer@norbit.de (=?iso-8859-1?q?J=FCrgen?= Fischer) +N: Jürgen Fischer +E: fischer@norbit.de D: Author of Adaptec AHA-152x SCSI driver -S: Schulstrae 18 +S: Schulstraße 18 S: 26506 Norden S: Germany @@ -1113,7 +1113,7 @@ E: fuganti@netbank.com.br D: random kernel hacker, ZF MachZ Watchdog driver S: Conectiva S.A. S: R. Tocantins, 89 - Cristo Rei -S: 80050-430 - Curitiba - Paran +S: 80050-430 - Curitiba - Paraná S: Brazil N: Kumar Gala @@ -1258,12 +1258,12 @@ S: 44 St. Joseph Street, Suite 506 S: Toronto, Ontario, M4Y 2W4 S: Canada -N: Richard Gnther +N: Richard Günther E: rguenth@tat.physik.uni-tuebingen.de W: http://www.tat.physik.uni-tuebingen.de/~rguenth P: 2048/2E829319 2F 83 FC 93 E9 E4 19 E2 93 7A 32 42 45 37 23 57 D: binfmt_misc -S: 72074 Tbingen +S: 72074 Tübingen S: Germany N: Justin Guyett @@ -1287,7 +1287,7 @@ N: Bruno Haible E: haible@ma2s2.mathematik.uni-karlsruhe.de D: SysV FS, shm swapping, memory management fixes S: 17 rue Danton -S: F - 94270 Le Kremlin-Bictre +S: F - 94270 Le Kremlin-Bicêtre S: France N: Greg Hankins @@ -1701,7 +1701,7 @@ S: Czech Republic N: Jakob Kemi E: jakob.kemi@telia.com D: V4L W9966 Webcam driver -S: Forsbyvgen 33 +S: Forsbyvägen 33 S: 74143 Knivsta S: Sweden @@ -2065,7 +2065,7 @@ D: misc. kernel hacking and debugging S: Cambridge, MA 02139 S: USA -N: Martin von Lwis +N: Martin von Löwis E: loewis@informatik.hu-berlin.de D: script binary format D: NTFS driver @@ -2142,7 +2142,7 @@ S: PO BOX 220, HFX. CENTRAL S: Halifax, Nova Scotia S: Canada B3J 3C8 -N: Kai Mkisara +N: Kai Mäkisara E: Kai.Makisara@kolumbus.fi D: SCSI Tape Driver @@ -2785,10 +2785,10 @@ N: Juan Quintela E: quintela@fi.udc.es D: Memory Management hacking S: LFCIA -S: Departamento de Computacin -S: Universidade da Corua +S: Departamento de Computación +S: Universidade da Coruña S: E-15071 -S: A Corua +S: A Coruña S: Spain N: Augusto Cesar Radtke @@ -2939,7 +2939,7 @@ E: aris@cathedrallabs.org D: Support for EtherExpress 10 ISA (i82595) in eepro driver D: User level driver support for input S: R. Jose Serrato, 130 - Santa Candida -S: 82640-320 - Curitiba - Paran +S: 82640-320 - Curitiba - Paraná S: Brazil N: Alessandro Rubini @@ -3345,15 +3345,15 @@ P: 1024D/D0FE7AFB B24A 65C9 1D71 2AC2 DE87 CA26 189B 9946 D0FE 7AFB D: rcutorture maintainer D: lock annotations, finding and fixing lock bugs -N: Winfried Trmper +N: Winfried Trümper E: winni@xpilot.org W: http://www.shop.de/~winni/ D: German HOWTO, Crash-Kurs Linux (German, 100 comprehensive pages) D: CD-Writing HOWTO, various mini-HOWTOs D: One-week tutorials on Linux twice a year (free of charge) -D: Linux-Workshop Kln (aka LUG Cologne, Germany), Installfests +D: Linux-Workshop Köln (aka LUG Cologne, Germany), Installfests S: Tacitusstr. 6 -S: D-50968 Kln +S: D-50968 Köln N: Tsu-Sheng Tsao E: tsusheng@scf.usc.edu diff --git a/MAINTAINERS b/MAINTAINERS index 41a4b477c57..be37a06bd31 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2800,7 +2800,7 @@ L: linux-abi-devel@lists.sourceforge.net S: Maintained PHRAM MTD DRIVER -P: Jrn Engel +P: Jörn Engel M: joern@wh.fh-wedel.de L: linux-mtd@lists.infradead.org S: Maintained @@ -3074,7 +3074,7 @@ T: git kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git S: Maintained SCSI TAPE DRIVER -P: Kai Mkisara +P: Kai Mäkisara M: Kai.Makisara@kolumbus.fi L: linux-scsi@vger.kernel.org S: Maintained -- cgit v1.2.3 From be2a608bd08ec00e19e14b322e07f443d2711084 Mon Sep 17 00:00:00 2001 From: John Anthony Kazos Jr Date: Wed, 9 May 2007 08:50:42 +0200 Subject: documentation: convert the Documentation directory to UTF-8 Convert files within the Documentation directory to UTF-8. Adrian Bunk: small additional fixes Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Adrian Bunk --- Documentation/arm/Samsung-S3C24XX/H1940.txt | 4 +-- Documentation/auxdisplay/cfag12864b | 6 ++--- Documentation/binfmt_misc.txt | 2 +- Documentation/crypto/api-intro.txt | 6 ++--- Documentation/dvb/README.dvb-usb | 2 +- Documentation/dvb/contributors.txt | 2 +- Documentation/fb/framebuffer.txt | 16 +++++------ Documentation/fb/sstfb.txt | 4 +-- Documentation/hwmon/adm1026 | 2 +- Documentation/hwmon/gl518sm | 2 +- Documentation/hwmon/lm83 | 2 +- Documentation/hwmon/sis5595 | 2 +- Documentation/hwmon/via686a | 2 +- Documentation/hwmon/w83792d | 2 +- Documentation/i2c/busses/i2c-i810 | 2 +- Documentation/i2c/busses/i2c-sis96x | 2 +- Documentation/i2c/busses/i2c-via | 2 +- Documentation/i2c/busses/i2c-viapro | 2 +- Documentation/i2c/i2c-protocol | 2 +- Documentation/i2o/README | 4 +-- Documentation/isdn/CREDITS | 4 +-- Documentation/isdn/README | 2 +- Documentation/isdn/README.icn | 4 +-- Documentation/java.txt | 2 +- Documentation/kernel-docs.txt | 2 +- Documentation/m68k/README.buddha | 2 +- Documentation/networking/6pack.txt | 2 +- Documentation/networking/packet_mmap.txt | 2 +- Documentation/networking/slicecom.hun | 2 +- Documentation/networking/slicecom.txt | 4 +-- Documentation/networking/tms380tr.txt | 42 ++++++++++++++--------------- Documentation/pcieaer-howto.txt | 2 +- Documentation/power/swsusp.txt | 2 +- Documentation/s390/Debugging390.txt | 2 +- Documentation/scsi/aha152x.txt | 2 +- Documentation/scsi/st.txt | 2 +- Documentation/scsi/tmscsim.txt | 2 +- Documentation/sonypi.txt | 2 +- Documentation/sound/oss/mwave | 2 +- Documentation/usb/CREDITS | 2 +- Documentation/usb/usb-serial.txt | 6 ++--- Documentation/video4linux/README.pvrusb2 | 2 +- Documentation/video4linux/Zoran | 2 +- Documentation/video4linux/meye.txt | 2 +- Documentation/video4linux/ov511.txt | 4 +-- 45 files changed, 85 insertions(+), 85 deletions(-) diff --git a/Documentation/arm/Samsung-S3C24XX/H1940.txt b/Documentation/arm/Samsung-S3C24XX/H1940.txt index d6b1de92b11..f4a7b22c866 100644 --- a/Documentation/arm/Samsung-S3C24XX/H1940.txt +++ b/Documentation/arm/Samsung-S3C24XX/H1940.txt @@ -23,7 +23,7 @@ Support http://handhelds.org/moin/moin.cgi/HpIpaqH1940 - Herbert Ptzl pages: + Herbert Pötzl pages: http://vserver.13thfloor.at/H1940/ @@ -32,7 +32,7 @@ Maintainers ----------- This project is being maintained and developed by a variety - of people, including Ben Dooks, Arnaud Patard, and Herbert Ptzl. + of people, including Ben Dooks, Arnaud Patard, and Herbert Pötzl. Thanks to the many others who have also provided support. diff --git a/Documentation/auxdisplay/cfag12864b b/Documentation/auxdisplay/cfag12864b index 3572b98f45b..b714183d412 100644 --- a/Documentation/auxdisplay/cfag12864b +++ b/Documentation/auxdisplay/cfag12864b @@ -78,9 +78,9 @@ Select (17)------------------------------(16) Data / Instruction Ground (18)---[GND] [+5v]---(19) LED + Ground (19)---[GND] Ground (20)---[GND] E A Values: -Ground (21)---[GND] [GND]---[P1]---(18) Vee R = Resistor = 22 ohm -Ground (22)---[GND] | P1 = Preset = 10 Kohm -Ground (23)---[GND] ---- S ------( 3) V0 P2 = Preset = 1 Kohm +Ground (21)---[GND] [GND]---[P1]---(18) Vee - R = Resistor = 22 ohm +Ground (22)---[GND] | - P1 = Preset = 10 Kohm +Ground (23)---[GND] ---- S ------( 3) V0 - P2 = Preset = 1 Kohm Ground (24)---[GND] | | Ground (25)---[GND] [GND]---[P2]---[R]---(20) LED - diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt index d097f09ee15..f609ebf9c78 100644 --- a/Documentation/binfmt_misc.txt +++ b/Documentation/binfmt_misc.txt @@ -113,4 +113,4 @@ cause unexpected behaviour and can be a security hazard. There is a web page about binfmt_misc at http://www.tat.physik.uni-tuebingen.de/~rguenth/linux/binfmt_misc.html -Richard Gnther +Richard Günther diff --git a/Documentation/crypto/api-intro.txt b/Documentation/crypto/api-intro.txt index 9b84b805ab7..a2ac6d29479 100644 --- a/Documentation/crypto/api-intro.txt +++ b/Documentation/crypto/api-intro.txt @@ -177,7 +177,7 @@ Portions of this API were derived from the following projects: and; Nettle (http://www.lysator.liu.se/~nisse/nettle/) - Niels Mller + Niels Möller Original developers of the crypto algorithms: @@ -200,8 +200,8 @@ SHA1 algorithm contributors: DES algorithm contributors: Raimar Falke - Gisle Slensminde - Niels Mller + Gisle Sælensminde + Niels Möller Blowfish algorithm contributors: Herbert Valerio Riedel diff --git a/Documentation/dvb/README.dvb-usb b/Documentation/dvb/README.dvb-usb index 46b78b7331c..bf2a9cdfe7b 100644 --- a/Documentation/dvb/README.dvb-usb +++ b/Documentation/dvb/README.dvb-usb @@ -228,5 +228,5 @@ Patches, comments and suggestions are very very welcome. Ulf Hermenau for helping me out with traditional chinese. - Andr Smoktun and Christian Frmmel for supporting me with + André Smoktun and Christian Frömmel for supporting me with hardware and listening to my problems very patiently. diff --git a/Documentation/dvb/contributors.txt b/Documentation/dvb/contributors.txt index 4c33cced5f6..4865addebe1 100644 --- a/Documentation/dvb/contributors.txt +++ b/Documentation/dvb/contributors.txt @@ -66,7 +66,7 @@ Michael Dreher Andreas 'randy' Weinberger for the support of the Fujitsu-Siemens Activy budget DVB-S -Kenneth Aafly +Kenneth Aafløy for adding support for Typhoon DVB-S budget card Ernst Peinlich diff --git a/Documentation/fb/framebuffer.txt b/Documentation/fb/framebuffer.txt index 610e7801207..b3e3a035683 100644 --- a/Documentation/fb/framebuffer.txt +++ b/Documentation/fb/framebuffer.txt @@ -215,11 +215,11 @@ vertical retrace time is the sum of the upper margin, the lower margin and the vsync length. +----------+---------------------------------------------+----------+-------+ - | | ^ | | | + | | ↑ | | | | | |upper_margin | | | - | | | | | + | | ↓ | | | +----------###############################################----------+-------+ - | # ^ # | | + | # ↑ # | | | # | # | | | # | # | | | # | # | | @@ -238,15 +238,15 @@ vsync length. | # | # | | | # | # | | | # | # | | - | # # | | + | # ↓ # | | +----------###############################################----------+-------+ - | | ^ | | | + | | ↑ | | | | | |lower_margin | | | - | | | | | + | | ↓ | | | +----------+---------------------------------------------+----------+-------+ - | | ^ | | | + | | ↑ | | | | | |vsync_len | | | - | | | | | + | | ↓ | | | +----------+---------------------------------------------+----------+-------+ The frame buffer device expects all horizontal timings in number of dotclocks diff --git a/Documentation/fb/sstfb.txt b/Documentation/fb/sstfb.txt index df27f5bf15d..550ca775a4c 100644 --- a/Documentation/fb/sstfb.txt +++ b/Documentation/fb/sstfb.txt @@ -2,9 +2,9 @@ Introduction This is a frame buffer device driver for 3dfx' Voodoo Graphics - (aka voodoo 1, aka sst1) and Voodoo (aka Voodoo 2, aka CVG) based + (aka voodoo 1, aka sst1) and Voodoo² (aka Voodoo 2, aka CVG) based video boards. It's highly experimental code, but is guaranteed to work - on my computer, with my "Maxi Gamer 3D" and "Maxi Gamer 3d" boards, + on my computer, with my "Maxi Gamer 3D" and "Maxi Gamer 3d²" boards, and with me "between chair and keyboard". Some people tested other combinations and it seems that it works. The main page is located at , and if diff --git a/Documentation/hwmon/adm1026 b/Documentation/hwmon/adm1026 index 473c689d792..f4327db2307 100644 --- a/Documentation/hwmon/adm1026 +++ b/Documentation/hwmon/adm1026 @@ -80,7 +80,7 @@ temperature sensor inputs. Both the PWM output and the DAC output can be used to control fan speed. Usually only one of these two outputs will be used. Write the minimum PWM or DAC value to the appropriate control register. Then set the low temperature limit in the tmin values for each -temperature sensor. The range of control is fixed at 20 C, and the +temperature sensor. The range of control is fixed at 20 °C, and the largest difference between current and tmin of the temperature sensors sets the control output. See the datasheet for several example circuits for controlling fan speed with the PWM and DAC outputs. The fan speed sensors diff --git a/Documentation/hwmon/gl518sm b/Documentation/hwmon/gl518sm index ce0881883bc..229f8b78918 100644 --- a/Documentation/hwmon/gl518sm +++ b/Documentation/hwmon/gl518sm @@ -13,7 +13,7 @@ Supported chips: Authors: Frodo Looijaard , - Kysti Mlkki + Kyösti Mälkki Hong-Gunn Chew Jean Delvare diff --git a/Documentation/hwmon/lm83 b/Documentation/hwmon/lm83 index f7aad1489cb..a04d1fe9269 100644 --- a/Documentation/hwmon/lm83 +++ b/Documentation/hwmon/lm83 @@ -45,7 +45,7 @@ Unconfirmed motherboards: The LM82 is confirmed to have been found on most AMD Geode reference designs and test platforms. -The driver has been successfully tested by Magnus Forsstrm, who I'd +The driver has been successfully tested by Magnus Forsström, who I'd like to thank here. More testers will be of course welcome. The fact that the LM83 is only scarcely used can be easily explained. diff --git a/Documentation/hwmon/sis5595 b/Documentation/hwmon/sis5595 index b7ae36b8cdf..4f8877a34f3 100644 --- a/Documentation/hwmon/sis5595 +++ b/Documentation/hwmon/sis5595 @@ -8,7 +8,7 @@ Supported chips: Datasheet: Publicly available at the Silicon Integrated Systems Corp. site. Authors: - Kysti Mlkki , + Kyösti Mälkki , Mark D. Studebaker , Aurelien Jarno 2.6 port diff --git a/Documentation/hwmon/via686a b/Documentation/hwmon/via686a index a936fb3824b..d651b25f751 100644 --- a/Documentation/hwmon/via686a +++ b/Documentation/hwmon/via686a @@ -8,7 +8,7 @@ Supported chips: Datasheet: On request through web form (http://www.via.com.tw/en/support/datasheets/) Authors: - Kysti Mlkki , + Kyösti Mälkki , Mark D. Studebaker Bob Dougherty (Some conversion-factor data were contributed by diff --git a/Documentation/hwmon/w83792d b/Documentation/hwmon/w83792d index 8171c285bb5..14a668ed8aa 100644 --- a/Documentation/hwmon/w83792d +++ b/Documentation/hwmon/w83792d @@ -107,7 +107,7 @@ Known problems: by CR[0x49h]. - The function of vid and vrm has not been finished, because I'm NOT very familiar with them. Adding support is welcome. - - The function of chassis open detection needs more tests. +  - The function of chassis open detection needs more tests. - If you have ASUS server board and chip was not found: Then you will need to upgrade to latest (or beta) BIOS. If it does not help please contact us. diff --git a/Documentation/i2c/busses/i2c-i810 b/Documentation/i2c/busses/i2c-i810 index 83c3b9743c3..778210ee158 100644 --- a/Documentation/i2c/busses/i2c-i810 +++ b/Documentation/i2c/busses/i2c-i810 @@ -7,7 +7,7 @@ Supported adapters: Authors: Frodo Looijaard , Philip Edelbrock , - Kysti Mlkki , + Kyösti Mälkki , Ralph Metzler , Mark D. Studebaker diff --git a/Documentation/i2c/busses/i2c-sis96x b/Documentation/i2c/busses/i2c-sis96x index 08d7b2dac69..266481fd26e 100644 --- a/Documentation/i2c/busses/i2c-sis96x +++ b/Documentation/i2c/busses/i2c-sis96x @@ -60,7 +60,7 @@ Mark D. Studebaker - design hints and bug fixes Alexander Maylsh - ditto, plus an important datasheet... almost the one I really wanted -Hans-Gnter Ltke Uphues +Hans-Günter Lütke Uphues - patch for SiS735 Robert Zwerus - testing for SiS645DX diff --git a/Documentation/i2c/busses/i2c-via b/Documentation/i2c/busses/i2c-via index 55edfe1a640..343870661ac 100644 --- a/Documentation/i2c/busses/i2c-via +++ b/Documentation/i2c/busses/i2c-via @@ -4,7 +4,7 @@ Supported adapters: * VIA Technologies, InC. VT82C586B Datasheet: Publicly available at the VIA website -Author: Kysti Mlkki +Author: Kyösti Mälkki Description ----------- diff --git a/Documentation/i2c/busses/i2c-viapro b/Documentation/i2c/busses/i2c-viapro index 775f489e86f..06b4be3ef6d 100644 --- a/Documentation/i2c/busses/i2c-viapro +++ b/Documentation/i2c/busses/i2c-viapro @@ -17,7 +17,7 @@ Supported adapters: Datasheet: available on request and under NDA from VIA Authors: - Kysti Mlkki , + Kyösti Mälkki , Mark D. Studebaker , Jean Delvare diff --git a/Documentation/i2c/i2c-protocol b/Documentation/i2c/i2c-protocol index b4022c91421..579b92d5f3a 100644 --- a/Documentation/i2c/i2c-protocol +++ b/Documentation/i2c/i2c-protocol @@ -68,7 +68,7 @@ We have found some I2C devices that needs the following modifications: Flags I2C_M_IGNORE_NAK Normally message is interrupted immediately if there is [NA] from the - client. Setting this flag treats any [NA] as[A], and all of + client. Setting this flag treats any [NA] as [A], and all of message is sent. These messages may still fail to SCL lo->hi timeout. diff --git a/Documentation/i2o/README b/Documentation/i2o/README index 9aa6ddb446e..0ebf58c73f5 100644 --- a/Documentation/i2o/README +++ b/Documentation/i2o/README @@ -30,13 +30,13 @@ Juha Sievanen, University of Helsinki Finland Bug fixes Core code extensions -Auvo Hkkinen, University of Helsinki Finland +Auvo Häkkinen, University of Helsinki Finland LAN OSM code /Proc interface to LAN class Bug fixes Core code extensions -Taneli Vhkangas, University of Helsinki Finland +Taneli Vähäkangas, University of Helsinki Finland Fixes to i2o_config CREDITS diff --git a/Documentation/isdn/CREDITS b/Documentation/isdn/CREDITS index e1b3023efaa..7c17c837064 100644 --- a/Documentation/isdn/CREDITS +++ b/Documentation/isdn/CREDITS @@ -2,7 +2,7 @@ I want to thank all who contributed to this project and especially to: (in alphabetical order) -Thomas Bogendrfer (tsbogend@bigbug.franken.de) +Thomas Bogendörfer (tsbogend@bigbug.franken.de) Tester, lots of bugfixes and hints. Alan Cox (alan@redhat.com) @@ -11,7 +11,7 @@ Alan Cox (alan@redhat.com) Henner Eisen (eis@baty.hanse.de) For X.25 implementation. -Volker Gtz (volker@oops.franken.de) +Volker Götz (volker@oops.franken.de) For contribution of man-pages, the imontty-tool and a perfect maintaining of the mailing-list at hub-wue. diff --git a/Documentation/isdn/README b/Documentation/isdn/README index 76159524393..6783437f21c 100644 --- a/Documentation/isdn/README +++ b/Documentation/isdn/README @@ -402,7 +402,7 @@ README for the ISDN-subsystem the script tools/tcltk/isdnmon. You can add actions for line-status changes. See the comments at the beginning of the script for how to do that. There are other tty-based tools in the tools-subdirectory - contributed by Michael Knigge (imon), Volker Gtz (imontty) and + contributed by Michael Knigge (imon), Volker Götz (imontty) and Andreas Kool (isdnmon). l) For initial testing, you can set the verbose-level to 2 (default: 0). diff --git a/Documentation/isdn/README.icn b/Documentation/isdn/README.icn index a5f55eadb3c..13f833d4e91 100644 --- a/Documentation/isdn/README.icn +++ b/Documentation/isdn/README.icn @@ -3,8 +3,8 @@ $Id: README.icn,v 1.7 2000/08/06 09:22:51 armin Exp $ You can get the ICN-ISDN-card from: Thinking Objects Software GmbH -Versbacher Rthe 159 -97078 Wrzburg +Versbacher Röthe 159 +97078 Würzburg Tel: +49 931 2877950 Fax: +49 931 2877951 diff --git a/Documentation/java.txt b/Documentation/java.txt index c768dc63b34..3cce3fbb664 100644 --- a/Documentation/java.txt +++ b/Documentation/java.txt @@ -390,7 +390,7 @@ the execution bit, then just do originally by Brian A. Lantz, brian@lantz.com -heavily edited for binfmt_misc by Richard Gnther +heavily edited for binfmt_misc by Richard Günther new scripts by Colin J. Watson added executable Jar file support by Kurt Huwig diff --git a/Documentation/kernel-docs.txt b/Documentation/kernel-docs.txt index c68dafeda7a..d9e3b199929 100644 --- a/Documentation/kernel-docs.txt +++ b/Documentation/kernel-docs.txt @@ -236,7 +236,7 @@ * Title: "Design and Implementation of the Second Extended Filesystem" - Author: Rmy Card, Theodore Ts'o, Stephen Tweedie. + Author: Rémy Card, Theodore Ts'o, Stephen Tweedie. URL: http://web.mit.edu/tytso/www/linux/ext2intro.html Keywords: ext2, linux fs history, inode, directory, link, devices, VFS, physical structure, performance, benchmarks, ext2fs library, diff --git a/Documentation/m68k/README.buddha b/Documentation/m68k/README.buddha index ef484a719bb..3ea9827ba3c 100644 --- a/Documentation/m68k/README.buddha +++ b/Documentation/m68k/README.buddha @@ -204,7 +204,7 @@ always shows a "no IRQ here" on the Buddha, and accesses to the third IDE port are going into data's Nirwana on the Buddha. - Jens Schnfeld february 19th, 1997 + Jens Schönfeld february 19th, 1997 updated may 27th, 1997 eMail: sysop@nostlgic.tng.oche.de diff --git a/Documentation/networking/6pack.txt b/Documentation/networking/6pack.txt index 48ed2b711bd..d0777a1200e 100644 --- a/Documentation/networking/6pack.txt +++ b/Documentation/networking/6pack.txt @@ -1,6 +1,6 @@ This is the 6pack-mini-HOWTO, written by -Andreas Knsgen DG3KQ +Andreas Könsgen DG3KQ Internet: ajk@iehk.rwth-aachen.de AMPR-net: dg3kq@db0pra.ampr.org AX.25: dg3kq@db0ach.#nrw.deu.eu diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 5a232d946be..db0cd516958 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -13,7 +13,7 @@ You can find the latest version of this document at Please send me your comments to - Ulisses Alonso Camar + Ulisses Alonso Camaró ------------------------------------------------------------------------------- + Why use PACKET_MMAP diff --git a/Documentation/networking/slicecom.hun b/Documentation/networking/slicecom.hun index 5acf1918694..bed2f045e55 100644 --- a/Documentation/networking/slicecom.hun +++ b/Documentation/networking/slicecom.hun @@ -1,7 +1,7 @@ SliceCOM adapter felhasznaloi dokumentacioja - 0.51 verziohoz -Bartk Istvn +Bartók István Utolso modositas: Wed Aug 29 17:26:58 CEST 2001 ----------------------------------------------------------------- diff --git a/Documentation/networking/slicecom.txt b/Documentation/networking/slicecom.txt index 32d3b916afa..c82c0cf981b 100644 --- a/Documentation/networking/slicecom.txt +++ b/Documentation/networking/slicecom.txt @@ -1,9 +1,9 @@ SliceCOM adapter user's documentation - for the 0.51 driver version -Written by Bartk Istvn +Written by Bartók István -English translation: Lakatos Gyrgy +English translation: Lakatos György Mon Dec 11 15:28:42 CET 2000 Last modified: Wed Aug 29 17:25:37 CEST 2001 diff --git a/Documentation/networking/tms380tr.txt b/Documentation/networking/tms380tr.txt index c169a57bc92..1f73e13058d 100644 --- a/Documentation/networking/tms380tr.txt +++ b/Documentation/networking/tms380tr.txt @@ -71,24 +71,24 @@ Below find attached the setting for the SK NET TR 4/16 ISA adapters CHAPTER 1 LOCATION OF DIP-SWITCH ============================================================== -UĿ -UĿ UĿ UĿ -AU W1 AU UĿ -UĿ Uſ -AU UĿ AU -UĿ UĿ AU AU -AU TMS380C26 -UĿ AU AĿ -AU - AU - - AU - - - - -AAAAAU - AU AU +UÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +þUÄÄÄÄÄÄ¿ UÄÄÄÄÄ¿ UÄÄÄ¿ þ +þAÄÄÄÄÄÄU W1 AÄÄÄÄÄU UÄÄÄÄ¿ þ þ þ +þUÄÄÄÄÄÄ¿ þ þ þ þ UÄÄÅ¿ +þAÄÄÄÄÄÄU UÄÄÄÄÄÄÄÄÄÄÄ¿ AÄÄÄÄU þ þ þ þþ +þUÄÄÄÄÄÄ¿ þ þ UÄÄÄ¿ AÄÄÄU AÄÄÅU +þAÄÄÄÄÄÄU þ TMS380C26 þ þ þ þ +þUÄÄÄÄÄÄ¿ þ þ AÄÄÄU AÄ¿ +þAÄÄÄÄÄÄU þ þ þ þ +þ AÄÄÄÄÄÄÄÄÄÄÄU þ þ +þ þ þ +þ AÄU +þ þ +þ þ +þ þ +þ þ +AÄÄÄÄÄÄÄÄÄÄÄÄAÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄAÄÄAÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄAÄÄÄÄÄÄÄÄÄU + AÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄU AÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄU ============================================================== CHAPTER 2 DEFAULT SETTINGS @@ -108,9 +108,9 @@ A CHAPTER 3 DIP SWITCH W1 DESCRIPTION ============================================================== - UAAAAAAAĿ ON - 1 2 3 4 5 6 7 8 - AAAAAAAAU OFF + UÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄ¿ ON + þ 1 þ 2 þ 3 þ 4 þ 5 þ 6 þ 7 þ 8 þ + AÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄAÄÄÄU OFF |AD | BootROM Addr. | I/O | +-+-+-------+-------+-----+-----+ | | | diff --git a/Documentation/pcieaer-howto.txt b/Documentation/pcieaer-howto.txt index 16c251230c8..d5da8617010 100644 --- a/Documentation/pcieaer-howto.txt +++ b/Documentation/pcieaer-howto.txt @@ -13,7 +13,7 @@ Reporting (AER) driver and provides information on how to use it, as well as how to enable the drivers of endpoint devices to conform with PCI Express AER driver. -1.2 Copyright Intel Corporation 2006. +1.2 Copyright © Intel Corporation 2006. 1.3 What is the PCI Express AER Driver? diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index c55bd5079b9..5b8d6953f05 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -48,7 +48,7 @@ before suspend (it is limited to 500 MB by default). Article about goals and implementation of Software Suspend for Linux ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Author: Gbor Kuti +Author: G‚ábor Kuti Last revised: 2003-10-20 by Pavel Machek Idea and goals to achieve diff --git a/Documentation/s390/Debugging390.txt b/Documentation/s390/Debugging390.txt index 0993969609c..d30a281c570 100644 --- a/Documentation/s390/Debugging390.txt +++ b/Documentation/s390/Debugging390.txt @@ -2209,7 +2209,7 @@ Breakpoint 2 at 0x4d87a4: file top.c, line 2609. #3 0x5167e6 in readline_internal_char () at readline.c:454 #4 0x5168ee in readline_internal_charloop () at readline.c:507 #5 0x51692c in readline_internal () at readline.c:521 -#6 0x5164fe in readline (prompt=0x7ffff810 "\177x\177\177x") +#6 0x5164fe in readline (prompt=0x7ffff810 "\177ÿøx\177ÿ÷Ø\177ÿøxÀ") at readline.c:349 #7 0x4d7a8a in command_line_input (prrompt=0x564420 "(gdb) ", repeat=1, annotation_suffix=0x4d6b44 "prompt") at top.c:2091 diff --git a/Documentation/scsi/aha152x.txt b/Documentation/scsi/aha152x.txt index 2ce022cec9b..29ce6d87e45 100644 --- a/Documentation/scsi/aha152x.txt +++ b/Documentation/scsi/aha152x.txt @@ -1,7 +1,7 @@ $Id: README.aha152x,v 1.2 1999/12/25 15:32:30 fischer Exp fischer $ Adaptec AHA-1520/1522 SCSI driver for Linux (aha152x) -Copyright 1993-1999 Jrgen Fischer +Copyright 1993-1999 Jürgen Fischer TC1550 patches by Luuk van Dijk (ldz@xs4all.nl) diff --git a/Documentation/scsi/st.txt b/Documentation/scsi/st.txt index 3c12422f7f4..b7be95b5bd2 100644 --- a/Documentation/scsi/st.txt +++ b/Documentation/scsi/st.txt @@ -1,5 +1,5 @@ This file contains brief information about the SCSI tape driver. -The driver is currently maintained by Kai Mkisara (email +The driver is currently maintained by Kai Mäkisara (email Kai.Makisara@kolumbus.fi) Last modified: Mon Mar 7 21:14:44 2005 by kai.makisara diff --git a/Documentation/scsi/tmscsim.txt b/Documentation/scsi/tmscsim.txt index 8b2168aa4fc..61c0531e044 100644 --- a/Documentation/scsi/tmscsim.txt +++ b/Documentation/scsi/tmscsim.txt @@ -426,7 +426,7 @@ Thanks to Linus Torvalds, Alan Cox, the FSF people, the XFree86 team and all the others for the wonderful OS and software. Thanks to C.L. Huang and Philip Giang (Tekram) for the initial driver release and support. -Thanks to Doug Ledford, Grard Roudier for support with SCSI coding. +Thanks to Doug Ledford, Gérard Roudier for support with SCSI coding. Thanks to a lot of people (espec. Chiaki Ishikawa, Andreas Haumer, Hubert Tonneau) for intensively testing the driver (and even risking data loss doing this during early revisions). diff --git a/Documentation/sonypi.txt b/Documentation/sonypi.txt index c1237a92550..4857acfc50f 100644 --- a/Documentation/sonypi.txt +++ b/Documentation/sonypi.txt @@ -1,7 +1,7 @@ Sony Programmable I/O Control Device Driver Readme -------------------------------------------------- Copyright (C) 2001-2004 Stelian Pop - Copyright (C) 2001-2002 Alcve + Copyright (C) 2001-2002 Alcôve Copyright (C) 2001 Michael Ashley Copyright (C) 2001 Junichi Morita Copyright (C) 2000 Takaya Kinjo diff --git a/Documentation/sound/oss/mwave b/Documentation/sound/oss/mwave index 858334bb46b..5fbcb160927 100644 --- a/Documentation/sound/oss/mwave +++ b/Documentation/sound/oss/mwave @@ -163,7 +163,7 @@ OR the Default= line COULD be Default=SBPRO Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure -the sound modules and voil - ThinkPad sound with Linux. +the sound modules and voilà - ThinkPad sound with Linux. Now the gotchas - you can either have CD sound OR Mixers but not both. That's a problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why diff --git a/Documentation/usb/CREDITS b/Documentation/usb/CREDITS index 27a721635f9..67c59cdc995 100644 --- a/Documentation/usb/CREDITS +++ b/Documentation/usb/CREDITS @@ -65,7 +65,7 @@ THANKS file in Inaky's driver): will sell keyboards to some of the 3 million (at least) Linux users. - - Many thanks to ing bro h doran [http://www.ibhdoran.com]! + - Many thanks to ing büro h doran [http://www.ibhdoran.com]! It was almost impossible to get a PC backplate USB connector for the motherboard here at Europe (mine, home-made, was quite lousy :). Now I know where to acquire nice USB stuff! diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index b18e86a2250..5b635ae8494 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -45,9 +45,9 @@ ConnectTech WhiteHEAT 4 port converter Connect Tech's Support Department at support@connecttech.com -HandSpring Visor, Palm USB, and Cli USB driver +HandSpring Visor, Palm USB, and Clié USB driver - This driver works with all HandSpring USB, Palm USB, and Sony Cli USB + This driver works with all HandSpring USB, Palm USB, and Sony Clié USB devices. Only when the device tries to connect to the host, will the device show @@ -69,7 +69,7 @@ HandSpring Visor, Palm USB, and Cli the port to use for the HotSync transfer. The "Generic" port can be used for other device communication, such as a PPP link. - For some Sony Cli devices, /dev/ttyUSB0 must be used to talk to the + For some Sony Clié devices, /dev/ttyUSB0 must be used to talk to the device. This is true for all OS version 3.5 devices, and most devices that have had a flash upgrade to a newer version of the OS. See the kernel system log for information on which is the correct port to use. diff --git a/Documentation/video4linux/README.pvrusb2 b/Documentation/video4linux/README.pvrusb2 index a4b7ae80086..a747200fe67 100644 --- a/Documentation/video4linux/README.pvrusb2 +++ b/Documentation/video4linux/README.pvrusb2 @@ -8,7 +8,7 @@ Background: This driver is intended for the "Hauppauge WinTV PVR USB 2.0", which is a USB 2.0 hosted TV Tuner. This driver is a work in progress. - Its history started with the reverse-engineering effort by Bjrn + Its history started with the reverse-engineering effort by Björn Danielsson whose web page can be found here: http://pvrusb2.dax.nu/ diff --git a/Documentation/video4linux/Zoran b/Documentation/video4linux/Zoran index 85c575ac4fb..295462b2317 100644 --- a/Documentation/video4linux/Zoran +++ b/Documentation/video4linux/Zoran @@ -242,7 +242,7 @@ can generate: PAL , NTSC , SECAM Conexant bt866 TV encoder is used in AVS6EYES, and -can generate: NTSC/PAL, PALM, PALN +can generate: NTSC/PAL, PAL­M, PAL­N The adv717x, should be able to produce PAL N. But you find nothing PAL N specific in the registers. Seem that you have to reuse a other standard diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt index 5e51c59bf2b..bf3af5fe558 100644 --- a/Documentation/video4linux/meye.txt +++ b/Documentation/video4linux/meye.txt @@ -1,7 +1,7 @@ Vaio Picturebook Motion Eye Camera Driver Readme ------------------------------------------------ Copyright (C) 2001-2004 Stelian Pop - Copyright (C) 2001-2002 Alcve + Copyright (C) 2001-2002 Alcôve Copyright (C) 2000 Andrew Tridgell This driver enable the use of video4linux compatible applications with the diff --git a/Documentation/video4linux/ov511.txt b/Documentation/video4linux/ov511.txt index 79af610d4ba..b3326b167ad 100644 --- a/Documentation/video4linux/ov511.txt +++ b/Documentation/video4linux/ov511.txt @@ -195,11 +195,11 @@ MODULE PARAMETERS: NAME: bandingfilter TYPE: integer (Boolean) DEFAULT: 0 (off) - DESC: Enables the sensors banding filter exposure algorithm. This reduces + DESC: Enables the sensor´s banding filter exposure algorithm. This reduces or stabilizes the "banding" caused by some artificial light sources (especially fluorescent). You might have to set lightfreq correctly for this to work right. As an added bonus, this sometimes makes it - possible to capture your monitors output. + possible to capture your monitor´s output. NAME: fastset TYPE: integer (Boolean) -- cgit v1.2.3 From f42df9e658be10ca10d0d9b19a0e9d484694f04f Mon Sep 17 00:00:00 2001 From: John Anthony Kazos Jr Date: Wed, 9 May 2007 08:23:08 +0200 Subject: general: convert "kernel" subdirectory to UTF-8 Convert the "kernel" subdirectory of the tree to UTF-8. The only file modified is . Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Adrian Bunk --- kernel/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sys.c b/kernel/sys.c index 926bf9d7ac4..0742c938dfa 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1292,7 +1292,7 @@ asmlinkage long sys_setfsuid(uid_t uid) } /* - * Samma p svenska.. + * Samma på svenska.. */ asmlinkage long sys_setfsgid(gid_t gid) { -- cgit v1.2.3 From 121e70b69aef898a3c02fa90d0a2108381bcf975 Mon Sep 17 00:00:00 2001 From: John Anthony Kazos Jr Date: Wed, 9 May 2007 08:30:57 +0200 Subject: include files: convert "include" subdirectory to UTF-8 Convert the "include" subdirectory to UTF-8. Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Adrian Bunk --- include/asm-arm/arch-imx/imx-regs.h | 2 +- include/asm-arm/arch-integrator/platform.h | 2 +- include/asm-arm/arch-omap/board.h | 2 +- include/asm-arm/arch-omap/dma.h | 2 +- include/asm-arm/arch-omap/gpio.h | 2 +- include/asm-m68k/atarihw.h | 2 +- include/asm-m68k/atariints.h | 2 +- include/asm-ppc/hydra.h | 2 +- include/linux/i2c-algo-bit.h | 2 +- include/linux/i2c-algo-pcf.h | 2 +- include/linux/irda.h | 2 +- include/linux/meye.h | 2 +- include/linux/sonypi.h | 2 +- include/net/irda/af_irda.h | 2 +- include/net/irda/irda.h | 2 +- include/net/irda/iriap.h | 2 +- include/net/irda/iriap_event.h | 2 +- include/net/irda/irias_object.h | 2 +- include/net/irda/irlan_client.h | 2 +- include/net/irda/irlan_common.h | 2 +- include/net/irda/irlan_eth.h | 2 +- include/net/irda/irlan_event.h | 2 +- include/net/irda/irlan_filter.h | 2 +- include/net/irda/irlan_provider.h | 2 +- include/net/irda/irlap.h | 2 +- include/net/irda/irlmp.h | 2 +- include/net/irda/irlmp_event.h | 2 +- include/net/irda/irlmp_frame.h | 2 +- include/net/irda/irmod.h | 2 +- include/net/irda/irqueue.h | 2 +- include/net/irda/irttp.h | 2 +- include/net/irda/parameters.h | 2 +- include/net/irda/timer.h | 2 +- include/net/irda/wrapper.h | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/include/asm-arm/arch-imx/imx-regs.h b/include/asm-arm/arch-imx/imx-regs.h index de6494a4dc6..30de404c61f 100644 --- a/include/asm-arm/arch-imx/imx-regs.h +++ b/include/asm-arm/arch-imx/imx-regs.h @@ -297,7 +297,7 @@ #define SAR(x) __REG2( IMX_DMAC_BASE + 0x80, (x) << 6) /* Source Address Registers */ #define DAR(x) __REG2( IMX_DMAC_BASE + 0x84, (x) << 6) /* Destination Address Registers */ #define CNTR(x) __REG2( IMX_DMAC_BASE + 0x88, (x) << 6) /* Count Registers */ -#define CCR(x) __REG2( IMX_DMAC_BASE + 0x8c, (x) << 6) /*Control Registers */ +#define CCR(x) __REG2( IMX_DMAC_BASE + 0x8c, (x) << 6) /* Control Registers */ #define RSSR(x) __REG2( IMX_DMAC_BASE + 0x90, (x) << 6) /* Request source select Registers */ #define BLR(x) __REG2( IMX_DMAC_BASE + 0x94, (x) << 6) /* Burst length Registers */ #define RTOR(x) __REG2( IMX_DMAC_BASE + 0x98, (x) << 6) /* Request timeout Registers */ diff --git a/include/asm-arm/arch-integrator/platform.h b/include/asm-arm/arch-integrator/platform.h index 96ad3d2a66d..83c4c1ceb41 100644 --- a/include/asm-arm/arch-integrator/platform.h +++ b/include/asm-arm/arch-integrator/platform.h @@ -17,7 +17,7 @@ * from .s file by awk -f s2h.awk */ /************************************************************************** - * * Copyright ARM Limited 1998. All rights reserved. + * * Copyright © ARM Limited 1998. All rights reserved. * ***********************************************************************/ /* ************************************************************************ * diff --git a/include/asm-arm/arch-omap/board.h b/include/asm-arm/arch-omap/board.h index edf1dc6ad91..4fd717e626a 100644 --- a/include/asm-arm/arch-omap/board.h +++ b/include/asm-arm/arch-omap/board.h @@ -4,7 +4,7 @@ * Information structures for board-specific data * * Copyright (C) 2004 Nokia Corporation - * Written by Juha Yrjl + * Written by Juha Yrjölä */ #ifndef _OMAP_BOARD_H diff --git a/include/asm-arm/arch-omap/dma.h b/include/asm-arm/arch-omap/dma.h index d591d0585bb..f7774192a41 100644 --- a/include/asm-arm/arch-omap/dma.h +++ b/include/asm-arm/arch-omap/dma.h @@ -2,7 +2,7 @@ * linux/include/asm-arm/arch-omap/dma.h * * Copyright (C) 2003 Nokia Corporation - * Author: Juha Yrjl + * Author: Juha Yrjölä * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/asm-arm/arch-omap/gpio.h b/include/asm-arm/arch-omap/gpio.h index 590917efc94..97b397dd7e8 100644 --- a/include/asm-arm/arch-omap/gpio.h +++ b/include/asm-arm/arch-omap/gpio.h @@ -5,7 +5,7 @@ * * Copyright (C) 2003-2005 Nokia Corporation * - * Written by Juha Yrjl + * Written by Juha Yrjölä * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/asm-m68k/atarihw.h b/include/asm-m68k/atarihw.h index f28acd0fd68..6211363a345 100644 --- a/include/asm-m68k/atarihw.h +++ b/include/asm-m68k/atarihw.h @@ -2,7 +2,7 @@ ** linux/atarihw.h -- This header defines some macros and pointers for ** the various Atari custom hardware registers. ** -** Copyright 1994 by Bjrn Brauel +** Copyright 1994 by Bj”rn Brauel ** ** 5/1/94 Roman Hodek: ** Added definitions for TT specific chips. diff --git a/include/asm-m68k/atariints.h b/include/asm-m68k/atariints.h index 0ed454fc24b..ce6c445805b 100644 --- a/include/asm-m68k/atariints.h +++ b/include/asm-m68k/atariints.h @@ -1,7 +1,7 @@ /* ** atariints.h -- Atari Linux interrupt handling structs and prototypes ** -** Copyright 1994 by Bjrn Brauel +** Copyright 1994 by Bj”rn Brauel ** ** 5/2/94 Roman Hodek: ** TT interrupt definitions added. diff --git a/include/asm-ppc/hydra.h b/include/asm-ppc/hydra.h index 833a8aff2a8..1ad4eed07fb 100644 --- a/include/asm-ppc/hydra.h +++ b/include/asm-ppc/hydra.h @@ -8,7 +8,7 @@ * Macintosh Technology in the Common Hardware Reference Platform * Apple Computer, Inc. * - * Copyright 1995 Apple Computer, Inc. All rights reserved. + * © Copyright 1995 Apple Computer, Inc. All rights reserved. * * It's available online from http://chrp.apple.com/MacTech.pdf. * You can obtain paper copies of this book from computer bookstores or by diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index 9ee0f800592..111334f5b92 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -18,7 +18,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ------------------------------------------------------------------------- */ -/* With some changes from Kysti Mlkki and even +/* With some changes from Kyösti Mälkki and even Frodo Looijaard */ #ifndef _LINUX_I2C_ALGO_BIT_H diff --git a/include/linux/i2c-algo-pcf.h b/include/linux/i2c-algo-pcf.h index 994eb86f882..77afbb60fd1 100644 --- a/include/linux/i2c-algo-pcf.h +++ b/include/linux/i2c-algo-pcf.h @@ -19,7 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ------------------------------------------------------------------------- */ -/* With some changes from Kysti Mlkki and even +/* With some changes from Kyösti Mälkki and even Frodo Looijaard */ #ifndef _LINUX_I2C_ALGO_PCF_H diff --git a/include/linux/irda.h b/include/linux/irda.h index 09d8f105a5a..945ba311087 100644 --- a/include/linux/irda.h +++ b/include/linux/irda.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/linux/meye.h b/include/linux/meye.h index 11ec45e9a13..39fd9c8ddd4 100644 --- a/include/linux/meye.h +++ b/include/linux/meye.h @@ -3,7 +3,7 @@ * * Copyright (C) 2001-2003 Stelian Pop * - * Copyright (C) 2001-2002 Alcve + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2000 Andrew Tridgell * diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index f56d2473495..34d4b075f7b 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -5,7 +5,7 @@ * * Copyright (C) 2005 Narayanan R S - * Copyright (C) 2001-2002 Alcve + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2001 Michael Ashley * diff --git a/include/net/irda/af_irda.h b/include/net/irda/af_irda.h index 7a209f61c48..0df57493152 100644 --- a/include/net/irda/af_irda.h +++ b/include/net/irda/af_irda.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irda.h b/include/net/irda/irda.h index 89fe534045f..36bee441aa5 100644 --- a/include/net/irda/irda.h +++ b/include/net/irda/irda.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/iriap.h b/include/net/irda/iriap.h index 2007c5a0a43..fcc896491a9 100644 --- a/include/net/irda/iriap.h +++ b/include/net/irda/iriap.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/iriap_event.h b/include/net/irda/iriap_event.h index 4ca3d2071b0..89747f06d9e 100644 --- a/include/net/irda/iriap_event.h +++ b/include/net/irda/iriap_event.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irias_object.h b/include/net/irda/irias_object.h index c41196b8795..83f78081799 100644 --- a/include/net/irda/irias_object.h +++ b/include/net/irda/irias_object.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_client.h b/include/net/irda/irlan_client.h index 736dabe211e..fa8455eda28 100644 --- a/include/net/irda/irlan_client.h +++ b/include/net/irda/irlan_client.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_common.h b/include/net/irda/irlan_common.h index 9592c374b41..73cacb3ac16 100644 --- a/include/net/irda/irlan_common.h +++ b/include/net/irda/irlan_common.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_eth.h b/include/net/irda/irlan_eth.h index 9a9b3619d30..0062347600b 100644 --- a/include/net/irda/irlan_eth.h +++ b/include/net/irda/irlan_eth.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_event.h b/include/net/irda/irlan_event.h index b9baac9eb8b..6d9539f0580 100644 --- a/include/net/irda/irlan_event.h +++ b/include/net/irda/irlan_event.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_filter.h b/include/net/irda/irlan_filter.h index 1720539ac2c..a5a2539485b 100644 --- a/include/net/irda/irlan_filter.h +++ b/include/net/irda/irlan_filter.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlan_provider.h b/include/net/irda/irlan_provider.h index ca51d5b7c99..92f3b0e1029 100644 --- a/include/net/irda/irlan_provider.h +++ b/include/net/irda/irlan_provider.h @@ -16,7 +16,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlap.h b/include/net/irda/irlap.h index e77eb88d922..f0248fb8e19 100644 --- a/include/net/irda/irlap.h +++ b/include/net/irda/irlap.h @@ -18,7 +18,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlmp.h b/include/net/irda/irlmp.h index e212b9bc250..3ffc1d0f93d 100644 --- a/include/net/irda/irlmp.h +++ b/include/net/irda/irlmp.h @@ -18,7 +18,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlmp_event.h b/include/net/irda/irlmp_event.h index 03c6f81a502..e03ae4ae396 100644 --- a/include/net/irda/irlmp_event.h +++ b/include/net/irda/irlmp_event.h @@ -18,7 +18,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irlmp_frame.h b/include/net/irda/irlmp_frame.h index c463f8bca85..1906eb71422 100644 --- a/include/net/irda/irlmp_frame.h +++ b/include/net/irda/irlmp_frame.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irmod.h b/include/net/irda/irmod.h index 72b446c1e22..86f0dbb8ee5 100644 --- a/include/net/irda/irmod.h +++ b/include/net/irda/irmod.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charg. * diff --git a/include/net/irda/irqueue.h b/include/net/irda/irqueue.h index 335b0ace966..37f512bd673 100644 --- a/include/net/irda/irqueue.h +++ b/include/net/irda/irqueue.h @@ -21,7 +21,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/irttp.h b/include/net/irda/irttp.h index a899e5837be..cf80c1af585 100644 --- a/include/net/irda/irttp.h +++ b/include/net/irda/irttp.h @@ -18,7 +18,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/parameters.h b/include/net/irda/parameters.h index 3a605d37ddb..c0d938847bd 100644 --- a/include/net/irda/parameters.h +++ b/include/net/irda/parameters.h @@ -26,7 +26,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * Michel Dnzer , 10/2001 + * Michel Dänzer , 10/2001 * - simplify irda_pv_t to avoid endianness issues * ********************************************************************/ diff --git a/include/net/irda/timer.h b/include/net/irda/timer.h index cb61568547d..cb2615ccf76 100644 --- a/include/net/irda/timer.h +++ b/include/net/irda/timer.h @@ -18,7 +18,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * diff --git a/include/net/irda/wrapper.h b/include/net/irda/wrapper.h index 98768b3f9e3..2942ad6ab93 100644 --- a/include/net/irda/wrapper.h +++ b/include/net/irda/wrapper.h @@ -17,7 +17,7 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * - * Neither Dag Brattli nor University of Troms admit liability nor + * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * -- cgit v1.2.3 From 9ae5e3bc316e753d76950ded0ea75d77e9459ba6 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Wed, 9 May 2007 08:38:10 +0200 Subject: MAINTAINERS: Add cxacru website/mailing list Signed-off-by: Simon Arlott Signed-off-by: Adrian Bunk --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index be37a06bd31..3b46f43c338 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1038,6 +1038,8 @@ S: Maintained CONEXANT ACCESSRUNNER USB DRIVER P: Simon Arlott M: cxacru@fire.lp0.eu +L: accessrunner-general@lists.sourceforge.net +W: http://accessrunner.sourceforge.net/ S: Maintained CORETEMP HARDWARE MONITORING DRIVER -- cgit v1.2.3 From 7bb2acb76e8168ca5d0bde5a5a56585a11b3525a Mon Sep 17 00:00:00 2001 From: John Anthony Kazos Jr Date: Wed, 9 May 2007 08:41:36 +0200 Subject: sound: convert "sound" subdirectory to UTF-8 Convert the "sound" subdirectory to UTF-8. Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Adrian Bunk --- sound/oss/es1371.c | 2 +- sound/oss/pas2_pcm.c | 2 +- sound/oss/trident.c | 4 ++-- sound/pci/mixart/mixart.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/oss/es1371.c b/sound/oss/es1371.c index 974dd732b14..593a3aac12c 100644 --- a/sound/oss/es1371.c +++ b/sound/oss/es1371.c @@ -100,7 +100,7 @@ * Tjeerd Mulder * 05.01.2001 0.29 Hopefully updates will not be required anymore when Creative bumps * the CT5880 revision. - * suggested by Stephan Mller + * suggested by Stephan Müller * 31.01.2001 0.30 Register/Unregister gameport * Fix SETTRIGGER non OSS API conformity * 14.07.2001 0.31 Add list of laptops needing amplifier control diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c index 4af6aafa3d8..36c3ea62086 100644 --- a/sound/oss/pas2_pcm.c +++ b/sound/oss/pas2_pcm.c @@ -85,7 +85,7 @@ static int pcm_set_speed(int arg) * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. * * I cleared bit 5 of these values, since that bit controls the master - * mute flag. (Olav Wlfelschneider) + * mute flag. (Olav Wölfelschneider) * */ #if !defined NO_AUTO_FILTER_SET diff --git a/sound/oss/trident.c b/sound/oss/trident.c index d98d311542e..3bc1f6e9e4a 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -18,7 +18,7 @@ * Ollie Lho SiS 7018 Audio Core Support * Ching-Ling Lee ALi 5451 Audio Core Support * Matt Wu ALi 5451 Audio Core Support - * Peter Wchtler CyberPro5050 support + * Peter Wächtler CyberPro5050 support * Muli Ben-Yehuda * * @@ -89,7 +89,7 @@ * use set_current_state, properly release resources on failure in * trident_probe, get rid of check_region * v0.14.9c - * August 10 2001 Peter Wchtler + * August 10 2001 Peter Wächtler * added support for Tvia (formerly Integraphics/IGST) CyberPro5050 * this chip is often found in settop boxes (combined video+audio) * v0.14.9b diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 21386da3bc8..ac007cec087 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -472,7 +472,7 @@ static int snd_mixart_prepare(struct snd_pcm_substream *subs) struct snd_mixart *chip = snd_pcm_substream_chip(subs); struct mixart_stream *stream = subs->runtime->private_data; - /* TODO de faon non bloquante, rappliquer les hw_params (rate, bits, codec) */ + /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */ snd_printdd("snd_mixart_prepare\n"); -- cgit v1.2.3 From b3cfe0cb37ac7c3ca05a29e308f01c8eb27e06d4 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 9 May 2007 09:26:18 +0200 Subject: [AVR32] Fix section mismatch .taglist -> .init.text Rename .taglist to .taglist.init to silence section mismatch warnings. The .taglist.init section was already placed in the .init output section along with .init.text, so the warning didn't indicate any real problems. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/vmlinux.lds.c | 2 +- include/asm-avr32/setup.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/avr32/kernel/vmlinux.lds.c b/arch/avr32/kernel/vmlinux.lds.c index 7ad20cfb48a..e7f72c995a3 100644 --- a/arch/avr32/kernel/vmlinux.lds.c +++ b/arch/avr32/kernel/vmlinux.lds.c @@ -35,7 +35,7 @@ SECTIONS _einittext = .; . = ALIGN(4); __tagtable_begin = .; - *(.taglist) + *(.taglist.init) __tagtable_end = .; *(.init.data) . = ALIGN(16); diff --git a/include/asm-avr32/setup.h b/include/asm-avr32/setup.h index 1ff1a217015..b0828d43e11 100644 --- a/include/asm-avr32/setup.h +++ b/include/asm-avr32/setup.h @@ -110,7 +110,7 @@ struct tagtable { int (*parse)(struct tag *); }; -#define __tag __attribute_used__ __attribute__((__section__(".taglist"))) +#define __tag __attribute_used__ __attribute__((__section__(".taglist.init"))) #define __tagtable(tag, fn) \ static struct tagtable __tagtable_##fn __tag = { tag, fn } -- cgit v1.2.3 From 47cc3e780474be2e462278d63d77c27a493b28ed Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 9 May 2007 10:23:11 +0200 Subject: [AVR32] Wire up sys_utimensat Tested with a slightly hacked version of the test case included with the original utimensat patch. All OK. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/syscall_table.S | 1 + include/asm-avr32/unistd.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/avr32/kernel/syscall_table.S b/arch/avr32/kernel/syscall_table.S index 7c279586fbb..07f6a6fa340 100644 --- a/arch/avr32/kernel/syscall_table.S +++ b/arch/avr32/kernel/syscall_table.S @@ -291,4 +291,5 @@ sys_call_table: .long sys_shmget /* 275 */ .long sys_shmdt .long sys_shmctl + .long sys_utimensat .long sys_ni_syscall /* r8 is saturated at nr_syscalls */ diff --git a/include/asm-avr32/unistd.h b/include/asm-avr32/unistd.h index 8f512047181..2418cce624c 100644 --- a/include/asm-avr32/unistd.h +++ b/include/asm-avr32/unistd.h @@ -295,8 +295,10 @@ #define __NR_shmdt 276 #define __NR_shmctl 277 +#define __NR_utimensat 278 + #ifdef __KERNEL__ -#define NR_syscalls 278 +#define NR_syscalls 279 #define __ARCH_WANT_IPC_PARSE_VERSION -- cgit v1.2.3 From 57be2b484a417bffae66359b9b89e7239480b729 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 17:33:24 +0900 Subject: sh: clockevent/clocksource/hrtimers/nohz TMU support. This adds basic support for clockevents and clocksources, presently only implemented for TMU-based systems (which are the majority of SH-3 and SH-4 systems). The old NO_IDLE_HZ implementation is also dropped completely, the only users of this were on TMU-based systems anyways. More work needs to be done to generalize the TMU handling, in that the current implementation is rather tied to the notion of TMU0 and TMU1 utilization. Additionally, as more SH timers switch over to this scheme, we'll be able to gut most of the remaining system timer infrastructure that existed before. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 29 ++---- arch/sh/kernel/process.c | 3 + arch/sh/kernel/time.c | 172 ++++++++++++----------------------- arch/sh/kernel/timers/timer-tmu.c | 182 +++++++++++++++++++++++--------------- include/asm-sh/timer.h | 25 +----- 5 files changed, 178 insertions(+), 233 deletions(-) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index d74eb120a9c..038179ecf6a 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -52,6 +52,9 @@ config GENERIC_IOMAP config GENERIC_TIME def_bool n +config GENERIC_CLOCKEVENTS + def_bool n + config SYS_SUPPORTS_APM_EMULATION bool @@ -436,11 +439,11 @@ endmenu menu "Timer and clock configuration" -if !GENERIC_TIME - config SH_TMU bool "TMU timer support" depends on CPU_SH3 || CPU_SH4 + select GENERIC_TIME + select GENERIC_CLOCKEVENTS default y help This enables the use of the TMU as the system timer. @@ -459,8 +462,6 @@ config SH_MTU2 help This enables the use of the MTU2 as the system timer. -endif - config SH_TIMER_IRQ int default "28" if CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785 @@ -468,24 +469,6 @@ config SH_TIMER_IRQ default "140" if CPU_SUBTYPE_SH7206 default "16" -config NO_IDLE_HZ - bool "Dynamic tick timer" - help - Select this option if you want to disable continuous timer ticks - and have them programmed to occur as required. This option saves - power as the system can remain in idle state for longer. - - By default dynamic tick is disabled during the boot, and can be - manually enabled with: - - echo 1 > /sys/devices/system/timer/timer0/dyn_tick - - Alternatively, if you want dynamic tick automatically enabled - during boot, pass "dyntick=enable" via the kernel command string. - - Please note that dynamic tick may affect the accuracy of - timekeeping on some platforms depending on the implementation. - config SH_PCLK_FREQ int "Peripheral clock frequency (in Hz)" default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343 @@ -509,6 +492,8 @@ config SH_CLK_MD help MD2 - MD0 pin setting. +source "kernel/time/Kconfig" + endmenu menu "CPU Frequency scaling" diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index d755589ba8b..6b4f5748d0b 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +61,10 @@ void cpu_idle(void) if (!idle) idle = default_idle; + tick_nohz_stop_sched_tick(); while (!need_resched()) idle(); + tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); schedule(); diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index d47e775962e..a3a67d151e5 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -3,7 +3,7 @@ * * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka * Copyright (C) 2000 Philipp Rumpf - * Copyright (C) 2002 - 2006 Paul Mundt + * Copyright (C) 2002 - 2007 Paul Mundt * Copyright (C) 2002 M. R. Brown * * Some code taken from i386 version. @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,14 @@ static int null_rtc_set_time(const time_t secs) return 0; } +/* + * Null high precision timer functions for systems lacking one. + */ +static cycle_t null_hpt_read(void) +{ + return 0; +} + void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; @@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); #endif /* !CONFIG_GENERIC_TIME */ +#ifndef CONFIG_GENERIC_CLOCKEVENTS /* last time the RTC clock got updated */ static long last_rtc_update; @@ -138,6 +148,7 @@ void handle_timer_tick(void) last_rtc_update = xtime.tv_sec - 600; } } +#endif /* !CONFIG_GENERIC_CLOCKEVENTS */ #ifdef CONFIG_PM int timer_suspend(struct sys_device *dev, pm_message_t state) @@ -168,136 +179,58 @@ static struct sysdev_class timer_sysclass = { .resume = timer_resume, }; -#ifdef CONFIG_NO_IDLE_HZ -static int timer_dyn_tick_enable(void) +static int __init timer_init_sysfs(void) { - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long flags; - int ret = -ENODEV; - - if (dyn_tick) { - spin_lock_irqsave(&dyn_tick->lock, flags); - ret = 0; - if (!(dyn_tick->state & DYN_TICK_ENABLED)) { - ret = dyn_tick->enable(); - - if (ret == 0) - dyn_tick->state |= DYN_TICK_ENABLED; - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); - } + int ret = sysdev_class_register(&timer_sysclass); + if (ret != 0) + return ret; - return ret; + sys_timer->dev.cls = &timer_sysclass; + return sysdev_register(&sys_timer->dev); } +device_initcall(timer_init_sysfs); -static int timer_dyn_tick_disable(void) -{ - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long flags; - int ret = -ENODEV; - - if (dyn_tick) { - spin_lock_irqsave(&dyn_tick->lock, flags); - ret = 0; - if (dyn_tick->state & DYN_TICK_ENABLED) { - ret = dyn_tick->disable(); - - if (ret == 0) - dyn_tick->state &= ~DYN_TICK_ENABLED; - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); - } - - return ret; -} +void (*board_time_init)(void); /* - * Reprogram the system timer for at least the calculated time interval. - * This function should be called from the idle thread with IRQs disabled, - * immediately before sleeping. + * Shamelessly based on the MIPS and Sparc64 work. */ -void timer_dyn_reprogram(void) -{ - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long next, seq, flags; - - if (!dyn_tick) - return; - - spin_lock_irqsave(&dyn_tick->lock, flags); - if (dyn_tick->state & DYN_TICK_ENABLED) { - next = next_timer_interrupt(); - do { - seq = read_seqbegin(&xtime_lock); - dyn_tick->reprogram(next - jiffies); - } while (read_seqretry(&xtime_lock, seq)); - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); -} +static unsigned long timer_ticks_per_nsec_quotient __read_mostly; +unsigned long sh_hpt_frequency = 0; + +#define NSEC_PER_CYC_SHIFT 10 + +struct clocksource clocksource_sh = { + .name = "SuperH", + .rating = 200, + .mask = CLOCKSOURCE_MASK(32), + .read = null_hpt_read, + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; -static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) +static void __init init_sh_clocksource(void) { - return sprintf(buf, "%i\n", - (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); -} + if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read) + return; -static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, - size_t count) -{ - unsigned int enable = simple_strtoul(buf, NULL, 2); + clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency, + clocksource_sh.shift); - if (enable) - timer_dyn_tick_enable(); - else - timer_dyn_tick_disable(); + timer_ticks_per_nsec_quotient = + clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT); - return count; + clocksource_register(&clocksource_sh); } -static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); -/* - * dyntick=enable|disable - */ -static char dyntick_str[4] __initdata = ""; - -static int __init dyntick_setup(char *str) +#ifdef CONFIG_GENERIC_TIME +unsigned long long sched_clock(void) { - if (str) - strlcpy(dyntick_str, str, sizeof(dyntick_str)); - return 1; + unsigned long long ticks = clocksource_sh.read(); + return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT; } - -__setup("dyntick=", dyntick_setup); -#endif - -static int __init timer_init_sysfs(void) -{ - int ret = sysdev_class_register(&timer_sysclass); - if (ret != 0) - return ret; - - sys_timer->dev.cls = &timer_sysclass; - ret = sysdev_register(&sys_timer->dev); - -#ifdef CONFIG_NO_IDLE_HZ - if (ret == 0 && sys_timer->dyn_tick) { - ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); - - /* - * Turn on dynamic tick after calibrate delay - * for correct bogomips - */ - if (ret == 0 && dyntick_str[0] == 'e') - ret = timer_dyn_tick_enable(); - } #endif - return ret; -} -device_initcall(timer_init_sysfs); - -void (*board_time_init)(void); - void __init time_init(void) { if (board_time_init) @@ -316,10 +249,15 @@ void __init time_init(void) sys_timer = get_sys_timer(); printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); -#ifdef CONFIG_NO_IDLE_HZ - if (sys_timer->dyn_tick) - spin_lock_init(&sys_timer->dyn_tick->lock); -#endif + if (sys_timer->ops->read) + clocksource_sh.read = sys_timer->ops->read; + + init_sh_clocksource(); + + if (sh_hpt_frequency) + printk("Using %lu.%03lu MHz high precision timer.\n", + ((sh_hpt_frequency + 500) / 1000) / 1000, + ((sh_hpt_frequency + 500) / 1000) % 1000); #if defined(CONFIG_SH_KGDB) /* diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c index d9e3151c891..2d997e2a5b6 100644 --- a/arch/sh/kernel/timers/timer-tmu.c +++ b/arch/sh/kernel/timers/timer-tmu.c @@ -1,7 +1,7 @@ /* * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support * - * Copyright (C) 2005 Paul Mundt + * Copyright (C) 2005 - 2007 Paul Mundt * * TMU handling code hacked out of arch/sh/kernel/time.c * @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,56 +26,75 @@ #include #define TMU_TOCR_INIT 0x00 -#define TMU0_TCR_INIT 0x0020 -#define TMU_TSTR_INIT 1 +#define TMU_TCR_INIT 0x0020 -#define TMU0_TCR_CALIB 0x0000 +static int tmu_timer_start(void) +{ + ctrl_outb(ctrl_inb(TMU_TSTR) | 0x3, TMU_TSTR); + return 0; +} -static unsigned long tmu_timer_get_offset(void) +static void tmu0_timer_set_interval(unsigned long interval, unsigned int reload) { - int count; - static int count_p = 0x7fffffff; /* for the first call after boot */ - static unsigned long jiffies_p = 0; + ctrl_outl(interval, TMU0_TCNT); /* - * cache volatile jiffies temporarily; we have IRQs turned off. + * TCNT reloads from TCOR on underflow, clear it if we don't + * intend to auto-reload */ - unsigned long jiffies_t; + if (reload) + ctrl_outl(interval, TMU0_TCOR); + else + ctrl_outl(0, TMU0_TCOR); - /* timer count may underflow right here */ - count = ctrl_inl(TMU0_TCNT); /* read the latched count */ + tmu_timer_start(); +} - jiffies_t = jiffies; +static int tmu_timer_stop(void) +{ + ctrl_outb(ctrl_inb(TMU_TSTR) & ~0x3, TMU_TSTR); + return 0; +} - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there is one kind of problem that must be avoided here: - * 1. the timer counter underflows - */ +static cycle_t tmu_timer_read(void) +{ + return ~ctrl_inl(TMU1_TCNT); +} + +static int tmu_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + tmu0_timer_set_interval(cycles, 1); + return 0; +} - if (jiffies_t == jiffies_p) { - if (count > count_p) { - /* the nutcase */ - if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */ - count -= LATCH; - } else { - printk("%s (): hardware timer problem?\n", - __FUNCTION__); - } - } - } else - jiffies_p = jiffies_t; - - count_p = count; - - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; - - return count; +static void tmu_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ctrl_outl(ctrl_inl(TMU0_TCNT), TMU0_TCOR); + break; + case CLOCK_EVT_MODE_ONESHOT: + ctrl_outl(0, TMU0_TCOR); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + } } +static struct clock_event_device tmu0_clockevent = { + .name = "tmu0", + .shift = 32, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = tmu_set_mode, + .set_next_event = tmu_set_next_event, +}; + static irqreturn_t tmu_timer_interrupt(int irq, void *dummy) { + struct clock_event_device *evt = &tmu0_clockevent; unsigned long timer_status; /* Clear UNF bit */ @@ -82,72 +102,76 @@ static irqreturn_t tmu_timer_interrupt(int irq, void *dummy) timer_status &= ~0x100; ctrl_outw(timer_status, TMU0_TCR); - /* - * Here we are in the timer irq handler. We just have irqs locally - * disabled but we don't know if the timer_bh is running on the other - * CPU. We need to avoid to SMP race with it. NOTE: we don' t need - * the irq version of write_lock because as just said we have irq - * locally disabled. -arca - */ - write_seqlock(&xtime_lock); - handle_timer_tick(); - write_sequnlock(&xtime_lock); + evt->event_handler(evt); return IRQ_HANDLED; } -static struct irqaction tmu_irq = { - .name = "timer", +static struct irqaction tmu0_irq = { + .name = "periodic timer", .handler = tmu_timer_interrupt, .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .mask = CPU_MASK_NONE, }; -static void tmu_clk_init(struct clk *clk) +static void tmu0_clk_init(struct clk *clk) { - u8 divisor = TMU0_TCR_INIT & 0x7; - ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); + u8 divisor = TMU_TCR_INIT & 0x7; + ctrl_outw(TMU_TCR_INIT, TMU0_TCR); clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static void tmu_clk_recalc(struct clk *clk) +static void tmu0_clk_recalc(struct clk *clk) { u8 divisor = ctrl_inw(TMU0_TCR) & 0x7; clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static struct clk_ops tmu_clk_ops = { - .init = tmu_clk_init, - .recalc = tmu_clk_recalc, +static struct clk_ops tmu0_clk_ops = { + .init = tmu0_clk_init, + .recalc = tmu0_clk_recalc, }; static struct clk tmu0_clk = { .name = "tmu0_clk", - .ops = &tmu_clk_ops, + .ops = &tmu0_clk_ops, }; -static int tmu_timer_start(void) +static void tmu1_clk_init(struct clk *clk) { - ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); - return 0; + u8 divisor = TMU_TCR_INIT & 0x7; + ctrl_outw(divisor, TMU1_TCR); + clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static int tmu_timer_stop(void) +static void tmu1_clk_recalc(struct clk *clk) { - ctrl_outb(0, TMU_TSTR); - return 0; + u8 divisor = ctrl_inw(TMU1_TCR) & 0x7; + clk->rate = clk->parent->rate / (4 << (divisor << 1)); } +static struct clk_ops tmu1_clk_ops = { + .init = tmu1_clk_init, + .recalc = tmu1_clk_recalc, +}; + +static struct clk tmu1_clk = { + .name = "tmu1_clk", + .ops = &tmu1_clk_ops, +}; + static int tmu_timer_init(void) { unsigned long interval; + unsigned long frequency; - setup_irq(CONFIG_SH_TIMER_IRQ, &tmu_irq); + setup_irq(CONFIG_SH_TIMER_IRQ, &tmu0_irq); tmu0_clk.parent = clk_get(NULL, "module_clk"); + tmu1_clk.parent = clk_get(NULL, "module_clk"); - /* Start TMU0 */ tmu_timer_stop(); + #if !defined(CONFIG_CPU_SUBTYPE_SH7300) && \ !defined(CONFIG_CPU_SUBTYPE_SH7760) && \ !defined(CONFIG_CPU_SUBTYPE_SH7785) @@ -155,15 +179,29 @@ static int tmu_timer_init(void) #endif clk_register(&tmu0_clk); + clk_register(&tmu1_clk); clk_enable(&tmu0_clk); + clk_enable(&tmu1_clk); - interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ; - printk(KERN_INFO "Interval = %ld\n", interval); + frequency = clk_get_rate(&tmu0_clk); + interval = (frequency + HZ / 2) / HZ; - ctrl_outl(interval, TMU0_TCOR); - ctrl_outl(interval, TMU0_TCNT); + sh_hpt_frequency = clk_get_rate(&tmu1_clk); + ctrl_outl(~0, TMU1_TCNT); + ctrl_outl(~0, TMU1_TCOR); - tmu_timer_start(); + tmu0_timer_set_interval(interval, 1); + + tmu0_clockevent.mult = div_sc(frequency, NSEC_PER_SEC, + tmu0_clockevent.shift); + tmu0_clockevent.max_delta_ns = + clockevent_delta2ns(-1, &tmu0_clockevent); + tmu0_clockevent.min_delta_ns = + clockevent_delta2ns(1, &tmu0_clockevent); + + tmu0_clockevent.cpumask = cpumask_of_cpu(0); + + clockevents_register_device(&tmu0_clockevent); return 0; } @@ -172,9 +210,7 @@ struct sys_timer_ops tmu_timer_ops = { .init = tmu_timer_init, .start = tmu_timer_start, .stop = tmu_timer_stop, -#ifndef CONFIG_GENERIC_TIME - .get_offset = tmu_timer_get_offset, -#endif + .read = tmu_timer_read, }; struct sys_timer tmu_timer = { diff --git a/include/asm-sh/timer.h b/include/asm-sh/timer.h index 17b5e76a4c3..701ba84c704 100644 --- a/include/asm-sh/timer.h +++ b/include/asm-sh/timer.h @@ -2,12 +2,14 @@ #define __ASM_SH_TIMER_H #include +#include #include struct sys_timer_ops { int (*init)(void); int (*start)(void); int (*stop)(void); + cycle_t (*read)(void); #ifndef CONFIG_GENERIC_TIME unsigned long (*get_offset)(void); #endif @@ -18,29 +20,8 @@ struct sys_timer { struct sys_device dev; struct sys_timer_ops *ops; - -#ifdef CONFIG_NO_IDLE_HZ - struct dyn_tick_timer *dyn_tick; -#endif }; -#ifdef CONFIG_NO_IDLE_HZ -#define DYN_TICK_ENABLED (1 << 1) - -struct dyn_tick_timer { - spinlock_t lock; - unsigned int state; /* Current state */ - int (*enable)(void); /* Enables dynamic tick */ - int (*disable)(void); /* Disables dynamic tick */ - void (*reprogram)(unsigned long); /* Reprograms the timer */ - int (*handler)(int, void *); -}; - -void timer_dyn_reprogram(void); -#else -#define timer_dyn_reprogram() do { } while (0) -#endif - #define TICK_SIZE (tick_nsec / 1000) extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer; @@ -58,5 +39,7 @@ struct sys_timer *get_sys_timer(void); /* arch/sh/kernel/time.c */ void handle_timer_tick(void); +extern unsigned long sh_hpt_frequency; +extern struct clocksource clocksource_sh; #endif /* __ASM_SH_TIMER_H */ -- cgit v1.2.3 From fc467a2623029976899261d6d379779c950ddcba Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 9 May 2007 17:36:15 +0900 Subject: sh: SH7760 DMABRG support. The DMABRG is a special DMA unit within the SH7760 which does data transfers from main memory to Audio units and USB shared memory. It has 3 IRQ lines which generate 10 events, which have to be masked unmasked and acked in a single 32bit register. It works independently from the tradition SH DMAC, but blocks usage of DMAC channel 0. This patch adds 2 functions to associate callbacks with DMABRG events and initialization. Signed-off-by: Manuel Lauss Signed-off-by: Paul Mundt --- arch/sh/drivers/Makefile | 3 +- arch/sh/drivers/dma/Kconfig | 20 +++-- arch/sh/drivers/dma/Makefile | 4 +- arch/sh/drivers/dma/dmabrg.c | 196 +++++++++++++++++++++++++++++++++++++++++++ include/asm-sh/dmabrg.h | 23 +++++ 5 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 arch/sh/drivers/dma/dmabrg.c create mode 100644 include/asm-sh/dmabrg.h diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile index 6cb92676c5f..e13f06bebd9 100644 --- a/arch/sh/drivers/Makefile +++ b/arch/sh/drivers/Makefile @@ -2,8 +2,9 @@ # Makefile for the Linux SuperH-specific device drivers. # +obj-y += dma/ + obj-$(CONFIG_PCI) += pci/ -obj-$(CONFIG_SH_DMA) += dma/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_PUSH_SWITCH) += push-switch.o obj-$(CONFIG_HEARTBEAT) += heartbeat.o diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig index defc13c37d4..99935f9daf4 100644 --- a/arch/sh/drivers/dma/Kconfig +++ b/arch/sh/drivers/dma/Kconfig @@ -1,12 +1,12 @@ menu "DMA support" -config SH_DMA - bool "DMA controller (DMAC) support" - help - Selecting this option will provide same API as PC's Direct Memory - Access Controller(8237A) for SuperH DMAC. +config SH_DMA_API + bool - If unsure, say N. +config SH_DMA + bool "SuperH on-chip DMA controller (DMAC) support" + select SH_DMA_API + default n config NR_ONCHIP_DMA_CHANNELS depends on SH_DMA @@ -53,4 +53,12 @@ config DMA_PAGE_OPS_CHANNEL in case channel 3 is unavailable. On the SH4, channels 1,2, and 3 are dual-address capable. +config SH_DMABRG + bool "SH7760 DMABRG support" + depends on CPU_SUBTYPE_SH7760 + help + The DMABRG does data transfers from main memory to Audio/USB units + of the SH7760. + Say Y if you want to use Audio/USB DMA on your SH7760 board. + endmenu diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile index db1295d3226..1ac812d2448 100644 --- a/arch/sh/drivers/dma/Makefile +++ b/arch/sh/drivers/dma/Makefile @@ -2,8 +2,8 @@ # Makefile for the SuperH DMA specific kernel interface routines under Linux. # -obj-y += dma-api.o +obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o obj-$(CONFIG_ISA_DMA_API) += dma-isa.o -obj-$(CONFIG_SYSFS) += dma-sysfs.o obj-$(CONFIG_SH_DMA) += dma-sh.o obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o +obj-$(CONFIG_SH_DMABRG) += dmabrg.o diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c new file mode 100644 index 00000000000..9d0a29370f2 --- /dev/null +++ b/arch/sh/drivers/dma/dmabrg.c @@ -0,0 +1,196 @@ +/* + * SH7760 DMABRG IRQ handling + * + * (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss + * licensed under the GPLv2. + * + */ + +#include +#include +#include +#include +#include + +/* + * The DMABRG is a special DMA unit within the SH7760. It does transfers + * from USB-SRAM/Audio units to main memory (and also the LCDC; but that + * part is sensibly placed in the LCDC registers and requires no irqs) + * It has 3 IRQ lines which trigger 10 events, and works independently + * from the traditional SH DMAC (although it blocks usage of DMAC 0) + * + * BRGIRQID | component | dir | meaning | source + * ----------------------------------------------------- + * 0 | USB-DMA | ... | xfer done | DMABRGI1 + * 1 | USB-UAE | ... | USB addr err.| DMABRGI0 + * 2 | HAC0/SSI0 | play| all done | DMABRGI1 + * 3 | HAC0/SSI0 | play| half done | DMABRGI2 + * 4 | HAC0/SSI0 | rec | all done | DMABRGI1 + * 5 | HAC0/SSI0 | rec | half done | DMABRGI2 + * 6 | HAC1/SSI1 | play| all done | DMABRGI1 + * 7 | HAC1/SSI1 | play| half done | DMABRGI2 + * 8 | HAC1/SSI1 | rec | all done | DMABRGI1 + * 9 | HAC1/SSI1 | rec | half done | DMABRGI2 + * + * all can be enabled/disabled in the DMABRGCR register, + * as well as checked if they occured. + * + * DMABRGI0 services USB DMA Address errors, but it still must be + * enabled/acked in the DMABRGCR register. USB-DMA complete indicator + * is grouped together with the audio buffer end indicators, too bad... + * + * DMABRGCR: Bits 31-24: audio-dma ENABLE flags, + * Bits 23-16: audio-dma STATUS flags, + * Bits 9-8: USB error/xfer ENABLE, + * Bits 1-0: USB error/xfer STATUS. + * Ack an IRQ by writing 0 to the STATUS flag. + * Mask IRQ by writing 0 to ENABLE flag. + * + * Usage is almost like with any other IRQ: + * dmabrg_request_irq(BRGIRQID, handler, data) + * dmabrg_free_irq(BRGIRQID) + * + * handler prototype: void brgirqhandler(void *data) + */ + +#define DMARSRA 0xfe090000 +#define DMAOR 0xffa00040 +#define DMACHCR0 0xffa0000c +#define DMABRGCR 0xfe3c0000 + +#define DMAOR_BRG 0x0000c000 +#define DMAOR_DMEN 0x00000001 + +#define DMABRGI0 68 +#define DMABRGI1 69 +#define DMABRGI2 70 + +struct dmabrg_handler { + void (*handler)(void *); + void *data; +} *dmabrg_handlers; + +static inline void dmabrg_call_handler(int i) +{ + dmabrg_handlers[i].handler(dmabrg_handlers[i].data); +} + +/* + * main DMABRG irq handler. It acks irqs and then + * handles every set and unmasked bit sequentially. + * No locking and no validity checks; it should be + * as fast as possible (audio!) + */ +static irqreturn_t dmabrg_irq(int irq, void *data) +{ + unsigned long dcr; + unsigned int i; + + dcr = ctrl_inl(DMABRGCR); + ctrl_outl(dcr & ~0x00ff0003, DMABRGCR); /* ack all */ + dcr &= dcr >> 8; /* ignore masked */ + + /* USB stuff, get it out of the way first */ + if (dcr & 1) + dmabrg_call_handler(DMABRGIRQ_USBDMA); + if (dcr & 2) + dmabrg_call_handler(DMABRGIRQ_USBDMAERR); + + /* Audio */ + dcr >>= 16; + while (dcr) { + i = __ffs(dcr); + dcr &= dcr - 1; + dmabrg_call_handler(i + DMABRGIRQ_A0TXF); + } + return IRQ_HANDLED; +} + +static void dmabrg_disable_irq(unsigned int dmairq) +{ + unsigned long dcr; + dcr = ctrl_inl(DMABRGCR); + dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); + ctrl_outl(dcr, DMABRGCR); +} + +static void dmabrg_enable_irq(unsigned int dmairq) +{ + unsigned long dcr; + dcr = ctrl_inl(DMABRGCR); + dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); + ctrl_outl(dcr, DMABRGCR); +} + +int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*), + void *data) +{ + if ((dmairq > 9) || !handler) + return -ENOENT; + if (dmabrg_handlers[dmairq].handler) + return -EBUSY; + + dmabrg_handlers[dmairq].handler = handler; + dmabrg_handlers[dmairq].data = data; + + dmabrg_enable_irq(dmairq); + return 0; +} +EXPORT_SYMBOL_GPL(dmabrg_request_irq); + +void dmabrg_free_irq(unsigned int dmairq) +{ + if (likely(dmairq < 10)) { + dmabrg_disable_irq(dmairq); + dmabrg_handlers[dmairq].handler = NULL; + dmabrg_handlers[dmairq].data = NULL; + } +} +EXPORT_SYMBOL_GPL(dmabrg_free_irq); + +static int __init dmabrg_init(void) +{ + unsigned long or; + int ret; + + dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler), + GFP_KERNEL); + if (!dmabrg_handlers) + return -ENOMEM; + +#ifdef CONFIG_SH_DMA + /* request DMAC channel 0 before anyone else can get it */ + ret = request_dma(0, "DMAC 0 (DMABRG)"); + if (ret < 0) + printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n"); +#endif + + ctrl_outl(0, DMABRGCR); + ctrl_outl(0, DMACHCR0); + ctrl_outl(0x94000000, DMARSRA); /* enable DMABRG in DMAC 0 */ + + /* enable DMABRG mode, enable the DMAC */ + or = ctrl_inl(DMAOR); + ctrl_outl(or | DMAOR_BRG | DMAOR_DMEN, DMAOR); + + ret = request_irq(DMABRGI0, dmabrg_irq, IRQF_DISABLED, + "DMABRG USB address error", NULL); + if (ret) + goto out0; + + ret = request_irq(DMABRGI1, dmabrg_irq, IRQF_DISABLED, + "DMABRG Transfer End", NULL); + if (ret) + goto out1; + + ret = request_irq(DMABRGI2, dmabrg_irq, IRQF_DISABLED, + "DMABRG Transfer Half", NULL); + if (ret == 0) + return ret; + + free_irq(DMABRGI1, 0); +out1: free_irq(DMABRGI0, 0); +out0: kfree(dmabrg_handlers); + return ret; +} +subsys_initcall(dmabrg_init); diff --git a/include/asm-sh/dmabrg.h b/include/asm-sh/dmabrg.h new file mode 100644 index 00000000000..c5edba216cf --- /dev/null +++ b/include/asm-sh/dmabrg.h @@ -0,0 +1,23 @@ +/* + * SH7760 DMABRG (USB/Audio) support + */ + +#ifndef _DMABRG_H_ +#define _DMABRG_H_ + +/* IRQ sources */ +#define DMABRGIRQ_USBDMA 0 +#define DMABRGIRQ_USBDMAERR 1 +#define DMABRGIRQ_A0TXF 2 +#define DMABRGIRQ_A0TXH 3 +#define DMABRGIRQ_A0RXF 4 +#define DMABRGIRQ_A0RXH 5 +#define DMABRGIRQ_A1TXF 6 +#define DMABRGIRQ_A1TXH 7 +#define DMABRGIRQ_A1RXF 8 +#define DMABRGIRQ_A1RXH 9 + +extern int dmabrg_request_irq(unsigned int, void(*)(void *), void *); +extern void dmabrg_free_irq(unsigned int); + +#endif -- cgit v1.2.3 From 2d0fa586facb740b9ef9a01dcedc94c126c6f148 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 9 May 2007 11:55:51 +1000 Subject: [MTD] [MAPS] Fix missing printk() parameter in physmap_of.c MTD driver Squashes a compiler warning, and provides more useful information in the case messed up device tree information. Signed-off-by: David Gibson Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 7efe744ad31..8ec2fbe1074 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -186,7 +186,7 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev else { if (strcmp(of_probe, "ROM")) dev_dbg(&dev->dev, "map_probe: don't know probe type " - "'%s', mapping as rom\n"); + "'%s', mapping as rom\n", of_probe); info->mtd = do_map_probe("mtd_rom", &info->map); } if (info->mtd == NULL) { -- cgit v1.2.3 From 025257c7a7665a117b72097cc12021140eac8a34 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 9 May 2007 17:13:31 +1000 Subject: [MTD] [MAPS] Remove flash maps for no longer supported 405LP boards drivers/mtd/maps includes flash maps for the Beech and Arctic PowerPC 405LP based boards. However, the 405LP was discontinued before any quantity were distributed and those boards no longer have kernel support in general. Therefore, this patch removes this obsolete code. Signed-off-by: David Gibson Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 16 ----- drivers/mtd/maps/Makefile | 2 - drivers/mtd/maps/arctic-mtd.c | 145 ------------------------------------------ drivers/mtd/maps/beech-mtd.c | 122 ----------------------------------- 4 files changed, 285 deletions(-) delete mode 100644 drivers/mtd/maps/arctic-mtd.c delete mode 100644 drivers/mtd/maps/beech-mtd.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index d990d8141ef..aab5506adfa 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -358,22 +358,6 @@ config MTD_CFI_FLAGADM Mapping for the Flaga digital module. If you don't have one, ignore this setting. -config MTD_BEECH - tristate "CFI Flash device mapped on IBM 405LP Beech" - depends on MTD_CFI && BEECH - help - This enables access routines for the flash chips on the IBM - 405LP Beech board. If you have one of these boards and would like - to use the flash chips on it, say 'Y'. - -config MTD_ARCTIC - tristate "CFI Flash device mapped on IBM 405LP Arctic" - depends on MTD_CFI && ARCTIC2 - help - This enables access routines for the flash chips on the IBM 405LP - Arctic board. If you have one of these boards and would like to - use the flash chips on it, say 'Y'. - config MTD_WALNUT tristate "Flash device mapped on IBM 405GP Walnut" depends on MTD_JEDECPROBE && WALNUT diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index de036c5e613..3acbb5d01ca 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -58,8 +58,6 @@ obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_EBONY) += ebony.o obj-$(CONFIG_MTD_OCOTEA) += ocotea.o -obj-$(CONFIG_MTD_BEECH) += beech-mtd.o -obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o obj-$(CONFIG_MTD_WALNUT) += walnut.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o obj-$(CONFIG_MTD_SBC8240) += sbc8240.o diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c deleted file mode 100644 index 2cc90243627..00000000000 --- a/drivers/mtd/maps/arctic-mtd.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * $Id: arctic-mtd.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $ - * - * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for - * IBM 405LP Arctic boards. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Copyright (C) 2002, International Business Machines Corporation - * All Rights Reserved. - * - * Bishop Brock - * IBM Research, Austin Center for Low-Power Computing - * bcbrock@us.ibm.com - * March 2002 - * - * modified for Arctic by, - * David Gibson - * IBM OzLabs, Canberra, Australia - * - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -/* - * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) - * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) - * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) - * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) - */ - -#define FFS1_SIZE 0x01000000 /* 16MiB */ -#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ -#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ -#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ - - -#define NAME "Arctic Linux Flash" -#define PADDR SUBZERO_BOOTFLASH_PADDR -#define BUSWIDTH 2 -#define SIZE SUBZERO_BOOTFLASH_SIZE -#define PARTITIONS 4 - -/* Flash memories on these boards are memory resources, accessed big-endian. */ - -{ - /* do nothing for now */ -} - -static struct map_info arctic_mtd_map = { - .name = NAME, - .size = SIZE, - .bankwidth = BUSWIDTH, - .phys = PADDR, -}; - -static struct mtd_info *arctic_mtd; - -static struct mtd_partition arctic_partitions[PARTITIONS] = { - { .name = "Filesystem", - .size = FFS1_SIZE, - .offset = 0,}, - { .name = "Kernel", - .size = KERNEL_SIZE, - .offset = FFS1_SIZE,}, - { .name = "Filesystem", - .size = FFS2_SIZE, - .offset = FFS1_SIZE + KERNEL_SIZE,}, - { .name = "Firmware", - .size = FIRMWARE_SIZE, - .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, -}; - -static int __init -init_arctic_mtd(void) -{ - int err; - - printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); - - arctic_mtd_map.virt = ioremap(PADDR, SIZE); - - if (!arctic_mtd_map.virt) { - printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); - return -EIO; - } - simple_map_init(&arctic_mtd_map); - - printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); - arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); - - if (!arctic_mtd) { - iounmap(arctic_mtd_map.virt); - return -ENXIO; - } - - arctic_mtd->owner = THIS_MODULE; - - err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); - if (err) { - printk("%s: add_mtd_partitions failed\n", NAME); - iounmap(arctic_mtd_map.virt); - } - - return err; -} - -static void __exit -cleanup_arctic_mtd(void) -{ - if (arctic_mtd) { - del_mtd_partitions(arctic_mtd); - map_destroy(arctic_mtd); - iounmap((void *) arctic_mtd_map.virt); - } -} - -module_init(init_arctic_mtd); -module_exit(cleanup_arctic_mtd); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Gibson "); -MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c deleted file mode 100644 index d76d5981b86..00000000000 --- a/drivers/mtd/maps/beech-mtd.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * $Id: beech-mtd.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $ - * - * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for - * IBM 405LP Beech boards. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Copyright (C) 2002, International Business Machines Corporation - * All Rights Reserved. - * - * Bishop Brock - * IBM Research, Austin Center for Low-Power Computing - * bcbrock@us.ibm.com - * March 2002 - * - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define NAME "Beech Linux Flash" -#define PADDR BEECH_BIGFLASH_PADDR -#define SIZE BEECH_BIGFLASH_SIZE -#define BUSWIDTH 1 - -/* Flash memories on these boards are memory resources, accessed big-endian. */ - - -static struct map_info beech_mtd_map = { - .name = NAME, - .size = SIZE, - .bankwidth = BUSWIDTH, - .phys = PADDR -}; - -static struct mtd_info *beech_mtd; - -static struct mtd_partition beech_partitions[2] = { - { - .name = "Linux Kernel", - .size = BEECH_KERNEL_SIZE, - .offset = BEECH_KERNEL_OFFSET - }, { - .name = "Free Area", - .size = BEECH_FREE_AREA_SIZE, - .offset = BEECH_FREE_AREA_OFFSET - } -}; - -static int __init -init_beech_mtd(void) -{ - int err; - - printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); - - beech_mtd_map.virt = ioremap(PADDR, SIZE); - - if (!beech_mtd_map.virt) { - printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); - return -EIO; - } - - simple_map_init(&beech_mtd_map); - - printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); - beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); - - if (!beech_mtd) { - iounmap(beech_mtd_map.virt); - return -ENXIO; - } - - beech_mtd->owner = THIS_MODULE; - - err = add_mtd_partitions(beech_mtd, beech_partitions, 2); - if (err) { - printk("%s: add_mtd_partitions failed\n", NAME); - iounmap(beech_mtd_map.virt); - } - - return err; -} - -static void __exit -cleanup_beech_mtd(void) -{ - if (beech_mtd) { - del_mtd_partitions(beech_mtd); - map_destroy(beech_mtd); - iounmap((void *) beech_mtd_map.virt); - } -} - -module_init(init_beech_mtd); -module_exit(cleanup_beech_mtd); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Bishop Brock "); -MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); -- cgit v1.2.3 From 065cf519c32984b7a78777aae3859baf5f5fd3d3 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 9 May 2007 09:50:23 +0100 Subject: [ARM] armv7: add support for asid-tagged VIVT I-cache ARMv7 can have VIPT, PIPT or ASID-tagged VIVT I-cache. This patch adds the necessary invalidation of the I-cache when the ASID numbers are re-used. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/mm/context.c | 7 +++++++ include/asm-arm/cacheflush.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 9da43a0fdcd..c9e9a558626 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -47,6 +47,13 @@ void __new_context(struct mm_struct *mm) : "r" (0)); isb(); flush_tlb_all(); + if (icache_is_vivt_asid_tagged()) { + asm("mcr p15, 0, %0, c7, c5, 0 @ invalidate I-cache\n" + "mcr p15, 0, %0, c7, c5, 6 @ flush BTAC/BTB\n" + : + : "r" (0)); + dsb(); + } } mm->context.id = asid; diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index 6832ef96bc1..d1294a46c70 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -438,6 +438,7 @@ static inline void flush_anon_page(struct vm_area_struct *vma, #define __cacheid_vipt(val) (__cacheid_type_v7(val) ? 1 : __cacheid_vipt_prev7(val)) #define __cacheid_vipt_nonaliasing(val) (__cacheid_type_v7(val) ? 1 : __cacheid_vipt_nonaliasing_prev7(val)) #define __cacheid_vipt_aliasing(val) (__cacheid_type_v7(val) ? 0 : __cacheid_vipt_aliasing_prev7(val)) +#define __cacheid_vivt_asid_tagged_instr(val) (__cacheid_type_v7(val) ? ((val & (3 << 14)) == (1 << 14)) : 0) #if defined(CONFIG_CPU_CACHE_VIVT) && !defined(CONFIG_CPU_CACHE_VIPT) @@ -445,6 +446,7 @@ static inline void flush_anon_page(struct vm_area_struct *vma, #define cache_is_vipt() 0 #define cache_is_vipt_nonaliasing() 0 #define cache_is_vipt_aliasing() 0 +#define icache_is_vivt_asid_tagged() 0 #elif defined(CONFIG_CPU_CACHE_VIPT) @@ -462,6 +464,12 @@ static inline void flush_anon_page(struct vm_area_struct *vma, __cacheid_vipt_aliasing(__val); \ }) +#define icache_is_vivt_asid_tagged() \ + ({ \ + unsigned int __val = read_cpuid(CPUID_CACHETYPE); \ + __cacheid_vivt_asid_tagged_instr(__val); \ + }) + #else #define cache_is_vivt() \ @@ -490,6 +498,13 @@ static inline void flush_anon_page(struct vm_area_struct *vma, __cacheid_vipt_aliasing(__val); \ }) +#define icache_is_vivt_asid_tagged() \ + ({ \ + unsigned int __val = read_cpuid(CPUID_CACHETYPE); \ + __cacheid_present(__val) && \ + __cacheid_vivt_asid_tagged_instr(__val); \ + }) + #endif #endif -- cgit v1.2.3 From 23688e999eda70f1ce1c61a68b865b996e3c6c4c Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 8 May 2007 22:45:26 +0100 Subject: [ARM] armv7: add Makefile and Kconfig entries This patch adds the necessary lines to the Makefile and Kconfig files for enabling the compilation of the ARMv7 CPU support. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/Makefile | 5 +++++ arch/arm/mm/Kconfig | 32 ++++++++++++++++++++++++++++++-- arch/arm/mm/Makefile | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index ab9f2d4bd04..00ea4305ad5 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -47,8 +47,13 @@ comma = , # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes # testing for a specific architecture or later rather impossible. +arch-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7a,-march=armv5t -Wa$(comma)-march=armv7a) arch-$(CONFIG_CPU_32v6) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) +# Only override the compiler option if ARMv6. The ARMv6K extensions are +# always available in ARMv7 +ifeq ($(CONFIG_CPU_32v6),y) arch-$(CONFIG_CPU_32v6K) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6k,-march=armv5t -Wa$(comma)-march=armv6k) +endif arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 $(call cc-option,-march=armv5te,-march=armv4t) arch-$(CONFIG_CPU_32v4T) :=-D__LINUX_ARM_ARCH__=4 -march=armv4t arch-$(CONFIG_CPU_32v4) :=-D__LINUX_ARM_ARCH__=4 -march=armv4 diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index e684e9b3821..b81391a4e37 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -366,6 +366,19 @@ config CPU_32v6K enabled will not boot on processors with do not support these instructions. +# ARMv7 +config CPU_V7 + bool "Support ARM V7 processor" + depends on ARCH_INTEGRATOR + select CPU_32v6K + select CPU_32v7 + select CPU_ABRT_EV7 + select CPU_CACHE_V7 + select CPU_CACHE_VIPT + select CPU_CP15_MMU + select CPU_COPY_V6 if MMU + select CPU_TLB_V6 if MMU + # Figure out what processor architecture version we should be using. # This defines the compiler instruction set which depends on the machine type. config CPU_32v3 @@ -391,6 +404,9 @@ config CPU_32v5 config CPU_32v6 bool +config CPU_32v7 + bool + # The abort model config CPU_ABRT_NOMMU bool @@ -413,6 +429,9 @@ config CPU_ABRT_EV5TJ config CPU_ABRT_EV6 bool +config CPU_ABRT_EV7 + bool + # The cache model config CPU_CACHE_V3 bool @@ -429,6 +448,9 @@ config CPU_CACHE_V4WB config CPU_CACHE_V6 bool +config CPU_CACHE_V7 + bool + config CPU_CACHE_VIVT bool @@ -503,7 +525,7 @@ comment "Processor Features" config ARM_THUMB bool "Support Thumb user binaries" - depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_XSC3 || CPU_V6 + depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_XSC3 || CPU_V6 || CPU_V7 default y help Say Y if you want to include kernel support for running user space @@ -578,9 +600,15 @@ config CPU_CACHE_ROUND_ROBIN Say Y here to use the predictable round-robin cache replacement policy. Unless you specifically require this or are unsure, say N. +config CPU_L2CACHE_DISABLE + bool "Disable level 2 cache" + depends on CPU_V7 + help + Say Y here to disable the level 2 cache. If unsure, say N. + config CPU_BPREDICT_DISABLE bool "Disable branch prediction" - depends on CPU_ARM1020 || CPU_V6 || CPU_XSC3 + depends on CPU_ARM1020 || CPU_V6 || CPU_XSC3 || CPU_V7 help Say Y here to disable branch prediction. If unsure, say N. diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 2f8b9594777..b5bd335ff14 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -24,12 +24,14 @@ obj-$(CONFIG_CPU_ABRT_LV4T) += abort-lv4t.o obj-$(CONFIG_CPU_ABRT_EV5T) += abort-ev5t.o obj-$(CONFIG_CPU_ABRT_EV5TJ) += abort-ev5tj.o obj-$(CONFIG_CPU_ABRT_EV6) += abort-ev6.o +obj-$(CONFIG_CPU_ABRT_EV7) += abort-ev7.o obj-$(CONFIG_CPU_CACHE_V3) += cache-v3.o obj-$(CONFIG_CPU_CACHE_V4) += cache-v4.o obj-$(CONFIG_CPU_CACHE_V4WT) += cache-v4wt.o obj-$(CONFIG_CPU_CACHE_V4WB) += cache-v4wb.o obj-$(CONFIG_CPU_CACHE_V6) += cache-v6.o +obj-$(CONFIG_CPU_CACHE_V7) += cache-v7.o obj-$(CONFIG_CPU_COPY_V3) += copypage-v3.o obj-$(CONFIG_CPU_COPY_V4WT) += copypage-v4wt.o @@ -66,5 +68,6 @@ obj-$(CONFIG_CPU_SA1100) += proc-sa1100.o obj-$(CONFIG_CPU_XSCALE) += proc-xscale.o obj-$(CONFIG_CPU_XSC3) += proc-xsc3.o obj-$(CONFIG_CPU_V6) += proc-v6.o +obj-$(CONFIG_CPU_V7) += proc-v7.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o -- cgit v1.2.3 From 340a614ac6766df18cba87ff7e66182193c2bd6d Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU Date: Thu, 7 Dec 2006 15:43:59 -0800 Subject: ARM: OMAP: Add mailbox support for IVA This patch adds a generic mailbox interface for for DSP and IVA (Image Video Accelerator). This patch itself doesn't contain any IVA driver. Signed-off-by: Hiroshi DOYU Signed-off-by: Juha Yrjola Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap1/mailbox.c | 206 +++++++++++++++ arch/arm/mach-omap2/mailbox.c | 318 ++++++++++++++++++++++ arch/arm/plat-omap/mailbox.c | 509 ++++++++++++++++++++++++++++++++++++ arch/arm/plat-omap/mailbox.h | 100 +++++++ include/asm-arm/arch-omap/mailbox.h | 73 ++++++ 5 files changed, 1206 insertions(+) create mode 100644 arch/arm/mach-omap1/mailbox.c create mode 100644 arch/arm/mach-omap2/mailbox.c create mode 100644 arch/arm/plat-omap/mailbox.c create mode 100644 arch/arm/plat-omap/mailbox.h create mode 100644 include/asm-arm/arch-omap/mailbox.h diff --git a/arch/arm/mach-omap1/mailbox.c b/arch/arm/mach-omap1/mailbox.c new file mode 100644 index 00000000000..d3abf560990 --- /dev/null +++ b/arch/arm/mach-omap1/mailbox.c @@ -0,0 +1,206 @@ +/* + * Mailbox reservation modules for DSP + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_ARM2DSP1 0x00 +#define MAILBOX_ARM2DSP1b 0x04 +#define MAILBOX_DSP2ARM1 0x08 +#define MAILBOX_DSP2ARM1b 0x0c +#define MAILBOX_DSP2ARM2 0x10 +#define MAILBOX_DSP2ARM2b 0x14 +#define MAILBOX_ARM2DSP1_Flag 0x18 +#define MAILBOX_DSP2ARM1_Flag 0x1c +#define MAILBOX_DSP2ARM2_Flag 0x20 + +unsigned long mbox_base; + +struct omap_mbox1_fifo { + unsigned long cmd; + unsigned long data; + unsigned long flag; +}; + +struct omap_mbox1_priv { + struct omap_mbox1_fifo tx_fifo; + struct omap_mbox1_fifo rx_fifo; +}; + +static inline int mbox_read_reg(unsigned int reg) +{ + return __raw_readw(mbox_base + reg); +} + +static inline void mbox_write_reg(unsigned int val, unsigned int reg) +{ + __raw_writew(val, mbox_base + reg); +} + +/* msg */ +static inline mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; + mbox_msg_t msg; + + msg = mbox_read_reg(fifo->data); + msg |= ((mbox_msg_t) mbox_read_reg(fifo->cmd)) << 16; + + return msg; +} + +static inline void +omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->tx_fifo; + + mbox_write_reg(msg & 0xffff, fifo->data); + mbox_write_reg(msg >> 16, fifo->cmd); +} + +static inline int omap1_mbox_fifo_empty(struct omap_mbox *mbox) +{ + return 0; +} + +static inline int omap1_mbox_fifo_full(struct omap_mbox *mbox) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; + + return (mbox_read_reg(fifo->flag)); +} + +/* irq */ +static inline void +omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_RX) + enable_irq(mbox->irq); +} + +static inline void +omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_RX) + disable_irq(mbox->irq); +} + +static inline int +omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_TX) + return 0; + return 1; +} + +static struct omap_mbox_ops omap1_mbox_ops = { + .type = OMAP_MBOX_TYPE1, + .fifo_read = omap1_mbox_fifo_read, + .fifo_write = omap1_mbox_fifo_write, + .fifo_empty = omap1_mbox_fifo_empty, + .fifo_full = omap1_mbox_fifo_full, + .enable_irq = omap1_mbox_enable_irq, + .disable_irq = omap1_mbox_disable_irq, + .is_irq = omap1_mbox_is_irq, +}; + +/* FIXME: the following struct should be created automatically by the user id */ + +/* DSP */ +static struct omap_mbox1_priv omap1_mbox_dsp_priv = { + .tx_fifo = { + .cmd = MAILBOX_ARM2DSP1b, + .data = MAILBOX_ARM2DSP1, + .flag = MAILBOX_ARM2DSP1_Flag, + }, + .rx_fifo = { + .cmd = MAILBOX_DSP2ARM1b, + .data = MAILBOX_DSP2ARM1, + .flag = MAILBOX_DSP2ARM1_Flag, + }, +}; + +struct omap_mbox mbox_dsp_info = { + .name = "dsp", + .ops = &omap1_mbox_ops, + .priv = &omap1_mbox_dsp_priv, +}; +EXPORT_SYMBOL(mbox_dsp_info); + +static int __init omap1_mbox_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + if (pdev->num_resources != 2) { + dev_err(&pdev->dev, "invalid number of resources: %d\n", + pdev->num_resources); + return -ENODEV; + } + + /* MBOX base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid mem resource\n"); + return -ENODEV; + } + mbox_base = res->start; + + /* DSP IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_dsp_info.irq = res->start; + + ret = omap_mbox_register(&mbox_dsp_info); + + return ret; +} + +static int omap1_mbox_remove(struct platform_device *pdev) +{ + omap_mbox_unregister(&mbox_dsp_info); + + return 0; +} + +static struct platform_driver omap1_mbox_driver = { + .probe = omap1_mbox_probe, + .remove = omap1_mbox_remove, + .driver = { + .name = "mailbox", + }, +}; + +static int __init omap1_mbox_init(void) +{ + return platform_driver_register(&omap1_mbox_driver); +} + +static void __exit omap1_mbox_exit(void) +{ + platform_driver_unregister(&omap1_mbox_driver); +} + +module_init(omap1_mbox_init); +module_exit(omap1_mbox_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-omap2/mailbox.c b/arch/arm/mach-omap2/mailbox.c new file mode 100644 index 00000000000..b03cd06e055 --- /dev/null +++ b/arch/arm/mach-omap2/mailbox.c @@ -0,0 +1,318 @@ +/* + * Mailbox reservation modules for OMAP2 + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * and Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_REVISION 0x00 +#define MAILBOX_SYSCONFIG 0x10 +#define MAILBOX_SYSSTATUS 0x14 +#define MAILBOX_MESSAGE_0 0x40 +#define MAILBOX_MESSAGE_1 0x44 +#define MAILBOX_MESSAGE_2 0x48 +#define MAILBOX_MESSAGE_3 0x4c +#define MAILBOX_MESSAGE_4 0x50 +#define MAILBOX_MESSAGE_5 0x54 +#define MAILBOX_FIFOSTATUS_0 0x80 +#define MAILBOX_FIFOSTATUS_1 0x84 +#define MAILBOX_FIFOSTATUS_2 0x88 +#define MAILBOX_FIFOSTATUS_3 0x8c +#define MAILBOX_FIFOSTATUS_4 0x90 +#define MAILBOX_FIFOSTATUS_5 0x94 +#define MAILBOX_MSGSTATUS_0 0xc0 +#define MAILBOX_MSGSTATUS_1 0xc4 +#define MAILBOX_MSGSTATUS_2 0xc8 +#define MAILBOX_MSGSTATUS_3 0xcc +#define MAILBOX_MSGSTATUS_4 0xd0 +#define MAILBOX_MSGSTATUS_5 0xd4 +#define MAILBOX_IRQSTATUS_0 0x100 +#define MAILBOX_IRQENABLE_0 0x104 +#define MAILBOX_IRQSTATUS_1 0x108 +#define MAILBOX_IRQENABLE_1 0x10c +#define MAILBOX_IRQSTATUS_2 0x110 +#define MAILBOX_IRQENABLE_2 0x114 +#define MAILBOX_IRQSTATUS_3 0x118 +#define MAILBOX_IRQENABLE_3 0x11c + +static unsigned long mbox_base; + +#define MAILBOX_IRQ_NOTFULL(n) (1 << (2 * (n) + 1)) +#define MAILBOX_IRQ_NEWMSG(n) (1 << (2 * (n))) + +struct omap_mbox2_fifo { + unsigned long msg; + unsigned long fifo_stat; + unsigned long msg_stat; +}; + +struct omap_mbox2_priv { + struct omap_mbox2_fifo tx_fifo; + struct omap_mbox2_fifo rx_fifo; + unsigned long irqenable; + unsigned long irqstatus; + u32 newmsg_bit; + u32 notfull_bit; +}; + +static struct clk *mbox_ick_handle; + +static inline unsigned int mbox_read_reg(unsigned int reg) +{ + return __raw_readl(mbox_base + reg); +} + +static inline void mbox_write_reg(unsigned int val, unsigned int reg) +{ + __raw_writel(val, mbox_base + reg); +} + +/* Mailbox H/W preparations */ +static inline int omap2_mbox_startup(struct omap_mbox *mbox) +{ + unsigned int l; + + mbox_ick_handle = clk_get(NULL, "mailboxes_ick"); + if (IS_ERR(mbox_ick_handle)) { + printk("Could not get mailboxes_ick\n"); + return -ENODEV; + } + clk_enable(mbox_ick_handle); + + /* set smart-idle & autoidle */ + l = mbox_read_reg(MAILBOX_SYSCONFIG); + l |= 0x00000011; + mbox_write_reg(l, MAILBOX_SYSCONFIG); + + return 0; +} + +static inline void omap2_mbox_shutdown(struct omap_mbox *mbox) +{ + clk_disable(mbox_ick_handle); + clk_put(mbox_ick_handle); +} + +/* Mailbox FIFO handle functions */ +static inline mbox_msg_t omap2_mbox_fifo_read(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = + &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo; + return (mbox_msg_t) mbox_read_reg(fifo->msg); +} + +static inline void omap2_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) +{ + struct omap_mbox2_fifo *fifo = + &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo; + mbox_write_reg(msg, fifo->msg); +} + +static inline int omap2_mbox_fifo_empty(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = + &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo; + return (mbox_read_reg(fifo->msg_stat) == 0); +} + +static inline int omap2_mbox_fifo_full(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = + &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo; + return (mbox_read_reg(fifo->fifo_stat)); +} + +/* Mailbox IRQ handle functions */ +static inline void omap2_mbox_enable_irq(struct omap_mbox *mbox, + omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)mbox->priv; + u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + l = mbox_read_reg(p->irqenable); + l |= bit; + mbox_write_reg(l, p->irqenable); +} + +static inline void omap2_mbox_disable_irq(struct omap_mbox *mbox, + omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)mbox->priv; + u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + l = mbox_read_reg(p->irqenable); + l &= ~bit; + mbox_write_reg(l, p->irqenable); +} + +static inline void omap2_mbox_ack_irq(struct omap_mbox *mbox, + omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + mbox_write_reg(bit, p->irqstatus); +} + +static inline int omap2_mbox_is_irq(struct omap_mbox *mbox, + omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + u32 enable = mbox_read_reg(p->irqenable); + u32 status = mbox_read_reg(p->irqstatus); + + return (enable & status & bit); +} + +static struct omap_mbox_ops omap2_mbox_ops = { + .type = OMAP_MBOX_TYPE2, + .startup = omap2_mbox_startup, + .shutdown = omap2_mbox_shutdown, + .fifo_read = omap2_mbox_fifo_read, + .fifo_write = omap2_mbox_fifo_write, + .fifo_empty = omap2_mbox_fifo_empty, + .fifo_full = omap2_mbox_fifo_full, + .enable_irq = omap2_mbox_enable_irq, + .disable_irq = omap2_mbox_disable_irq, + .ack_irq = omap2_mbox_ack_irq, + .is_irq = omap2_mbox_is_irq, +}; + +/* + * MAILBOX 0: ARM -> DSP, + * MAILBOX 1: ARM <- DSP. + * MAILBOX 2: ARM -> IVA, + * MAILBOX 3: ARM <- IVA. + */ + +/* FIXME: the following structs should be filled automatically by the user id */ + +/* DSP */ +static struct omap_mbox2_priv omap2_mbox_dsp_priv = { + .tx_fifo = { + .msg = MAILBOX_MESSAGE_0, + .fifo_stat = MAILBOX_FIFOSTATUS_0, + }, + .rx_fifo = { + .msg = MAILBOX_MESSAGE_1, + .msg_stat = MAILBOX_MSGSTATUS_1, + }, + .irqenable = MAILBOX_IRQENABLE_0, + .irqstatus = MAILBOX_IRQSTATUS_0, + .notfull_bit = MAILBOX_IRQ_NOTFULL(0), + .newmsg_bit = MAILBOX_IRQ_NEWMSG(1), +}; + +struct omap_mbox mbox_dsp_info = { + .name = "dsp", + .ops = &omap2_mbox_ops, + .priv = &omap2_mbox_dsp_priv, +}; +EXPORT_SYMBOL(mbox_dsp_info); + +/* IVA */ +static struct omap_mbox2_priv omap2_mbox_iva_priv = { + .tx_fifo = { + .msg = MAILBOX_MESSAGE_2, + .fifo_stat = MAILBOX_FIFOSTATUS_2, + }, + .rx_fifo = { + .msg = MAILBOX_MESSAGE_3, + .msg_stat = MAILBOX_MSGSTATUS_3, + }, + .irqenable = MAILBOX_IRQENABLE_3, + .irqstatus = MAILBOX_IRQSTATUS_3, + .notfull_bit = MAILBOX_IRQ_NOTFULL(2), + .newmsg_bit = MAILBOX_IRQ_NEWMSG(3), +}; + +static struct omap_mbox mbox_iva_info = { + .name = "iva", + .ops = &omap2_mbox_ops, + .priv = &omap2_mbox_iva_priv, +}; + +static int __init omap2_mbox_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + if (pdev->num_resources != 3) { + dev_err(&pdev->dev, "invalid number of resources: %d\n", + pdev->num_resources); + return -ENODEV; + } + + /* MBOX base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid mem resource\n"); + return -ENODEV; + } + mbox_base = res->start; + + /* DSP IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_dsp_info.irq = res->start; + + ret = omap_mbox_register(&mbox_dsp_info); + + /* IVA IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_iva_info.irq = res->start; + + ret = omap_mbox_register(&mbox_iva_info); + + return ret; +} + +static int omap2_mbox_remove(struct platform_device *pdev) +{ + omap_mbox_unregister(&mbox_dsp_info); + return 0; +} + +static struct platform_driver omap2_mbox_driver = { + .probe = omap2_mbox_probe, + .remove = omap2_mbox_remove, + .driver = { + .name = "mailbox", + }, +}; + +static int __init omap2_mbox_init(void) +{ + return platform_driver_register(&omap2_mbox_driver); +} + +static void __exit omap2_mbox_exit(void) +{ + platform_driver_unregister(&omap2_mbox_driver); +} + +module_init(omap2_mbox_init); +module_exit(omap2_mbox_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c new file mode 100644 index 00000000000..de7e6ef48bd --- /dev/null +++ b/arch/arm/plat-omap/mailbox.c @@ -0,0 +1,509 @@ +/* + * OMAP mailbox driver + * + * Copyright (C) 2006 Nokia Corporation. All rights reserved. + * + * Contact: Toshihiro Kobayashi + * Restructured by Hiroshi DOYU + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mailbox.h" + +static struct omap_mbox *mboxes; +static DEFINE_RWLOCK(mboxes_lock); + +/* Mailbox Sequence Bit function */ +void omap_mbox_init_seq(struct omap_mbox *mbox) +{ + mbox_seq_init(mbox); +} +EXPORT_SYMBOL(omap_mbox_init_seq); + +/* + * message sender + */ +static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void *arg) +{ + int ret = 0, i = 1000; + + while (mbox_fifo_full(mbox)) { + if (mbox->ops->type == OMAP_MBOX_TYPE2) + return -1; + if (--i == 0) + return -1; + udelay(1); + } + + if (arg && mbox->txq->callback) { + ret = mbox->txq->callback(arg); + if (ret) + goto out; + } + + mbox_seq_toggle(mbox, &msg); + mbox_fifo_write(mbox, msg); + out: + return ret; +} + +int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg) +{ + struct request *rq; + struct request_queue *q = mbox->txq->queue; + int ret = 0; + + rq = blk_get_request(q, WRITE, GFP_ATOMIC); + if (unlikely(!rq)) { + ret = -ENOMEM; + goto fail; + } + + rq->data = (void *)msg; + blk_insert_request(q, rq, 0, arg); + + schedule_work(&mbox->txq->work); + fail: + return ret; +} +EXPORT_SYMBOL(omap_mbox_msg_send); + +static void mbox_tx_work(struct work_struct *work) +{ + int ret; + struct request *rq; + struct omap_mbox_queue *mq = container_of(work, + struct omap_mbox_queue, work); + struct omap_mbox *mbox = mq->queue->queuedata; + struct request_queue *q = mbox->txq->queue; + + while (1) { + spin_lock(q->queue_lock); + rq = elv_next_request(q); + spin_unlock(q->queue_lock); + + if (!rq) + break; + + ret = __mbox_msg_send(mbox, (mbox_msg_t) rq->data, rq->special); + if (ret) { + enable_mbox_irq(mbox, IRQ_TX); + return; + } + + spin_lock(q->queue_lock); + blkdev_dequeue_request(rq); + end_that_request_last(rq, 0); + spin_unlock(q->queue_lock); + } +} + +/* + * Message receiver(workqueue) + */ +static void mbox_rx_work(struct work_struct *work) +{ + struct omap_mbox_queue *mq = + container_of(work, struct omap_mbox_queue, work); + struct omap_mbox *mbox = mq->queue->queuedata; + struct request_queue *q = mbox->rxq->queue; + struct request *rq; + mbox_msg_t msg; + unsigned long flags; + + if (mbox->rxq->callback == NULL) { + sysfs_notify(&mbox->dev.kobj, NULL, "mbox"); + return; + } + + while (1) { + spin_lock_irqsave(q->queue_lock, flags); + rq = elv_next_request(q); + spin_unlock_irqrestore(q->queue_lock, flags); + if (!rq) + break; + + msg = (mbox_msg_t) rq->data; + + spin_lock_irqsave(q->queue_lock, flags); + blkdev_dequeue_request(rq); + end_that_request_last(rq, 0); + spin_unlock_irqrestore(q->queue_lock, flags); + + mbox->rxq->callback((void *)msg); + } +} + +/* + * Mailbox interrupt handler + */ +static void mbox_txq_fn(request_queue_t * q) +{ +} + +static void mbox_rxq_fn(request_queue_t * q) +{ +} + +static void __mbox_tx_interrupt(struct omap_mbox *mbox) +{ + disable_mbox_irq(mbox, IRQ_TX); + ack_mbox_irq(mbox, IRQ_TX); + schedule_work(&mbox->txq->work); +} + +static void __mbox_rx_interrupt(struct omap_mbox *mbox) +{ + struct request *rq; + mbox_msg_t msg; + request_queue_t *q = mbox->rxq->queue; + + disable_mbox_irq(mbox, IRQ_RX); + + while (!mbox_fifo_empty(mbox)) { + rq = blk_get_request(q, WRITE, GFP_ATOMIC); + if (unlikely(!rq)) + goto nomem; + + msg = mbox_fifo_read(mbox); + rq->data = (void *)msg; + + if (unlikely(mbox_seq_test(mbox, msg))) { + pr_info("mbox: Illegal seq bit!(%08x)\n", msg); + if (mbox->err_notify) + mbox->err_notify(); + } + + blk_insert_request(q, rq, 0, NULL); + if (mbox->ops->type == OMAP_MBOX_TYPE1) + break; + } + + /* no more messages in the fifo. clear IRQ source. */ + ack_mbox_irq(mbox, IRQ_RX); + enable_mbox_irq(mbox, IRQ_RX); + nomem: + schedule_work(&mbox->rxq->work); +} + +static irqreturn_t mbox_interrupt(int irq, void *p) +{ + struct omap_mbox *mbox = (struct omap_mbox *)p; + + if (is_mbox_irq(mbox, IRQ_TX)) + __mbox_tx_interrupt(mbox); + + if (is_mbox_irq(mbox, IRQ_RX)) + __mbox_rx_interrupt(mbox); + + return IRQ_HANDLED; +} + +/* + * sysfs files + */ +static ssize_t +omap_mbox_write(struct device *dev, struct device_attribute *attr, + const char * buf, size_t count) +{ + int ret; + mbox_msg_t *p = (mbox_msg_t *)buf; + struct omap_mbox *mbox = dev_get_drvdata(dev); + + for (; count >= sizeof(mbox_msg_t); count -= sizeof(mbox_msg_t)) { + ret = omap_mbox_msg_send(mbox, be32_to_cpu(*p), NULL); + if (ret) + return -EAGAIN; + p++; + } + + return (size_t)((char *)p - buf); +} + +static ssize_t +omap_mbox_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + unsigned long flags; + struct request *rq; + mbox_msg_t *p = (mbox_msg_t *) buf; + struct omap_mbox *mbox = dev_get_drvdata(dev); + struct request_queue *q = mbox->rxq->queue; + + while (1) { + spin_lock_irqsave(q->queue_lock, flags); + rq = elv_next_request(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + if (!rq) + break; + + *p = (mbox_msg_t) rq->data; + + spin_lock_irqsave(q->queue_lock, flags); + blkdev_dequeue_request(rq); + end_that_request_last(rq, 0); + spin_unlock_irqrestore(q->queue_lock, flags); + + if (unlikely(mbox_seq_test(mbox, *p))) { + pr_info("mbox: Illegal seq bit!(%08x) ignored\n", *p); + continue; + } + p++; + } + + pr_debug("%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); + + return (size_t) ((char *)p - buf); +} + +static DEVICE_ATTR(mbox, S_IRUGO | S_IWUSR, omap_mbox_read, omap_mbox_write); + +static ssize_t mbox_show(struct class *class, char *buf) +{ + return sprintf(buf, "mbox"); +} + +static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL); + +static struct class omap_mbox_class = { + .name = "omap_mbox", +}; + +static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, + request_fn_proc * proc, + void (*work) (struct work_struct *)) +{ + request_queue_t *q; + struct omap_mbox_queue *mq; + + mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); + if (!mq) + return NULL; + + spin_lock_init(&mq->lock); + + q = blk_init_queue(proc, &mq->lock); + if (!q) + goto error; + q->queuedata = mbox; + mq->queue = q; + + INIT_WORK(&mq->work, work); + + return mq; +error: + kfree(mq); + return NULL; +} + +static void mbox_queue_free(struct omap_mbox_queue *q) +{ + blk_cleanup_queue(q->queue); + kfree(q); +} + +static int omap_mbox_init(struct omap_mbox *mbox) +{ + int ret; + struct omap_mbox_queue *mq; + + if (likely(mbox->ops->startup)) { + ret = mbox->ops->startup(mbox); + if (unlikely(ret)) + return ret; + } + + mbox->dev.class = &omap_mbox_class; + strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN); + dev_set_drvdata(&mbox->dev, mbox); + + ret = device_register(&mbox->dev); + if (unlikely(ret)) + goto fail_device_reg; + + ret = device_create_file(&mbox->dev, &dev_attr_mbox); + if (unlikely(ret)) { + printk(KERN_ERR + "device_create_file failed: %d\n", ret); + goto fail_create_mbox; + } + + ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED, + mbox->name, mbox); + if (unlikely(ret)) { + printk(KERN_ERR + "failed to register mailbox interrupt:%d\n", ret); + goto fail_request_irq; + } + enable_mbox_irq(mbox, IRQ_RX); + + mq = mbox_queue_alloc(mbox, mbox_txq_fn, mbox_tx_work); + if (!mq) { + ret = -ENOMEM; + goto fail_alloc_txq; + } + mbox->txq = mq; + + mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work); + if (!mq) { + ret = -ENOMEM; + goto fail_alloc_rxq; + } + mbox->rxq = mq; + + return 0; + + fail_alloc_rxq: + mbox_queue_free(mbox->txq); + fail_alloc_txq: + free_irq(mbox->irq, mbox); + fail_request_irq: + device_remove_file(&mbox->dev, &dev_attr_mbox); + fail_create_mbox: + device_unregister(&mbox->dev); + fail_device_reg: + if (unlikely(mbox->ops->shutdown)) + mbox->ops->shutdown(mbox); + + return ret; +} + +static void omap_mbox_fini(struct omap_mbox *mbox) +{ + mbox_queue_free(mbox->txq); + mbox_queue_free(mbox->rxq); + + free_irq(mbox->irq, mbox); + device_remove_file(&mbox->dev, &dev_attr_mbox); + class_unregister(&omap_mbox_class); + + if (unlikely(mbox->ops->shutdown)) + mbox->ops->shutdown(mbox); +} + +static struct omap_mbox **find_mboxes(const char *name) +{ + struct omap_mbox **p; + + for (p = &mboxes; *p; p = &(*p)->next) { + if (strcmp((*p)->name, name) == 0) + break; + } + + return p; +} + +struct omap_mbox *omap_mbox_get(const char *name) +{ + struct omap_mbox *mbox; + int ret; + + read_lock(&mboxes_lock); + mbox = *(find_mboxes(name)); + if (mbox == NULL) { + read_unlock(&mboxes_lock); + return ERR_PTR(-ENOENT); + } + + read_unlock(&mboxes_lock); + + ret = omap_mbox_init(mbox); + if (ret) + return ERR_PTR(-ENODEV); + + return mbox; +} +EXPORT_SYMBOL(omap_mbox_get); + +void omap_mbox_put(struct omap_mbox *mbox) +{ + omap_mbox_fini(mbox); +} +EXPORT_SYMBOL(omap_mbox_put); + +int omap_mbox_register(struct omap_mbox *mbox) +{ + int ret = 0; + struct omap_mbox **tmp; + + if (!mbox) + return -EINVAL; + if (mbox->next) + return -EBUSY; + + write_lock(&mboxes_lock); + tmp = find_mboxes(mbox->name); + if (*tmp) + ret = -EBUSY; + else + *tmp = mbox; + write_unlock(&mboxes_lock); + + return ret; +} +EXPORT_SYMBOL(omap_mbox_register); + +int omap_mbox_unregister(struct omap_mbox *mbox) +{ + struct omap_mbox **tmp; + + write_lock(&mboxes_lock); + tmp = &mboxes; + while (*tmp) { + if (mbox == *tmp) { + *tmp = mbox->next; + mbox->next = NULL; + write_unlock(&mboxes_lock); + return 0; + } + tmp = &(*tmp)->next; + } + write_unlock(&mboxes_lock); + + return -EINVAL; +} +EXPORT_SYMBOL(omap_mbox_unregister); + +static int __init omap_mbox_class_init(void) +{ + int ret = class_register(&omap_mbox_class); + if (!ret) + ret = class_create_file(&omap_mbox_class, &class_attr_mbox); + + return ret; +} + +static void __exit omap_mbox_class_exit(void) +{ + class_remove_file(&omap_mbox_class, &class_attr_mbox); + class_unregister(&omap_mbox_class); +} + +subsys_initcall(omap_mbox_class_init); +module_exit(omap_mbox_class_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/mailbox.h b/arch/arm/plat-omap/mailbox.h new file mode 100644 index 00000000000..67c6740b8ad --- /dev/null +++ b/arch/arm/plat-omap/mailbox.h @@ -0,0 +1,100 @@ +/* + * Mailbox internal functions + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __ARCH_ARM_PLAT_MAILBOX_H +#define __ARCH_ARM_PLAT_MAILBOX_H + +/* + * Mailbox sequence bit API + */ +#if defined(CONFIG_ARCH_OMAP1) +# define MBOX_USE_SEQ_BIT +#elif defined(CONFIG_ARCH_OMAP2) +# define MBOX_USE_SEQ_BIT +#endif + +#ifdef MBOX_USE_SEQ_BIT +/* seq_rcv should be initialized with any value other than + * 0 and 1 << 31, to allow either value for the first + * message. */ +static inline void mbox_seq_init(struct omap_mbox *mbox) +{ + /* any value other than 0 and 1 << 31 */ + mbox->seq_rcv = 0xffffffff; +} + +static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg) +{ + /* add seq_snd to msg */ + *msg = (*msg & 0x7fffffff) | mbox->seq_snd; + /* flip seq_snd */ + mbox->seq_snd ^= 1 << 31; +} + +static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg) +{ + mbox_msg_t seq = msg & (1 << 31); + if (seq == mbox->seq_rcv) + return -1; + mbox->seq_rcv = seq; + return 0; +} +#else +static inline void mbox_seq_init(struct omap_mbox *mbox) +{ +} +static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg) +{ +} +static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg) +{ + return 0; +} +#endif + +/* Mailbox FIFO handle functions */ +static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) +{ + return mbox->ops->fifo_read(mbox); +} +static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) +{ + mbox->ops->fifo_write(mbox, msg); +} +static inline int mbox_fifo_empty(struct omap_mbox *mbox) +{ + return mbox->ops->fifo_empty(mbox); +} +static inline int mbox_fifo_full(struct omap_mbox *mbox) +{ + return mbox->ops->fifo_full(mbox); +} + +/* Mailbox IRQ handle functions */ +static inline void enable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + mbox->ops->enable_irq(mbox, irq); +} +static inline void disable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + mbox->ops->disable_irq(mbox, irq); +} +static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + if (mbox->ops->ack_irq) + mbox->ops->ack_irq(mbox, irq); +} +static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + return mbox->ops->is_irq(mbox, irq); +} + +#endif /* __ARCH_ARM_PLAT_MAILBOX_H */ diff --git a/include/asm-arm/arch-omap/mailbox.h b/include/asm-arm/arch-omap/mailbox.h new file mode 100644 index 00000000000..4bf0909461f --- /dev/null +++ b/include/asm-arm/arch-omap/mailbox.h @@ -0,0 +1,73 @@ +/* mailbox.h */ + +#ifndef MAILBOX_H +#define MAILBOX_H + +#include +#include +#include + +typedef u32 mbox_msg_t; +typedef void (mbox_receiver_t)(mbox_msg_t msg); +struct omap_mbox; + +typedef int __bitwise omap_mbox_irq_t; +#define IRQ_TX ((__force omap_mbox_irq_t) 1) +#define IRQ_RX ((__force omap_mbox_irq_t) 2) + +typedef int __bitwise omap_mbox_type_t; +#define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1) +#define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2) + +struct omap_mbox_ops { + omap_mbox_type_t type; + int (*startup)(struct omap_mbox *mbox); + void (*shutdown)(struct omap_mbox *mbox); + /* fifo */ + mbox_msg_t (*fifo_read)(struct omap_mbox *mbox); + void (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg); + int (*fifo_empty)(struct omap_mbox *mbox); + int (*fifo_full)(struct omap_mbox *mbox); + /* irq */ + void (*enable_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + void (*disable_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + void (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + int (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); +}; + +struct omap_mbox_queue { + spinlock_t lock; + request_queue_t *queue; + struct work_struct work; + int (*callback)(void *); + struct omap_mbox *mbox; +}; + +struct omap_mbox { + char *name; + unsigned int irq; + + struct omap_mbox_queue *txq, *rxq; + + struct omap_mbox_ops *ops; + + mbox_msg_t seq_snd, seq_rcv; + + struct device dev; + + struct omap_mbox *next; + void *priv; + + void (*err_notify)(void); +}; + +int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg, void *); +void omap_mbox_init_seq(struct omap_mbox *); + +struct omap_mbox *omap_mbox_get(const char *); +void omap_mbox_put(struct omap_mbox *); + +int omap_mbox_register(struct omap_mbox *); +int omap_mbox_unregister(struct omap_mbox *); + +#endif /* MAILBOX_H */ -- cgit v1.2.3 From 39b8e6986739c34da4e45eb4b8d2bb707292c591 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 12 Dec 2006 23:02:43 -0800 Subject: ARM: OMAP: Fix gpmc header Fix gpmc header Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- include/asm-arm/arch-omap/gpmc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h index fa33149f597..995cc83482e 100644 --- a/include/asm-arm/arch-omap/gpmc.h +++ b/include/asm-arm/arch-omap/gpmc.h @@ -87,7 +87,7 @@ extern int gpmc_cs_calc_divider(int cs, unsigned int sync_clk); extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); extern void gpmc_cs_free(int cs); -extern void gpmc_cs_set_reserved(int cs, int reserved); +extern int gpmc_cs_set_reserved(int cs, int reserved); extern int gpmc_cs_reserved(int cs); #endif -- cgit v1.2.3 From d82973d1cdc149d06ac9685ae008105d382c4236 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 26 Jan 2007 12:01:17 -0800 Subject: ARM: OMAP: Mostly cosmetic to sync up with linux-omap tree Mostly cosmetic to sync up with linux-omap tree Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap1/Kconfig | 2 +- arch/arm/mach-omap2/devices.c | 6 +++--- arch/arm/plat-omap/devices.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 18ea7313a49..ab1b17d35bd 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -46,7 +46,7 @@ config MACH_OMAP_H3 config MACH_OMAP_OSK bool "TI OSK Support" depends on ARCH_OMAP1 && ARCH_OMAP16XX - select TPS65010 + select OMAP_MCBSP help TI OMAP 5912 OSK (OMAP Starter Kit) board support. Say Y here if you have such a board. diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index b3ac9622f4a..52ec2f2d636 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -24,7 +24,7 @@ #include #include -#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) +#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) #define OMAP2_I2C_BASE2 0x48072000 #define OMAP2_I2C_INT2 57 @@ -42,8 +42,8 @@ static struct resource i2c_resources2[] = { }; static struct platform_device omap_i2c_device2 = { - .name = "i2c_omap", - .id = 2, + .name = "i2c_omap", + .id = 2, .num_resources = ARRAY_SIZE(i2c_resources2), .resource = i2c_resources2, }; diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index 2fac7d3f2af..c5dab1d6417 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -112,8 +112,8 @@ static struct resource i2c_resources1[] = { /* DMA not used; works around erratum writing to non-empty i2c fifo */ static struct platform_device omap_i2c_device1 = { - .name = "i2c_omap", - .id = 1, + .name = "i2c_omap", + .id = 1, .num_resources = ARRAY_SIZE(i2c_resources1), .resource = i2c_resources1, }; -- cgit v1.2.3 From b01d96d653b1387ee0a91094ee54a8c523c3be09 Mon Sep 17 00:00:00 2001 From: Kai Svahn Date: Thu, 1 Feb 2007 16:19:15 -0800 Subject: ARM: OMAP: Sync framebuffer headers with N800 tree This patch syncs framebuffer headers with N800 tree. Signed-off-by: Kai Svahn Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- include/asm-arm/arch-omap/hwa742.h | 12 ++++++++ include/asm-arm/arch-omap/omapfb.h | 61 +++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 include/asm-arm/arch-omap/hwa742.h diff --git a/include/asm-arm/arch-omap/hwa742.h b/include/asm-arm/arch-omap/hwa742.h new file mode 100644 index 00000000000..577f492f2d3 --- /dev/null +++ b/include/asm-arm/arch-omap/hwa742.h @@ -0,0 +1,12 @@ +#ifndef _HWA742_H +#define _HWA742_H + +struct hwa742_platform_data { + void (*power_up)(struct device *dev); + void (*power_down)(struct device *dev); + unsigned long (*get_clock_rate)(struct device *dev); + + unsigned te_connected:1; +}; + +#endif diff --git a/include/asm-arm/arch-omap/omapfb.h b/include/asm-arm/arch-omap/omapfb.h index adf1d81f22d..46d7a4f6085 100644 --- a/include/asm-arm/arch-omap/omapfb.h +++ b/include/asm-arm/arch-omap/omapfb.h @@ -38,30 +38,47 @@ #define OMAPFB_SYNC_GFX OMAP_IO(37) #define OMAPFB_VSYNC OMAP_IO(38) #define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) -#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(41, struct omapfb_update_window_old) +#define OMAPFB_GET_CAPS OMAP_IOR(42, struct omapfb_caps) #define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) #define OMAPFB_LCD_TEST OMAP_IOW(45, int) #define OMAPFB_CTRL_TEST OMAP_IOW(46, int) -#define OMAPFB_UPDATE_WINDOW OMAP_IOW(47, struct omapfb_update_window) +#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old) #define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) #define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) #define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) #define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) +#define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window) +#define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info) +#define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info) #define OMAPFB_CAPS_GENERIC_MASK 0x00000fff #define OMAPFB_CAPS_LCDC_MASK 0x00fff000 #define OMAPFB_CAPS_PANEL_MASK 0xff000000 #define OMAPFB_CAPS_MANUAL_UPDATE 0x00001000 +#define OMAPFB_CAPS_TEARSYNC 0x00002000 +#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000 +#define OMAPFB_CAPS_PLANE_SCALE 0x00008000 +#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 +#define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 +#define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 #define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 /* Values from DSP must map to lower 16-bits */ -#define OMAPFB_FORMAT_MASK 0x00ff -#define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 +#define OMAPFB_FORMAT_MASK 0x00ff +#define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 +#define OMAPFB_FORMAT_FLAG_TEARSYNC 0x0200 +#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400 +#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY 0x0800 +#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY 0x1000 #define OMAPFB_EVENT_READY 1 #define OMAPFB_EVENT_DISABLED 2 +#define OMAPFB_MEMTYPE_SDRAM 0 +#define OMAPFB_MEMTYPE_SRAM 1 +#define OMAPFB_MEMTYPE_MAX 1 + enum omapfb_color_format { OMAPFB_COLOR_RGB565 = 0, OMAPFB_COLOR_YUV422, @@ -78,11 +95,15 @@ struct omapfb_update_window { __u32 x, y; __u32 width, height; __u32 format; + __u32 out_x, out_y; + __u32 out_width, out_height; + __u32 reserved[8]; }; struct omapfb_update_window_old { __u32 x, y; __u32 width, height; + __u32 format; }; enum omapfb_plane { @@ -108,6 +129,18 @@ struct omapfb_plane_info { __u32 reserved2[12]; }; +struct omapfb_mem_info { + __u32 size; + __u8 type; + __u8 reserved[3]; +}; + +struct omapfb_caps { + __u32 ctrl; + __u32 plane_color; + __u32 wnd_color; +}; + enum omapfb_color_key_type { OMAPFB_COLOR_KEY_DISABLED = 0, OMAPFB_COLOR_KEY_GFX_DST, @@ -191,8 +224,6 @@ struct lcd_panel { int (*run_test) (struct lcd_panel *panel, int test_num); }; -struct omapfb_device; - struct extif_timings { int cs_on_time; int cs_off_time; @@ -216,6 +247,7 @@ struct lcd_ctrl_extif { int (*init) (struct omapfb_device *fbdev); void (*cleanup) (void); void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); + unsigned long (*get_max_tx_rate)(void); int (*convert_timings) (struct extif_timings *timings); void (*set_timings) (const struct extif_timings *timings); void (*set_bits_per_cycle)(int bpc); @@ -224,6 +256,10 @@ struct lcd_ctrl_extif { void (*write_data) (const void *buf, unsigned int len); void (*transfer_area) (int width, int height, void (callback)(void * data), void *data); + int (*setup_tearsync) (unsigned pin_cnt, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int div); + int (*enable_tearsync) (int enable, unsigned line); unsigned long max_transmit_size; }; @@ -242,7 +278,9 @@ struct omapfb_mem_region { dma_addr_t paddr; void *vaddr; unsigned long size; - int alloc:1; + u8 type; /* OMAPFB_PLANE_MEM_* */ + unsigned alloc:1; /* allocated by the driver */ + unsigned map:1; /* kernel mapped by the driver */ }; struct omapfb_mem_desc { @@ -259,7 +297,7 @@ struct lcd_ctrl { struct omapfb_mem_desc *req_md); void (*cleanup) (void); void (*bind_client) (struct omapfb_notifier_block *nb); - unsigned long (*get_caps) (void); + void (*get_caps) (int plane, struct omapfb_caps *caps); int (*set_update_mode)(enum omapfb_update_mode mode); enum omapfb_update_mode (*get_update_mode)(void); int (*setup_plane) (int plane, int channel_out, @@ -267,6 +305,10 @@ struct lcd_ctrl { int screen_width, int pos_x, int pos_y, int width, int height, int color_mode); + int (*setup_mem) (int plane, size_t size, + int mem_type, unsigned long *paddr); + int (*mmap) (struct fb_info *info, + struct vm_area_struct *vma); int (*set_scale) (int plane, int orig_width, int orig_height, int out_width, int out_height); @@ -284,7 +326,6 @@ struct lcd_ctrl { int update_hw_mem); int (*set_color_key) (struct omapfb_color_key *ck); int (*get_color_key) (struct omapfb_color_key *ck); - }; enum omapfb_state { @@ -315,6 +356,7 @@ struct omapfb_device { struct lcd_ctrl_extif *ext_if; /* LCD ctrl external interface */ struct device *dev; + struct fb_var_screeninfo new_var; /* for mode changes */ struct omapfb_mem_desc mem_desc; struct fb_info *fb_info[OMAPFB_PLANE_NUM]; @@ -346,7 +388,6 @@ extern int omapfb_update_window_async(struct fb_info *fbi, void *callback_data); /* in arch/arm/plat-omap/fb.c */ -extern void omapfb_reserve_mem(void); extern void omapfb_set_ctrl_platform_data(void *pdata); #endif /* __KERNEL__ */ -- cgit v1.2.3 From b7cc6d46b4f8157bfc58a6ed143ffa83575e236a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 6 Mar 2007 03:16:36 -0800 Subject: ARM: OMAP: FB sync with N800 tree (support for dynamic SRAM allocations) - in addition to fixed FB regions - as passed by the bootloader - allow dynamic allocations - do some more checking against overlapping / reserved regions - move the FB specific parts out from sram.c to fb.c Signed-off-by: Imre Deak Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap1/io.c | 4 +- arch/arm/mach-omap2/io.c | 3 +- arch/arm/plat-omap/fb.c | 277 ++++++++++++++++++++++++++++++++++----- arch/arm/plat-omap/sram.c | 63 ++------- include/asm-arm/arch-omap/sram.h | 3 - 5 files changed, 264 insertions(+), 86 deletions(-) diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c index fab8b0b27cf..81c4e738506 100644 --- a/arch/arm/mach-omap1/io.c +++ b/arch/arm/mach-omap1/io.c @@ -17,11 +17,11 @@ #include #include #include -#include extern int omap1_clk_init(void); extern void omap_check_revision(void); extern void omap_sram_init(void); +extern void omapfb_reserve_sdram(void); /* * The machine specific code may provide the extra mapping besides the @@ -121,7 +121,7 @@ void __init omap1_map_common_io(void) #endif omap_sram_init(); - omapfb_reserve_mem(); + omapfb_reserve_sdram(); } /* diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index 59458642e9e..82dc70f6b77 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -27,6 +27,7 @@ extern void omap_sram_init(void); extern int omap2_clk_init(void); extern void omap2_check_revision(void); extern void gpmc_init(void); +extern void omapfb_reserve_sdram(void); /* * The machine specific code may provide the extra mapping besides the @@ -72,7 +73,7 @@ void __init omap2_map_common_io(void) omap2_check_revision(); omap_sram_init(); - omapfb_reserve_mem(); + omapfb_reserve_sdram(); } void __init omap2_init_common_hw(void) diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index a302d9194f5..469f580ca05 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -39,6 +39,8 @@ #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) static struct omapfb_platform_data omapfb_config; +static int config_invalid; +static int configured_regions; static u64 omap_fb_dma_mask = ~(u32)0; @@ -53,46 +55,246 @@ static struct platform_device omap_fb_device = { .num_resources = 0, }; -/* called from map_io */ -void omapfb_reserve_mem(void) +static inline int ranges_overlap(unsigned long start1, unsigned long size1, + unsigned long start2, unsigned long size2) { - const struct omap_fbmem_config *fbmem_conf; - unsigned long total_size; + return (start1 >= start2 && start1 < start2 + size2) || + (start2 >= start1 && start2 < start1 + size1); +} + +static inline int range_included(unsigned long start1, unsigned long size1, + unsigned long start2, unsigned long size2) +{ + return start1 >= start2 && start1 + size1 <= start2 + size2; +} + + +/* Check if there is an overlapping region. */ +static int fbmem_region_reserved(unsigned long start, size_t size) +{ + struct omapfb_mem_region *rg; int i; - if (!omap_fb_sram_valid) { - /* FBMEM SRAM configuration was already found to be invalid. - * Ignore the whole configuration block. */ - omapfb_config.mem_desc.region_cnt = 0; - return; + rg = &omapfb_config.mem_desc.region[0]; + for (i = 0; i < OMAPFB_PLANE_NUM; i++, rg++) { + if (!rg->paddr) + /* Empty slot. */ + continue; + if (ranges_overlap(start, size, rg->paddr, rg->size)) + return 1; } + return 0; +} - i = 0; - total_size = 0; - while ((fbmem_conf = omap_get_nr_config(OMAP_TAG_FBMEM, - struct omap_fbmem_config, i)) != NULL) { - unsigned long start; - unsigned long size; +/* + * Get the region_idx`th region from board config/ATAG and convert it to + * our internal format. + */ +static int get_fbmem_region(int region_idx, struct omapfb_mem_region *rg) +{ + const struct omap_fbmem_config *conf; + u32 paddr; - if (i == OMAPFB_PLANE_NUM) { - printk(KERN_ERR "ignoring extra plane info\n"); + conf = omap_get_nr_config(OMAP_TAG_FBMEM, + struct omap_fbmem_config, region_idx); + if (conf == NULL) + return -ENOENT; + + paddr = conf->start; + /* + * Low bits encode the page allocation mode, if high bits + * are zero. Otherwise we need a page aligned fixed + * address. + */ + memset(rg, 0, sizeof(*rg)); + rg->type = paddr & ~PAGE_MASK; + rg->paddr = paddr & PAGE_MASK; + rg->size = PAGE_ALIGN(conf->size); + return 0; +} + +static int set_fbmem_region_type(struct omapfb_mem_region *rg, int mem_type, + unsigned long mem_start, + unsigned long mem_size) +{ + /* + * Check if the configuration specifies the type explicitly. + * type = 0 && paddr = 0, a default don't care case maps to + * the SDRAM type. + */ + if (rg->type || (!rg->type && !rg->paddr)) + return 0; + if (ranges_overlap(rg->paddr, rg->size, mem_start, mem_size)) { + rg->type = mem_type; + return 0; + } + /* Can't determine it. */ + return -1; +} + +static int check_fbmem_region(int region_idx, struct omapfb_mem_region *rg, + unsigned long start_avail, unsigned size_avail) +{ + unsigned long paddr = rg->paddr; + size_t size = rg->size; + + if (rg->type > OMAPFB_MEMTYPE_MAX) { + printk(KERN_ERR + "Invalid start address for FB region %d\n", region_idx); + return -EINVAL; + } + + if (!rg->size) { + printk(KERN_ERR "Zero size for FB region %d\n", region_idx); + return -EINVAL; + } + + if (!paddr) + /* Allocate this dynamically, leave paddr 0 for now. */ + return 0; + + /* + * Fixed region for the given RAM range. Check if it's already + * reserved by the FB code or someone else. + */ + if (fbmem_region_reserved(paddr, size) || + !range_included(paddr, size, start_avail, size_avail)) { + printk(KERN_ERR "Trying to use reserved memory " + "for FB region %d\n", region_idx); + return -EINVAL; + } + + return 0; +} + +/* + * Called from map_io. We need to call to this early enough so that we + * can reserve the fixed SDRAM regions before VM could get hold of them. + */ +void omapfb_reserve_sdram(void) +{ + struct bootmem_data *bdata; + unsigned long sdram_start, sdram_size; + unsigned long reserved; + int i; + + if (config_invalid) + return; + + bdata = NODE_DATA(0)->bdata; + sdram_start = bdata->node_boot_start; + sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; + reserved = 0; + for (i = 0; ; i++) { + struct omapfb_mem_region rg; + + if (get_fbmem_region(i, &rg) < 0) break; + if (i == OMAPFB_PLANE_NUM) { + printk(KERN_ERR + "Extraneous FB mem configuration entries\n"); + config_invalid = 1; + return; } - start = fbmem_conf->start; - size = fbmem_conf->size; - omapfb_config.mem_desc.region[i].paddr = start; - omapfb_config.mem_desc.region[i].size = size; - if (omap_fb_sram_plane != i && start) { - reserve_bootmem(start, size); - total_size += size; + /* Check if it's our memory type. */ + if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SDRAM, + sdram_start, sdram_size) < 0 || + (rg.type != OMAPFB_MEMTYPE_SDRAM)) + continue; + BUG_ON(omapfb_config.mem_desc.region[i].size); + if (check_fbmem_region(i, &rg, sdram_start, sdram_size) < 0) { + config_invalid = 1; + return; } - i++; + if (rg.paddr) + reserve_bootmem(rg.paddr, rg.size); + reserved += rg.size; + omapfb_config.mem_desc.region[i] = rg; + configured_regions++; } omapfb_config.mem_desc.region_cnt = i; - if (total_size) + if (reserved) pr_info("Reserving %lu bytes SDRAM for frame buffer\n", - total_size); + reserved); +} + +/* + * Called at sram init time, before anything is pushed to the SRAM stack. + * Because of the stack scheme, we will allocate everything from the + * start of the lowest address region to the end of SRAM. This will also + * include padding for page alignment and possible holes between regions. + * + * As opposed to the SDRAM case, we'll also do any dynamic allocations at + * this point, since the driver built as a module would have problem with + * freeing / reallocating the regions. + */ +unsigned long omapfb_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail) +{ + struct omapfb_mem_region rg; + unsigned long pend_avail; + unsigned long reserved; + int i; + + if (config_invalid) + return 0; + + reserved = 0; + pend_avail = pstart_avail + size_avail; + for (i = 0; ; i++) { + if (get_fbmem_region(i, &rg) < 0) + break; + if (i == OMAPFB_PLANE_NUM) { + printk(KERN_ERR + "Extraneous FB mem configuration entries\n"); + config_invalid = 1; + return 0; + } + /* Check if it's our memory type. */ + if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SRAM, + sram_pstart, sram_size) < 0 || + (rg.type != OMAPFB_MEMTYPE_SRAM)) + continue; + BUG_ON(omapfb_config.mem_desc.region[i].size); + + if (check_fbmem_region(i, &rg, pstart_avail, size_avail) < 0) { + config_invalid = 1; + return 0; + } + + if (!rg.paddr) { + /* Dynamic allocation */ + if ((size_avail & PAGE_MASK) < rg.size) { + printk("Not enough SRAM for FB region %d\n", + i); + config_invalid = 1; + return 0; + } + size_avail = (size_avail - rg.size) & PAGE_MASK; + rg.paddr = pstart_avail + size_avail; + } + /* Reserve everything above the start of the region. */ + if (pend_avail - rg.paddr > reserved) + reserved = pend_avail - rg.paddr; + size_avail = pend_avail - reserved - pstart_avail; + + /* + * We have a kernel mapping for this already, so the + * driver won't have to make one. + */ + rg.vaddr = (void *)(sram_vstart + rg.paddr - sram_pstart); + omapfb_config.mem_desc.region[i] = rg; + configured_regions++; + } + omapfb_config.mem_desc.region_cnt = i; + if (reserved) + pr_info("Reserving %lu bytes SRAM for frame buffer\n", + reserved); + return reserved; } void omapfb_set_ctrl_platform_data(void *data) @@ -104,10 +306,19 @@ static inline int omap_init_fb(void) { const struct omap_lcd_config *conf; + if (config_invalid) + return 0; + if (configured_regions != omapfb_config.mem_desc.region_cnt) { + printk(KERN_ERR "Invalid FB mem configuration entries\n"); + return 0; + } conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf == NULL) + if (conf == NULL) { + if (configured_regions) + /* FB mem config, but no LCD config? */ + printk(KERN_ERR "Missing LCD configuration\n"); return 0; - + } omapfb_config.lcd = *conf; return platform_device_register(&omap_fb_device); @@ -117,7 +328,13 @@ arch_initcall(omap_init_fb); #else -void omapfb_reserve_mem(void) {} +void omapfb_reserve_sdram(void) {} +unsigned long omapfb_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long start_avail, + unsigned long size_avail) {} + #endif diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index 7e5f8877e05..bc46f33aede 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -51,10 +51,14 @@ static unsigned long omap_sram_base; static unsigned long omap_sram_size; static unsigned long omap_sram_ceil; -int omap_fb_sram_plane = -1; -int omap_fb_sram_valid; +extern unsigned long omapfb_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail); -/* Depending on the target RAMFS firewall setup, the public usable amount of +/* + * Depending on the target RAMFS firewall setup, the public usable amount of * SRAM varies. The default accessable size for all device types is 2k. A GP * device allows ARM11 but not other initators for full size. This * functionality seems ok until some nice security API happens. @@ -78,45 +82,6 @@ static int is_sram_locked(void) return 1; /* assume locked with no PPA or security driver */ } -static int get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, - unsigned long *start, int *plane_idx) -{ - const struct omap_fbmem_config *fbmem_conf; - unsigned long size = 0; - int i; - - i = 0; - *start = 0; - *plane_idx = -1; - while ((fbmem_conf = omap_get_nr_config(OMAP_TAG_FBMEM, - struct omap_fbmem_config, i)) != NULL) { - u32 paddr, end; - - paddr = fbmem_conf->start; - end = fbmem_conf->start + fbmem_conf->size; - if (paddr > omap_sram_start && - paddr < omap_sram_start + omap_sram_size) { - if (*plane_idx != -1 || paddr < start_avail || - paddr == end || - end > start_avail + size_avail) { - printk(KERN_ERR "invalid FB SRAM configuration"); - *start = 0; - return -1; - } - *plane_idx = i; - *start = fbmem_conf->start; - size = fbmem_conf->size; - } - i++; - } - - if (*plane_idx >= 0) - pr_info("Reserving %lu bytes SRAM frame buffer " - "for plane %d\n", size, *plane_idx); - - return 0; -} - /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -125,7 +90,7 @@ static int get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, */ void __init omap_detect_sram(void) { - unsigned long fb_sram_start; + unsigned long reserved; if (cpu_is_omap24xx()) { if (is_sram_locked()) { @@ -158,13 +123,11 @@ void __init omap_detect_sram(void) omap_sram_size = 0x4000; } } - if (get_fb_sram_conf(omap_sram_start + SRAM_BOOTLOADER_SZ, - omap_sram_size - SRAM_BOOTLOADER_SZ, - &fb_sram_start, &omap_fb_sram_plane) == 0) - omap_fb_sram_valid = 1; - if (omap_fb_sram_valid && omap_fb_sram_plane >= 0) - omap_sram_size -= omap_sram_start + omap_sram_size - - fb_sram_start; + reserved = omapfb_reserve_sram(omap_sram_start, omap_sram_base, + omap_sram_size, + omap_sram_start + SRAM_BOOTLOADER_SZ, + omap_sram_size - SRAM_BOOTLOADER_SZ); + omap_sram_size -= reserved; omap_sram_ceil = omap_sram_base + omap_sram_size; } diff --git a/include/asm-arm/arch-omap/sram.h b/include/asm-arm/arch-omap/sram.h index cc6161d6398..bb9bb3fd532 100644 --- a/include/asm-arm/arch-omap/sram.h +++ b/include/asm-arm/arch-omap/sram.h @@ -20,9 +20,6 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, u32 mem_type); extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); -extern int omap_fb_sram_plane; -extern int omap_fb_sram_valid; - /* Do not use these */ extern void sram_reprogram_clock(u32 ckctl, u32 dpllctl); extern unsigned long sram_reprogram_clock_sz; -- cgit v1.2.3 From b1465bf7098fa761962f09a6bb2c0e831af85e63 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 6 Mar 2007 03:52:01 -0800 Subject: ARM: OMAP: add SoSSI clock (call propagate_rate for childrens) Clocks with the follow parent rate mode were not updating their children at propagate rate time. Signed-off-by: Imre Deak Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/clock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 3d017b04784..0a603242f36 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -284,6 +284,8 @@ void followparent_recalc(struct clk *clk) return; clk->rate = clk->parent->rate; + if (unlikely(clk->flags & RATE_PROPAGATES)) + propagate_rate(clk); } /* Propagate rate to children */ -- cgit v1.2.3 From e0b50d3c644f10a0022b23fccb58c8003741db52 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 28 Mar 2007 13:09:36 -0700 Subject: ARM: OMAP: partial LED fixes Partial fix for CONFIG_LEDS breakage ... at least allow platforms using the debug-leds support (H4 for now) to build with the generic LED support, and default the LED that would be the timer LED to trigger using the "heartbeat" (timer driven, rate depends on load). Right now only H2 and P2 seem to have working LED support; this at least makes H4 less broken. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/debug-leds.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c index 511d6a50041..9128a80d228 100644 --- a/arch/arm/plat-omap/debug-leds.c +++ b/arch/arm/plat-omap/debug-leds.c @@ -39,12 +39,6 @@ static struct h2p2_dbg_fpga __iomem *fpga; static u16 led_state, hw_led_state; -#ifdef CONFIG_LEDS -#define old_led_api() 1 -#else -#define old_led_api() 0 -#endif - #ifdef CONFIG_LEDS_OMAP_DEBUG #define new_led_api() 1 #else @@ -202,7 +196,8 @@ struct dbg_led { static struct dbg_led dbg_leds[] = { /* REVISIT at least H2 uses different timer & cpu leds... */ #ifndef CONFIG_LEDS_TIMER - { .mask = 1 << 0, .cdev.name = "d4:green", }, /* timer */ + { .mask = 1 << 0, .cdev.name = "d4:green", + .cdev.default_trigger = "heartbeat", }, #endif #ifndef CONFIG_LEDS_CPU { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */ @@ -274,10 +269,10 @@ static int /* __init */ fpga_probe(struct platform_device *pdev) fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE); __raw_writew(~0, &fpga->leds); - if (old_led_api()) { - leds_event = h2p2_dbg_leds_event; - leds_event(led_start); - } +#ifdef CONFIG_LEDS + leds_event = h2p2_dbg_leds_event; + leds_event(led_start); +#endif if (new_led_api()) { newled_init(&pdev->dev); -- cgit v1.2.3 From 25ff0a653067eec56efc730dbed664d5cc77e9f3 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 28 Mar 2007 13:21:20 -0700 Subject: ARM: OMAP: restore CONFIG_GENERIC_TIME Somehow this got lost in a merge ... Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e7baca29f3f..346f62aedbd 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -338,6 +338,7 @@ config ARCH_SA1100 config ARCH_S3C2410 bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443" select GENERIC_GPIO + select GENERIC_TIME help Samsung S3C2410X CPU based systems, such as the Simtec Electronics BAST (), the IPAQ 1940 or -- cgit v1.2.3 From cc150b03ae79b20e4c023f71913c1225f8a19a6b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 28 Mar 2007 16:38:14 -0700 Subject: ARM: OMAP: Fix GCC-reported compile time bug Fix GCC-reported compile time bug which prevents booting when the framebuffer code is disabled. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/plat-omap/fb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 469f580ca05..4493bcff517 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -333,7 +333,10 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart, unsigned long sram_vstart, unsigned long sram_size, unsigned long start_avail, - unsigned long size_avail) {} + unsigned long size_avail) +{ + return 0; +} #endif -- cgit v1.2.3 From a3cf4ea8729a5d448742fd5a0a003827c9f25cb6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 18:55:14 +0900 Subject: sh: Fix stacktrace simplification fallout. When the stacktrace simplification changes went in the function definition on SH got skipped, fix it up so things build again. Signed-off-by: Paul Mundt --- arch/sh/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/kernel/stacktrace.c b/arch/sh/kernel/stacktrace.c index 4bdd2f83535..d41e561be20 100644 --- a/arch/sh/kernel/stacktrace.c +++ b/arch/sh/kernel/stacktrace.c @@ -17,7 +17,7 @@ /* * Save stack-backtrace addresses into a stack_trace buffer. */ -void save_stack_trace(struct stack_trace *trace, struct task_struct *task) +void save_stack_trace(struct stack_trace *trace) { unsigned long *sp = (unsigned long *)current_stack_pointer; -- cgit v1.2.3 From 36200b76008d52d16b170d4f7dae9cfe00f5eb2b Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Thu, 3 May 2007 15:58:49 -0400 Subject: [MTD] Remove unnecessary user space check from mtd.h. Since the header file include/linux/mtd/mtd.h is not exported to user space, remove the user space check and error. Signed-off-by: Robert P. J. Day Signed-off-by: David Woodhouse --- include/linux/mtd/mtd.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 45d482ce839..12a9a18f6e1 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -9,10 +9,6 @@ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ -#ifndef __KERNEL__ -#error This is a kernel header. Perhaps include mtd-user.h instead? -#endif - #include #include #include -- cgit v1.2.3 From 42f209d3c94516affeb5e578fae62925f531a2d9 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 4 May 2007 15:49:38 -0400 Subject: [MTD] Delete allegedly obsolete "bank_size" field of mtd_info. Delete the allegedly obsolete "bank_size" member of struct mtd_info. Signed-off-by: Robert P. J. Day Signed-off-by: David Woodhouse --- drivers/mtd/mtdpart.c | 1 - include/linux/mtd/mtd.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 1af989023c6..9c623685294 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -347,7 +347,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; - slave->mtd.bank_size = master->bank_size; slave->mtd.owner = master->owner; slave->mtd.read = part_read; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 12a9a18f6e1..fd64ccfbce0 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -133,9 +133,6 @@ struct mtd_info { int numeraseregions; struct mtd_erase_region_info *eraseregions; - /* This really shouldn't be here. It can go away in 2.5 */ - u_int32_t bank_size; - int (*erase) (struct mtd_info *mtd, struct erase_info *instr); /* This stuff for eXecute-In-Place */ -- cgit v1.2.3 From b7aa48be1e7a11e36448a7db58931bbf735d2718 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 9 May 2007 13:34:37 +0100 Subject: [MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp) Signed-off-by: David Woodhouse --- drivers/mtd/chips/Kconfig | 40 -- drivers/mtd/chips/Makefile | 4 - drivers/mtd/chips/amd_flash.c | 1396 ----------------------------------------- drivers/mtd/chips/jedec.c | 935 --------------------------- drivers/mtd/chips/sharp.c | 601 ------------------ 5 files changed, 2976 deletions(-) delete mode 100644 drivers/mtd/chips/amd_flash.c delete mode 100644 drivers/mtd/chips/jedec.c delete mode 100644 drivers/mtd/chips/sharp.c diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index d28e0fc85e1..479d32b57a1 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.18 2005/11/07 11:14:22 gleixner Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -231,45 +230,6 @@ config MTD_ABSENT the system regardless of media presence. Device nodes created with this driver will return -ENODEV upon access. -config MTD_OBSOLETE_CHIPS - bool "Older (theoretically obsoleted now) drivers for non-CFI chips" - help - This option does not enable any code directly, but will allow you to - select some other chip drivers which are now considered obsolete, - because the generic CONFIG_JEDECPROBE code above should now detect - the chips which are supported by these drivers, and allow the generic - CFI-compatible drivers to drive the chips. Say 'N' here unless you have - already tried the CONFIG_JEDECPROBE method and reported its failure - to the MTD mailing list at - -config MTD_AMDSTD - tristate "AMD compatible flash chip support (non-CFI)" - depends on MTD_OBSOLETE_CHIPS && BROKEN - help - This option enables support for flash chips using AMD-compatible - commands, including some which are not CFI-compatible and hence - cannot be used with the CONFIG_MTD_CFI_AMDSTD option. - - It also works on AMD compatible chips that do conform to CFI. - -config MTD_SHARP - tristate "pre-CFI Sharp chip support" - depends on MTD_OBSOLETE_CHIPS - help - This option enables support for flash chips using Sharp-compatible - commands, including some which are not CFI-compatible and hence - cannot be used with the CONFIG_MTD_CFI_INTELxxx options. - -config MTD_JEDEC - tristate "JEDEC device support" - depends on MTD_OBSOLETE_CHIPS && BROKEN - help - Enable older JEDEC flash interface devices for self - programming flash. It is commonly used in older AMD chips. It is - only called JEDEC because the JEDEC association - distributes the identification codes for the - chips. - config MTD_XIP bool "XIP aware MTD support" depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL && ARCH_MTD_XIP diff --git a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile index 75bc1c2a0f4..36582412ccd 100644 --- a/drivers/mtd/chips/Makefile +++ b/drivers/mtd/chips/Makefile @@ -1,19 +1,15 @@ # # linux/drivers/chips/Makefile # -# $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $ obj-$(CONFIG_MTD) += chipreg.o -obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o obj-$(CONFIG_MTD_CFI) += cfi_probe.o obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o -obj-$(CONFIG_MTD_JEDEC) += jedec.o obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o obj-$(CONFIG_MTD_RAM) += map_ram.o obj-$(CONFIG_MTD_ROM) += map_rom.o -obj-$(CONFIG_MTD_SHARP) += sharp.o obj-$(CONFIG_MTD_ABSENT) += map_absent.o diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c deleted file mode 100644 index e7999f15d85..00000000000 --- a/drivers/mtd/chips/amd_flash.c +++ /dev/null @@ -1,1396 +0,0 @@ -/* - * MTD map driver for AMD compatible flash chips (non-CFI) - * - * Author: Jonas Holmberg - * - * $Id: amd_flash.c,v 1.28 2005/11/07 11:14:22 gleixner Exp $ - * - * Copyright (c) 2001 Axis Communications AB - * - * This file is under GPL. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* There's no limit. It exists only to avoid realloc. */ -#define MAX_AMD_CHIPS 8 - -#define DEVICE_TYPE_X8 (8 / 8) -#define DEVICE_TYPE_X16 (16 / 8) -#define DEVICE_TYPE_X32 (32 / 8) - -/* Addresses */ -#define ADDR_MANUFACTURER 0x0000 -#define ADDR_DEVICE_ID 0x0001 -#define ADDR_SECTOR_LOCK 0x0002 -#define ADDR_HANDSHAKE 0x0003 -#define ADDR_UNLOCK_1 0x0555 -#define ADDR_UNLOCK_2 0x02AA - -/* Commands */ -#define CMD_UNLOCK_DATA_1 0x00AA -#define CMD_UNLOCK_DATA_2 0x0055 -#define CMD_MANUFACTURER_UNLOCK_DATA 0x0090 -#define CMD_UNLOCK_BYPASS_MODE 0x0020 -#define CMD_PROGRAM_UNLOCK_DATA 0x00A0 -#define CMD_RESET_DATA 0x00F0 -#define CMD_SECTOR_ERASE_UNLOCK_DATA 0x0080 -#define CMD_SECTOR_ERASE_UNLOCK_DATA_2 0x0030 - -#define CMD_UNLOCK_SECTOR 0x0060 - -/* Manufacturers */ -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001F -#define MANUFACTURER_FUJITSU 0x0004 -#define MANUFACTURER_ST 0x0020 -#define MANUFACTURER_SST 0x00BF -#define MANUFACTURER_TOSHIBA 0x0098 - -/* AMD */ -#define AM29F800BB 0x2258 -#define AM29F800BT 0x22D6 -#define AM29LV800BB 0x225B -#define AM29LV800BT 0x22DA -#define AM29LV160DT 0x22C4 -#define AM29LV160DB 0x2249 -#define AM29BDS323D 0x22D1 - -/* Atmel */ -#define AT49xV16x 0x00C0 -#define AT49xV16xT 0x00C2 - -/* Fujitsu */ -#define MBM29LV160TE 0x22C4 -#define MBM29LV160BE 0x2249 -#define MBM29LV800BB 0x225B - -/* ST - www.st.com */ -#define M29W800T 0x00D7 -#define M29W160DT 0x22C4 -#define M29W160DB 0x2249 - -/* SST */ -#define SST39LF800 0x2781 -#define SST39LF160 0x2782 - -/* Toshiba */ -#define TC58FVT160 0x00C2 -#define TC58FVB160 0x0043 - -#define D6_MASK 0x40 - -struct amd_flash_private { - int device_type; - int interleave; - int numchips; - unsigned long chipshift; - struct flchip chips[0]; -}; - -struct amd_flash_info { - const __u16 mfr_id; - const __u16 dev_id; - const char *name; - const u_long size; - const int numeraseregions; - const struct mtd_erase_region_info regions[4]; -}; - - - -static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *, - u_char *); -static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *, - const u_char *); -static int amd_flash_erase(struct mtd_info *, struct erase_info *); -static void amd_flash_sync(struct mtd_info *); -static int amd_flash_suspend(struct mtd_info *); -static void amd_flash_resume(struct mtd_info *); -static void amd_flash_destroy(struct mtd_info *); -static struct mtd_info *amd_flash_probe(struct map_info *map); - - -static struct mtd_chip_driver amd_flash_chipdrv = { - .probe = amd_flash_probe, - .destroy = amd_flash_destroy, - .name = "amd_flash", - .module = THIS_MODULE -}; - -static inline __u32 wide_read(struct map_info *map, __u32 addr) -{ - if (map->buswidth == 1) { - return map_read8(map, addr); - } else if (map->buswidth == 2) { - return map_read16(map, addr); - } else if (map->buswidth == 4) { - return map_read32(map, addr); - } - - return 0; -} - -static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) -{ - if (map->buswidth == 1) { - map_write8(map, val, addr); - } else if (map->buswidth == 2) { - map_write16(map, val, addr); - } else if (map->buswidth == 4) { - map_write32(map, val, addr); - } -} - -static inline __u32 make_cmd(struct map_info *map, __u32 cmd) -{ - const struct amd_flash_private *private = map->fldrv_priv; - if ((private->interleave == 2) && - (private->device_type == DEVICE_TYPE_X16)) { - cmd |= (cmd << 16); - } - - return cmd; -} - -static inline void send_unlock(struct map_info *map, unsigned long base) -{ - wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1, - base + (map->buswidth * ADDR_UNLOCK_1)); - wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2, - base + (map->buswidth * ADDR_UNLOCK_2)); -} - -static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd) -{ - send_unlock(map, base); - wide_write(map, make_cmd(map, cmd), - base + (map->buswidth * ADDR_UNLOCK_1)); -} - -static inline void send_cmd_to_addr(struct map_info *map, unsigned long base, - __u32 cmd, unsigned long addr) -{ - send_unlock(map, base); - wide_write(map, make_cmd(map, cmd), addr); -} - -static inline int flash_is_busy(struct map_info *map, unsigned long addr, - int interleave) -{ - - if ((interleave == 2) && (map->buswidth == 4)) { - __u32 read1, read2; - - read1 = wide_read(map, addr); - read2 = wide_read(map, addr); - - return (((read1 >> 16) & D6_MASK) != - ((read2 >> 16) & D6_MASK)) || - (((read1 & 0xffff) & D6_MASK) != - ((read2 & 0xffff) & D6_MASK)); - } - - return ((wide_read(map, addr) & D6_MASK) != - (wide_read(map, addr) & D6_MASK)); -} - -static inline void unlock_sector(struct map_info *map, unsigned long sect_addr, - int unlock) -{ - /* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */ - int SLA = unlock ? - (sect_addr | (0x40 * map->buswidth)) : - (sect_addr & ~(0x40 * map->buswidth)) ; - - __u32 cmd = make_cmd(map, CMD_UNLOCK_SECTOR); - - wide_write(map, make_cmd(map, CMD_RESET_DATA), 0); - wide_write(map, cmd, SLA); /* 1st cycle: write cmd to any address */ - wide_write(map, cmd, SLA); /* 2nd cycle: write cmd to any address */ - wide_write(map, cmd, SLA); /* 3rd cycle: write cmd to SLA */ -} - -static inline int is_sector_locked(struct map_info *map, - unsigned long sect_addr) -{ - int status; - - wide_write(map, CMD_RESET_DATA, 0); - send_cmd(map, sect_addr, CMD_MANUFACTURER_UNLOCK_DATA); - - /* status is 0x0000 for unlocked and 0x0001 for locked */ - status = wide_read(map, sect_addr + (map->buswidth * ADDR_SECTOR_LOCK)); - wide_write(map, CMD_RESET_DATA, 0); - return status; -} - -static int amd_flash_do_unlock(struct mtd_info *mtd, loff_t ofs, size_t len, - int is_unlock) -{ - struct map_info *map; - struct mtd_erase_region_info *merip; - int eraseoffset, erasesize, eraseblocks; - int i; - int retval = 0; - int lock_status; - - map = mtd->priv; - - /* Pass the whole chip through sector by sector and check for each - sector if the sector and the given interval overlap */ - for(i = 0; i < mtd->numeraseregions; i++) { - merip = &mtd->eraseregions[i]; - - eraseoffset = merip->offset; - erasesize = merip->erasesize; - eraseblocks = merip->numblocks; - - if (ofs > eraseoffset + erasesize) - continue; - - while (eraseblocks > 0) { - if (ofs < eraseoffset + erasesize && ofs + len > eraseoffset) { - unlock_sector(map, eraseoffset, is_unlock); - - lock_status = is_sector_locked(map, eraseoffset); - - if (is_unlock && lock_status) { - printk("Cannot unlock sector at address %x length %xx\n", - eraseoffset, merip->erasesize); - retval = -1; - } else if (!is_unlock && !lock_status) { - printk("Cannot lock sector at address %x length %x\n", - eraseoffset, merip->erasesize); - retval = -1; - } - } - eraseoffset += erasesize; - eraseblocks --; - } - } - return retval; -} - -static int amd_flash_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return amd_flash_do_unlock(mtd, ofs, len, 1); -} - -static int amd_flash_lock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return amd_flash_do_unlock(mtd, ofs, len, 0); -} - - -/* - * Reads JEDEC manufacturer ID and device ID and returns the index of the first - * matching table entry (-1 if not found or alias for already found chip). - */ -static int probe_new_chip(struct mtd_info *mtd, __u32 base, - struct flchip *chips, - struct amd_flash_private *private, - const struct amd_flash_info *table, int table_size) -{ - __u32 mfr_id; - __u32 dev_id; - struct map_info *map = mtd->priv; - struct amd_flash_private temp; - int i; - - temp.device_type = DEVICE_TYPE_X16; // Assume X16 (FIXME) - temp.interleave = 2; - map->fldrv_priv = &temp; - - /* Enter autoselect mode. */ - send_cmd(map, base, CMD_RESET_DATA); - send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA); - - mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER)); - dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID)); - - if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) && - ((dev_id >> 16) == (dev_id & 0xffff))) { - mfr_id &= 0xffff; - dev_id &= 0xffff; - } else { - temp.interleave = 1; - } - - for (i = 0; i < table_size; i++) { - if ((mfr_id == table[i].mfr_id) && - (dev_id == table[i].dev_id)) { - if (chips) { - int j; - - /* Is this an alias for an already found chip? - * In that case that chip should be in - * autoselect mode now. - */ - for (j = 0; j < private->numchips; j++) { - __u32 mfr_id_other; - __u32 dev_id_other; - - mfr_id_other = - wide_read(map, chips[j].start + - (map->buswidth * - ADDR_MANUFACTURER - )); - dev_id_other = - wide_read(map, chips[j].start + - (map->buswidth * - ADDR_DEVICE_ID)); - if (temp.interleave == 2) { - mfr_id_other &= 0xffff; - dev_id_other &= 0xffff; - } - if ((mfr_id_other == mfr_id) && - (dev_id_other == dev_id)) { - - /* Exit autoselect mode. */ - send_cmd(map, base, - CMD_RESET_DATA); - - return -1; - } - } - - if (private->numchips == MAX_AMD_CHIPS) { - printk(KERN_WARNING - "%s: Too many flash chips " - "detected. Increase " - "MAX_AMD_CHIPS from %d.\n", - map->name, MAX_AMD_CHIPS); - - return -1; - } - - chips[private->numchips].start = base; - chips[private->numchips].state = FL_READY; - chips[private->numchips].mutex = - &chips[private->numchips]._spinlock; - private->numchips++; - } - - printk("%s: Found %d x %ldMiB %s at 0x%x\n", map->name, - temp.interleave, (table[i].size)/(1024*1024), - table[i].name, base); - - mtd->size += table[i].size * temp.interleave; - mtd->numeraseregions += table[i].numeraseregions; - - break; - } - } - - /* Exit autoselect mode. */ - send_cmd(map, base, CMD_RESET_DATA); - - if (i == table_size) { - printk(KERN_DEBUG "%s: unknown flash device at 0x%x, " - "mfr id 0x%x, dev id 0x%x\n", map->name, - base, mfr_id, dev_id); - map->fldrv_priv = NULL; - - return -1; - } - - private->device_type = temp.device_type; - private->interleave = temp.interleave; - - return i; -} - - - -static struct mtd_info *amd_flash_probe(struct map_info *map) -{ - static const struct amd_flash_info table[] = { - { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV160DT, - .name = "AMD AM29LV160DT", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV160DB, - .name = "AMD AM29LV160DB", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } - } - }, { - .mfr_id = MANUFACTURER_TOSHIBA, - .dev_id = TC58FVT160, - .name = "Toshiba TC58FVT160", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_FUJITSU, - .dev_id = MBM29LV160TE, - .name = "Fujitsu MBM29LV160TE", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_TOSHIBA, - .dev_id = TC58FVB160, - .name = "Toshiba TC58FVB160", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } - } - }, { - .mfr_id = MANUFACTURER_FUJITSU, - .dev_id = MBM29LV160BE, - .name = "Fujitsu MBM29LV160BE", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BB, - .name = "AMD AM29LV800BB", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29F800BB, - .name = "AMD AM29F800BB", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BT, - .name = "AMD AM29LV800BT", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29F800BT, - .name = "AMD AM29F800BT", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BB, - .name = "AMD AM29LV800BB", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_FUJITSU, - .dev_id = MBM29LV800BB, - .name = "Fujitsu MBM29LV800BB", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } - } - }, { - .mfr_id = MANUFACTURER_ST, - .dev_id = M29W800T, - .name = "ST M29W800T", - .size = 0x00100000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_ST, - .dev_id = M29W160DT, - .name = "ST M29W160DT", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } - } - }, { - .mfr_id = MANUFACTURER_ST, - .dev_id = M29W160DB, - .name = "ST M29W160DB", - .size = 0x00200000, - .numeraseregions = 4, - .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29BDS323D, - .name = "AMD AM29BDS323D", - .size = 0x00400000, - .numeraseregions = 3, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, - { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, - } - }, { - .mfr_id = MANUFACTURER_ATMEL, - .dev_id = AT49xV16x, - .name = "Atmel AT49xV16x", - .size = 0x00200000, - .numeraseregions = 2, - .regions = { - { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, - { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } - } - }, { - .mfr_id = MANUFACTURER_ATMEL, - .dev_id = AT49xV16xT, - .name = "Atmel AT49xV16xT", - .size = 0x00200000, - .numeraseregions = 2, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } - } - } - }; - - struct mtd_info *mtd; - struct flchip chips[MAX_AMD_CHIPS]; - int table_pos[MAX_AMD_CHIPS]; - struct amd_flash_private temp; - struct amd_flash_private *private; - u_long size; - unsigned long base; - int i; - int reg_idx; - int offset; - - mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) { - printk(KERN_WARNING - "%s: kmalloc failed for info structure\n", map->name); - return NULL; - } - mtd->priv = map; - - memset(&temp, 0, sizeof(temp)); - - printk("%s: Probing for AMD compatible flash...\n", map->name); - - if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table, - ARRAY_SIZE(table))) - == -1) { - printk(KERN_WARNING - "%s: Found no AMD compatible device at location zero\n", - map->name); - kfree(mtd); - - return NULL; - } - - chips[0].start = 0; - chips[0].state = FL_READY; - chips[0].mutex = &chips[0]._spinlock; - temp.numchips = 1; - for (size = mtd->size; size > 1; size >>= 1) { - temp.chipshift++; - } - switch (temp.interleave) { - case 2: - temp.chipshift += 1; - break; - case 4: - temp.chipshift += 2; - break; - } - - /* Find out if there are any more chips in the map. */ - for (base = (1 << temp.chipshift); - base < map->size; - base += (1 << temp.chipshift)) { - int numchips = temp.numchips; - table_pos[numchips] = probe_new_chip(mtd, base, chips, - &temp, table, ARRAY_SIZE(table)); - } - - mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * - mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { - printk(KERN_WARNING "%s: Failed to allocate " - "memory for MTD erase region info\n", map->name); - kfree(mtd); - map->fldrv_priv = NULL; - return NULL; - } - - reg_idx = 0; - offset = 0; - for (i = 0; i < temp.numchips; i++) { - int dev_size; - int j; - - dev_size = 0; - for (j = 0; j < table[table_pos[i]].numeraseregions; j++) { - mtd->eraseregions[reg_idx].offset = offset + - (table[table_pos[i]].regions[j].offset * - temp.interleave); - mtd->eraseregions[reg_idx].erasesize = - table[table_pos[i]].regions[j].erasesize * - temp.interleave; - mtd->eraseregions[reg_idx].numblocks = - table[table_pos[i]].regions[j].numblocks; - if (mtd->erasesize < - mtd->eraseregions[reg_idx].erasesize) { - mtd->erasesize = - mtd->eraseregions[reg_idx].erasesize; - } - dev_size += mtd->eraseregions[reg_idx].erasesize * - mtd->eraseregions[reg_idx].numblocks; - reg_idx++; - } - offset += dev_size; - } - mtd->type = MTD_NORFLASH; - mtd->writesize = 1; - mtd->flags = MTD_CAP_NORFLASH; - mtd->name = map->name; - mtd->erase = amd_flash_erase; - mtd->read = amd_flash_read; - mtd->write = amd_flash_write; - mtd->sync = amd_flash_sync; - mtd->suspend = amd_flash_suspend; - mtd->resume = amd_flash_resume; - mtd->lock = amd_flash_lock; - mtd->unlock = amd_flash_unlock; - - private = kmalloc(sizeof(*private) + (sizeof(struct flchip) * - temp.numchips), GFP_KERNEL); - if (!private) { - printk(KERN_WARNING - "%s: kmalloc failed for private structure\n", map->name); - kfree(mtd); - map->fldrv_priv = NULL; - return NULL; - } - memcpy(private, &temp, sizeof(temp)); - memcpy(private->chips, chips, - sizeof(struct flchip) * private->numchips); - for (i = 0; i < private->numchips; i++) { - init_waitqueue_head(&private->chips[i].wq); - spin_lock_init(&private->chips[i]._spinlock); - } - - map->fldrv_priv = private; - - map->fldrv = &amd_flash_chipdrv; - - __module_get(THIS_MODULE); - return mtd; -} - - - -static inline int read_one_chip(struct map_info *map, struct flchip *chip, - loff_t adr, size_t len, u_char *buf) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long timeo = jiffies + HZ; - -retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ - printk(KERN_INFO "%s: waiting for chip to read, state = %d\n", - map->name, chip->state); - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) { - return -EINTR; - } - - timeo = jiffies + HZ; - - goto retry; - } - - adr += chip->start; - - chip->state = FL_READY; - - map_copy_from(map, buf, adr, len); - - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return 0; -} - - - -static int amd_flash_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct amd_flash_private *private = map->fldrv_priv; - unsigned long ofs; - int chipnum; - int ret = 0; - - if ((from + len) > mtd->size) { - printk(KERN_WARNING "%s: read request past end of device " - "(0x%lx)\n", map->name, (unsigned long)from + len); - - return -EINVAL; - } - - /* Offset within the first chip that the first read should start. */ - chipnum = (from >> private->chipshift); - ofs = from - (chipnum << private->chipshift); - - *retlen = 0; - - while (len) { - unsigned long this_len; - - if (chipnum >= private->numchips) { - break; - } - - if ((len + ofs - 1) >> private->chipshift) { - this_len = (1 << private->chipshift) - ofs; - } else { - this_len = len; - } - - ret = read_one_chip(map, &private->chips[chipnum], ofs, - this_len, buf); - if (ret) { - break; - } - - *retlen += this_len; - len -= this_len; - buf += this_len; - - ofs = 0; - chipnum++; - } - - return ret; -} - - - -static int write_one_word(struct map_info *map, struct flchip *chip, - unsigned long adr, __u32 datum) -{ - unsigned long timeo = jiffies + HZ; - struct amd_flash_private *private = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); - int ret = 0; - int times_left; - -retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ - printk("%s: waiting for chip to write, state = %d\n", - map->name, chip->state); - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - printk(KERN_INFO "%s: woke up to write\n", map->name); - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - chip->state = FL_WRITING; - - adr += chip->start; - ENABLE_VPP(map); - send_cmd(map, chip->start, CMD_PROGRAM_UNLOCK_DATA); - wide_write(map, datum, adr); - - times_left = 500000; - while (times_left-- && flash_is_busy(map, adr, private->interleave)) { - if (need_resched()) { - spin_unlock_bh(chip->mutex); - schedule(); - spin_lock_bh(chip->mutex); - } - } - - if (!times_left) { - printk(KERN_WARNING "%s: write to 0x%lx timed out!\n", - map->name, adr); - ret = -EIO; - } else { - __u32 verify; - if ((verify = wide_read(map, adr)) != datum) { - printk(KERN_WARNING "%s: write to 0x%lx failed. " - "datum = %x, verify = %x\n", - map->name, adr, datum, verify); - ret = -EIO; - } - } - - DISABLE_VPP(map); - chip->state = FL_READY; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return ret; -} - - - -static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len, - size_t *retlen, const u_char *buf) -{ - struct map_info *map = mtd->priv; - struct amd_flash_private *private = map->fldrv_priv; - int ret = 0; - int chipnum; - unsigned long ofs; - unsigned long chipstart; - - *retlen = 0; - if (!len) { - return 0; - } - - chipnum = to >> private->chipshift; - ofs = to - (chipnum << private->chipshift); - chipstart = private->chips[chipnum].start; - - /* If it's not bus-aligned, do the first byte write. */ - if (ofs & (map->buswidth - 1)) { - unsigned long bus_ofs = ofs & ~(map->buswidth - 1); - int i = ofs - bus_ofs; - int n = 0; - u_char tmp_buf[4]; - __u32 datum; - - map_copy_from(map, tmp_buf, - bus_ofs + private->chips[chipnum].start, - map->buswidth); - while (len && i < map->buswidth) - tmp_buf[i++] = buf[n++], len--; - - if (map->buswidth == 2) { - datum = *(__u16*)tmp_buf; - } else if (map->buswidth == 4) { - datum = *(__u32*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } - - ret = write_one_word(map, &private->chips[chipnum], bus_ofs, - datum); - if (ret) { - return ret; - } - - ofs += n; - buf += n; - (*retlen) += n; - - if (ofs >> private->chipshift) { - chipnum++; - ofs = 0; - if (chipnum == private->numchips) { - return 0; - } - } - } - - /* We are now aligned, write as much as possible. */ - while(len >= map->buswidth) { - __u32 datum; - - if (map->buswidth == 1) { - datum = *(__u8*)buf; - } else if (map->buswidth == 2) { - datum = *(__u16*)buf; - } else if (map->buswidth == 4) { - datum = *(__u32*)buf; - } else { - return -EINVAL; - } - - ret = write_one_word(map, &private->chips[chipnum], ofs, datum); - - if (ret) { - return ret; - } - - ofs += map->buswidth; - buf += map->buswidth; - (*retlen) += map->buswidth; - len -= map->buswidth; - - if (ofs >> private->chipshift) { - chipnum++; - ofs = 0; - if (chipnum == private->numchips) { - return 0; - } - chipstart = private->chips[chipnum].start; - } - } - - if (len & (map->buswidth - 1)) { - int i = 0, n = 0; - u_char tmp_buf[2]; - __u32 datum; - - map_copy_from(map, tmp_buf, - ofs + private->chips[chipnum].start, - map->buswidth); - while (len--) { - tmp_buf[i++] = buf[n++]; - } - - if (map->buswidth == 2) { - datum = *(__u16*)tmp_buf; - } else if (map->buswidth == 4) { - datum = *(__u32*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } - - ret = write_one_word(map, &private->chips[chipnum], ofs, datum); - - if (ret) { - return ret; - } - - (*retlen) += n; - } - - return 0; -} - - - -static inline int erase_one_block(struct map_info *map, struct flchip *chip, - unsigned long adr, u_long size) -{ - unsigned long timeo = jiffies + HZ; - struct amd_flash_private *private = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); - -retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if (signal_pending(current)) { - return -EINTR; - } - - timeo = jiffies + HZ; - - goto retry; - } - - chip->state = FL_ERASING; - - adr += chip->start; - ENABLE_VPP(map); - send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA); - send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr); - - timeo = jiffies + (HZ * 20); - - spin_unlock_bh(chip->mutex); - msleep(1000); - spin_lock_bh(chip->mutex); - - while (flash_is_busy(map, adr, private->interleave)) { - - if (chip->state != FL_ERASING) { - /* Someone's suspended the erase. Sleep */ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - printk(KERN_INFO "%s: erase suspended. Sleeping\n", - map->name); - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if (signal_pending(current)) { - return -EINTR; - } - - timeo = jiffies + (HZ*2); /* FIXME */ - spin_lock_bh(chip->mutex); - continue; - } - - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - chip->state = FL_READY; - spin_unlock_bh(chip->mutex); - printk(KERN_WARNING "%s: waiting for erase to complete " - "timed out.\n", map->name); - DISABLE_VPP(map); - - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - if (need_resched()) - schedule(); - else - udelay(1); - - spin_lock_bh(chip->mutex); - } - - /* Verify every single word */ - { - int address; - int error = 0; - __u8 verify; - - for (address = adr; address < (adr + size); address++) { - if ((verify = map_read8(map, address)) != 0xFF) { - error = 1; - break; - } - } - if (error) { - chip->state = FL_READY; - spin_unlock_bh(chip->mutex); - printk(KERN_WARNING - "%s: verify error at 0x%x, size %ld.\n", - map->name, address, size); - DISABLE_VPP(map); - - return -EIO; - } - } - - DISABLE_VPP(map); - chip->state = FL_READY; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return 0; -} - - - -static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct map_info *map = mtd->priv; - struct amd_flash_private *private = map->fldrv_priv; - unsigned long adr, len; - int chipnum; - int ret = 0; - int i; - int first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (instr->addr > mtd->size) { - return -EINVAL; - } - - if ((instr->len + instr->addr) > mtd->size) { - return -EINVAL; - } - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while ((i < mtd->numeraseregions) && - (instr->addr >= regions[i].offset)) { - i++; - } - i--; - - /* OK, now i is pointing at the erase region in which this - * erase request starts. Check the start of the requested - * erase range is aligned with the erase size which is in - * effect here. - */ - - if (instr->addr & (regions[i].erasesize-1)) { - return -EINVAL; - } - - /* Remember the erase region we start on. */ - - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while ((i < mtd->numeraseregions) && - ((instr->addr + instr->len) >= regions[i].offset)) { - i++; - } - - /* As before, drop back one to point at the region in which - * the address actually falls. - */ - - i--; - - if ((instr->addr + instr->len) & (regions[i].erasesize-1)) { - return -EINVAL; - } - - chipnum = instr->addr >> private->chipshift; - adr = instr->addr - (chipnum << private->chipshift); - len = instr->len; - - i = first; - - while (len) { - ret = erase_one_block(map, &private->chips[chipnum], adr, - regions[i].erasesize); - - if (ret) { - return ret; - } - - adr += regions[i].erasesize; - len -= regions[i].erasesize; - - if ((adr % (1 << private->chipshift)) == - ((regions[i].offset + (regions[i].erasesize * - regions[i].numblocks)) - % (1 << private->chipshift))) { - i++; - } - - if (adr >> private->chipshift) { - adr = 0; - chipnum++; - if (chipnum >= private->numchips) { - break; - } - } - } - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; -} - - - -static void amd_flash_sync(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct amd_flash_private *private = map->fldrv_priv; - int i; - struct flchip *chip; - int ret = 0; - DECLARE_WAITQUEUE(wait, current); - - for (i = 0; !ret && (i < private->numchips); i++) { - chip = &private->chips[i]; - - retry: - spin_lock_bh(chip->mutex); - - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_SYNCING; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - case FL_SYNCING: - spin_unlock_bh(chip->mutex); - break; - - default: - /* Not an idle state */ - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - - remove_wait_queue(&chip->wq, &wait); - - goto retry; - } - } - - /* Unlock the chips again */ - for (i--; i >= 0; i--) { - chip = &private->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_SYNCING) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - spin_unlock_bh(chip->mutex); - } -} - - - -static int amd_flash_suspend(struct mtd_info *mtd) -{ -printk("amd_flash_suspend(): not implemented!\n"); - return -EINVAL; -} - - - -static void amd_flash_resume(struct mtd_info *mtd) -{ -printk("amd_flash_resume(): not implemented!\n"); -} - - - -static void amd_flash_destroy(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct amd_flash_private *private = map->fldrv_priv; - kfree(private); -} - -int __init amd_flash_init(void) -{ - register_mtd_chip_driver(&amd_flash_chipdrv); - return 0; -} - -void __exit amd_flash_exit(void) -{ - unregister_mtd_chip_driver(&amd_flash_chipdrv); -} - -module_init(amd_flash_init); -module_exit(amd_flash_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jonas Holmberg "); -MODULE_DESCRIPTION("Old MTD chip driver for AMD flash chips"); diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c deleted file mode 100644 index 14e57b2bf84..00000000000 --- a/drivers/mtd/chips/jedec.c +++ /dev/null @@ -1,935 +0,0 @@ - -/* JEDEC Flash Interface. - * This is an older type of interface for self programming flash. It is - * commonly use in older AMD chips and is obsolete compared with CFI. - * It is called JEDEC because the JEDEC association distributes the ID codes - * for the chips. - * - * See the AMD flash databook for information on how to operate the interface. - * - * This code does not support anything wider than 8 bit flash chips, I am - * not going to guess how to send commands to them, plus I expect they will - * all speak CFI.. - * - * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct mtd_info *jedec_probe(struct map_info *); -static int jedec_probe8(struct map_info *map,unsigned long base, - struct jedec_private *priv); -static int jedec_probe16(struct map_info *map,unsigned long base, - struct jedec_private *priv); -static int jedec_probe32(struct map_info *map,unsigned long base, - struct jedec_private *priv); -static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start, - unsigned long len); -static int flash_erase(struct mtd_info *mtd, struct erase_info *instr); -static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, - size_t *retlen, const u_char *buf); - -static unsigned long my_bank_size; - -/* Listing of parts and sizes. We need this table to learn the sector - size of the chip and the total length */ -static const struct JEDECTable JEDEC_table[] = { - { - .jedec = 0x013D, - .name = "AMD Am29F017D", - .size = 2*1024*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { - .jedec = 0x01AD, - .name = "AMD Am29F016", - .size = 2*1024*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { - .jedec = 0x01D5, - .name = "AMD Am29F080", - .size = 1*1024*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { - .jedec = 0x01A4, - .name = "AMD Am29F040", - .size = 512*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { - .jedec = 0x20E3, - .name = "AMD Am29W040B", - .size = 512*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { - .jedec = 0xC2AD, - .name = "Macronix MX29F016", - .size = 2*1024*1024, - .sectorsize = 64*1024, - .capabilities = MTD_CAP_NORFLASH - }, - { .jedec = 0x0 } -}; - -static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id); -static void jedec_sync(struct mtd_info *mtd) {}; -static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); -static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); - -static struct mtd_info *jedec_probe(struct map_info *map); - - - -static struct mtd_chip_driver jedec_chipdrv = { - .probe = jedec_probe, - .name = "jedec", - .module = THIS_MODULE -}; - -/* Probe entry point */ - -static struct mtd_info *jedec_probe(struct map_info *map) -{ - struct mtd_info *MTD; - struct jedec_private *priv; - unsigned long Base; - unsigned long SectorSize; - unsigned count; - unsigned I,Uniq; - char Part[200]; - memset(&priv,0,sizeof(priv)); - - MTD = kzalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL); - if (!MTD) - return NULL; - - priv = (struct jedec_private *)&MTD[1]; - - my_bank_size = map->size; - - if (map->size/my_bank_size > MAX_JEDEC_CHIPS) - { - printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n"); - kfree(MTD); - return NULL; - } - - for (Base = 0; Base < map->size; Base += my_bank_size) - { - // Perhaps zero could designate all tests? - if (map->buswidth == 0) - map->buswidth = 1; - - if (map->buswidth == 1){ - if (jedec_probe8(map,Base,priv) == 0) { - printk("did recognize jedec chip\n"); - kfree(MTD); - return NULL; - } - } - if (map->buswidth == 2) - jedec_probe16(map,Base,priv); - if (map->buswidth == 4) - jedec_probe32(map,Base,priv); - } - - // Get the biggest sector size - SectorSize = 0; - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - // printk("priv->chips[%d].jedec is %x\n",I,priv->chips[I].jedec); - // printk("priv->chips[%d].sectorsize is %lx\n",I,priv->chips[I].sectorsize); - if (priv->chips[I].sectorsize > SectorSize) - SectorSize = priv->chips[I].sectorsize; - } - - // Quickly ensure that the other sector sizes are factors of the largest - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - if ((SectorSize/priv->chips[I].sectorsize)*priv->chips[I].sectorsize != SectorSize) - { - printk("mtd: Failed. Device has incompatible mixed sector sizes\n"); - kfree(MTD); - return NULL; - } - } - - /* Generate a part name that includes the number of different chips and - other configuration information */ - count = 1; - strlcpy(Part,map->name,sizeof(Part)-10); - strcat(Part," "); - Uniq = 0; - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - const struct JEDECTable *JEDEC; - - if (priv->chips[I+1].jedec == priv->chips[I].jedec) - { - count++; - continue; - } - - // Locate the chip in the jedec table - JEDEC = jedec_idtoinf(priv->chips[I].jedec >> 8,priv->chips[I].jedec); - if (JEDEC == 0) - { - printk("mtd: Internal Error, JEDEC not set\n"); - kfree(MTD); - return NULL; - } - - if (Uniq != 0) - strcat(Part,","); - Uniq++; - - if (count != 1) - sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name); - else - sprintf(Part+strlen(Part),"%s",JEDEC->name); - if (strlen(Part) > sizeof(Part)*2/3) - break; - count = 1; - } - - /* Determine if the chips are organized in a linear fashion, or if there - are empty banks. Note, the last bank does not count here, only the - first banks are important. Holes on non-bank boundaries can not exist - due to the way the detection algorithm works. */ - if (priv->size < my_bank_size) - my_bank_size = priv->size; - priv->is_banked = 0; - //printk("priv->size is %x, my_bank_size is %x\n",priv->size,my_bank_size); - //printk("priv->bank_fill[0] is %x\n",priv->bank_fill[0]); - if (!priv->size) { - printk("priv->size is zero\n"); - kfree(MTD); - return NULL; - } - if (priv->size/my_bank_size) { - if (priv->size/my_bank_size == 1) { - priv->size = my_bank_size; - } - else { - for (I = 0; I != priv->size/my_bank_size - 1; I++) - { - if (priv->bank_fill[I] != my_bank_size) - priv->is_banked = 1; - - /* This even could be eliminated, but new de-optimized read/write - functions have to be written */ - printk("priv->bank_fill[%d] is %lx, priv->bank_fill[0] is %lx\n",I,priv->bank_fill[I],priv->bank_fill[0]); - if (priv->bank_fill[I] != priv->bank_fill[0]) - { - printk("mtd: Failed. Cannot handle unsymmetric banking\n"); - kfree(MTD); - return NULL; - } - } - } - } - if (priv->is_banked == 1) - strcat(Part,", banked"); - - // printk("Part: '%s'\n",Part); - - memset(MTD,0,sizeof(*MTD)); - // strlcpy(MTD->name,Part,sizeof(MTD->name)); - MTD->name = map->name; - MTD->type = MTD_NORFLASH; - MTD->flags = MTD_CAP_NORFLASH; - MTD->writesize = 1; - MTD->erasesize = SectorSize*(map->buswidth); - // printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize); - MTD->size = priv->size; - // printk("MTD->size is %x\n",(unsigned int)MTD->size); - //MTD->module = THIS_MODULE; // ? Maybe this should be the low level module? - MTD->erase = flash_erase; - if (priv->is_banked == 1) - MTD->read = jedec_read_banked; - else - MTD->read = jedec_read; - MTD->write = flash_write; - MTD->sync = jedec_sync; - MTD->priv = map; - map->fldrv_priv = priv; - map->fldrv = &jedec_chipdrv; - __module_get(THIS_MODULE); - return MTD; -} - -/* Helper for the JEDEC function, JEDEC numbers all have odd parity */ -static int checkparity(u_char C) -{ - u_char parity = 0; - while (C != 0) - { - parity ^= C & 1; - C >>= 1; - } - - return parity == 1; -} - - -/* Take an array of JEDEC numbers that represent interleved flash chips - and process them. Check to make sure they are good JEDEC numbers, look - them up and then add them to the chip list */ -static int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count, - unsigned long base,struct jedec_private *priv) -{ - unsigned I,J; - unsigned long Size; - unsigned long SectorSize; - const struct JEDECTable *JEDEC; - - // Test #2 JEDEC numbers exhibit odd parity - for (I = 0; I != Count; I++) - { - if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0) - return 0; - } - - // Finally, just make sure all the chip sizes are the same - JEDEC = jedec_idtoinf(Mfg[0],Id[0]); - - if (JEDEC == 0) - { - printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]); - return 0; - } - - Size = JEDEC->size; - SectorSize = JEDEC->sectorsize; - for (I = 0; I != Count; I++) - { - JEDEC = jedec_idtoinf(Mfg[0],Id[0]); - if (JEDEC == 0) - { - printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]); - return 0; - } - - if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize) - { - printk("mtd: Failed. Interleved flash does not have matching characteristics\n"); - return 0; - } - } - - // Load the Chips - for (I = 0; I != MAX_JEDEC_CHIPS; I++) - { - if (priv->chips[I].jedec == 0) - break; - } - - if (I + Count > MAX_JEDEC_CHIPS) - { - printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n"); - return 0; - } - - // Add them to the table - for (J = 0; J != Count; J++) - { - unsigned long Bank; - - JEDEC = jedec_idtoinf(Mfg[J],Id[J]); - priv->chips[I].jedec = (Mfg[J] << 8) | Id[J]; - priv->chips[I].size = JEDEC->size; - priv->chips[I].sectorsize = JEDEC->sectorsize; - priv->chips[I].base = base + J; - priv->chips[I].datashift = J*8; - priv->chips[I].capabilities = JEDEC->capabilities; - priv->chips[I].offset = priv->size + J; - - // log2 n :| - priv->chips[I].addrshift = 0; - for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++); - - // Determine how filled this bank is. - Bank = base & (~(my_bank_size-1)); - if (priv->bank_fill[Bank/my_bank_size] < base + - (JEDEC->size << priv->chips[I].addrshift) - Bank) - priv->bank_fill[Bank/my_bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank; - I++; - } - - priv->size += priv->chips[I-1].size*Count; - - return priv->chips[I-1].size; -} - -/* Lookup the chip information from the JEDEC ID table. */ -static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id) -{ - __u16 Id = (mfr << 8) | id; - unsigned long I = 0; - for (I = 0; JEDEC_table[I].jedec != 0; I++) - if (JEDEC_table[I].jedec == Id) - return JEDEC_table + I; - return NULL; -} - -// Look for flash using an 8 bit bus interface -static int jedec_probe8(struct map_info *map,unsigned long base, - struct jedec_private *priv) -{ - #define flread(x) map_read8(map,base+x) - #define flwrite(v,x) map_write8(map,v,base+x) - - const unsigned long AutoSel1 = 0xAA; - const unsigned long AutoSel2 = 0x55; - const unsigned long AutoSel3 = 0x90; - const unsigned long Reset = 0xF0; - __u32 OldVal; - __u8 Mfg[1]; - __u8 Id[1]; - unsigned I; - unsigned long Size; - - // Wait for any write/erase operation to settle - OldVal = flread(base); - for (I = 0; OldVal != flread(base) && I < 10000; I++) - OldVal = flread(base); - - // Reset the chip - flwrite(Reset,0x555); - - // Send the sequence - flwrite(AutoSel1,0x555); - flwrite(AutoSel2,0x2AA); - flwrite(AutoSel3,0x555); - - // Get the JEDEC numbers - Mfg[0] = flread(0); - Id[0] = flread(1); - // printk("Mfg is %x, Id is %x\n",Mfg[0],Id[0]); - - Size = handle_jedecs(map,Mfg,Id,1,base,priv); - // printk("handle_jedecs Size is %x\n",(unsigned int)Size); - if (Size == 0) - { - flwrite(Reset,0x555); - return 0; - } - - - // Reset. - flwrite(Reset,0x555); - - return 1; - - #undef flread - #undef flwrite -} - -// Look for flash using a 16 bit bus interface (ie 2 8-bit chips) -static int jedec_probe16(struct map_info *map,unsigned long base, - struct jedec_private *priv) -{ - return 0; -} - -// Look for flash using a 32 bit bus interface (ie 4 8-bit chips) -static int jedec_probe32(struct map_info *map,unsigned long base, - struct jedec_private *priv) -{ - #define flread(x) map_read32(map,base+((x)<<2)) - #define flwrite(v,x) map_write32(map,v,base+((x)<<2)) - - const unsigned long AutoSel1 = 0xAAAAAAAA; - const unsigned long AutoSel2 = 0x55555555; - const unsigned long AutoSel3 = 0x90909090; - const unsigned long Reset = 0xF0F0F0F0; - __u32 OldVal; - __u8 Mfg[4]; - __u8 Id[4]; - unsigned I; - unsigned long Size; - - // Wait for any write/erase operation to settle - OldVal = flread(base); - for (I = 0; OldVal != flread(base) && I < 10000; I++) - OldVal = flread(base); - - // Reset the chip - flwrite(Reset,0x555); - - // Send the sequence - flwrite(AutoSel1,0x555); - flwrite(AutoSel2,0x2AA); - flwrite(AutoSel3,0x555); - - // Test #1, JEDEC numbers are readable from 0x??00/0x??01 - if (flread(0) != flread(0x100) || - flread(1) != flread(0x101)) - { - flwrite(Reset,0x555); - return 0; - } - - // Split up the JEDEC numbers - OldVal = flread(0); - for (I = 0; I != 4; I++) - Mfg[I] = (OldVal >> (I*8)); - OldVal = flread(1); - for (I = 0; I != 4; I++) - Id[I] = (OldVal >> (I*8)); - - Size = handle_jedecs(map,Mfg,Id,4,base,priv); - if (Size == 0) - { - flwrite(Reset,0x555); - return 0; - } - - /* Check if there is address wrap around within a single bank, if this - returns JEDEC numbers then we assume that it is wrap around. Notice - we call this routine with the JEDEC return still enabled, if two or - more flashes have a truncated address space the probe test will still - work */ - if (base + (Size<<2)+0x555 < map->size && - base + (Size<<2)+0x555 < (base & (~(my_bank_size-1))) + my_bank_size) - { - if (flread(base+Size) != flread(base+Size + 0x100) || - flread(base+Size + 1) != flread(base+Size + 0x101)) - { - jedec_probe32(map,base+Size,priv); - } - } - - // Reset. - flwrite(0xF0F0F0F0,0x555); - - return 1; - - #undef flread - #undef flwrite -} - -/* Linear read. */ -static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - - map_copy_from(map, buf, from, len); - *retlen = len; - return 0; -} - -/* Banked read. Take special care to jump past the holes in the bank - mapping. This version assumes symetry in the holes.. */ -static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct jedec_private *priv = map->fldrv_priv; - - *retlen = 0; - while (len > 0) - { - // Determine what bank and offset into that bank the first byte is - unsigned long bank = from & (~(priv->bank_fill[0]-1)); - unsigned long offset = from & (priv->bank_fill[0]-1); - unsigned long get = len; - if (priv->bank_fill[0] - offset < len) - get = priv->bank_fill[0] - offset; - - bank /= priv->bank_fill[0]; - map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); - - len -= get; - *retlen += get; - from += get; - } - return 0; -} - -/* Pass the flags value that the flash return before it re-entered read - mode. */ -static void jedec_flash_failed(unsigned char code) -{ - /* Bit 5 being high indicates that there was an internal device - failure, erasure time limits exceeded or something */ - if ((code & (1 << 5)) != 0) - { - printk("mtd: Internal Flash failure\n"); - return; - } - printk("mtd: Programming didn't take\n"); -} - -/* This uses the erasure function described in the AMD Flash Handbook, - it will work for flashes with a fixed sector size only. Flashes with - a selection of sector sizes (ie the AMD Am29F800B) will need a different - routine. This routine tries to parallize erasing multiple chips/sectors - where possible */ -static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - // Does IO to the currently selected chip - #define flread(x) map_read8(map,chip->base+((x)<addrshift)) - #define flwrite(v,x) map_write8(map,v,chip->base+((x)<addrshift)) - - unsigned long Time = 0; - unsigned long NoTime = 0; - unsigned long start = instr->addr, len = instr->len; - unsigned int I; - struct map_info *map = mtd->priv; - struct jedec_private *priv = map->fldrv_priv; - - // Verify the arguments.. - if (start + len > mtd->size || - (start % mtd->erasesize) != 0 || - (len % mtd->erasesize) != 0 || - (len/mtd->erasesize) == 0) - return -EINVAL; - - jedec_flash_chip_scan(priv,start,len); - - // Start the erase sequence on each chip - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - unsigned long off; - struct jedec_flash_chip *chip = priv->chips + I; - - if (chip->length == 0) - continue; - - if (chip->start + chip->length > chip->size) - { - printk("DIE\n"); - return -EIO; - } - - flwrite(0xF0,chip->start + 0x555); - flwrite(0xAA,chip->start + 0x555); - flwrite(0x55,chip->start + 0x2AA); - flwrite(0x80,chip->start + 0x555); - flwrite(0xAA,chip->start + 0x555); - flwrite(0x55,chip->start + 0x2AA); - - /* Once we start selecting the erase sectors the delay between each - command must not exceed 50us or it will immediately start erasing - and ignore the other sectors */ - for (off = 0; off < len; off += chip->sectorsize) - { - // Check to make sure we didn't timeout - flwrite(0x30,chip->start + off); - if (off == 0) - continue; - if ((flread(chip->start + off) & (1 << 3)) != 0) - { - printk("mtd: Ack! We timed out the erase timer!\n"); - return -EIO; - } - } - } - - /* We could split this into a timer routine and return early, performing - background erasure.. Maybe later if the need warrents */ - - /* Poll the flash for erasure completion, specs say this can take as long - as 480 seconds to do all the sectors (for a 2 meg flash). - Erasure time is dependent on chip age, temp and wear.. */ - - /* This being a generic routine assumes a 32 bit bus. It does read32s - and bundles interleved chips into the same grouping. This will work - for all bus widths */ - Time = 0; - NoTime = 0; - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - struct jedec_flash_chip *chip = priv->chips + I; - unsigned long off = 0; - unsigned todo[4] = {0,0,0,0}; - unsigned todo_left = 0; - unsigned J; - - if (chip->length == 0) - continue; - - /* Find all chips in this data line, realistically this is all - or nothing up to the interleve count */ - for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++) - { - if ((priv->chips[J].base & (~((1<addrshift)-1))) == - (chip->base & (~((1<addrshift)-1)))) - { - todo_left++; - todo[priv->chips[J].base & ((1<addrshift)-1)] = 1; - } - } - - /* printk("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1], - (short)todo[2],(short)todo[3]); - */ - while (1) - { - __u32 Last[4]; - unsigned long Count = 0; - - /* During erase bit 7 is held low and bit 6 toggles, we watch this, - should it stop toggling or go high then the erase is completed, - or this is not really flash ;> */ - switch (map->buswidth) { - case 1: - Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - case 2: - Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - case 3: - Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - } - Count = 3; - while (todo_left != 0) - { - for (J = 0; J != 4; J++) - { - __u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF; - __u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF; - __u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF; - if (todo[J] == 0) - continue; - - if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2) - { -// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2); - continue; - } - - if (Byte1 == Byte2) - { - jedec_flash_failed(Byte3); - return -EIO; - } - - todo[J] = 0; - todo_left--; - } - -/* if (NoTime == 0) - Time += HZ/10 - schedule_timeout(HZ/10);*/ - NoTime = 0; - - switch (map->buswidth) { - case 1: - Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - case 2: - Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - case 4: - Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); - break; - } - Count++; - -/* // Count time, max of 15s per sector (according to AMD) - if (Time > 15*len/mtd->erasesize*HZ) - { - printk("mtd: Flash Erase Timed out\n"); - return -EIO; - } */ - } - - // Skip to the next chip if we used chip erase - if (chip->length == chip->size) - off = chip->size; - else - off += chip->sectorsize; - - if (off >= chip->length) - break; - NoTime = 1; - } - - for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++) - { - if ((priv->chips[J].base & (~((1<addrshift)-1))) == - (chip->base & (~((1<addrshift)-1)))) - priv->chips[J].length = 0; - } - } - - //printk("done\n"); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return 0; - - #undef flread - #undef flwrite -} - -/* This is the simple flash writing function. It writes to every byte, in - sequence. It takes care of how to properly address the flash if - the flash is interleved. It can only be used if all the chips in the - array are identical!*/ -static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, - size_t *retlen, const u_char *buf) -{ - /* Does IO to the currently selected chip. It takes the bank addressing - base (which is divisible by the chip size) adds the necessary lower bits - of addrshift (interleave index) and then adds the control register index. */ - #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) - #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) - - struct map_info *map = mtd->priv; - struct jedec_private *priv = map->fldrv_priv; - unsigned long base; - unsigned long off; - size_t save_len = len; - - if (start + len > mtd->size) - return -EIO; - - //printk("Here"); - - //printk("flash_write: start is %x, len is %x\n",start,(unsigned long)len); - while (len != 0) - { - struct jedec_flash_chip *chip = priv->chips; - unsigned long bank; - unsigned long boffset; - - // Compute the base of the flash. - off = ((unsigned long)start) % (chip->size << chip->addrshift); - base = start - off; - - // Perform banked addressing translation. - bank = base & (~(priv->bank_fill[0]-1)); - boffset = base & (priv->bank_fill[0]-1); - bank = (bank/priv->bank_fill[0])*my_bank_size; - base = bank + boffset; - - // printk("Flasing %X %X %X\n",base,chip->size,len); - // printk("off is %x, compare with %x\n",off,chip->size << chip->addrshift); - - // Loop over this page - for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) - { - unsigned char oldbyte = map_read8(map,base+off); - unsigned char Last[4]; - unsigned long Count = 0; - - if (oldbyte == *buf) { - // printk("oldbyte and *buf is %x,len is %x\n",oldbyte,len); - continue; - } - if (((~oldbyte) & *buf) != 0) - printk("mtd: warn: Trying to set a 0 to a 1\n"); - - // Write - flwrite(0xAA,0x555); - flwrite(0x55,0x2AA); - flwrite(0xA0,0x555); - map_write8(map,*buf,base + off); - Last[0] = map_read8(map,base + off); - Last[1] = map_read8(map,base + off); - Last[2] = map_read8(map,base + off); - - /* Wait for the flash to finish the operation. We store the last 4 - status bytes that have been retrieved so we can determine why - it failed. The toggle bits keep toggling when there is a - failure */ - for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && - Count < 10000; Count++) - Last[Count % 4] = map_read8(map,base + off); - if (Last[(Count - 1) % 4] != *buf) - { - jedec_flash_failed(Last[(Count - 3) % 4]); - return -EIO; - } - } - } - *retlen = save_len; - return 0; -} - -/* This is used to enhance the speed of the erase routine, - when things are being done to multiple chips it is possible to - parallize the operations, particularly full memory erases of multi - chip memories benifit */ -static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start, - unsigned long len) -{ - unsigned int I; - - // Zero the records - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - priv->chips[I].start = priv->chips[I].length = 0; - - // Intersect the region with each chip - for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) - { - struct jedec_flash_chip *chip = priv->chips + I; - unsigned long ByteStart; - unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift); - - // End is before this chip or the start is after it - if (start+len < chip->offset || - ChipEndByte - (1 << chip->addrshift) < start) - continue; - - if (start < chip->offset) - { - ByteStart = chip->offset; - chip->start = 0; - } - else - { - chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift; - ByteStart = start; - } - - if (start + len >= ChipEndByte) - chip->length = (ChipEndByte - ByteStart) >> chip->addrshift; - else - chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift; - } -} - -int __init jedec_init(void) -{ - register_mtd_chip_driver(&jedec_chipdrv); - return 0; -} - -static void __exit jedec_exit(void) -{ - unregister_mtd_chip_driver(&jedec_chipdrv); -} - -module_init(jedec_init); -module_exit(jedec_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jason Gunthorpe et al."); -MODULE_DESCRIPTION("Old MTD chip driver for JEDEC-compliant flash chips"); diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c deleted file mode 100644 index c9cd3d21ccf..00000000000 --- a/drivers/mtd/chips/sharp.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * MTD chip driver for pre-CFI Sharp flash chips - * - * Copyright 2000,2001 David A. Schleef - * 2000,2001 Lineo, Inc. - * - * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $ - * - * Devices supported: - * LH28F016SCT Symmetrical block flash memory, 2Mx8 - * LH28F008SCT Symmetrical block flash memory, 1Mx8 - * - * Documentation: - * http://www.sharpmeg.com/datasheets/memic/flashcmp/ - * http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf - * 016sctl9.pdf - * - * Limitations: - * This driver only supports 4x1 arrangement of chips. - * Not tested on anything but PowerPC. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CMD_RESET 0xffffffff -#define CMD_READ_ID 0x90909090 -#define CMD_READ_STATUS 0x70707070 -#define CMD_CLEAR_STATUS 0x50505050 -#define CMD_BLOCK_ERASE_1 0x20202020 -#define CMD_BLOCK_ERASE_2 0xd0d0d0d0 -#define CMD_BYTE_WRITE 0x40404040 -#define CMD_SUSPEND 0xb0b0b0b0 -#define CMD_RESUME 0xd0d0d0d0 -#define CMD_SET_BLOCK_LOCK_1 0x60606060 -#define CMD_SET_BLOCK_LOCK_2 0x01010101 -#define CMD_SET_MASTER_LOCK_1 0x60606060 -#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1 -#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060 -#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0 - -#define SR_READY 0x80808080 // 1 = ready -#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended -#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits -#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit -#define SR_VPP 0x08080808 // 1 = Vpp is low -#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended -#define SR_PROTECT 0x02020202 // 1 = lock bit set -#define SR_RESERVED 0x01010101 - -#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT) - -/* Configuration options */ - -#undef AUTOUNLOCK /* automatically unlocks blocks before erasing */ - -static struct mtd_info *sharp_probe(struct map_info *); - -static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd); - -static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); -static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, const u_char *buf); -static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr); -static void sharp_sync(struct mtd_info *mtd); -static int sharp_suspend(struct mtd_info *mtd); -static void sharp_resume(struct mtd_info *mtd); -static void sharp_destroy(struct mtd_info *mtd); - -static int sharp_write_oneword(struct map_info *map, struct flchip *chip, - unsigned long adr, __u32 datum); -static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr); -#ifdef AUTOUNLOCK -static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr); -#endif - - -struct sharp_info{ - struct flchip *chip; - int bogus; - int chipshift; - int numchips; - struct flchip chips[1]; -}; - -static void sharp_destroy(struct mtd_info *mtd); - -static struct mtd_chip_driver sharp_chipdrv = { - .probe = sharp_probe, - .destroy = sharp_destroy, - .name = "sharp", - .module = THIS_MODULE -}; - - -static struct mtd_info *sharp_probe(struct map_info *map) -{ - struct mtd_info *mtd = NULL; - struct sharp_info *sharp = NULL; - int width; - - mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if(!mtd) - return NULL; - - sharp = kzalloc(sizeof(*sharp), GFP_KERNEL); - if(!sharp) { - kfree(mtd); - return NULL; - } - - width = sharp_probe_map(map,mtd); - if(!width){ - kfree(mtd); - kfree(sharp); - return NULL; - } - - mtd->priv = map; - mtd->type = MTD_NORFLASH; - mtd->erase = sharp_erase; - mtd->read = sharp_read; - mtd->write = sharp_write; - mtd->sync = sharp_sync; - mtd->suspend = sharp_suspend; - mtd->resume = sharp_resume; - mtd->flags = MTD_CAP_NORFLASH; - mtd->writesize = 1; - mtd->name = map->name; - - sharp->chipshift = 23; - sharp->numchips = 1; - sharp->chips[0].start = 0; - sharp->chips[0].state = FL_READY; - sharp->chips[0].mutex = &sharp->chips[0]._spinlock; - sharp->chips[0].word_write_time = 0; - init_waitqueue_head(&sharp->chips[0].wq); - spin_lock_init(&sharp->chips[0]._spinlock); - - map->fldrv = &sharp_chipdrv; - map->fldrv_priv = sharp; - - __module_get(THIS_MODULE); - return mtd; -} - -static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr) -{ - map_word map_cmd; - map_cmd.x[0] = cmd; - map_write(map, map_cmd, adr); -} - -static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd) -{ - map_word tmp, read0, read4; - unsigned long base = 0; - int width = 4; - - tmp = map_read(map, base+0); - - sharp_send_cmd(map, CMD_READ_ID, base+0); - - read0 = map_read(map, base+0); - read4 = map_read(map, base+4); - if(read0.x[0] == 0x89898989){ - printk("Looks like sharp flash\n"); - switch(read4.x[0]){ - case 0xaaaaaaaa: - case 0xa0a0a0a0: - /* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/ - /* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/ - mtd->erasesize = 0x10000 * width; - mtd->size = 0x200000 * width; - return width; - case 0xa6a6a6a6: - /* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/ - /* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/ - mtd->erasesize = 0x10000 * width; - mtd->size = 0x100000 * width; - return width; -#if 0 - case 0x00000000: /* unknown */ - /* XX - LH28F004SCT 512kx8, 8 64k blocks*/ - mtd->erasesize = 0x10000 * width; - mtd->size = 0x80000 * width; - return width; -#endif - default: - printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n", - read0.x[0], read4.x[0]); - } - }else if((map_read(map, base+0).x[0] == CMD_READ_ID)){ - /* RAM, probably */ - printk("Looks like RAM\n"); - map_write(map, tmp, base+0); - }else{ - printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n", - read0.x[0], read4.x[0]); - } - - return 0; -} - -/* This function returns with the chip->mutex lock held. */ -static int sharp_wait(struct map_info *map, struct flchip *chip) -{ - int i; - map_word status; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - int adr = 0; - -retry: - spin_lock_bh(chip->mutex); - - switch(chip->state){ - case FL_READY: - sharp_send_cmd(map, CMD_READ_STATUS, adr); - chip->state = FL_STATUS; - case FL_STATUS: - for(i=0;i<100;i++){ - status = map_read(map, adr); - if((status.x[0] & SR_READY)==SR_READY) - break; - udelay(1); - } - break; - default: - printk("Waiting for chip\n"); - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - sharp_send_cmd(map, CMD_RESET, adr); - - chip->state = FL_READY; - - return 0; -} - -static void sharp_release(struct flchip *chip) -{ - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); -} - -static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct sharp_info *sharp = map->fldrv_priv; - int chipnum; - int ret = 0; - int ofs = 0; - - chipnum = (from >> sharp->chipshift); - ofs = from & ((1 << sharp->chipshift)-1); - - *retlen = 0; - - while(len){ - unsigned long thislen; - - if(chipnum>=sharp->numchips) - break; - - thislen = len; - if(ofs+thislen >= (1<chipshift)) - thislen = (1<chipshift) - ofs; - - ret = sharp_wait(map,&sharp->chips[chipnum]); - if(ret<0) - break; - - map_copy_from(map,buf,ofs,thislen); - - sharp_release(&sharp->chips[chipnum]); - - *retlen += thislen; - len -= thislen; - buf += thislen; - - ofs = 0; - chipnum++; - } - return ret; -} - -static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct map_info *map = mtd->priv; - struct sharp_info *sharp = map->fldrv_priv; - int ret = 0; - int i,j; - int chipnum; - unsigned long ofs; - union { u32 l; unsigned char uc[4]; } tbuf; - - *retlen = 0; - - while(len){ - tbuf.l = 0xffffffff; - chipnum = to >> sharp->chipshift; - ofs = to & ((1<chipshift)-1); - - j=0; - for(i=ofs&3;i<4 && len;i++){ - tbuf.uc[i] = *buf; - buf++; - to++; - len--; - j++; - } - sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l); - if(ret<0) - return ret; - (*retlen)+=j; - } - - return 0; -} - -static int sharp_write_oneword(struct map_info *map, struct flchip *chip, - unsigned long adr, __u32 datum) -{ - int ret; - int timeo; - int try; - int i; - map_word data, status; - - status.x[0] = 0; - ret = sharp_wait(map,chip); - - for(try=0;try<10;try++){ - sharp_send_cmd(map, CMD_BYTE_WRITE, adr); - /* cpu_to_le32 -> hack to fix the writel be->le conversion */ - data.x[0] = cpu_to_le32(datum); - map_write(map, data, adr); - - chip->state = FL_WRITING; - - timeo = jiffies + (HZ/2); - - sharp_send_cmd(map, CMD_READ_STATUS, adr); - for(i=0;i<100;i++){ - status = map_read(map, adr); - if((status.x[0] & SR_READY) == SR_READY) - break; - } - if(i==100){ - printk("sharp: timed out writing\n"); - } - - if(!(status.x[0] & SR_ERRORS)) - break; - - printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]); - - sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); - } - sharp_send_cmd(map, CMD_RESET, adr); - chip->state = FL_READY; - - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return 0; -} - -static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct map_info *map = mtd->priv; - struct sharp_info *sharp = map->fldrv_priv; - unsigned long adr,len; - int chipnum, ret=0; - -//printk("sharp_erase()\n"); - if(instr->addr & (mtd->erasesize - 1)) - return -EINVAL; - if(instr->len & (mtd->erasesize - 1)) - return -EINVAL; - if(instr->len + instr->addr > mtd->size) - return -EINVAL; - - chipnum = instr->addr >> sharp->chipshift; - adr = instr->addr & ((1<chipshift)-1); - len = instr->len; - - while(len){ - ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr); - if(ret)return ret; - - adr += mtd->erasesize; - len -= mtd->erasesize; - if(adr >> sharp->chipshift){ - adr = 0; - chipnum++; - if(chipnum>=sharp->numchips) - break; - } - } - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; -} - -static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip, - unsigned long adr) -{ - int ret; - unsigned long timeo; - map_word status; - DECLARE_WAITQUEUE(wait, current); - - sharp_send_cmd(map, CMD_READ_STATUS, adr); - status = map_read(map, adr); - - timeo = jiffies + HZ; - - while(time_before(jiffies, timeo)){ - sharp_send_cmd(map, CMD_READ_STATUS, adr); - status = map_read(map, adr); - if((status.x[0] & SR_READY)==SR_READY){ - ret = 0; - goto out; - } - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - //spin_unlock_bh(chip->mutex); - - schedule_timeout(1); - schedule(); - remove_wait_queue(&chip->wq, &wait); - - //spin_lock_bh(chip->mutex); - - if (signal_pending(current)){ - ret = -EINTR; - goto out; - } - - } - ret = -ETIME; -out: - return ret; -} - -static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr) -{ - int ret; - //int timeo; - map_word status; - //int i; - -//printk("sharp_erase_oneblock()\n"); - -#ifdef AUTOUNLOCK - /* This seems like a good place to do an unlock */ - sharp_unlock_oneblock(map,chip,adr); -#endif - - sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr); - sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr); - - chip->state = FL_ERASING; - - ret = sharp_do_wait_for_ready(map,chip,adr); - if(ret<0)return ret; - - sharp_send_cmd(map, CMD_READ_STATUS, adr); - status = map_read(map, adr); - - if(!(status.x[0] & SR_ERRORS)){ - sharp_send_cmd(map, CMD_RESET, adr); - chip->state = FL_READY; - //spin_unlock_bh(chip->mutex); - return 0; - } - - printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]); - sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); - - //spin_unlock_bh(chip->mutex); - - return -EIO; -} - -#ifdef AUTOUNLOCK -static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr) -{ - int i; - map_word status; - - sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr); - sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr); - - udelay(100); - - status = map_read(map, adr); - printk("status=%08lx\n", status.x[0]); - - for(i=0;i<1000;i++){ - //sharp_send_cmd(map, CMD_READ_STATUS, adr); - status = map_read(map, adr); - if((status.x[0] & SR_READY) == SR_READY) - break; - udelay(100); - } - if(i==1000){ - printk("sharp: timed out unlocking block\n"); - } - - if(!(status.x[0] & SR_ERRORS)){ - sharp_send_cmd(map, CMD_RESET, adr); - chip->state = FL_READY; - return; - } - - printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]); - sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); -} -#endif - -static void sharp_sync(struct mtd_info *mtd) -{ - //printk("sharp_sync()\n"); -} - -static int sharp_suspend(struct mtd_info *mtd) -{ - printk("sharp_suspend()\n"); - return -EINVAL; -} - -static void sharp_resume(struct mtd_info *mtd) -{ - printk("sharp_resume()\n"); - -} - -static void sharp_destroy(struct mtd_info *mtd) -{ - printk("sharp_destroy()\n"); - -} - -static int __init sharp_probe_init(void) -{ - printk("MTD Sharp chip driver \n"); - - register_mtd_chip_driver(&sharp_chipdrv); - - return 0; -} - -static void __exit sharp_probe_exit(void) -{ - unregister_mtd_chip_driver(&sharp_chipdrv); -} - -module_init(sharp_probe_init); -module_exit(sharp_probe_exit); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Schleef "); -MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips"); -- cgit v1.2.3 From 01f41ec7b36e14da18a4e162ef697ae358f36e37 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:32:34 -0700 Subject: mmc build fix Cc: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b6c16704aaa..7385acfa1dd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -501,9 +501,9 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { #ifdef CONFIG_MMC_DEBUG unsigned long flags; - spin_lock_irqsave(host->lock, flags); + spin_lock_irqsave(&host->lock, flags); BUG_ON(host->removed); - spin_unlock_irqrestore(host->lock, flags); + spin_unlock_irqrestore(&host->lock, flags); #endif mmc_schedule_delayed_work(&host->detect, delay); -- cgit v1.2.3 From 97416ce82e20a9511ec369822098a8d20998398a Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 9 May 2007 02:32:35 -0700 Subject: Declare {compat_}sys_utimensat This is needed before Powerpc can wire up the syscall. Signed-off-by: Stephen Rothwell Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compat.h | 3 +++ include/linux/syscalls.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/include/linux/compat.h b/include/linux/compat.h index ccd863dd77f..70a157a130b 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -253,5 +253,8 @@ asmlinkage long compat_sys_epoll_pwait(int epfd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); +asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, + struct compat_timespec __user *t, int flags); + #endif /* CONFIG_COMPAT */ #endif /* _LINUX_COMPAT_H */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1912c6cbef5..3139f441229 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -576,6 +576,8 @@ asmlinkage long sys_fstatat64(int dfd, char __user *filename, struct stat64 __user *statbuf, int flag); asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz); +asmlinkage long sys_utimensat(int dfd, char __user *filename, + struct timespec __user *utimes, int flags); asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t); asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename, -- cgit v1.2.3 From 65c02d4cfbbd10188ded3d6577922ab034d943ba Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:35 -0700 Subject: SLUB: add support for dynamic cacheline size determination SLUB currently assumes that the cacheline size is static. However, i386 f.e. supports dynamic cache line size determination. Use cache_line_size() instead of L1_CACHE_BYTES in the allocator. That also explains the purpose of SLAB_HWCACHE_ALIGN. So we will need to keep that one around to allow dynamic aligning of objects depending on boot determination of the cache line size. [akpm@linux-foundation.org: need to define it before we use it] Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 5db3da5a60b..40e92d8d4bc 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -157,6 +157,11 @@ /* Internal SLUB flags */ #define __OBJECT_POISON 0x80000000 /* Poison object */ +/* Not all arches define cache_line_size */ +#ifndef cache_line_size +#define cache_line_size() L1_CACHE_BYTES +#endif + static int kmem_size = sizeof(struct kmem_cache); #ifdef CONFIG_SMP @@ -1480,8 +1485,8 @@ static unsigned long calculate_alignment(unsigned long flags, * then use it. */ if ((flags & SLAB_HWCACHE_ALIGN) && - size > L1_CACHE_BYTES / 2) - return max_t(unsigned long, align, L1_CACHE_BYTES); + size > cache_line_size() / 2) + return max_t(unsigned long, align, cache_line_size()); if (align < ARCH_SLAB_MINALIGN) return ARCH_SLAB_MINALIGN; @@ -1667,8 +1672,8 @@ static int calculate_sizes(struct kmem_cache *s) size += sizeof(void *); /* * Determine the alignment based on various parameters that the - * user specified (this is unecessarily complex due to the attempt - * to be compatible with SLAB. Should be cleaned up some day). + * user specified and the dynamic determination of cache line size + * on bootup. */ align = calculate_alignment(flags, align, s->objsize); @@ -2280,7 +2285,7 @@ void __init kmem_cache_init(void) printk(KERN_INFO "SLUB: Genslabs=%d, HWalign=%d, Order=%d-%d, MinObjects=%d," " Processors=%d, Nodes=%d\n", - KMALLOC_SHIFT_HIGH, L1_CACHE_BYTES, + KMALLOC_SHIFT_HIGH, cache_line_size(), slub_min_order, slub_max_order, slub_min_objects, nr_cpu_ids, nr_node_ids); } -- cgit v1.2.3 From be7b3fbcef34452127bed93632b8e788f685d70e Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:36 -0700 Subject: SLUB: after object padding only needed for Redzoning If no redzoning is selected then we do not need padding before the next object. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/slub.c b/mm/slub.c index 40e92d8d4bc..beac34a5e4f 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1661,7 +1661,7 @@ static int calculate_sizes(struct kmem_cache *s) */ size += 2 * sizeof(struct track); - if (flags & DEBUG_DEFAULT_FLAGS) + if (flags & SLAB_RED_ZONE) /* * Add some empty padding so that we can catch * overwrites from earlier objects rather than let -- cgit v1.2.3 From a87615b8f9e2349f6d3770af3d72fd6a41ab4239 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:37 -0700 Subject: SLUB: slabinfo upgrade -e Show empty slabs -d Modification of slab debug options at runtime -o Operations. Display of ctor / dtor etc. -r Report: Display all available information about a slabcache. Cleanup tracking display and make it work right. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/slabinfo.c | 426 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 352 insertions(+), 74 deletions(-) diff --git a/Documentation/vm/slabinfo.c b/Documentation/vm/slabinfo.c index 41710ccf3a2..686a8e04a4f 100644 --- a/Documentation/vm/slabinfo.c +++ b/Documentation/vm/slabinfo.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MAX_SLABS 500 #define MAX_ALIASES 500 @@ -41,12 +42,15 @@ struct aliasinfo { } aliasinfo[MAX_ALIASES]; int slabs = 0; +int actual_slabs = 0; int aliases = 0; int alias_targets = 0; int highest_node = 0; char buffer[4096]; +int show_empty = 0; +int show_report = 0; int show_alias = 0; int show_slab = 0; int skip_zero = 1; @@ -59,6 +63,15 @@ int show_inverted = 0; int show_single_ref = 0; int show_totals = 0; int sort_size = 0; +int set_debug = 0; +int show_ops = 0; + +/* Debug options */ +int sanity = 0; +int redzone = 0; +int poison = 0; +int tracking = 0; +int tracing = 0; int page_size; @@ -76,20 +89,33 @@ void fatal(const char *x, ...) void usage(void) { - printf("slabinfo [-ahnpvtsz] [slab-regexp]\n" + printf("slabinfo 5/7/2007. (c) 2007 sgi. clameter@sgi.com\n\n" + "slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n" "-a|--aliases Show aliases\n" + "-d|--debug= Set/Clear Debug options\n" + "-e|--empty Show empty slabs\n" + "-f|--first-alias Show first alias\n" "-h|--help Show usage information\n" + "-i|--inverted Inverted list\n" + "-l|--slabs Show slabs\n" "-n|--numa Show NUMA information\n" + "-o|--ops Show kmem_cache_ops\n" "-s|--shrink Shrink slabs\n" - "-v|--validate Validate slabs\n" + "-r|--report Detailed report on single slabs\n" + "-S|--Size Sort by size\n" "-t|--tracking Show alloc/free information\n" "-T|--Totals Show summary information\n" - "-l|--slabs Show slabs\n" - "-S|--Size Sort by size\n" + "-v|--validate Validate slabs\n" "-z|--zero Include empty slabs\n" - "-f|--first-alias Show first alias\n" - "-i|--inverted Inverted list\n" "-1|--1ref Single reference\n" + "\nValid debug options (FZPUT may be combined)\n" + "a / A Switch on all debug options (=FZUP)\n" + "- Switch off all debug options\n" + "f / F Sanity Checks (SLAB_DEBUG_FREE)\n" + "z / Z Redzoning\n" + "p / P Poisoning\n" + "u / U Tracking\n" + "t / T Tracing\n" ); } @@ -143,11 +169,10 @@ unsigned long get_obj_and_str(char *name, char **x) void set_obj(struct slabinfo *s, char *name, int n) { char x[100]; + FILE *f; sprintf(x, "%s/%s", s->name, name); - - FILE *f = fopen(x, "w"); - + f = fopen(x, "w"); if (!f) fatal("Cannot write to %s\n", x); @@ -155,6 +180,26 @@ void set_obj(struct slabinfo *s, char *name, int n) fclose(f); } +unsigned long read_slab_obj(struct slabinfo *s, char *name) +{ + char x[100]; + FILE *f; + int l; + + sprintf(x, "%s/%s", s->name, name); + f = fopen(x, "r"); + if (!f) { + buffer[0] = 0; + l = 0; + } else { + l = fread(buffer, 1, sizeof(buffer), f); + buffer[l] = 0; + fclose(f); + } + return l; +} + + /* * Put a size string together */ @@ -226,7 +271,7 @@ int line = 0; void first_line(void) { - printf("Name Objects Objsize Space " + printf("Name Objects Objsize Space " "Slabs/Part/Cpu O/S O %%Fr %%Ef Flg\n"); } @@ -246,10 +291,7 @@ struct aliasinfo *find_one_alias(struct slabinfo *find) return best; } } - if (best) - return best; - fatal("Cannot find alias for %s\n", find->name); - return NULL; + return best; } unsigned long slab_size(struct slabinfo *s) @@ -257,6 +299,126 @@ unsigned long slab_size(struct slabinfo *s) return s->slabs * (page_size << s->order); } +void slab_numa(struct slabinfo *s, int mode) +{ + int node; + + if (strcmp(s->name, "*") == 0) + return; + + if (!highest_node) { + printf("\n%s: No NUMA information available.\n", s->name); + return; + } + + if (skip_zero && !s->slabs) + return; + + if (!line) { + printf("\n%-21s:", mode ? "NUMA nodes" : "Slab"); + for(node = 0; node <= highest_node; node++) + printf(" %4d", node); + printf("\n----------------------"); + for(node = 0; node <= highest_node; node++) + printf("-----"); + printf("\n"); + } + printf("%-21s ", mode ? "All slabs" : s->name); + for(node = 0; node <= highest_node; node++) { + char b[20]; + + store_size(b, s->numa[node]); + printf(" %4s", b); + } + printf("\n"); + if (mode) { + printf("%-21s ", "Partial slabs"); + for(node = 0; node <= highest_node; node++) { + char b[20]; + + store_size(b, s->numa_partial[node]); + printf(" %4s", b); + } + printf("\n"); + } + line++; +} + +void show_tracking(struct slabinfo *s) +{ + printf("\n%s: Kernel object allocation\n", s->name); + printf("-----------------------------------------------------------------------\n"); + if (read_slab_obj(s, "alloc_calls")) + printf(buffer); + else + printf("No Data\n"); + + printf("\n%s: Kernel object freeing\n", s->name); + printf("------------------------------------------------------------------------\n"); + if (read_slab_obj(s, "free_calls")) + printf(buffer); + else + printf("No Data\n"); + +} + +void ops(struct slabinfo *s) +{ + if (strcmp(s->name, "*") == 0) + return; + + if (read_slab_obj(s, "ops")) { + printf("\n%s: kmem_cache operations\n", s->name); + printf("--------------------------------------------\n"); + printf(buffer); + } else + printf("\n%s has no kmem_cache operations\n", s->name); +} + +const char *onoff(int x) +{ + if (x) + return "On "; + return "Off"; +} + +void report(struct slabinfo *s) +{ + if (strcmp(s->name, "*") == 0) + return; + printf("\nSlabcache: %-20s Aliases: %2d Order : %2d\n", s->name, s->aliases, s->order); + if (s->hwcache_align) + printf("** Hardware cacheline aligned\n"); + if (s->cache_dma) + printf("** Memory is allocated in a special DMA zone\n"); + if (s->destroy_by_rcu) + printf("** Slabs are destroyed via RCU\n"); + if (s->reclaim_account) + printf("** Reclaim accounting active\n"); + + printf("\nSizes (bytes) Slabs Debug Memory\n"); + printf("------------------------------------------------------------------------\n"); + printf("Object : %7d Total : %7ld Sanity Checks : %s Total: %7ld\n", + s->object_size, s->slabs, onoff(s->sanity_checks), + s->slabs * (page_size << s->order)); + printf("SlabObj: %7d Full : %7ld Redzoning : %s Used : %7ld\n", + s->slab_size, s->slabs - s->partial - s->cpu_slabs, + onoff(s->red_zone), s->objects * s->object_size); + printf("SlabSiz: %7d Partial: %7ld Poisoning : %s Loss : %7ld\n", + page_size << s->order, s->partial, onoff(s->poison), + s->slabs * (page_size << s->order) - s->objects * s->object_size); + printf("Loss : %7d CpuSlab: %7d Tracking : %s Lalig: %7ld\n", + s->slab_size - s->object_size, s->cpu_slabs, onoff(s->store_user), + (s->slab_size - s->object_size) * s->objects); + printf("Align : %7d Objects: %7d Tracing : %s Lpadd: %7ld\n", + s->align, s->objs_per_slab, onoff(s->trace), + ((page_size << s->order) - s->objs_per_slab * s->slab_size) * + s->slabs); + + ops(s); + show_tracking(s); + slab_numa(s, 1); +} void slabcache(struct slabinfo *s) { @@ -265,7 +427,18 @@ void slabcache(struct slabinfo *s) char flags[20]; char *p = flags; - if (skip_zero && !s->slabs) + if (strcmp(s->name, "*") == 0) + return; + + if (actual_slabs == 1) { + report(s); + return; + } + + if (skip_zero && !show_empty && !s->slabs) + return; + + if (show_empty && s->slabs) return; store_size(size_str, slab_size(s)); @@ -303,48 +476,128 @@ void slabcache(struct slabinfo *s) flags); } -void slab_numa(struct slabinfo *s) +/* + * Analyze debug options. Return false if something is amiss. + */ +int debug_opt_scan(char *opt) { - int node; + if (!opt || !opt[0] || strcmp(opt, "-") == 0) + return 1; + + if (strcasecmp(opt, "a") == 0) { + sanity = 1; + poison = 1; + redzone = 1; + tracking = 1; + return 1; + } - if (!highest_node) - fatal("No NUMA information available.\n"); + for ( ; *opt; opt++) + switch (*opt) { + case 'F' : case 'f': + if (sanity) + return 0; + sanity = 1; + break; + case 'P' : case 'p': + if (poison) + return 0; + poison = 1; + break; - if (skip_zero && !s->slabs) - return; + case 'Z' : case 'z': + if (redzone) + return 0; + redzone = 1; + break; - if (!line) { - printf("\nSlab Node "); - for(node = 0; node <= highest_node; node++) - printf(" %4d", node); - printf("\n----------------------"); - for(node = 0; node <= highest_node; node++) - printf("-----"); - printf("\n"); - } - printf("%-21s ", s->name); - for(node = 0; node <= highest_node; node++) { - char b[20]; + case 'U' : case 'u': + if (tracking) + return 0; + tracking = 1; + break; - store_size(b, s->numa[node]); - printf(" %4s", b); - } - printf("\n"); - line++; + case 'T' : case 't': + if (tracing) + return 0; + tracing = 1; + break; + default: + return 0; + } + return 1; } -void show_tracking(struct slabinfo *s) +int slab_empty(struct slabinfo *s) { - printf("\n%s: Calls to allocate a slab object\n", s->name); - printf("---------------------------------------------------\n"); - if (read_obj("alloc_calls")) - printf(buffer); + if (s->objects > 0) + return 0; - printf("%s: Calls to free a slab object\n", s->name); - printf("-----------------------------------------------\n"); - if (read_obj("free_calls")) - printf(buffer); + /* + * We may still have slabs even if there are no objects. Shrinking will + * remove them. + */ + if (s->slabs != 0) + set_obj(s, "shrink", 1); + return 1; +} + +void slab_debug(struct slabinfo *s) +{ + if (sanity && !s->sanity_checks) { + set_obj(s, "sanity", 1); + } + if (!sanity && s->sanity_checks) { + if (slab_empty(s)) + set_obj(s, "sanity", 0); + else + fprintf(stderr, "%s not empty cannot disable sanity checks\n", s->name); + } + if (redzone && !s->red_zone) { + if (slab_empty(s)) + set_obj(s, "red_zone", 1); + else + fprintf(stderr, "%s not empty cannot enable redzoning\n", s->name); + } + if (!redzone && s->red_zone) { + if (slab_empty(s)) + set_obj(s, "red_zone", 0); + else + fprintf(stderr, "%s not empty cannot disable redzoning\n", s->name); + } + if (poison && !s->poison) { + if (slab_empty(s)) + set_obj(s, "poison", 1); + else + fprintf(stderr, "%s not empty cannot enable poisoning\n", s->name); + } + if (!poison && s->poison) { + if (slab_empty(s)) + set_obj(s, "poison", 0); + else + fprintf(stderr, "%s not empty cannot disable poisoning\n", s->name); + } + if (tracking && !s->store_user) { + if (slab_empty(s)) + set_obj(s, "store_user", 1); + else + fprintf(stderr, "%s not empty cannot enable tracking\n", s->name); + } + if (!tracking && s->store_user) { + if (slab_empty(s)) + set_obj(s, "store_user", 0); + else + fprintf(stderr, "%s not empty cannot disable tracking\n", s->name); + } + if (tracing && !s->trace) { + if (slabs == 1) + set_obj(s, "trace", 1); + else + fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name); + } + if (!tracing && s->trace) + set_obj(s, "trace", 1); } void totals(void) @@ -673,7 +926,7 @@ void link_slabs(void) for (a = aliasinfo; a < aliasinfo + aliases; a++) { - for(s = slabinfo; s < slabinfo + slabs; s++) + for (s = slabinfo; s < slabinfo + slabs; s++) if (strcmp(a->ref, s->name) == 0) { a->slab = s; s->refs++; @@ -704,7 +957,7 @@ void alias(void) continue; } } - printf("\n%-20s <- %s", a->slab->name, a->name); + printf("\n%-12s <- %s", a->slab->name, a->name); active = a->slab->name; } else @@ -729,7 +982,12 @@ void rename_slabs(void) a = find_one_alias(s); - s->name = a->name; + if (a) + s->name = a->name; + else { + s->name = "*"; + actual_slabs--; + } } } @@ -748,11 +1006,14 @@ void read_slab_dir(void) char *t; int count; + if (chdir("/sys/slab")) + fatal("SYSFS support for SLUB not active\n"); + dir = opendir("."); while ((de = readdir(dir))) { if (de->d_name[0] == '.' || - slab_mismatch(de->d_name)) - continue; + (de->d_name[0] != ':' && slab_mismatch(de->d_name))) + continue; switch (de->d_type) { case DT_LNK: alias->name = strdup(de->d_name); @@ -807,6 +1068,7 @@ void read_slab_dir(void) } closedir(dir); slabs = slab - slabinfo; + actual_slabs = slabs; aliases = alias - aliasinfo; if (slabs > MAX_SLABS) fatal("Too many slabs\n"); @@ -825,34 +1087,37 @@ void output_slabs(void) if (show_numa) - slab_numa(slab); - else - if (show_track) + slab_numa(slab, 0); + else if (show_track) show_tracking(slab); - else - if (validate) + else if (validate) slab_validate(slab); - else - if (shrink) + else if (shrink) slab_shrink(slab); - else { - if (show_slab) - slabcache(slab); - } + else if (set_debug) + slab_debug(slab); + else if (show_ops) + ops(slab); + else if (show_slab) + slabcache(slab); } } struct option opts[] = { { "aliases", 0, NULL, 'a' }, - { "slabs", 0, NULL, 'l' }, - { "numa", 0, NULL, 'n' }, - { "zero", 0, NULL, 'z' }, - { "help", 0, NULL, 'h' }, - { "validate", 0, NULL, 'v' }, + { "debug", 2, NULL, 'd' }, + { "empty", 0, NULL, 'e' }, { "first-alias", 0, NULL, 'f' }, + { "help", 0, NULL, 'h' }, + { "inverted", 0, NULL, 'i'}, + { "numa", 0, NULL, 'n' }, + { "ops", 0, NULL, 'o' }, + { "report", 0, NULL, 'r' }, { "shrink", 0, NULL, 's' }, + { "slabs", 0, NULL, 'l' }, { "track", 0, NULL, 't'}, - { "inverted", 0, NULL, 'i'}, + { "validate", 0, NULL, 'v' }, + { "zero", 0, NULL, 'z' }, { "1ref", 0, NULL, '1'}, { NULL, 0, NULL, 0 } }; @@ -864,10 +1129,9 @@ int main(int argc, char *argv[]) char *pattern_source; page_size = getpagesize(); - if (chdir("/sys/slab")) - fatal("This kernel does not have SLUB support.\n"); - while ((c = getopt_long(argc, argv, "afhil1npstvzTS", opts, NULL)) != -1) + while ((c = getopt_long(argc, argv, "ad::efhil1noprstvzTS", + opts, NULL)) != -1) switch(c) { case '1': show_single_ref = 1; @@ -875,6 +1139,14 @@ int main(int argc, char *argv[]) case 'a': show_alias = 1; break; + case 'd': + set_debug = 1; + if (!debug_opt_scan(optarg)) + fatal("Invalid debug option '%s'\n", optarg); + break; + case 'e': + show_empty = 1; + break; case 'f': show_first_alias = 1; break; @@ -887,6 +1159,12 @@ int main(int argc, char *argv[]) case 'n': show_numa = 1; break; + case 'o': + show_ops = 1; + break; + case 'r': + show_report = 1; + break; case 's': shrink = 1; break; @@ -914,8 +1192,8 @@ int main(int argc, char *argv[]) } - if (!show_slab && !show_alias && !show_track - && !validate && !shrink) + if (!show_slab && !show_alias && !show_track && !show_report + && !validate && !shrink && !set_debug && !show_ops) show_slab = 1; if (argc > optind) -- cgit v1.2.3 From abcd08a6f564171ffa05bc77d1c2ba4cfa949653 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:37 -0700 Subject: SLUB: use check_valid_pointer in kmem_ptr_validate We needlessly duplicate code. Also make check_valid_pointer inline. Signed-off-by: Christoph LAemter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index beac34a5e4f..1832ae1ea53 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -405,9 +405,8 @@ static int check_bytes(u8 *start, unsigned int value, unsigned int bytes) return 1; } - -static int check_valid_pointer(struct kmem_cache *s, struct page *page, - void *object) +static inline int check_valid_pointer(struct kmem_cache *s, + struct page *page, const void *object) { void *base; @@ -1796,13 +1795,7 @@ int kmem_ptr_validate(struct kmem_cache *s, const void *object) /* No slab or wrong slab */ return 0; - addr = page_address(page); - if (object < addr || object >= addr + s->objects * s->size) - /* Out of bounds */ - return 0; - - if ((object - addr) % s->size) - /* Improperly aligned */ + if (!check_valid_pointer(s, page, object)) return 0; /* -- cgit v1.2.3 From 1f99a283dc13b167b93b2e453a30782955f165c2 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:38 -0700 Subject: SLUB: clean up krealloc We really do not need all this gaga there. ksize gives us all the information we need to figure out if the object can cope with the new size. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 1832ae1ea53..5d425d7116e 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2199,9 +2199,8 @@ EXPORT_SYMBOL(kmem_cache_shrink); */ void *krealloc(const void *p, size_t new_size, gfp_t flags) { - struct kmem_cache *new_cache; void *ret; - struct page *page; + size_t ks; if (unlikely(!p)) return kmalloc(new_size, flags); @@ -2211,19 +2210,13 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags) return NULL; } - page = virt_to_head_page(p); - - new_cache = get_slab(new_size, flags); - - /* - * If new size fits in the current cache, bail out. - */ - if (likely(page->slab == new_cache)) + ks = ksize(p); + if (ks >= new_size) return (void *)p; ret = kmalloc(new_size, flags); if (ret) { - memcpy(ret, p, min(new_size, ksize(p))); + memcpy(ret, p, min(new_size, ks)); kfree(p); } return ret; -- cgit v1.2.3 From 26a7bd030254c462a9e771f6edc54cb972044034 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:39 -0700 Subject: SLUB: get rid of finish_bootstrap Its only purpose was to bring some sort of symmetry to sysfs usage when dealing with bootstrapping per cpu flushing. Since we do not time out slabs anymore we have no need to run finish_bootstrap even without sysfs. Fold it back into slab_sysfs_init and drop the initcall for the !SYFS case. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 5d425d7116e..03d26f7142c 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1704,23 +1704,6 @@ static int calculate_sizes(struct kmem_cache *s) } -static int __init finish_bootstrap(void) -{ - struct list_head *h; - int err; - - slab_state = SYSFS; - - list_for_each(h, &slab_caches) { - struct kmem_cache *s = - container_of(h, struct kmem_cache, list); - - err = sysfs_slab_add(s); - BUG_ON(err); - } - return 0; -} - static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, const char *name, size_t size, size_t align, unsigned long flags, @@ -3482,6 +3465,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) static int __init slab_sysfs_init(void) { + struct list_head *h; int err; err = subsystem_register(&slab_subsys); @@ -3490,7 +3474,15 @@ static int __init slab_sysfs_init(void) return -ENOSYS; } - finish_bootstrap(); + slab_state = SYSFS; + + list_for_each(h, &slab_caches) { + struct kmem_cache *s = + container_of(h, struct kmem_cache, list); + + err = sysfs_slab_add(s); + BUG_ON(err); + } while (alias_list) { struct saved_alias *al = alias_list; @@ -3506,6 +3498,4 @@ static int __init slab_sysfs_init(void) } __initcall(slab_sysfs_init); -#else -__initcall(finish_bootstrap); #endif -- cgit v1.2.3 From 672bba3a4b2e65ed95ebd0cf764bd628bd1da74f Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:39 -0700 Subject: SLUB: update comments Update comments throughout SLUB to reflect the new developments. Fix up various awkward sentences. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 242 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 119 insertions(+), 123 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 03d26f7142c..15189d826f8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -66,11 +66,11 @@ * SLUB assigns one slab for allocation to each processor. * Allocations only occur from these slabs called cpu slabs. * - * Slabs with free elements are kept on a partial list. - * There is no list for full slabs. If an object in a full slab is + * Slabs with free elements are kept on a partial list and during regular + * operations no list for full slabs is used. If an object in a full slab is * freed then the slab will show up again on the partial lists. - * Otherwise there is no need to track full slabs unless we have to - * track full slabs for debugging purposes. + * We track full slabs for debugging purposes though because otherwise we + * cannot scan all objects. * * Slabs are freed when they become empty. Teardown and setup is * minimal so we rely on the page allocators per cpu caches for @@ -92,8 +92,8 @@ * * - The per cpu array is updated for each new slab and and is a remote * cacheline for most nodes. This could become a bouncing cacheline given - * enough frequent updates. There are 16 pointers in a cacheline.so at - * max 16 cpus could compete. Likely okay. + * enough frequent updates. There are 16 pointers in a cacheline, so at + * max 16 cpus could compete for the cacheline which may be okay. * * - Support PAGE_ALLOC_DEBUG. Should be easy to do. * @@ -137,6 +137,7 @@ #define DEBUG_DEFAULT_FLAGS (SLAB_DEBUG_FREE | SLAB_RED_ZONE | \ SLAB_POISON | SLAB_STORE_USER) + /* * Set of flags that will prevent slab merging */ @@ -171,7 +172,7 @@ static struct notifier_block slab_notifier; static enum { DOWN, /* No slab functionality available */ PARTIAL, /* kmem_cache_open() works but kmalloc does not */ - UP, /* Everything works */ + UP, /* Everything works but does not show up in sysfs */ SYSFS /* Sysfs up */ } slab_state = DOWN; @@ -245,9 +246,9 @@ static void print_section(char *text, u8 *addr, unsigned int length) /* * Slow version of get and set free pointer. * - * This requires touching the cache lines of kmem_cache. - * The offset can also be obtained from the page. In that - * case it is in the cacheline that we already need to touch. + * This version requires touching the cache lines of kmem_cache which + * we avoid to do in the fast alloc free paths. There we obtain the offset + * from the page struct. */ static void *get_freepointer(struct kmem_cache *s, void *object) { @@ -429,26 +430,34 @@ static inline int check_valid_pointer(struct kmem_cache *s, * Bytes of the object to be managed. * If the freepointer may overlay the object then the free * pointer is the first word of the object. + * * Poisoning uses 0x6b (POISON_FREE) and the last byte is * 0xa5 (POISON_END) * * object + s->objsize * Padding to reach word boundary. This is also used for Redzoning. - * Padding is extended to word size if Redzoning is enabled - * and objsize == inuse. + * Padding is extended by another word if Redzoning is enabled and + * objsize == inuse. + * * We fill with 0xbb (RED_INACTIVE) for inactive objects and with * 0xcc (RED_ACTIVE) for objects in use. * * object + s->inuse + * Meta data starts here. + * * A. Free pointer (if we cannot overwrite object on free) * B. Tracking data for SLAB_STORE_USER - * C. Padding to reach required alignment boundary - * Padding is done using 0x5a (POISON_INUSE) + * C. Padding to reach required alignment boundary or at mininum + * one word if debuggin is on to be able to detect writes + * before the word boundary. + * + * Padding is done using 0x5a (POISON_INUSE) * * object + s->size + * Nothing is used beyond s->size. * - * If slabcaches are merged then the objsize and inuse boundaries are to - * be ignored. And therefore no slab options that rely on these boundaries + * If slabcaches are merged then the objsize and inuse boundaries are mostly + * ignored. And therefore no slab options that rely on these boundaries * may be used with merged slabcaches. */ @@ -574,8 +583,7 @@ static int check_object(struct kmem_cache *s, struct page *page, /* * No choice but to zap it and thus loose the remainder * of the free objects in this slab. May cause - * another error because the object count maybe - * wrong now. + * another error because the object count is now wrong. */ set_freepointer(s, p, NULL); return 0; @@ -615,9 +623,8 @@ static int check_slab(struct kmem_cache *s, struct page *page) } /* - * Determine if a certain object on a page is on the freelist and - * therefore free. Must hold the slab lock for cpu slabs to - * guarantee that the chains are consistent. + * Determine if a certain object on a page is on the freelist. Must hold the + * slab lock to guarantee that the chains are in a consistent state. */ static int on_freelist(struct kmem_cache *s, struct page *page, void *search) { @@ -663,7 +670,7 @@ static int on_freelist(struct kmem_cache *s, struct page *page, void *search) } /* - * Tracking of fully allocated slabs for debugging + * Tracking of fully allocated slabs for debugging purposes. */ static void add_full(struct kmem_cache_node *n, struct page *page) { @@ -714,7 +721,7 @@ bad: /* * If this is a slab page then lets do the best we can * to avoid issues in the future. Marking all objects - * as used avoids touching the remainder. + * as used avoids touching the remaining objects. */ printk(KERN_ERR "@@@ SLUB: %s slab 0x%p. Marking all objects used.\n", s->name, page); @@ -970,9 +977,9 @@ static void remove_partial(struct kmem_cache *s, } /* - * Lock page and remove it from the partial list + * Lock slab and remove from the partial list. * - * Must hold list_lock + * Must hold list_lock. */ static int lock_and_del_slab(struct kmem_cache_node *n, struct page *page) { @@ -985,7 +992,7 @@ static int lock_and_del_slab(struct kmem_cache_node *n, struct page *page) } /* - * Try to get a partial slab from a specific node + * Try to allocate a partial slab from a specific node. */ static struct page *get_partial_node(struct kmem_cache_node *n) { @@ -994,7 +1001,8 @@ static struct page *get_partial_node(struct kmem_cache_node *n) /* * Racy check. If we mistakenly see no partial slabs then we * just allocate an empty slab. If we mistakenly try to get a - * partial slab then get_partials() will return NULL. + * partial slab and there is none available then get_partials() + * will return NULL. */ if (!n || !n->nr_partial) return NULL; @@ -1010,8 +1018,7 @@ out: } /* - * Get a page from somewhere. Search in increasing NUMA - * distances. + * Get a page from somewhere. Search in increasing NUMA distances. */ static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags) { @@ -1021,24 +1028,22 @@ static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags) struct page *page; /* - * The defrag ratio allows to configure the tradeoffs between - * inter node defragmentation and node local allocations. - * A lower defrag_ratio increases the tendency to do local - * allocations instead of scanning throught the partial - * lists on other nodes. + * The defrag ratio allows a configuration of the tradeoffs between + * inter node defragmentation and node local allocations. A lower + * defrag_ratio increases the tendency to do local allocations + * instead of attempting to obtain partial slabs from other nodes. * - * If defrag_ratio is set to 0 then kmalloc() always - * returns node local objects. If its higher then kmalloc() - * may return off node objects in order to avoid fragmentation. - * - * A higher ratio means slabs may be taken from other nodes - * thus reducing the number of partial slabs on those nodes. + * If the defrag_ratio is set to 0 then kmalloc() always + * returns node local objects. If the ratio is higher then kmalloc() + * may return off node objects because partial slabs are obtained + * from other nodes and filled up. * * If /sys/slab/xx/defrag_ratio is set to 100 (which makes - * defrag_ratio = 1000) then every (well almost) allocation - * will first attempt to defrag slab caches on other nodes. This - * means scanning over all nodes to look for partial slabs which - * may be a bit expensive to do on every slab allocation. + * defrag_ratio = 1000) then every (well almost) allocation will + * first attempt to defrag slab caches on other nodes. This means + * scanning over all nodes to look for partial slabs which may be + * expensive if we do it every time we are trying to find a slab + * with available objects. */ if (!s->defrag_ratio || get_cycles() % 1024 > s->defrag_ratio) return NULL; @@ -1098,11 +1103,12 @@ static void putback_slab(struct kmem_cache *s, struct page *page) } else { if (n->nr_partial < MIN_PARTIAL) { /* - * Adding an empty page to the partial slabs in order - * to avoid page allocator overhead. This page needs to - * come after all the others that are not fully empty - * in order to make sure that we do maximum - * defragmentation. + * Adding an empty slab to the partial slabs in order + * to avoid page allocator overhead. This slab needs + * to come after the other slabs with objects in + * order to fill them up. That way the size of the + * partial list stays small. kmem_cache_shrink can + * reclaim empty slabs from the partial list. */ add_partial_tail(n, page); slab_unlock(page); @@ -1170,7 +1176,7 @@ static void flush_all(struct kmem_cache *s) * 1. The page struct * 2. The first cacheline of the object to be allocated. * - * The only cache lines that are read (apart from code) is the + * The only other cache lines that are read (apart from code) is the * per cpu array in the kmem_cache struct. * * Fastpath is not possible if we need to get a new slab or have @@ -1224,9 +1230,11 @@ have_slab: cpu = smp_processor_id(); if (s->cpu_slab[cpu]) { /* - * Someone else populated the cpu_slab while we enabled - * interrupts, or we have got scheduled on another cpu. - * The page may not be on the requested node. + * Someone else populated the cpu_slab while we + * enabled interrupts, or we have gotten scheduled + * on another cpu. The page may not be on the + * requested node even if __GFP_THISNODE was + * specified. So we need to recheck. */ if (node == -1 || page_to_nid(s->cpu_slab[cpu]) == node) { @@ -1239,7 +1247,7 @@ have_slab: slab_lock(page); goto redo; } - /* Dump the current slab */ + /* New slab does not fit our expectations */ flush_slab(s, s->cpu_slab[cpu], cpu); } slab_lock(page); @@ -1280,7 +1288,8 @@ EXPORT_SYMBOL(kmem_cache_alloc_node); * The fastpath only writes the cacheline of the page struct and the first * cacheline of the object. * - * No special cachelines need to be read + * We read the cpu_slab cacheline to check if the slab is the per cpu + * slab for this processor. */ static void slab_free(struct kmem_cache *s, struct page *page, void *x, void *addr) @@ -1325,7 +1334,7 @@ out_unlock: slab_empty: if (prior) /* - * Slab on the partial list. + * Slab still on the partial list. */ remove_partial(s, page); @@ -1374,22 +1383,16 @@ static struct page *get_object_page(const void *x) } /* - * kmem_cache_open produces objects aligned at "size" and the first object - * is placed at offset 0 in the slab (We have no metainformation on the - * slab, all slabs are in essence "off slab"). - * - * In order to get the desired alignment one just needs to align the - * size. + * Object placement in a slab is made very easy because we always start at + * offset 0. If we tune the size of the object to the alignment then we can + * get the required alignment by putting one properly sized object after + * another. * * Notice that the allocation order determines the sizes of the per cpu * caches. Each processor has always one slab available for allocations. * Increasing the allocation order reduces the number of times that slabs - * must be moved on and off the partial lists and therefore may influence + * must be moved on and off the partial lists and is therefore a factor in * locking overhead. - * - * The offset is used to relocate the free list link in each object. It is - * therefore possible to move the free list link behind the object. This - * is necessary for RCU to work properly and also useful for debugging. */ /* @@ -1400,15 +1403,11 @@ static struct page *get_object_page(const void *x) */ static int slub_min_order; static int slub_max_order = DEFAULT_MAX_ORDER; - -/* - * Minimum number of objects per slab. This is necessary in order to - * reduce locking overhead. Similar to the queue size in SLAB. - */ static int slub_min_objects = DEFAULT_MIN_OBJECTS; /* * Merge control. If this is set then no merging of slab caches will occur. + * (Could be removed. This was introduced to pacify the merge skeptics.) */ static int slub_nomerge; @@ -1422,23 +1421,27 @@ static char *slub_debug_slabs; /* * Calculate the order of allocation given an slab object size. * - * The order of allocation has significant impact on other elements - * of the system. Generally order 0 allocations should be preferred - * since they do not cause fragmentation in the page allocator. Larger - * objects may have problems with order 0 because there may be too much - * space left unused in a slab. We go to a higher order if more than 1/8th - * of the slab would be wasted. + * The order of allocation has significant impact on performance and other + * system components. Generally order 0 allocations should be preferred since + * order 0 does not cause fragmentation in the page allocator. Larger objects + * be problematic to put into order 0 slabs because there may be too much + * unused space left. We go to a higher order if more than 1/8th of the slab + * would be wasted. + * + * In order to reach satisfactory performance we must ensure that a minimum + * number of objects is in one slab. Otherwise we may generate too much + * activity on the partial lists which requires taking the list_lock. This is + * less a concern for large slabs though which are rarely used. * - * In order to reach satisfactory performance we must ensure that - * a minimum number of objects is in one slab. Otherwise we may - * generate too much activity on the partial lists. This is less a - * concern for large slabs though. slub_max_order specifies the order - * where we begin to stop considering the number of objects in a slab. + * slub_max_order specifies the order where we begin to stop considering the + * number of objects in a slab as critical. If we reach slub_max_order then + * we try to keep the page order as low as possible. So we accept more waste + * of space in favor of a small page order. * - * Higher order allocations also allow the placement of more objects - * in a slab and thereby reduce object handling overhead. If the user - * has requested a higher mininum order then we start with that one - * instead of zero. + * Higher order allocations also allow the placement of more objects in a + * slab and thereby reduce object handling overhead. If the user has + * requested a higher mininum order then we start with that one instead of + * the smallest order which will fit the object. */ static int calculate_order(int size) { @@ -1458,18 +1461,18 @@ static int calculate_order(int size) rem = slab_size % size; - if (rem <= (PAGE_SIZE << order) / 8) + if (rem <= slab_size / 8) break; } if (order >= MAX_ORDER) return -E2BIG; + return order; } /* - * Function to figure out which alignment to use from the - * various ways of specifying it. + * Figure out what the alignment of the objects will be. */ static unsigned long calculate_alignment(unsigned long flags, unsigned long align, unsigned long size) @@ -1624,18 +1627,16 @@ static int calculate_sizes(struct kmem_cache *s) size = ALIGN(size, sizeof(void *)); /* - * If we are redzoning then check if there is some space between the + * If we are Redzoning then check if there is some space between the * end of the object and the free pointer. If not then add an - * additional word, so that we can establish a redzone between - * the object and the freepointer to be able to check for overwrites. + * additional word to have some bytes to store Redzone information. */ if ((flags & SLAB_RED_ZONE) && size == s->objsize) size += sizeof(void *); /* - * With that we have determined how much of the slab is in actual - * use by the object. This is the potential offset to the free - * pointer. + * With that we have determined the number of bytes in actual use + * by the object. This is the potential offset to the free pointer. */ s->inuse = size; @@ -1669,6 +1670,7 @@ static int calculate_sizes(struct kmem_cache *s) * of the object. */ size += sizeof(void *); + /* * Determine the alignment based on various parameters that the * user specified and the dynamic determination of cache line size @@ -1770,7 +1772,6 @@ EXPORT_SYMBOL(kmem_cache_open); int kmem_ptr_validate(struct kmem_cache *s, const void *object) { struct page * page; - void *addr; page = get_object_page(object); @@ -1807,7 +1808,8 @@ const char *kmem_cache_name(struct kmem_cache *s) EXPORT_SYMBOL(kmem_cache_name); /* - * Attempt to free all slabs on a node + * Attempt to free all slabs on a node. Return the number of slabs we + * were unable to free. */ static int free_list(struct kmem_cache *s, struct kmem_cache_node *n, struct list_head *list) @@ -1828,7 +1830,7 @@ static int free_list(struct kmem_cache *s, struct kmem_cache_node *n, } /* - * Release all resources used by slab cache + * Release all resources used by a slab cache. */ static int kmem_cache_close(struct kmem_cache *s) { @@ -2089,13 +2091,14 @@ void kfree(const void *x) EXPORT_SYMBOL(kfree); /* - * kmem_cache_shrink removes empty slabs from the partial lists - * and then sorts the partially allocated slabs by the number - * of items in use. The slabs with the most items in use - * come first. New allocations will remove these from the - * partial list because they are full. The slabs with the - * least items are placed last. If it happens that the objects - * are freed then the page can be returned to the page allocator. + * kmem_cache_shrink removes empty slabs from the partial lists and sorts + * the remaining slabs by the number of items in use. The slabs with the + * most items in use come first. New allocations will then fill those up + * and thus they can be removed from the partial lists. + * + * The slabs with the least items are placed last. This results in them + * being allocated from last increasing the chance that the last objects + * are freed in them. */ int kmem_cache_shrink(struct kmem_cache *s) { @@ -2124,12 +2127,10 @@ int kmem_cache_shrink(struct kmem_cache *s) spin_lock_irqsave(&n->list_lock, flags); /* - * Build lists indexed by the items in use in - * each slab or free slabs if empty. + * Build lists indexed by the items in use in each slab. * - * Note that concurrent frees may occur while - * we hold the list_lock. page->inuse here is - * the upper limit. + * Note that concurrent frees may occur while we hold the + * list_lock. page->inuse here is the upper limit. */ list_for_each_entry_safe(page, t, &n->partial, lru) { if (!page->inuse && slab_trylock(page)) { @@ -2153,8 +2154,8 @@ int kmem_cache_shrink(struct kmem_cache *s) goto out; /* - * Rebuild the partial list with the slabs filled up - * most first and the least used slabs at the end. + * Rebuild the partial list with the slabs filled up most + * first and the least used slabs at the end. */ for (i = s->objects - 1; i >= 0; i--) list_splice(slabs_by_inuse + i, n->partial.prev); @@ -2217,7 +2218,7 @@ void __init kmem_cache_init(void) #ifdef CONFIG_NUMA /* * Must first have the slab cache available for the allocations of the - * struct kmalloc_cache_node's. There is special bootstrap code in + * struct kmem_cache_node's. There is special bootstrap code in * kmem_cache_open for slab_state == DOWN. */ create_kmalloc_cache(&kmalloc_caches[0], "kmem_cache_node", @@ -2389,8 +2390,8 @@ static void for_all_slabs(void (*func)(struct kmem_cache *, int), int cpu) } /* - * Use the cpu notifier to insure that the slab are flushed - * when necessary. + * Use the cpu notifier to insure that the cpu slabs are flushed when + * necessary. */ static int __cpuinit slab_cpuup_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -2555,11 +2556,6 @@ static void resiliency_test(void) static void resiliency_test(void) {}; #endif -/* - * These are not as efficient as kmalloc for the non debug case. - * We do not have the page struct available so we have to touch one - * cacheline in struct kmem_cache to check slab flags. - */ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, void *caller) { struct kmem_cache *s = get_slab(size, gfpflags); @@ -2677,7 +2673,7 @@ static unsigned long validate_slab_cache(struct kmem_cache *s) } /* - * Generate lists of locations where slabcache objects are allocated + * Generate lists of code addresses where slabcache objects are allocated * and freed. */ @@ -2756,7 +2752,7 @@ static int add_location(struct loc_track *t, struct kmem_cache *s, } /* - * Not found. Insert new tracking element + * Not found. Insert new tracking element. */ if (t->count >= t->max && !alloc_loc_track(t, 2 * t->max)) return 0; -- cgit v1.2.3 From 7656c72b5a631452ace361037ccf8384454d0f72 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:40 -0700 Subject: SLUB: add macros for scanning objects in a slab Scanning of objects happens in a number of functions. Consolidate that code. DECLARE_BITMAP instead of coding the declaration for bitmaps. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 75 +++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 15189d826f8..e97627b0733 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -208,6 +208,38 @@ static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) #endif } +/* + * Slow version of get and set free pointer. + * + * This version requires touching the cache lines of kmem_cache which + * we avoid to do in the fast alloc free paths. There we obtain the offset + * from the page struct. + */ +static inline void *get_freepointer(struct kmem_cache *s, void *object) +{ + return *(void **)(object + s->offset); +} + +static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) +{ + *(void **)(object + s->offset) = fp; +} + +/* Loop over all objects in a slab */ +#define for_each_object(__p, __s, __addr) \ + for (__p = (__addr); __p < (__addr) + (__s)->objects * (__s)->size;\ + __p += (__s)->size) + +/* Scan freelist */ +#define for_each_free_object(__p, __s, __free) \ + for (__p = (__free); __p; __p = get_freepointer((__s), __p)) + +/* Determine object index from a given position */ +static inline int slab_index(void *p, struct kmem_cache *s, void *addr) +{ + return (p - addr) / s->size; +} + /* * Object debugging */ @@ -243,23 +275,6 @@ static void print_section(char *text, u8 *addr, unsigned int length) } } -/* - * Slow version of get and set free pointer. - * - * This version requires touching the cache lines of kmem_cache which - * we avoid to do in the fast alloc free paths. There we obtain the offset - * from the page struct. - */ -static void *get_freepointer(struct kmem_cache *s, void *object) -{ - return *(void **)(object + s->offset); -} - -static void set_freepointer(struct kmem_cache *s, void *object, void *fp) -{ - *(void **)(object + s->offset) = fp; -} - /* * Tracking user of a slab. */ @@ -852,7 +867,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) memset(start, POISON_INUSE, PAGE_SIZE << s->order); last = start; - for (p = start + s->size; p < end; p += s->size) { + for_each_object(p, s, start) { setup_object(s, page, last); set_freepointer(s, last, p); last = p; @@ -873,12 +888,10 @@ static void __free_slab(struct kmem_cache *s, struct page *page) int pages = 1 << s->order; if (unlikely(PageError(page) || s->dtor)) { - void *start = page_address(page); - void *end = start + (pages << PAGE_SHIFT); void *p; slab_pad_check(s, page); - for (p = start; p <= end - s->size; p += s->size) { + for_each_object(p, s, page_address(page)) { if (s->dtor) s->dtor(p, s, 0); check_object(s, page, p, 0); @@ -2583,7 +2596,7 @@ static int validate_slab(struct kmem_cache *s, struct page *page) { void *p; void *addr = page_address(page); - unsigned long map[BITS_TO_LONGS(s->objects)]; + DECLARE_BITMAP(map, s->objects); if (!check_slab(s, page) || !on_freelist(s, page, NULL)) @@ -2592,14 +2605,14 @@ static int validate_slab(struct kmem_cache *s, struct page *page) /* Now we know that a valid freelist exists */ bitmap_zero(map, s->objects); - for(p = page->freelist; p; p = get_freepointer(s, p)) { - set_bit((p - addr) / s->size, map); + for_each_free_object(p, s, page->freelist) { + set_bit(slab_index(p, s, addr), map); if (!check_object(s, page, p, 0)) return 0; } - for(p = addr; p < addr + s->objects * s->size; p += s->size) - if (!test_bit((p - addr) / s->size, map)) + for_each_object(p, s, addr) + if (!test_bit(slab_index(p, s, addr), map)) if (!check_object(s, page, p, 1)) return 0; return 1; @@ -2771,15 +2784,15 @@ static void process_slab(struct loc_track *t, struct kmem_cache *s, struct page *page, enum track_item alloc) { void *addr = page_address(page); - unsigned long map[BITS_TO_LONGS(s->objects)]; + DECLARE_BITMAP(map, s->objects); void *p; bitmap_zero(map, s->objects); - for (p = page->freelist; p; p = get_freepointer(s, p)) - set_bit((p - addr) / s->size, map); + for_each_free_object(p, s, page->freelist) + set_bit(slab_index(p, s, addr), map); - for (p = addr; p < addr + s->objects * s->size; p += s->size) - if (!test_bit((p - addr) / s->size, map)) { + for_each_object(p, s, addr) + if (!test_bit(slab_index(p, s, addr), map)) { void *addr = get_track(s, p, alloc)->addr; add_location(t, s, addr); -- cgit v1.2.3 From b345970905e34c1b632fe4d80e2af14c7de99b45 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:41 -0700 Subject: SLUB: move resiliency check into SYSFS section Move the resiliency check into the SYSFS section after validate_slab that is used by the resiliency check. This will avoid a forward declaration. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 112 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index e97627b0733..07492a83b46 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2512,63 +2512,6 @@ static int __init cpucache_init(void) __initcall(cpucache_init); #endif -#ifdef SLUB_RESILIENCY_TEST -static unsigned long validate_slab_cache(struct kmem_cache *s); - -static void resiliency_test(void) -{ - u8 *p; - - printk(KERN_ERR "SLUB resiliency testing\n"); - printk(KERN_ERR "-----------------------\n"); - printk(KERN_ERR "A. Corruption after allocation\n"); - - p = kzalloc(16, GFP_KERNEL); - p[16] = 0x12; - printk(KERN_ERR "\n1. kmalloc-16: Clobber Redzone/next pointer" - " 0x12->0x%p\n\n", p + 16); - - validate_slab_cache(kmalloc_caches + 4); - - /* Hmmm... The next two are dangerous */ - p = kzalloc(32, GFP_KERNEL); - p[32 + sizeof(void *)] = 0x34; - printk(KERN_ERR "\n2. kmalloc-32: Clobber next pointer/next slab" - " 0x34 -> -0x%p\n", p); - printk(KERN_ERR "If allocated object is overwritten then not detectable\n\n"); - - validate_slab_cache(kmalloc_caches + 5); - p = kzalloc(64, GFP_KERNEL); - p += 64 + (get_cycles() & 0xff) * sizeof(void *); - *p = 0x56; - printk(KERN_ERR "\n3. kmalloc-64: corrupting random byte 0x56->0x%p\n", - p); - printk(KERN_ERR "If allocated object is overwritten then not detectable\n\n"); - validate_slab_cache(kmalloc_caches + 6); - - printk(KERN_ERR "\nB. Corruption after free\n"); - p = kzalloc(128, GFP_KERNEL); - kfree(p); - *p = 0x78; - printk(KERN_ERR "1. kmalloc-128: Clobber first word 0x78->0x%p\n\n", p); - validate_slab_cache(kmalloc_caches + 7); - - p = kzalloc(256, GFP_KERNEL); - kfree(p); - p[50] = 0x9a; - printk(KERN_ERR "\n2. kmalloc-256: Clobber 50th byte 0x9a->0x%p\n\n", p); - validate_slab_cache(kmalloc_caches + 8); - - p = kzalloc(512, GFP_KERNEL); - kfree(p); - p[512] = 0xab; - printk(KERN_ERR "\n3. kmalloc-512: Clobber redzone 0xab->0x%p\n\n", p); - validate_slab_cache(kmalloc_caches + 9); -} -#else -static void resiliency_test(void) {}; -#endif - void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, void *caller) { struct kmem_cache *s = get_slab(size, gfpflags); @@ -2685,6 +2628,61 @@ static unsigned long validate_slab_cache(struct kmem_cache *s) return count; } +#ifdef SLUB_RESILIENCY_TEST +static void resiliency_test(void) +{ + u8 *p; + + printk(KERN_ERR "SLUB resiliency testing\n"); + printk(KERN_ERR "-----------------------\n"); + printk(KERN_ERR "A. Corruption after allocation\n"); + + p = kzalloc(16, GFP_KERNEL); + p[16] = 0x12; + printk(KERN_ERR "\n1. kmalloc-16: Clobber Redzone/next pointer" + " 0x12->0x%p\n\n", p + 16); + + validate_slab_cache(kmalloc_caches + 4); + + /* Hmmm... The next two are dangerous */ + p = kzalloc(32, GFP_KERNEL); + p[32 + sizeof(void *)] = 0x34; + printk(KERN_ERR "\n2. kmalloc-32: Clobber next pointer/next slab" + " 0x34 -> -0x%p\n", p); + printk(KERN_ERR "If allocated object is overwritten then not detectable\n\n"); + + validate_slab_cache(kmalloc_caches + 5); + p = kzalloc(64, GFP_KERNEL); + p += 64 + (get_cycles() & 0xff) * sizeof(void *); + *p = 0x56; + printk(KERN_ERR "\n3. kmalloc-64: corrupting random byte 0x56->0x%p\n", + p); + printk(KERN_ERR "If allocated object is overwritten then not detectable\n\n"); + validate_slab_cache(kmalloc_caches + 6); + + printk(KERN_ERR "\nB. Corruption after free\n"); + p = kzalloc(128, GFP_KERNEL); + kfree(p); + *p = 0x78; + printk(KERN_ERR "1. kmalloc-128: Clobber first word 0x78->0x%p\n\n", p); + validate_slab_cache(kmalloc_caches + 7); + + p = kzalloc(256, GFP_KERNEL); + kfree(p); + p[50] = 0x9a; + printk(KERN_ERR "\n2. kmalloc-256: Clobber 50th byte 0x9a->0x%p\n\n", p); + validate_slab_cache(kmalloc_caches + 8); + + p = kzalloc(512, GFP_KERNEL); + kfree(p); + p[512] = 0xab; + printk(KERN_ERR "\n3. kmalloc-512: Clobber redzone 0xab->0x%p\n\n", p); + validate_slab_cache(kmalloc_caches + 9); +} +#else +static void resiliency_test(void) {}; +#endif + /* * Generate lists of code addresses where slabcache objects are allocated * and freed. -- cgit v1.2.3 From 35e5d7ee27680aef6dc3fab45a5ecd9952d9791a Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:42 -0700 Subject: SLUB: introduce DebugSlab(page) This replaces the PageError() checking. DebugSlab is clearer and allows for future changes to the page bit used. We also need it to support CONFIG_SLUB_DEBUG. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 07492a83b46..c58a974d15a 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -87,6 +87,21 @@ * the fast path. */ +static inline int SlabDebug(struct page *page) +{ + return PageError(page); +} + +static inline void SetSlabDebug(struct page *page) +{ + SetPageError(page); +} + +static inline void ClearSlabDebug(struct page *page) +{ + ClearPageError(page); +} + /* * Issues still to be resolved: * @@ -823,7 +838,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node) static void setup_object(struct kmem_cache *s, struct page *page, void *object) { - if (PageError(page)) { + if (SlabDebug(page)) { init_object(s, object, 0); init_tracking(s, object); } @@ -858,7 +873,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) page->flags |= 1 << PG_slab; if (s->flags & (SLAB_DEBUG_FREE | SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | SLAB_TRACE)) - page->flags |= 1 << PG_error; + SetSlabDebug(page); start = page_address(page); end = start + s->objects * s->size; @@ -887,7 +902,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) { int pages = 1 << s->order; - if (unlikely(PageError(page) || s->dtor)) { + if (unlikely(SlabDebug(page) || s->dtor)) { void *p; slab_pad_check(s, page); @@ -934,7 +949,8 @@ static void discard_slab(struct kmem_cache *s, struct page *page) atomic_long_dec(&n->nr_slabs); reset_page_mapcount(page); - page->flags &= ~(1 << PG_slab | 1 << PG_error); + ClearSlabDebug(page); + __ClearPageSlab(page); free_slab(s, page); } @@ -1109,7 +1125,7 @@ static void putback_slab(struct kmem_cache *s, struct page *page) if (page->freelist) add_partial(n, page); - else if (PageError(page) && (s->flags & SLAB_STORE_USER)) + else if (SlabDebug(page) && (s->flags & SLAB_STORE_USER)) add_full(n, page); slab_unlock(page); @@ -1193,7 +1209,7 @@ static void flush_all(struct kmem_cache *s) * per cpu array in the kmem_cache struct. * * Fastpath is not possible if we need to get a new slab or have - * debugging enabled (which means all slabs are marked with PageError) + * debugging enabled (which means all slabs are marked with SlabDebug) */ static void *slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, void *addr) @@ -1216,7 +1232,7 @@ redo: object = page->freelist; if (unlikely(!object)) goto another_slab; - if (unlikely(PageError(page))) + if (unlikely(SlabDebug(page))) goto debug; have_object: @@ -1314,7 +1330,7 @@ static void slab_free(struct kmem_cache *s, struct page *page, local_irq_save(flags); slab_lock(page); - if (unlikely(PageError(page))) + if (unlikely(SlabDebug(page))) goto debug; checks_ok: prior = object[page->offset] = page->freelist; @@ -2571,12 +2587,12 @@ static void validate_slab_slab(struct kmem_cache *s, struct page *page) s->name, page); if (s->flags & DEBUG_DEFAULT_FLAGS) { - if (!PageError(page)) - printk(KERN_ERR "SLUB %s: PageError not set " + if (!SlabDebug(page)) + printk(KERN_ERR "SLUB %s: SlabDebug not set " "on slab 0x%p\n", s->name, page); } else { - if (PageError(page)) - printk(KERN_ERR "SLUB %s: PageError set on " + if (SlabDebug(page)) + printk(KERN_ERR "SLUB %s: SlabDebug set on " "slab 0x%p\n", s->name, page); } } -- cgit v1.2.3 From 636f0d7de8dc0282cce9905e035c04dd60db19dd Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:42 -0700 Subject: SLUB: consolidate trace code Trace in both slab_alloc and slab_free has a lot of common code. Use a single function for both. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index c58a974d15a..dfbd0d874a0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -805,6 +805,22 @@ fail: return 0; } +static void trace(struct kmem_cache *s, struct page *page, void *object, int alloc) +{ + if (s->flags & SLAB_TRACE) { + printk(KERN_INFO "TRACE %s %s 0x%p inuse=%d fp=0x%p\n", + s->name, + alloc ? "alloc" : "free", + object, page->inuse, + page->freelist); + + if (!alloc) + print_section("Object", (void *)object, s->objsize); + + dump_stack(); + } +} + /* * Slab allocation and freeing */ @@ -1289,12 +1305,7 @@ debug: goto another_slab; if (s->flags & SLAB_STORE_USER) set_track(s, object, TRACK_ALLOC, addr); - if (s->flags & SLAB_TRACE) { - printk(KERN_INFO "TRACE %s alloc 0x%p inuse=%d fp=0x%p\n", - s->name, object, page->inuse, - page->freelist); - dump_stack(); - } + trace(s, page, object, 1); init_object(s, object, 1); goto have_object; } @@ -1379,13 +1390,7 @@ debug: remove_full(s, page); if (s->flags & SLAB_STORE_USER) set_track(s, x, TRACK_FREE, addr); - if (s->flags & SLAB_TRACE) { - printk(KERN_INFO "TRACE %s free 0x%p inuse=%d fp=0x%p\n", - s->name, object, page->inuse, - page->freelist); - print_section("Object", (void *)object, s->objsize); - dump_stack(); - } + trace(s, page, object, 0); init_object(s, object, 0); goto checks_ok; } -- cgit v1.2.3 From 02cbc874463924de2c3403379b698bce3cd277a5 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:43 -0700 Subject: SLUB: move tracking definitions and check_valid_pointer() away from debug code Move the tracking definitions and the check_valid_pointer() function away from the debugging related functions. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 58 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index dfbd0d874a0..b9e05366afd 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -195,6 +195,18 @@ static enum { static DECLARE_RWSEM(slub_lock); LIST_HEAD(slab_caches); +/* + * Tracking user of a slab. + */ +struct track { + void *addr; /* Called from address */ + int cpu; /* Was running on cpu */ + int pid; /* Pid context */ + unsigned long when; /* When did the operation occur */ +}; + +enum track_item { TRACK_ALLOC, TRACK_FREE }; + #ifdef CONFIG_SYSFS static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); @@ -223,6 +235,23 @@ static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) #endif } +static inline int check_valid_pointer(struct kmem_cache *s, + struct page *page, const void *object) +{ + void *base; + + if (!object) + return 1; + + base = page_address(page); + if (object < base || object >= base + s->objects * s->size || + (object - base) % s->size) { + return 0; + } + + return 1; +} + /* * Slow version of get and set free pointer. * @@ -290,18 +319,6 @@ static void print_section(char *text, u8 *addr, unsigned int length) } } -/* - * Tracking user of a slab. - */ -struct track { - void *addr; /* Called from address */ - int cpu; /* Was running on cpu */ - int pid; /* Pid context */ - unsigned long when; /* When did the operation occur */ -}; - -enum track_item { TRACK_ALLOC, TRACK_FREE }; - static struct track *get_track(struct kmem_cache *s, void *object, enum track_item alloc) { @@ -436,23 +453,6 @@ static int check_bytes(u8 *start, unsigned int value, unsigned int bytes) return 1; } -static inline int check_valid_pointer(struct kmem_cache *s, - struct page *page, const void *object) -{ - void *base; - - if (!object) - return 1; - - base = page_address(page); - if (object < base || object >= base + s->objects * s->size || - (object - base) % s->size) { - return 0; - } - - return 1; -} - /* * Object layout: * -- cgit v1.2.3 From 41ecc55b8a8ca618e6d490982c7ce45d230d4399 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:44 -0700 Subject: SLUB: add CONFIG_SLUB_DEBUG CONFIG_SLUB_DEBUG can be used to switch off the debugging and sysfs components of SLUB. Thus SLUB will be able to replace SLOB. SLUB can arrange objects in a denser way than SLOB and the code size should be minimal without debugging and sysfs support. Note that CONFIG_SLUB_DEBUG is materially different from CONFIG_SLAB_DEBUG. CONFIG_SLAB_DEBUG is used to enable slab debugging in SLAB. SLUB enables debugging via a boot parameter. SLUB debug code should always be present. CONFIG_SLUB_DEBUG can be modified in the embedded config section. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- init/Kconfig | 9 +++ mm/slub.c | 189 +++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 123 insertions(+), 75 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index d0edf42f4db..da6a91c4a05 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -504,6 +504,15 @@ config VM_EVENT_COUNTERS on EMBEDDED systems. /proc/vmstat will only show page counts if VM event counters are disabled. +config SLUB_DEBUG + default y + bool "Enable SLUB debugging support" if EMBEDDED + help + SLUB has extensive debug support features. Disabling these can + result in significant savings in code size. This also disables + SLUB sysfs support. /sys/slab will not exist and there will be + no support for cache validation etc. + choice prompt "Choose SLAB allocator" default SLAB diff --git a/mm/slub.c b/mm/slub.c index b9e05366afd..3e614c1901b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -89,17 +89,25 @@ static inline int SlabDebug(struct page *page) { +#ifdef CONFIG_SLUB_DEBUG return PageError(page); +#else + return 0; +#endif } static inline void SetSlabDebug(struct page *page) { +#ifdef CONFIG_SLUB_DEBUG SetPageError(page); +#endif } static inline void ClearSlabDebug(struct page *page) { +#ifdef CONFIG_SLUB_DEBUG ClearPageError(page); +#endif } /* @@ -207,7 +215,7 @@ struct track { enum track_item { TRACK_ALLOC, TRACK_FREE }; -#ifdef CONFIG_SYSFS +#if defined(CONFIG_SYSFS) && defined(CONFIG_SLUB_DEBUG) static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); static void sysfs_slab_remove(struct kmem_cache *); @@ -284,6 +292,14 @@ static inline int slab_index(void *p, struct kmem_cache *s, void *addr) return (p - addr) / s->size; } +#ifdef CONFIG_SLUB_DEBUG +/* + * Debug settings: + */ +static int slub_debug; + +static char *slub_debug_slabs; + /* * Object debugging */ @@ -821,6 +837,97 @@ static void trace(struct kmem_cache *s, struct page *page, void *object, int all } } +static int __init setup_slub_debug(char *str) +{ + if (!str || *str != '=') + slub_debug = DEBUG_DEFAULT_FLAGS; + else { + str++; + if (*str == 0 || *str == ',') + slub_debug = DEBUG_DEFAULT_FLAGS; + else + for( ;*str && *str != ','; str++) + switch (*str) { + case 'f' : case 'F' : + slub_debug |= SLAB_DEBUG_FREE; + break; + case 'z' : case 'Z' : + slub_debug |= SLAB_RED_ZONE; + break; + case 'p' : case 'P' : + slub_debug |= SLAB_POISON; + break; + case 'u' : case 'U' : + slub_debug |= SLAB_STORE_USER; + break; + case 't' : case 'T' : + slub_debug |= SLAB_TRACE; + break; + default: + printk(KERN_ERR "slub_debug option '%c' " + "unknown. skipped\n",*str); + } + } + + if (*str == ',') + slub_debug_slabs = str + 1; + return 1; +} + +__setup("slub_debug", setup_slub_debug); + +static void kmem_cache_open_debug_check(struct kmem_cache *s) +{ + /* + * The page->offset field is only 16 bit wide. This is an offset + * in units of words from the beginning of an object. If the slab + * size is bigger then we cannot move the free pointer behind the + * object anymore. + * + * On 32 bit platforms the limit is 256k. On 64bit platforms + * the limit is 512k. + * + * Debugging or ctor/dtors may create a need to move the free + * pointer. Fail if this happens. + */ + if (s->size >= 65535 * sizeof(void *)) { + BUG_ON(s->flags & (SLAB_RED_ZONE | SLAB_POISON | + SLAB_STORE_USER | SLAB_DESTROY_BY_RCU)); + BUG_ON(s->ctor || s->dtor); + } + else + /* + * Enable debugging if selected on the kernel commandline. + */ + if (slub_debug && (!slub_debug_slabs || + strncmp(slub_debug_slabs, s->name, + strlen(slub_debug_slabs)) == 0)) + s->flags |= slub_debug; +} +#else + +static inline int alloc_object_checks(struct kmem_cache *s, + struct page *page, void *object) { return 0; } + +static inline int free_object_checks(struct kmem_cache *s, + struct page *page, void *object) { return 0; } + +static inline void add_full(struct kmem_cache_node *n, struct page *page) {} +static inline void remove_full(struct kmem_cache *s, struct page *page) {} +static inline void trace(struct kmem_cache *s, struct page *page, + void *object, int alloc) {} +static inline void init_object(struct kmem_cache *s, + void *object, int active) {} +static inline void init_tracking(struct kmem_cache *s, void *object) {} +static inline int slab_pad_check(struct kmem_cache *s, struct page *page) + { return 1; } +static inline int check_object(struct kmem_cache *s, struct page *page, + void *object, int active) { return 1; } +static inline void set_track(struct kmem_cache *s, void *object, + enum track_item alloc, void *addr) {} +static inline void kmem_cache_open_debug_check(struct kmem_cache *s) {} +#define slub_debug 0 +#endif /* * Slab allocation and freeing */ @@ -1445,13 +1552,6 @@ static int slub_min_objects = DEFAULT_MIN_OBJECTS; */ static int slub_nomerge; -/* - * Debug settings: - */ -static int slub_debug; - -static char *slub_debug_slabs; - /* * Calculate the order of allocation given an slab object size. * @@ -1660,6 +1760,7 @@ static int calculate_sizes(struct kmem_cache *s) */ size = ALIGN(size, sizeof(void *)); +#ifdef CONFIG_SLUB_DEBUG /* * If we are Redzoning then check if there is some space between the * end of the object and the free pointer. If not then add an @@ -1667,6 +1768,7 @@ static int calculate_sizes(struct kmem_cache *s) */ if ((flags & SLAB_RED_ZONE) && size == s->objsize) size += sizeof(void *); +#endif /* * With that we have determined the number of bytes in actual use @@ -1674,6 +1776,7 @@ static int calculate_sizes(struct kmem_cache *s) */ s->inuse = size; +#ifdef CONFIG_SLUB_DEBUG if (((flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)) || s->ctor || s->dtor)) { /* @@ -1704,6 +1807,7 @@ static int calculate_sizes(struct kmem_cache *s) * of the object. */ size += sizeof(void *); +#endif /* * Determine the alignment based on various parameters that the @@ -1753,32 +1857,7 @@ static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, s->objsize = size; s->flags = flags; s->align = align; - - /* - * The page->offset field is only 16 bit wide. This is an offset - * in units of words from the beginning of an object. If the slab - * size is bigger then we cannot move the free pointer behind the - * object anymore. - * - * On 32 bit platforms the limit is 256k. On 64bit platforms - * the limit is 512k. - * - * Debugging or ctor/dtors may create a need to move the free - * pointer. Fail if this happens. - */ - if (s->size >= 65535 * sizeof(void *)) { - BUG_ON(flags & (SLAB_RED_ZONE | SLAB_POISON | - SLAB_STORE_USER | SLAB_DESTROY_BY_RCU)); - BUG_ON(ctor || dtor); - } - else - /* - * Enable debugging if selected on the kernel commandline. - */ - if (slub_debug && (!slub_debug_slabs || - strncmp(slub_debug_slabs, name, - strlen(slub_debug_slabs)) == 0)) - s->flags |= slub_debug; + kmem_cache_open_debug_check(s); if (!calculate_sizes(s)) goto error; @@ -1949,45 +2028,6 @@ static int __init setup_slub_nomerge(char *str) __setup("slub_nomerge", setup_slub_nomerge); -static int __init setup_slub_debug(char *str) -{ - if (!str || *str != '=') - slub_debug = DEBUG_DEFAULT_FLAGS; - else { - str++; - if (*str == 0 || *str == ',') - slub_debug = DEBUG_DEFAULT_FLAGS; - else - for( ;*str && *str != ','; str++) - switch (*str) { - case 'f' : case 'F' : - slub_debug |= SLAB_DEBUG_FREE; - break; - case 'z' : case 'Z' : - slub_debug |= SLAB_RED_ZONE; - break; - case 'p' : case 'P' : - slub_debug |= SLAB_POISON; - break; - case 'u' : case 'U' : - slub_debug |= SLAB_STORE_USER; - break; - case 't' : case 'T' : - slub_debug |= SLAB_TRACE; - break; - default: - printk(KERN_ERR "slub_debug option '%c' " - "unknown. skipped\n",*str); - } - } - - if (*str == ',') - slub_debug_slabs = str + 1; - return 1; -} - -__setup("slub_debug", setup_slub_debug); - static struct kmem_cache *create_kmalloc_cache(struct kmem_cache *s, const char *name, int size, gfp_t gfp_flags) { @@ -2554,8 +2594,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags, return slab_alloc(s, gfpflags, node, caller); } -#ifdef CONFIG_SYSFS - +#if defined(CONFIG_SYSFS) && defined(CONFIG_SLUB_DEBUG) static int validate_slab(struct kmem_cache *s, struct page *page) { void *p; -- cgit v1.2.3 From 45edfa580b8e638c44ec26872bfe75b307ba12d1 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:45 -0700 Subject: SLUB: include lifetime stats and sets of cpus / nodes in tracking output We have information about how long an object existed and about the nodes and cpus where the allocations and frees took place. Add that information to the tracking output in /sys/slab/xx/alloc_calls and /sys/slab/free_calls This will then enable slabinfo to output nice reports like this: christoph@qirst:~/slub$ ./slabinfo kmalloc-128 Slabcache: kmalloc-128 Aliases: 0 Order : 0 Sizes (bytes) Slabs Debug Memory ------------------------------------------------------------------------ Object : 128 Total : 12 Sanity Checks : On Total: 49152 SlabObj: 200 Full : 7 Redzoning : On Used : 24832 SlabSiz: 4096 Partial: 4 Poisoning : On Loss : 24320 Loss : 72 CpuSlab: 1 Tracking : On Lalig: 13968 Align : 8 Objects: 20 Tracing : Off Lpadd: 1152 kmalloc-128 has no kmem_cache operations kmalloc-128: Kernel object allocation ----------------------------------------------------------------------- 6 param_sysfs_setup+0x71/0x130 age=284512/284512/284512 pid=1 nodes=0-1,3 11 percpu_populate+0x39/0x80 age=283914/284428/284512 pid=1 nodes=0 21 __register_chrdev_region+0x31/0x170 age=282896/284347/284473 pid=1-1705 nodes=0-2 1 sys_inotify_init+0x76/0x1c0 age=283423 pid=1004 nodes=0 19 as_get_io_context+0x32/0xd0 age=6/247567/283988 pid=1-11782 nodes=0,2 10 ida_pre_get+0x4a/0x80 age=277666/283773/284526 pid=0-2177 nodes=0,2 24 kobject_kset_add_dir+0x37/0xb0 age=282727/283860/284472 pid=1-1723 nodes=0-2 1 acpi_ds_build_internal_buffer_obj+0xd3/0x11d age=284508 pid=1 nodes=0 24 con_insert_unipair+0xd7/0x110 age=284438/284438/284438 pid=1 nodes=0,2 1 uart_open+0x2d2/0x4b0 age=283896 pid=1 nodes=0 26 dma_pool_create+0x73/0x1a0 age=282762/282833/282916 pid=1705-1723 nodes=0 1 neigh_table_init_no_netlink+0xd2/0x210 age=284461 pid=1 nodes=0 2 neigh_parms_alloc+0x2b/0xe0 age=284410/284411/284412 pid=1 nodes=2 2 neigh_resolve_output+0x1e1/0x280 age=276289/276291/276293 pid=0-2443 nodes=0 1 netlink_kernel_create+0x90/0x170 age=284472 pid=1 nodes=0 4 xt_alloc_table_info+0x39/0xf0 age=283958/283958/283959 pid=1 nodes=1 3 fn_hash_insert+0x473/0x720 age=277653/277661/277666 pid=2177-2185 nodes=0 1 get_mtrr_state+0x285/0x2a0 age=284526 pid=0 nodes=0 1 cacheinfo_cpu_callback+0x26d/0x3e0 age=284458 pid=1 nodes=0 29 kernel_param_sysfs_setup+0x25/0x90 age=284511/284511/284512 pid=1 nodes=0-1,3 5 process_zones+0x5e/0x170 age=284546/284546/284546 pid=0 nodes=0 1 drm_core_init+0x48/0x160 age=284421 pid=1 nodes=2 kmalloc-128: Kernel object freeing ------------------------------------------------------------------------ 163 age=4295176847 pid=0 nodes=0-3 1 __vunmap+0x6e/0xf0 age=282907 pid=1723 nodes=0 28 free_as_io_context+0x12/0x90 age=9243/262197/283474 pid=42-11754 nodes=0 1 acpi_get_object_info+0x1b7/0x1d4 age=284475 pid=1 nodes=0 1 do_acpi_find_child+0x45/0x4e age=284475 pid=1 nodes=0 NUMA nodes : 0 1 2 3 ------------------------------------------ All slabs 7 2 2 1 Partial slabs 2 2 0 0 Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 15 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 3e614c1901b..ae283102780 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1586,13 +1586,16 @@ static int calculate_order(int size) order < MAX_ORDER; order++) { unsigned long slab_size = PAGE_SIZE << order; - if (slub_max_order > order && + if (order < slub_max_order && slab_size < slub_min_objects * size) continue; if (slab_size < size) continue; + if (order >= slub_max_order) + break; + rem = slab_size % size; if (rem <= slab_size / 8) @@ -2751,6 +2754,13 @@ static void resiliency_test(void) {}; struct location { unsigned long count; void *addr; + long long sum_time; + long min_time; + long max_time; + long min_pid; + long max_pid; + cpumask_t cpus; + nodemask_t nodes; }; struct loc_track { @@ -2791,11 +2801,12 @@ static int alloc_loc_track(struct loc_track *t, unsigned long max) } static int add_location(struct loc_track *t, struct kmem_cache *s, - void *addr) + const struct track *track) { long start, end, pos; struct location *l; void *caddr; + unsigned long age = jiffies - track->when; start = -1; end = t->count; @@ -2811,12 +2822,29 @@ static int add_location(struct loc_track *t, struct kmem_cache *s, break; caddr = t->loc[pos].addr; - if (addr == caddr) { - t->loc[pos].count++; + if (track->addr == caddr) { + + l = &t->loc[pos]; + l->count++; + if (track->when) { + l->sum_time += age; + if (age < l->min_time) + l->min_time = age; + if (age > l->max_time) + l->max_time = age; + + if (track->pid < l->min_pid) + l->min_pid = track->pid; + if (track->pid > l->max_pid) + l->max_pid = track->pid; + + cpu_set(track->cpu, l->cpus); + } + node_set(page_to_nid(virt_to_page(track)), l->nodes); return 1; } - if (addr < caddr) + if (track->addr < caddr) end = pos; else start = pos; @@ -2834,7 +2862,16 @@ static int add_location(struct loc_track *t, struct kmem_cache *s, (t->count - pos) * sizeof(struct location)); t->count++; l->count = 1; - l->addr = addr; + l->addr = track->addr; + l->sum_time = age; + l->min_time = age; + l->max_time = age; + l->min_pid = track->pid; + l->max_pid = track->pid; + cpus_clear(l->cpus); + cpu_set(track->cpu, l->cpus); + nodes_clear(l->nodes); + node_set(page_to_nid(virt_to_page(track)), l->nodes); return 1; } @@ -2850,11 +2887,8 @@ static void process_slab(struct loc_track *t, struct kmem_cache *s, set_bit(slab_index(p, s, addr), map); for_each_object(p, s, addr) - if (!test_bit(slab_index(p, s, addr), map)) { - void *addr = get_track(s, p, alloc)->addr; - - add_location(t, s, addr); - } + if (!test_bit(slab_index(p, s, addr), map)) + add_location(t, s, get_track(s, p, alloc)); } static int list_locations(struct kmem_cache *s, char *buf, @@ -2888,15 +2922,47 @@ static int list_locations(struct kmem_cache *s, char *buf, } for (i = 0; i < t.count; i++) { - void *addr = t.loc[i].addr; + struct location *l = &t.loc[i]; if (n > PAGE_SIZE - 100) break; - n += sprintf(buf + n, "%7ld ", t.loc[i].count); - if (addr) - n += sprint_symbol(buf + n, (unsigned long)t.loc[i].addr); + n += sprintf(buf + n, "%7ld ", l->count); + + if (l->addr) + n += sprint_symbol(buf + n, (unsigned long)l->addr); else n += sprintf(buf + n, ""); + + if (l->sum_time != l->min_time) { + unsigned long remainder; + + n += sprintf(buf + n, " age=%ld/%ld/%ld", + l->min_time, + div_long_long_rem(l->sum_time, l->count, &remainder), + l->max_time); + } else + n += sprintf(buf + n, " age=%ld", + l->min_time); + + if (l->min_pid != l->max_pid) + n += sprintf(buf + n, " pid=%ld-%ld", + l->min_pid, l->max_pid); + else + n += sprintf(buf + n, " pid=%ld", + l->min_pid); + + if (num_online_cpus() > 1 && !cpus_empty(l->cpus)) { + n += sprintf(buf + n, " cpus="); + n += cpulist_scnprintf(buf + n, PAGE_SIZE - n - 50, + l->cpus); + } + + if (num_online_nodes() > 1 && !nodes_empty(l->nodes)) { + n += sprintf(buf + n, " nodes="); + n += nodelist_scnprintf(buf + n, PAGE_SIZE - n - 50, + l->nodes); + } + n += sprintf(buf + n, "\n"); } -- cgit v1.2.3 From 5e6d444ea1f72b8148354a9baf0ea8fa3dd0425b Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:46 -0700 Subject: SLUB: rework slab order determination In some cases SLUB is creating uselessly slabs that are larger than slub_max_order. Also the layout of some of the slabs was not satisfactory. Go to an iterarive approach. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slub.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index ae283102780..c81f52a7215 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1577,37 +1577,75 @@ static int slub_nomerge; * requested a higher mininum order then we start with that one instead of * the smallest order which will fit the object. */ -static int calculate_order(int size) +static inline int slab_order(int size, int min_objects, + int max_order, int fract_leftover) { int order; int rem; - for (order = max(slub_min_order, fls(size - 1) - PAGE_SHIFT); - order < MAX_ORDER; order++) { - unsigned long slab_size = PAGE_SIZE << order; + for (order = max(slub_min_order, + fls(min_objects * size - 1) - PAGE_SHIFT); + order <= max_order; order++) { - if (order < slub_max_order && - slab_size < slub_min_objects * size) - continue; + unsigned long slab_size = PAGE_SIZE << order; - if (slab_size < size) + if (slab_size < min_objects * size) continue; - if (order >= slub_max_order) - break; - rem = slab_size % size; - if (rem <= slab_size / 8) + if (rem <= slab_size / fract_leftover) break; } - if (order >= MAX_ORDER) - return -E2BIG; return order; } +static inline int calculate_order(int size) +{ + int order; + int min_objects; + int fraction; + + /* + * Attempt to find best configuration for a slab. This + * works by first attempting to generate a layout with + * the best configuration and backing off gradually. + * + * First we reduce the acceptable waste in a slab. Then + * we reduce the minimum objects required in a slab. + */ + min_objects = slub_min_objects; + while (min_objects > 1) { + fraction = 8; + while (fraction >= 4) { + order = slab_order(size, min_objects, + slub_max_order, fraction); + if (order <= slub_max_order) + return order; + fraction /= 2; + } + min_objects /= 2; + } + + /* + * We were unable to place multiple objects in a slab. Now + * lets see if we can place a single object there. + */ + order = slab_order(size, 1, slub_max_order, 1); + if (order <= slub_max_order) + return order; + + /* + * Doh this slab cannot be placed using slub_max_order. + */ + order = slab_order(size, 1, MAX_ORDER, 1); + if (order <= MAX_ORDER) + return order; + return -ENOSYS; +} + /* * Figure out what the alignment of the objects will be. */ -- cgit v1.2.3 From 7ae439ce0c01d7db0c70d1542985969e95ef750d Mon Sep 17 00:00:00 2001 From: Pekka J Enberg Date: Wed, 9 May 2007 02:32:46 -0700 Subject: krealloc: fix kerneldoc comments No "blank" (or "*") line is allowed between the function name and lines for it parameter(s). Cc: Randy Dunlap Signed-off-by: Pekka Enberg Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slab.c | 1 - mm/slub.c | 1 - 2 files changed, 2 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index acda7e2d66e..01262d26e19 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3742,7 +3742,6 @@ EXPORT_SYMBOL(__kmalloc); /** * krealloc - reallocate memory. The contents will remain unchanged. - * * @p: object to reallocate memory for. * @new_size: how many bytes of memory are required. * @flags: the type of memory to allocate. diff --git a/mm/slub.c b/mm/slub.c index c81f52a7215..f7c120b93c4 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2286,7 +2286,6 @@ EXPORT_SYMBOL(kmem_cache_shrink); /** * krealloc - reallocate memory. The contents will remain unchanged. - * * @p: object to reallocate memory for. * @new_size: how many bytes of memory are required. * @flags: the type of memory to allocate. -- cgit v1.2.3 From 34013886ef47ea72e412beb04558431b57a68d51 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:47 -0700 Subject: Fix spellings of slab allocator section in init/Kconfig Fix some of the spelling issues. Fix sentences. Discourage SLOB use since SLUB can pack objects denser. Signed-off-by: Christoph Lameter Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- init/Kconfig | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index da6a91c4a05..4ad6de16323 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -523,9 +523,9 @@ config SLAB bool "SLAB" help The regular slab allocator that is established and known to work - well in all environments. It organizes chache hot objects in + well in all environments. It organizes cache hot objects in per cpu and per node queues. SLAB is the default choice for - slab allocator. + a slab allocator. config SLUB depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT @@ -535,21 +535,20 @@ config SLUB instead of managing queues of cached objects (SLAB approach). Per cpu caching is realized using slabs of objects instead of queues of objects. SLUB can use memory efficiently - way and has enhanced diagnostics. + and has enhanced diagnostics. config SLOB # -# SLOB cannot support SMP because SLAB_DESTROY_BY_RCU does not work -# properly. +# SLOB does not support SMP because SLAB_DESTROY_BY_RCU is unsupported # depends on EMBEDDED && !SMP && !SPARSEMEM bool "SLOB (Simple Allocator)" help SLOB replaces the SLAB allocator with a drastically simpler allocator. SLOB is more space efficient that SLAB but does not - scale well (single lock for all operations) and is more susceptible - to fragmentation. SLOB it is a great choice to reduce - memory usage and code size for embedded systems. + scale well (single lock for all operations) and is also highly + susceptible to fragmentation. SLUB can accomplish a higher object + density. It is usually better to use SLUB instead of SLOB. endchoice -- cgit v1.2.3 From 8defab33774a5c33920196a2ee9c0a946d22ba67 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:32:48 -0700 Subject: FRV: Replace pgd management via slabs through quicklists This is done in order to be able to run SLUB which expects no modifications to its page structs. Signed-off-by: Christoph Lameter Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/Kconfig | 6 +++--- arch/frv/kernel/process.c | 4 ++++ arch/frv/mm/pgalloc.c | 22 +++++++++++----------- include/asm-frv/tlb.h | 4 ++++ 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index eed694312a7..114738a4558 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -45,15 +45,15 @@ config TIME_LOW_RES bool default y -config ARCH_HAS_ILOG2_U32 +config QUICKLIST bool default y -config ARCH_HAS_ILOG2_U64 +config ARCH_HAS_ILOG2_U32 bool default y -config ARCH_USES_SLAB_PAGE_STRUCT +config ARCH_HAS_ILOG2_U64 bool default y diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c index 515a5cea546..9583a338e9d 100644 --- a/arch/frv/kernel/process.c +++ b/arch/frv/kernel/process.c @@ -25,12 +25,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -88,6 +90,8 @@ void cpu_idle(void) while (!need_resched()) { irq_stat[cpu].idle_timestamp = jiffies; + check_pgt_cache(); + if (!frv_dma_inprogress && idle) idle(); } diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c index 598a26ab8ad..7787c3cc52c 100644 --- a/arch/frv/mm/pgalloc.c +++ b/arch/frv/mm/pgalloc.c @@ -13,12 +13,12 @@ #include #include #include +#include #include #include #include pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((aligned(PAGE_SIZE))); -struct kmem_cache *pgd_cache; pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { @@ -100,7 +100,7 @@ static inline void pgd_list_del(pgd_t *pgd) set_page_private(next, (unsigned long) pprev); } -void pgd_ctor(void *pgd, struct kmem_cache *cache, unsigned long unused) +void pgd_ctor(void *pgd) { unsigned long flags; @@ -120,7 +120,7 @@ void pgd_ctor(void *pgd, struct kmem_cache *cache, unsigned long unused) } /* never called when PTRS_PER_PMD > 1 */ -void pgd_dtor(void *pgd, struct kmem_cache *cache, unsigned long unused) +void pgd_dtor(void *pgd) { unsigned long flags; /* can be called from interrupt context */ @@ -133,7 +133,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) { pgd_t *pgd; - pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL); + pgd = quicklist_alloc(0, GFP_KERNEL, pgd_ctor); if (!pgd) return pgd; @@ -143,15 +143,15 @@ pgd_t *pgd_alloc(struct mm_struct *mm) void pgd_free(pgd_t *pgd) { /* in the non-PAE case, clear_page_tables() clears user pgd entries */ - kmem_cache_free(pgd_cache, pgd); + quicklist_free(0, pgd_dtor, pgd); } void __init pgtable_cache_init(void) { - pgd_cache = kmem_cache_create("pgd", - PTRS_PER_PGD * sizeof(pgd_t), - PTRS_PER_PGD * sizeof(pgd_t), - SLAB_PANIC, - pgd_ctor, - pgd_dtor); } + +void check_pgt_cache(void) +{ + quicklist_trim(0, pgd_dtor, 25, 16); +} + diff --git a/include/asm-frv/tlb.h b/include/asm-frv/tlb.h index f94fe5cb9b3..cd458eb6d75 100644 --- a/include/asm-frv/tlb.h +++ b/include/asm-frv/tlb.h @@ -3,7 +3,11 @@ #include +#ifdef CONFIG_MMU +extern void check_pgt_cache(void); +#else #define check_pgt_cache() do {} while(0) +#endif /* * we don't need any special per-pte or per-vma handling... -- cgit v1.2.3 From 6ad36fe2b451cc85cc7b14f4128286759e217124 Mon Sep 17 00:00:00 2001 From: Holger Smolinski Date: Wed, 9 May 2007 02:32:50 -0700 Subject: dm raid1: one kmirrord per mirror This patch replaces the single instance of kmirrord by one instance per mirror set. This change is required to avoid a deadlock in kmirrord when the persistent dirty log of a mirror itself resides on a mirror. The single instance of kmirrord then issues a sync write to the dirty log in write_bits which gets deferred to kmirrord itself later in the call chain. But kmirrord never does the deferred work because it is still waiting for the sync write_bits. _mirror_sets is removed as it no longer needed, and we always flush the workqueue before destroying it to ensure all work is complete before destroying it. Signed-off-by: Holger Smolinski Signed-off-by: Alasdair G Kergon Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-raid1.c | 81 +++++++++++++++++---------------------------------- 1 file changed, 27 insertions(+), 54 deletions(-) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 23a642619be..c8b4c108da9 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -22,15 +22,8 @@ #define DM_MSG_PREFIX "raid1" -static struct workqueue_struct *_kmirrord_wq; -static struct work_struct _kmirrord_work; static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped); -static inline void wake(void) -{ - queue_work(_kmirrord_wq, &_kmirrord_work); -} - /*----------------------------------------------------------------- * Region hash * @@ -136,6 +129,9 @@ struct mirror_set { struct mirror *default_mirror; /* Default mirror */ + struct workqueue_struct *kmirrord_wq; + struct work_struct kmirrord_work; + unsigned int nr_mirrors; struct mirror mirror[0]; }; @@ -153,6 +149,11 @@ static inline sector_t region_to_sector(struct region_hash *rh, region_t region) return region << rh->region_shift; } +static void wake(struct mirror_set *ms) +{ + queue_work(ms->kmirrord_wq, &ms->kmirrord_work); +} + /* FIXME move this */ static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw); @@ -471,7 +472,7 @@ static void rh_dec(struct region_hash *rh, region_t region) spin_unlock_irqrestore(&rh->region_lock, flags); if (should_wake) - wake(); + wake(rh->ms); } /* @@ -558,7 +559,7 @@ static void rh_recovery_end(struct region *reg, int success) list_add(®->list, ®->rh->recovered_regions); spin_unlock_irq(&rh->region_lock); - wake(); + wake(rh->ms); } static void rh_flush(struct region_hash *rh) @@ -592,7 +593,7 @@ static void rh_start_recovery(struct region_hash *rh) for (i = 0; i < MAX_RECOVERY; i++) up(&rh->recovery_count); - wake(); + wake(rh->ms); } /* @@ -870,11 +871,10 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes) /*----------------------------------------------------------------- * kmirrord *---------------------------------------------------------------*/ -static LIST_HEAD(_mirror_sets); -static DECLARE_RWSEM(_mirror_sets_lock); - -static void do_mirror(struct mirror_set *ms) +static void do_mirror(struct work_struct *work) { + struct mirror_set *ms =container_of(work, struct mirror_set, + kmirrord_work); struct bio_list reads, writes; spin_lock(&ms->lock); @@ -890,16 +890,6 @@ static void do_mirror(struct mirror_set *ms) do_writes(ms, &writes); } -static void do_work(struct work_struct *ignored) -{ - struct mirror_set *ms; - - down_read(&_mirror_sets_lock); - list_for_each_entry (ms, &_mirror_sets, list) - do_mirror(ms); - up_read(&_mirror_sets_lock); -} - /*----------------------------------------------------------------- * Target functions *---------------------------------------------------------------*/ @@ -978,23 +968,6 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti, return 0; } -static int add_mirror_set(struct mirror_set *ms) -{ - down_write(&_mirror_sets_lock); - list_add_tail(&ms->list, &_mirror_sets); - up_write(&_mirror_sets_lock); - wake(); - - return 0; -} - -static void del_mirror_set(struct mirror_set *ms) -{ - down_write(&_mirror_sets_lock); - list_del(&ms->list); - up_write(&_mirror_sets_lock); -} - /* * Create dirty log: log_type #log_params */ @@ -1096,13 +1069,22 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->private = ms; ti->split_io = ms->rh.region_size; + ms->kmirrord_wq = create_singlethread_workqueue("kmirrord"); + if (!ms->kmirrord_wq) { + DMERR("couldn't start kmirrord"); + free_context(ms, ti, m); + return -ENOMEM; + } + INIT_WORK(&ms->kmirrord_work, do_mirror); + r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client); if (r) { + destroy_workqueue(ms->kmirrord_wq); free_context(ms, ti, ms->nr_mirrors); return r; } - add_mirror_set(ms); + wake(ms); return 0; } @@ -1110,8 +1092,9 @@ static void mirror_dtr(struct dm_target *ti) { struct mirror_set *ms = (struct mirror_set *) ti->private; - del_mirror_set(ms); + flush_workqueue(ms->kmirrord_wq); kcopyd_client_destroy(ms->kcopyd_client); + destroy_workqueue(ms->kmirrord_wq); free_context(ms, ti, ms->nr_mirrors); } @@ -1127,7 +1110,7 @@ static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw) spin_unlock(&ms->lock); if (should_wake) - wake(); + wake(ms); } /* @@ -1270,20 +1253,11 @@ static int __init dm_mirror_init(void) if (r) return r; - _kmirrord_wq = create_singlethread_workqueue("kmirrord"); - if (!_kmirrord_wq) { - DMERR("couldn't start kmirrord"); - dm_dirty_log_exit(); - return r; - } - INIT_WORK(&_kmirrord_work, do_work); - r = dm_register_target(&mirror_target); if (r < 0) { DMERR("%s: Failed to register mirror target", mirror_target.name); dm_dirty_log_exit(); - destroy_workqueue(_kmirrord_wq); } return r; @@ -1297,7 +1271,6 @@ static void __exit dm_mirror_exit(void) if (r < 0) DMERR("%s: unregister failed %d", mirror_target.name, r); - destroy_workqueue(_kmirrord_wq); dm_dirty_log_exit(); } -- cgit v1.2.3 From 9c89f8be1a7d14aad9d2c3f7d90d7d88f82c61e2 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 9 May 2007 02:32:51 -0700 Subject: dm crypt: disable barriers Disable barriers in dm-crypt because of current workqueue processing can reorder requests. This must be addresed later but for now disabling barriers is needed to prevent data corruption. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index d8121234c34..8527827b24f 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -954,6 +954,9 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, struct crypt_config *cc = ti->private; struct crypt_io *io; + if (bio_barrier(bio)) + return -EOPNOTSUPP; + io = mempool_alloc(cc->io_pool, GFP_NOIO); io->target = ti; io->base_bio = bio; -- cgit v1.2.3 From 027581f3515b5ec2218847dab578afa439a9d6b9 Mon Sep 17 00:00:00 2001 From: Olaf Kirch Date: Wed, 9 May 2007 02:32:52 -0700 Subject: dm crypt: fix call to clone_init Call clone_init early We need to call clone_init as early as possible - at least before call bio_put(clone) in any error path. Otherwise, the destructor will try to dereference bi_private, which may still be NULL. Signed-off-by: Olaf Kirch Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 8527827b24f..db439363f97 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -107,6 +107,8 @@ struct crypt_config { static struct kmem_cache *_crypt_io_pool; +static void clone_init(struct crypt_io *, struct bio *); + /* * Different IV generation algorithms: * @@ -379,9 +381,10 @@ static int crypt_convert(struct crypt_config *cc, * May return a smaller bio when running out of pages */ static struct bio * -crypt_alloc_buffer(struct crypt_config *cc, unsigned int size, +crypt_alloc_buffer(struct crypt_io *io, unsigned int size, struct bio *base_bio, unsigned int *bio_vec_idx) { + struct crypt_config *cc = io->target->private; struct bio *clone; unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; @@ -396,7 +399,7 @@ crypt_alloc_buffer(struct crypt_config *cc, unsigned int size, if (!clone) return NULL; - clone->bi_destructor = dm_crypt_bio_destructor; + clone_init(io, clone); /* if the last bio was not complete, continue where that one ended */ clone->bi_idx = *bio_vec_idx; @@ -562,6 +565,7 @@ static void clone_init(struct crypt_io *io, struct bio *clone) clone->bi_end_io = crypt_endio; clone->bi_bdev = cc->dev->bdev; clone->bi_rw = io->base_bio->bi_rw; + clone->bi_destructor = dm_crypt_bio_destructor; } static void process_read(struct crypt_io *io) @@ -585,7 +589,6 @@ static void process_read(struct crypt_io *io) } clone_init(io, clone); - clone->bi_destructor = dm_crypt_bio_destructor; clone->bi_idx = 0; clone->bi_vcnt = bio_segments(base_bio); clone->bi_size = base_bio->bi_size; @@ -615,7 +618,7 @@ static void process_write(struct crypt_io *io) * so repeat the whole process until all the data can be handled. */ while (remaining) { - clone = crypt_alloc_buffer(cc, base_bio->bi_size, + clone = crypt_alloc_buffer(io, base_bio->bi_size, io->first_clone, &bvec_idx); if (unlikely(!clone)) { dec_pending(io, -ENOMEM); @@ -631,7 +634,6 @@ static void process_write(struct crypt_io *io) return; } - clone_init(io, clone); clone->bi_sector = cc->start + sector; if (!io->first_clone) { -- cgit v1.2.3 From 98221eb757de03d9aa6262b1eded2be708640ccc Mon Sep 17 00:00:00 2001 From: Olaf Kirch Date: Wed, 9 May 2007 02:32:52 -0700 Subject: dm crypt: fix avoid cloned bio ref after free Do not access the bio after generic_make_request We should never access a bio after generic_make_request - there's no guarantee it still exists. Signed-off-by: Olaf Kirch Signed-off-by: Alasdair G Kergon Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index db439363f97..1dc2c62200e 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -655,9 +655,12 @@ static void process_write(struct crypt_io *io) generic_make_request(clone); + /* Do not reference clone after this - it + * may be gone already. */ + /* out of memory -> run queues */ if (remaining) - congestion_wait(bio_data_dir(clone), HZ/100); + congestion_wait(WRITE, HZ/100); } } -- cgit v1.2.3 From 2f9941b6c55d70103c1bc3f2c7676acd9f20bf8a Mon Sep 17 00:00:00 2001 From: Olaf Kirch Date: Wed, 9 May 2007 02:32:53 -0700 Subject: dm crypt: fix remove first_clone Get rid of first_clone in dm-crypt This gets rid of first_clone, which is not really needed. Apparently, cloned bios used to share their bvec some time way in the past - this is no longer the case. Contrarily, this even hurts us if we try to create a clone off first_clone after it has completed, and crypt_endio has destroyed its bvec. Signed-off-by: Olaf Kirch Signed-off-by: Alasdair G Kergon Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 1dc2c62200e..339b575ce07 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -33,7 +33,6 @@ struct crypt_io { struct dm_target *target; struct bio *base_bio; - struct bio *first_clone; struct work_struct work; atomic_t pending; int error; @@ -380,9 +379,8 @@ static int crypt_convert(struct crypt_config *cc, * This should never violate the device limitations * May return a smaller bio when running out of pages */ -static struct bio * -crypt_alloc_buffer(struct crypt_io *io, unsigned int size, - struct bio *base_bio, unsigned int *bio_vec_idx) +static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size, + unsigned int *bio_vec_idx) { struct crypt_config *cc = io->target->private; struct bio *clone; @@ -390,12 +388,7 @@ crypt_alloc_buffer(struct crypt_io *io, unsigned int size, gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; unsigned int i; - if (base_bio) { - clone = bio_alloc_bioset(GFP_NOIO, base_bio->bi_max_vecs, cc->bs); - __bio_clone(clone, base_bio); - } else - clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); - + clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); if (!clone) return NULL; @@ -498,9 +491,6 @@ static void dec_pending(struct crypt_io *io, int error) if (!atomic_dec_and_test(&io->pending)) return; - if (io->first_clone) - bio_put(io->first_clone); - bio_endio(io->base_bio, io->base_bio->bi_size, io->error); mempool_free(io, cc->io_pool); @@ -618,8 +608,7 @@ static void process_write(struct crypt_io *io) * so repeat the whole process until all the data can be handled. */ while (remaining) { - clone = crypt_alloc_buffer(io, base_bio->bi_size, - io->first_clone, &bvec_idx); + clone = crypt_alloc_buffer(io, base_bio->bi_size, &bvec_idx); if (unlikely(!clone)) { dec_pending(io, -ENOMEM); return; @@ -635,21 +624,11 @@ static void process_write(struct crypt_io *io) } clone->bi_sector = cc->start + sector; - - if (!io->first_clone) { - /* - * hold a reference to the first clone, because it - * holds the bio_vec array and that can't be freed - * before all other clones are released - */ - bio_get(clone); - io->first_clone = clone; - } - remaining -= clone->bi_size; sector += bio_sectors(clone); - /* prevent bio_put of first_clone */ + /* Grab another reference to the io struct + * before we kick off the request */ if (remaining) atomic_inc(&io->pending); @@ -965,7 +944,6 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, io = mempool_alloc(cc->io_pool, GFP_NOIO); io->target = ti; io->base_bio = bio; - io->first_clone = NULL; io->error = io->post_process = 0; atomic_set(&io->pending, 0); kcryptd_queue_io(io); -- cgit v1.2.3 From f97380bcadd6bd2e368727de4061aaba4989c426 Mon Sep 17 00:00:00 2001 From: Olaf Kirch Date: Wed, 9 May 2007 02:32:54 -0700 Subject: dm crypt: use smaller bvecs in clones Allocate smaller clones With the previous dm-crypt fixes, there is no need for the clone bios to have the same bvec size as the original - we just need to make them big enough for the remaining number of pages. The only requirement is that we clear the "out" index in convert_context, so that crypt_convert starts storing data at the right position within the clone bio. Signed-off-by: Olaf Kirch Signed-off-by: Alasdair G Kergon Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 339b575ce07..1ecee5e1c54 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -379,8 +379,7 @@ static int crypt_convert(struct crypt_config *cc, * This should never violate the device limitations * May return a smaller bio when running out of pages */ -static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size, - unsigned int *bio_vec_idx) +static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size) { struct crypt_config *cc = io->target->private; struct bio *clone; @@ -394,16 +393,7 @@ static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size, clone_init(io, clone); - /* if the last bio was not complete, continue where that one ended */ - clone->bi_idx = *bio_vec_idx; - clone->bi_vcnt = *bio_vec_idx; - clone->bi_size = 0; - clone->bi_flags &= ~(1 << BIO_SEG_VALID); - - /* clone->bi_idx pages have already been allocated */ - size -= clone->bi_idx * PAGE_SIZE; - - for (i = clone->bi_idx; i < nr_iovecs; i++) { + for (i = 0; i < nr_iovecs; i++) { struct bio_vec *bv = bio_iovec_idx(clone, i); bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask); @@ -415,7 +405,7 @@ static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size, * return a partially allocated bio, the caller will then try * to allocate additional bios while submitting this partial bio */ - if ((i - clone->bi_idx) == (MIN_BIO_PAGES - 1)) + if (i == (MIN_BIO_PAGES - 1)) gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT; bv->bv_offset = 0; @@ -434,12 +424,6 @@ static struct bio *crypt_alloc_buffer(struct crypt_io *io, unsigned int size, return NULL; } - /* - * Remember the last bio_vec allocated to be able - * to correctly continue after the splitting. - */ - *bio_vec_idx = clone->bi_vcnt; - return clone; } @@ -597,7 +581,6 @@ static void process_write(struct crypt_io *io) struct convert_context ctx; unsigned remaining = base_bio->bi_size; sector_t sector = base_bio->bi_sector - io->target->begin; - unsigned bvec_idx = 0; atomic_inc(&io->pending); @@ -608,13 +591,14 @@ static void process_write(struct crypt_io *io) * so repeat the whole process until all the data can be handled. */ while (remaining) { - clone = crypt_alloc_buffer(io, base_bio->bi_size, &bvec_idx); + clone = crypt_alloc_buffer(io, remaining); if (unlikely(!clone)) { dec_pending(io, -ENOMEM); return; } ctx.bio_out = clone; + ctx.idx_out = 0; if (unlikely(crypt_convert(cc, &ctx) < 0)) { crypt_free_buffer_pages(cc, clone, clone->bi_size); @@ -623,6 +607,9 @@ static void process_write(struct crypt_io *io) return; } + /* crypt_convert should have filled the clone bio */ + BUG_ON(ctx.idx_out < clone->bi_vcnt); + clone->bi_sector = cc->start + sector; remaining -= clone->bi_size; sector += bio_sectors(clone); -- cgit v1.2.3 From 46b477306afcd0516924b26792c7a42f4dbfa9f0 Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Wed, 9 May 2007 02:32:55 -0700 Subject: dm crypt: add null iv Add a new IV generation method 'null' to read old filesystem images created with SuSE's loop_fish2 module. Signed-off-by: Ludwig Nussel Acked-By: Christophe Saout Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-crypt.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 1ecee5e1c54..7b0fcfc9eaa 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -121,6 +121,9 @@ static void clone_init(struct crypt_io *, struct bio *); * benbi: the 64-bit "big-endian 'narrow block'-count", starting at 1 * (needed for LRW-32-AES and possible other narrow block modes) * + * null: the initial vector is always zero. Provides compatibility with + * obsolete loop_fish2 devices. Do not use for new devices. + * * plumb: unimplemented, see: * http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/454 */ @@ -257,6 +260,13 @@ static int crypt_iv_benbi_gen(struct crypt_config *cc, u8 *iv, sector_t sector) return 0; } +static int crypt_iv_null_gen(struct crypt_config *cc, u8 *iv, sector_t sector) +{ + memset(iv, 0, cc->iv_size); + + return 0; +} + static struct crypt_iv_operations crypt_iv_plain_ops = { .generator = crypt_iv_plain_gen }; @@ -273,6 +283,10 @@ static struct crypt_iv_operations crypt_iv_benbi_ops = { .generator = crypt_iv_benbi_gen }; +static struct crypt_iv_operations crypt_iv_null_ops = { + .generator = crypt_iv_null_gen +}; + static int crypt_convert_scatterlist(struct crypt_config *cc, struct scatterlist *out, struct scatterlist *in, unsigned int length, @@ -803,6 +817,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) cc->iv_gen_ops = &crypt_iv_essiv_ops; else if (strcmp(ivmode, "benbi") == 0) cc->iv_gen_ops = &crypt_iv_benbi_ops; + else if (strcmp(ivmode, "null") == 0) + cc->iv_gen_ops = &crypt_iv_null_ops; else { ti->error = "Invalid IV mode"; goto bad2; @@ -1030,7 +1046,7 @@ error: static struct target_type crypt_target = { .name = "crypt", - .version= {1, 3, 0}, + .version= {1, 5, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, -- cgit v1.2.3 From 79eb885c96b5ccf8bbffd0dddc4c15a5dd436a1c Mon Sep 17 00:00:00 2001 From: Edward Goggin Date: Wed, 9 May 2007 02:32:56 -0700 Subject: dm mpath: log device name Make the mapped device structure accessible to hardware handlers so error messages can include the device name. Signed-off-by: Edward Goggin Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-hw-handler.h | 1 + drivers/md/dm-mpath.c | 3 +++ drivers/md/dm.c | 1 + 3 files changed, 5 insertions(+) diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h index 32eff28e4ad..e0832e6fcf3 100644 --- a/drivers/md/dm-hw-handler.h +++ b/drivers/md/dm-hw-handler.h @@ -16,6 +16,7 @@ struct hw_handler_type; struct hw_handler { struct hw_handler_type *type; + struct mapped_device *md; void *context; }; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 3aa01350696..de54b39e6ff 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -668,6 +668,9 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m) return -EINVAL; } + m->hw_handler.md = dm_table_get_md(ti->table); + dm_put(m->hw_handler.md); + r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv); if (r) { dm_put_hw_handler(hwht); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 11a98df298e..2717a355dc5 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1236,6 +1236,7 @@ void dm_put(struct mapped_device *md) free_dev(md); } } +EXPORT_SYMBOL_GPL(dm_put); /* * Process the deferred bios -- cgit v1.2.3 From 2cd54d9bedb79a97f014e86c0da393416b264eb3 Mon Sep 17 00:00:00 2001 From: Mike Anderson Date: Wed, 9 May 2007 02:32:57 -0700 Subject: dm: allow offline devices Allow check_device_area to succeed if a device has an i_size of zero. This addresses an issue seen on DASD devices setting up a multipath table for paths in online and offline state. Signed-off-by: Mike Anderson Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-table.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 05befa91807..2fc199b0016 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -425,13 +425,15 @@ static void close_dev(struct dm_dev *d, struct mapped_device *md) } /* - * If possible (ie. blk_size[major] is set), this checks an area - * of a destination device is valid. + * If possible, this checks an area of a destination device is valid. */ static int check_device_area(struct dm_dev *dd, sector_t start, sector_t len) { - sector_t dev_size; - dev_size = dd->bdev->bd_inode->i_size >> SECTOR_SHIFT; + sector_t dev_size = dd->bdev->bd_inode->i_size >> SECTOR_SHIFT; + + if (!dev_size) + return 1; + return ((start < dev_size) && (len <= (dev_size - start))); } -- cgit v1.2.3 From 01d03a660e73fb524957c09825a3eb7c2ae7c205 Mon Sep 17 00:00:00 2001 From: Jonathan E Brassow Date: Wed, 9 May 2007 02:32:57 -0700 Subject: dm log: fault detection This patch gives the disk logging code the ability to store the fact that an error occured on the log device. In addition, an event is raised when an error is encountered during I/O to the log device. Signed-off-by: Jonathan E Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-log.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 6a926135184..a503d12c2ff 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -152,6 +152,7 @@ struct log_c { /* * Disk log fields */ + int log_dev_failed; struct dm_dev *log_dev; struct log_header header; @@ -315,6 +316,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, lc->disk_header = NULL; } else { lc->log_dev = dev; + lc->log_dev_failed = 0; lc->header_location.bdev = lc->log_dev->bdev; lc->header_location.sector = 0; @@ -437,6 +439,15 @@ static int count_bits32(uint32_t *addr, unsigned size) return count; } +static void fail_log_device(struct log_c *lc) +{ + if (lc->log_dev_failed) + return; + + lc->log_dev_failed = 1; + dm_table_event(lc->ti->table); +} + static int disk_resume(struct dirty_log *log) { int r; @@ -446,8 +457,12 @@ static int disk_resume(struct dirty_log *log) /* read the disk header */ r = read_header(lc); - if (r) + if (r) { + DMWARN("%s: Failed to read header on mirror log device", + lc->log_dev->name); + fail_log_device(lc); return r; + } /* set or clear any new bits -- device has grown */ if (lc->sync == NOSYNC) @@ -472,7 +487,14 @@ static int disk_resume(struct dirty_log *log) lc->header.nr_regions = lc->region_count; /* write the new header */ - return write_header(lc); + r = write_header(lc); + if (r) { + DMWARN("%s: Failed to write header on mirror log device", + lc->log_dev->name); + fail_log_device(lc); + } + + return r; } static uint32_t core_get_region_size(struct dirty_log *log) @@ -516,7 +538,9 @@ static int disk_flush(struct dirty_log *log) return 0; r = write_header(lc); - if (!r) + if (r) + fail_log_device(lc); + else lc->touched = 0; return r; -- cgit v1.2.3 From 315dcc226f066c1d3cef79283dcde807fe0e32d1 Mon Sep 17 00:00:00 2001 From: Jonathan E Brassow Date: Wed, 9 May 2007 02:32:58 -0700 Subject: dm log: report fault status This patch reports the status of the log device so that userspace can detect the error and take appropriate action. Signed-off-by: Jonathan E Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-log.c | 7 ++++--- drivers/md/dm-raid1.c | 11 +++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index a503d12c2ff..8d301409d81 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -615,6 +615,7 @@ static int core_status(struct dirty_log *log, status_type_t status, switch(status) { case STATUSTYPE_INFO: + DMEMIT("1 %s", log->type->name); break; case STATUSTYPE_TABLE: @@ -630,17 +631,17 @@ static int disk_status(struct dirty_log *log, status_type_t status, char *result, unsigned int maxlen) { int sz = 0; - char buffer[16]; struct log_c *lc = log->context; switch(status) { case STATUSTYPE_INFO: + DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name, + lc->log_dev_failed ? 'D' : 'A'); break; case STATUSTYPE_TABLE: - format_dev_t(buffer, lc->log_dev->bdev->bd_dev); DMEMIT("%s %u %s %u ", log->type->name, - lc->sync == DEFAULTSYNC ? 2 : 3, buffer, + lc->sync == DEFAULTSYNC ? 2 : 3, lc->log_dev->name, lc->region_size); DMEMIT_SYNC; } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index c8b4c108da9..cb3f318a369 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1205,11 +1205,9 @@ static void mirror_resume(struct dm_target *ti) static int mirror_status(struct dm_target *ti, status_type_t type, char *result, unsigned int maxlen) { - unsigned int m, sz; + unsigned int m, sz = 0; struct mirror_set *ms = (struct mirror_set *) ti->private; - sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen); - switch (type) { case STATUSTYPE_INFO: DMEMIT("%d ", ms->nr_mirrors); @@ -1220,9 +1218,14 @@ static int mirror_status(struct dm_target *ti, status_type_t type, (unsigned long long)ms->rh.log->type-> get_sync_count(ms->rh.log), (unsigned long long)ms->nr_regions); + + sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen); + break; case STATUSTYPE_TABLE: + sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen); + DMEMIT("%d", ms->nr_mirrors); for (m = 0; m < ms->nr_mirrors; m++) DMEMIT(" %s %llu", ms->mirror[m].dev->name, @@ -1234,7 +1237,7 @@ static int mirror_status(struct dm_target *ti, status_type_t type, static struct target_type mirror_target = { .name = "mirror", - .version = {1, 0, 2}, + .version = {1, 0, 3}, .module = THIS_MODULE, .ctr = mirror_ctr, .dtr = mirror_dtr, -- cgit v1.2.3 From a8e6afa2363de7ee0dea1a3297f6236f421c2dd4 Mon Sep 17 00:00:00 2001 From: Jonathan E Brassow Date: Wed, 9 May 2007 02:32:59 -0700 Subject: dm raid1: add handle_errors feature flag This patch adds the ability to specify desired features in the mirror constructor/mapping table. The first feature of interest is "handle_errors". Currently, mirroring will ignore any I/O errors from the devices. Subsequent patches will check for this flag and handle the errors. If flag/feature is not present, mirror will do nothing - maintaining backwards compatibility. Signed-off-by: Jonathan E Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-raid1.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index cb3f318a369..2a3e2f80aa4 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -22,6 +22,8 @@ #define DM_MSG_PREFIX "raid1" +#define DM_RAID1_HANDLE_ERRORS 0x01 + static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped); /*----------------------------------------------------------------- @@ -118,6 +120,7 @@ struct mirror_set { struct list_head list; struct region_hash rh; struct kcopyd_client *kcopyd_client; + uint64_t features; spinlock_t lock; /* protects the next two lists */ struct bio_list reads; @@ -1010,14 +1013,54 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti, return dl; } +static int parse_features(struct mirror_set *ms, unsigned argc, char **argv, + unsigned *args_used) +{ + unsigned num_features; + struct dm_target *ti = ms->ti; + + *args_used = 0; + + if (!argc) + return 0; + + if (sscanf(argv[0], "%u", &num_features) != 1) { + ti->error = "Invalid number of features"; + return -EINVAL; + } + + argc--; + argv++; + (*args_used)++; + + if (num_features > argc) { + ti->error = "Not enough arguments to support feature count"; + return -EINVAL; + } + + if (!strcmp("handle_errors", argv[0])) + ms->features |= DM_RAID1_HANDLE_ERRORS; + else { + ti->error = "Unrecognised feature requested"; + return -EINVAL; + } + + (*args_used)++; + + return 0; +} + /* * Construct a mirror mapping: * * log_type #log_params * #mirrors [mirror_path offset]{2,} + * [#features ] * * log_type is "core" or "disk" * #log_params is between 1 and 3 + * + * If present, features must be "handle_errors". */ #define DM_IO_PAGES 64 static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) @@ -1043,8 +1086,8 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) argv++, argc--; - if (argc != nr_mirrors * 2) { - ti->error = "Wrong number of mirror arguments"; + if (argc < nr_mirrors * 2) { + ti->error = "Too few mirror arguments"; dm_destroy_dirty_log(dl); return -EINVAL; } @@ -1077,6 +1120,21 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) } INIT_WORK(&ms->kmirrord_work, do_mirror); + r = parse_features(ms, argc, argv, &args_used); + if (r) { + free_context(ms, ti, ms->nr_mirrors); + return r; + } + + argv += args_used; + argc -= args_used; + + if (argc) { + ti->error = "Too many mirror arguments"; + free_context(ms, ti, ms->nr_mirrors); + return -EINVAL; + } + r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client); if (r) { destroy_workqueue(ms->kmirrord_wq); @@ -1230,6 +1288,9 @@ static int mirror_status(struct dm_target *ti, status_type_t type, for (m = 0; m < ms->nr_mirrors; m++) DMEMIT(" %s %llu", ms->mirror[m].dev->name, (unsigned long long)ms->mirror[m].offset); + + if (ms->features & DM_RAID1_HANDLE_ERRORS) + DMEMIT(" 1 handle_errors"); } return 0; -- cgit v1.2.3 From c897feb3dcf3c3300849056ee82b01df7bf66d3c Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:00 -0700 Subject: dm io: delay dec_count Delay decrementing the 'struct io' reference count until after the bio has been freed so that a bio destructor function may reference it. Required by a later patch. Signed-off-by: Heinz Mauelshagen Signed-off-by: Alasdair G Kergon Cc: Milan Broz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-io.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 8bdc8a87b24..4d19c45158b 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -126,7 +126,8 @@ static void dec_count(struct io *io, unsigned int region, int error) static int endio(struct bio *bio, unsigned int done, int error) { - struct io *io = (struct io *) bio->bi_private; + struct io *io; + unsigned region; /* keep going until we've finished */ if (bio->bi_size) @@ -135,10 +136,17 @@ static int endio(struct bio *bio, unsigned int done, int error) if (error && bio_data_dir(bio) == READ) zero_fill_bio(bio); - dec_count(io, bio_get_region(bio), error); + /* + * The bio destructor in bio_put() may use the io object. + */ + io = bio->bi_private; + region = bio_get_region(bio); + bio->bi_max_vecs++; bio_put(bio); + dec_count(io, region, error); + return 0; } -- cgit v1.2.3 From 891ce207011d3d9219f79fd5114c8594bbacc653 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:00 -0700 Subject: dm io: prepare for new interface Introduce struct dm_io_client to prepare for per-client mempools and bio_sets. Temporary functions bios() and io_pool() choose between the per-client structures and the global ones so the old and new interfaces can co-exist. Make error_bits optional. Signed-off-by: Heinz Mauelshagen Signed-off-by: Alasdair G Kergon Cc: Milan Broz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-io.c | 61 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 4d19c45158b..66db79208c1 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2003 Sistina Software + * Copyright (C) 2006 Red Hat GmbH * * This file is released under the GPL. */ @@ -14,11 +15,17 @@ static struct bio_set *_bios; +struct dm_io_client { + mempool_t *pool; + struct bio_set *bios; +}; + /* FIXME: can we shrink this ? */ struct io { unsigned long error; atomic_t count; struct task_struct *sleeper; + struct dm_io_client *client; io_notify_fn callback; void *context; }; @@ -26,12 +33,24 @@ struct io { /* * io contexts are only dynamically allocated for asynchronous * io. Since async io is likely to be the majority of io we'll - * have the same number of io contexts as buffer heads ! (FIXME: - * must reduce this). + * have the same number of io contexts as bios! (FIXME: must reduce this). */ static unsigned _num_ios; static mempool_t *_io_pool; +/* + * Temporary functions to allow old and new interfaces to co-exist. + */ +static struct bio_set *bios(struct dm_io_client *client) +{ + return client ? client->bios : _bios; +} + +static mempool_t *io_pool(struct dm_io_client *client) +{ + return client ? client->pool : _io_pool; +} + static unsigned int pages_to_ios(unsigned int pages) { return 4 * pages; /* too many ? */ @@ -118,7 +137,7 @@ static void dec_count(struct io *io, unsigned int region, int error) io_notify_fn fn = io->callback; void *context = io->context; - mempool_free(io, _io_pool); + mempool_free(io, io_pool(io->client)); fn(r, context); } } @@ -241,7 +260,9 @@ static void vm_dp_init(struct dpages *dp, void *data) static void dm_bio_destructor(struct bio *bio) { - bio_free(bio, _bios); + struct io *io = bio->bi_private; + + bio_free(bio, bios(io->client)); } /*----------------------------------------------------------------- @@ -264,7 +285,7 @@ static void do_region(int rw, unsigned int region, struct io_region *where, * to hide it from bio_add_page(). */ num_bvecs = (remaining / (PAGE_SIZE >> SECTOR_SHIFT)) + 2; - bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, _bios); + bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, bios(io->client)); bio->bi_sector = where->sector + (where->count - remaining); bio->bi_bdev = where->bdev; bio->bi_end_io = endio; @@ -319,8 +340,9 @@ static void dispatch_io(int rw, unsigned int num_regions, dec_count(io, 0, 0); } -static int sync_io(unsigned int num_regions, struct io_region *where, - int rw, struct dpages *dp, unsigned long *error_bits) +static int sync_io(struct dm_io_client *client, unsigned int num_regions, + struct io_region *where, int rw, struct dpages *dp, + unsigned long *error_bits) { struct io io; @@ -332,6 +354,7 @@ static int sync_io(unsigned int num_regions, struct io_region *where, io.error = 0; atomic_set(&io.count, 1); /* see dispatch_io() */ io.sleeper = current; + io.client = client; dispatch_io(rw, num_regions, where, dp, &io, 1); @@ -348,12 +371,15 @@ static int sync_io(unsigned int num_regions, struct io_region *where, if (atomic_read(&io.count)) return -EINTR; - *error_bits = io.error; + if (error_bits) + *error_bits = io.error; + return io.error ? -EIO : 0; } -static int async_io(unsigned int num_regions, struct io_region *where, int rw, - struct dpages *dp, io_notify_fn fn, void *context) +static int async_io(struct dm_io_client *client, unsigned int num_regions, + struct io_region *where, int rw, struct dpages *dp, + io_notify_fn fn, void *context) { struct io *io; @@ -363,10 +389,11 @@ static int async_io(unsigned int num_regions, struct io_region *where, int rw, return -EIO; } - io = mempool_alloc(_io_pool, GFP_NOIO); + io = mempool_alloc(io_pool(client), GFP_NOIO); io->error = 0; atomic_set(&io->count, 1); /* see dispatch_io() */ io->sleeper = NULL; + io->client = client; io->callback = fn; io->context = context; @@ -380,7 +407,7 @@ int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; list_dp_init(&dp, pl, offset); - return sync_io(num_regions, where, rw, &dp, error_bits); + return sync_io(NULL, num_regions, where, rw, &dp, error_bits); } int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw, @@ -388,7 +415,7 @@ int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; bvec_dp_init(&dp, bvec); - return sync_io(num_regions, where, rw, &dp, error_bits); + return sync_io(NULL, num_regions, where, rw, &dp, error_bits); } int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw, @@ -396,7 +423,7 @@ int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; vm_dp_init(&dp, data); - return sync_io(num_regions, where, rw, &dp, error_bits); + return sync_io(NULL, num_regions, where, rw, &dp, error_bits); } int dm_io_async(unsigned int num_regions, struct io_region *where, int rw, @@ -405,7 +432,7 @@ int dm_io_async(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; list_dp_init(&dp, pl, offset); - return async_io(num_regions, where, rw, &dp, fn, context); + return async_io(NULL, num_regions, where, rw, &dp, fn, context); } int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw, @@ -413,7 +440,7 @@ int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; bvec_dp_init(&dp, bvec); - return async_io(num_regions, where, rw, &dp, fn, context); + return async_io(NULL, num_regions, where, rw, &dp, fn, context); } int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, @@ -421,7 +448,7 @@ int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, { struct dpages dp; vm_dp_init(&dp, data); - return async_io(num_regions, where, rw, &dp, fn, context); + return async_io(NULL, num_regions, where, rw, &dp, fn, context); } EXPORT_SYMBOL(dm_io_get); -- cgit v1.2.3 From c8b03afe3d38a635861e4bfa5c563d844e754a91 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:01 -0700 Subject: dm io: new interface Add a new API to dm-io.c that uses a private mempool and bio_set for each client. The new functions to use are dm_io_client_create(), dm_io_client_destroy(), dm_io_client_resize() and dm_io(). Signed-off-by: Heinz Mauelshagen Signed-off-by: Alasdair G Kergon Cc: Milan Broz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-io.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/md/dm-io.h | 52 ++++++++++++++++++++++- 2 files changed, 173 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 66db79208c1..0c63809ab70 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -103,6 +103,51 @@ void dm_io_put(unsigned int num_pages) resize_pool(_num_ios - pages_to_ios(num_pages)); } +/* + * Create a client with mempool and bioset. + */ +struct dm_io_client *dm_io_client_create(unsigned num_pages) +{ + unsigned ios = pages_to_ios(num_pages); + struct dm_io_client *client; + + client = kmalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->pool = mempool_create_kmalloc_pool(ios, sizeof(struct io)); + if (!client->pool) + goto bad; + + client->bios = bioset_create(16, 16); + if (!client->bios) + goto bad; + + return client; + + bad: + if (client->pool) + mempool_destroy(client->pool); + kfree(client); + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(dm_io_client_create); + +int dm_io_client_resize(unsigned num_pages, struct dm_io_client *client) +{ + return mempool_resize(client->pool, pages_to_ios(num_pages), + GFP_KERNEL); +} +EXPORT_SYMBOL(dm_io_client_resize); + +void dm_io_client_destroy(struct dm_io_client *client) +{ + mempool_destroy(client->pool); + bioset_free(client->bios); + kfree(client); +} +EXPORT_SYMBOL(dm_io_client_destroy); + /*----------------------------------------------------------------- * We need to keep track of which region a bio is doing io for. * In order to save a memory allocation we store this the last @@ -236,6 +281,9 @@ static void bvec_dp_init(struct dpages *dp, struct bio_vec *bvec) dp->context_ptr = bvec; } +/* + * Functions for getting the pages from a VMA. + */ static void vm_get_page(struct dpages *dp, struct page **p, unsigned long *len, unsigned *offset) { @@ -265,6 +313,31 @@ static void dm_bio_destructor(struct bio *bio) bio_free(bio, bios(io->client)); } +/* + * Functions for getting the pages from kernel memory. + */ +static void km_get_page(struct dpages *dp, struct page **p, unsigned long *len, + unsigned *offset) +{ + *p = virt_to_page(dp->context_ptr); + *offset = dp->context_u; + *len = PAGE_SIZE - dp->context_u; +} + +static void km_next_page(struct dpages *dp) +{ + dp->context_ptr += PAGE_SIZE - dp->context_u; + dp->context_u = 0; +} + +static void km_dp_init(struct dpages *dp, void *data) +{ + dp->get_page = km_get_page; + dp->next_page = km_next_page; + dp->context_u = ((unsigned long) data) & (PAGE_SIZE - 1); + dp->context_ptr = data; +} + /*----------------------------------------------------------------- * IO routines that accept a list of pages. *---------------------------------------------------------------*/ @@ -451,6 +524,55 @@ int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, return async_io(NULL, num_regions, where, rw, &dp, fn, context); } +static int dp_init(struct dm_io_request *io_req, struct dpages *dp) +{ + /* Set up dpages based on memory type */ + switch (io_req->mem.type) { + case DM_IO_PAGE_LIST: + list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset); + break; + + case DM_IO_BVEC: + bvec_dp_init(dp, io_req->mem.ptr.bvec); + break; + + case DM_IO_VMA: + vm_dp_init(dp, io_req->mem.ptr.vma); + break; + + case DM_IO_KMEM: + km_dp_init(dp, io_req->mem.ptr.addr); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * New collapsed (a)synchronous interface + */ +int dm_io(struct dm_io_request *io_req, unsigned num_regions, + struct io_region *where, unsigned long *sync_error_bits) +{ + int r; + struct dpages dp; + + r = dp_init(io_req, &dp); + if (r) + return r; + + if (!io_req->notify.fn) + return sync_io(io_req->client, num_regions, where, + io_req->bi_rw, &dp, sync_error_bits); + + return async_io(io_req->client, num_regions, where, io_req->bi_rw, + &dp, io_req->notify.fn, io_req->notify.context); +} +EXPORT_SYMBOL(dm_io); + EXPORT_SYMBOL(dm_io_get); EXPORT_SYMBOL(dm_io_put); EXPORT_SYMBOL(dm_io_sync); diff --git a/drivers/md/dm-io.h b/drivers/md/dm-io.h index f9035bfd1a9..05b13382558 100644 --- a/drivers/md/dm-io.h +++ b/drivers/md/dm-io.h @@ -20,13 +20,47 @@ struct page_list { struct page *page; }; - /* * 'error' is a bitset, with each bit indicating whether an error * occurred doing io to the corresponding region. */ typedef void (*io_notify_fn)(unsigned long error, void *context); +enum dm_io_mem_type { + DM_IO_PAGE_LIST,/* Page list */ + DM_IO_BVEC, /* Bio vector */ + DM_IO_VMA, /* Virtual memory area */ + DM_IO_KMEM, /* Kernel memory */ +}; + +struct dm_io_memory { + enum dm_io_mem_type type; + + union { + struct page_list *pl; + struct bio_vec *bvec; + void *vma; + void *addr; + } ptr; + + unsigned offset; +}; + +struct dm_io_notify { + io_notify_fn fn; /* Callback for asynchronous requests */ + void *context; /* Passed to callback */ +}; + +/* + * IO request structure + */ +struct dm_io_client; +struct dm_io_request { + int bi_rw; /* READ|WRITE - not READA */ + struct dm_io_memory mem; /* Memory to use for io */ + struct dm_io_notify notify; /* Synchronous if notify.fn is NULL */ + struct dm_io_client *client; /* Client memory handler */ +}; /* * Before anyone uses the IO interface they should call @@ -38,6 +72,16 @@ typedef void (*io_notify_fn)(unsigned long error, void *context); int dm_io_get(unsigned int num_pages); void dm_io_put(unsigned int num_pages); +/* + * For async io calls, users can alternatively use the dm_io() function below + * and dm_io_client_create() to create private mempools for the client. + * + * Create/destroy may block. + */ +struct dm_io_client *dm_io_client_create(unsigned num_pages); +int dm_io_client_resize(unsigned num_pages, struct dm_io_client *client); +void dm_io_client_destroy(struct dm_io_client *client); + /* * Synchronous IO. * @@ -71,4 +115,10 @@ int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw, int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, void *data, io_notify_fn fn, void *context); +/* + * IO interface using private per-client pools. + */ +int dm_io(struct dm_io_request *io_req, unsigned num_regions, + struct io_region *region, unsigned long *sync_error_bits); + #endif -- cgit v1.2.3 From 373a392bd76c4cc2cbbdab3906aee2ae4dc6702e Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 9 May 2007 02:33:02 -0700 Subject: dm kcopyd: update dm io interface This patch ports kcopyd.c to the new, scalable dm_io() interface. Signed-off-by: Milan Broz Signed-off-by: Heinz Mauelshagen Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/kcopyd.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c index b46f6c575f7..dbc234e3c69 100644 --- a/drivers/md/kcopyd.c +++ b/drivers/md/kcopyd.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2002 Sistina Software (UK) Limited. + * Copyright (C) 2006 Red Hat GmbH * * This file is released under the GPL. * @@ -45,6 +46,8 @@ struct kcopyd_client { unsigned int nr_pages; unsigned int nr_free_pages; + struct dm_io_client *io_client; + wait_queue_head_t destroyq; atomic_t nr_jobs; }; @@ -342,16 +345,20 @@ static void complete_io(unsigned long error, void *context) static int run_io_job(struct kcopyd_job *job) { int r; + struct dm_io_request io_req = { + .bi_rw = job->rw, + .mem.type = DM_IO_PAGE_LIST, + .mem.ptr.pl = job->pages, + .mem.offset = job->offset, + .notify.fn = complete_io, + .notify.context = job, + .client = job->kc->io_client, + }; if (job->rw == READ) - r = dm_io_async(1, &job->source, job->rw, - job->pages, - job->offset, complete_io, job); - + r = dm_io(&io_req, 1, &job->source, NULL); else - r = dm_io_async(job->num_dests, job->dests, job->rw, - job->pages, - job->offset, complete_io, job); + r = dm_io(&io_req, job->num_dests, job->dests, NULL); return r; } @@ -670,8 +677,9 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) return r; } - r = dm_io_get(nr_pages); - if (r) { + kc->io_client = dm_io_client_create(nr_pages); + if (IS_ERR(kc->io_client)) { + r = PTR_ERR(kc->io_client); client_free_pages(kc); kfree(kc); kcopyd_exit(); @@ -691,7 +699,7 @@ void kcopyd_client_destroy(struct kcopyd_client *kc) /* Wait for completion of all jobs submitted by this client. */ wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs)); - dm_io_put(kc->nr_pages); + dm_io_client_destroy(kc->io_client); client_free_pages(kc); client_del(kc); kfree(kc); -- cgit v1.2.3 From 6cca1e7af543aa396b3e4919c251080741a49e69 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:02 -0700 Subject: dm exception store: update dm io interface This patch ports dm-exception-store.c to the new, scalable dm_io() interface. It replaces dm_io_get()/dm_io_put() by dm_io_client_create()/dm_io_client_destroy() calls and dm_io_sync_vm() by dm_io() to achive this. Signed-off-by: Heinz Mauelshagen Cc: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-exception-store.c | 54 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 99cdffa7fbf..07e0a0c84f6 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -1,7 +1,8 @@ /* - * dm-snapshot.c + * dm-exception-store.c * * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * Copyright (C) 2006 Red Hat GmbH * * This file is released under the GPL. */ @@ -123,6 +124,7 @@ struct pstore { atomic_t pending_count; uint32_t callback_count; struct commit_callback *callbacks; + struct dm_io_client *io_client; }; static inline unsigned int sectors_to_pages(unsigned int sectors) @@ -159,14 +161,20 @@ static void free_area(struct pstore *ps) */ static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) { - struct io_region where; - unsigned long bits; - - where.bdev = ps->snap->cow->bdev; - where.sector = ps->snap->chunk_size * chunk; - where.count = ps->snap->chunk_size; - - return dm_io_sync_vm(1, &where, rw, ps->area, &bits); + struct io_region where = { + .bdev = ps->snap->cow->bdev, + .sector = ps->snap->chunk_size * chunk, + .count = ps->snap->chunk_size, + }; + struct dm_io_request io_req = { + .bi_rw = rw, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = ps->area, + .client = ps->io_client, + .notify.fn = NULL, + }; + + return dm_io(&io_req, 1, &where, NULL); } /* @@ -213,17 +221,18 @@ static int read_header(struct pstore *ps, int *new_snapshot) chunk_size_supplied = 0; } - r = dm_io_get(sectors_to_pages(ps->snap->chunk_size)); - if (r) - return r; + ps->io_client = dm_io_client_create(sectors_to_pages(ps->snap-> + chunk_size)); + if (IS_ERR(ps->io_client)) + return PTR_ERR(ps->io_client); r = alloc_area(ps); if (r) - goto bad1; + return r; r = chunk_io(ps, 0, READ); if (r) - goto bad2; + goto bad; dh = (struct disk_header *) ps->area; @@ -235,7 +244,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { DMWARN("Invalid or corrupt snapshot"); r = -ENXIO; - goto bad2; + goto bad; } *new_snapshot = 0; @@ -252,27 +261,22 @@ static int read_header(struct pstore *ps, int *new_snapshot) (unsigned long long)ps->snap->chunk_size); /* We had a bogus chunk_size. Fix stuff up. */ - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); free_area(ps); ps->snap->chunk_size = chunk_size; ps->snap->chunk_mask = chunk_size - 1; ps->snap->chunk_shift = ffs(chunk_size) - 1; - r = dm_io_get(sectors_to_pages(chunk_size)); + r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), + ps->io_client); if (r) return r; r = alloc_area(ps); - if (r) - goto bad1; - - return 0; + return r; -bad2: +bad: free_area(ps); -bad1: - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); return r; } @@ -405,7 +409,7 @@ static void persistent_destroy(struct exception_store *store) { struct pstore *ps = get_info(store); - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); + dm_io_client_destroy(ps->io_client); vfree(ps->callbacks); free_area(ps); kfree(ps); -- cgit v1.2.3 From 5d234d1e03d0a4cef4da32177be6657b45cc513f Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:03 -0700 Subject: dm log: update dm io interface This patch ports dm-log.c to the new dm-io interface in order to make it scalable to have a large number of persistent dirty logs active in parallel. Signed-off-by: Heinz Mauelshagen Cc: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-log.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 8d301409d81..a60acf8d385 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -149,6 +149,8 @@ struct log_c { FORCESYNC, /* Force a sync to happen */ } sync; + struct dm_io_request io_req; + /* * Disk log fields */ @@ -200,13 +202,20 @@ static void header_from_disk(struct log_header *core, struct log_header *disk) core->nr_regions = le64_to_cpu(disk->nr_regions); } +static int rw_header(struct log_c *lc, int rw) +{ + lc->io_req.bi_rw = rw; + lc->io_req.mem.ptr.vma = lc->disk_header; + lc->io_req.notify.fn = NULL; + + return dm_io(&lc->io_req, 1, &lc->header_location, NULL); +} + static int read_header(struct log_c *log) { int r; - unsigned long ebits; - r = dm_io_sync_vm(1, &log->header_location, READ, - log->disk_header, &ebits); + r = rw_header(log, READ); if (r) return r; @@ -234,11 +243,8 @@ static int read_header(struct log_c *log) static inline int write_header(struct log_c *log) { - unsigned long ebits; - header_to_disk(&log->header, log->disk_header); - return dm_io_sync_vm(1, &log->header_location, WRITE, - log->disk_header, &ebits); + return rw_header(log, WRITE); } /*---------------------------------------------------------------- @@ -257,6 +263,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, uint32_t region_size; unsigned int region_count; size_t bitset_size, buf_size; + int r; if (argc < 1 || argc > 2) { DMWARN("wrong number of arguments to mirror log"); @@ -326,6 +333,15 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); lc->header_location.count = buf_size >> SECTOR_SHIFT; + lc->io_req.mem.type = DM_IO_VMA; + lc->io_req.client = dm_io_client_create(dm_div_up(buf_size, + PAGE_SIZE)); + if (IS_ERR(lc->io_req.client)) { + r = PTR_ERR(lc->io_req.client); + DMWARN("couldn't allocate disk io client"); + kfree(lc); + return -ENOMEM; + } lc->disk_header = vmalloc(buf_size); if (!lc->disk_header) { @@ -426,6 +442,7 @@ static void disk_dtr(struct dirty_log *log) dm_put_device(lc->ti, lc->log_dev); vfree(lc->disk_header); + dm_io_client_destroy(lc->io_req.client); destroy_log_context(lc); } -- cgit v1.2.3 From 88be163abb5324bab09f5eff9646590eec5314eb Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 9 May 2007 02:33:04 -0700 Subject: dm raid1: update dm io interface This patch ports dm-raid1.c to the new dm-io interface. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-raid1.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 2a3e2f80aa4..bde90460609 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -21,6 +21,7 @@ #include #define DM_MSG_PREFIX "raid1" +#define DM_IO_PAGES 64 #define DM_RAID1_HANDLE_ERRORS 0x01 @@ -126,6 +127,8 @@ struct mirror_set { struct bio_list reads; struct bio_list writes; + struct dm_io_client *io_client; + /* recovery */ region_t nr_regions; int in_sync; @@ -796,6 +799,14 @@ static void do_write(struct mirror_set *ms, struct bio *bio) unsigned int i; struct io_region io[KCOPYD_MAX_REGIONS+1]; struct mirror *m; + struct dm_io_request io_req = { + .bi_rw = WRITE, + .mem.type = DM_IO_BVEC, + .mem.ptr.bvec = bio->bi_io_vec + bio->bi_idx, + .notify.fn = write_callback, + .notify.context = bio, + .client = ms->io_client, + }; for (i = 0; i < ms->nr_mirrors; i++) { m = ms->mirror + i; @@ -806,9 +817,8 @@ static void do_write(struct mirror_set *ms, struct bio *bio) } bio_set_ms(bio, ms); - dm_io_async_bvec(ms->nr_mirrors, io, WRITE, - bio->bi_io_vec + bio->bi_idx, - write_callback, bio); + + (void) dm_io(&io_req, ms->nr_mirrors, io, NULL); } static void do_writes(struct mirror_set *ms, struct bio_list *writes) @@ -924,6 +934,13 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors, ms->in_sync = 0; ms->default_mirror = &ms->mirror[DEFAULT_MIRROR]; + ms->io_client = dm_io_client_create(DM_IO_PAGES); + if (IS_ERR(ms->io_client)) { + ti->error = "Error creating dm_io client"; + kfree(ms); + return NULL; + } + if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) { ti->error = "Error creating dirty region hash"; kfree(ms); @@ -939,6 +956,7 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti, while (m--) dm_put_device(ti, ms->mirror[m].dev); + dm_io_client_destroy(ms->io_client); rh_exit(&ms->rh); kfree(ms); } @@ -1062,7 +1080,6 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv, * * If present, features must be "handle_errors". */ -#define DM_IO_PAGES 64 static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) { int r; -- cgit v1.2.3 From bf17ce3a604d943f29bf1bc1a66a4e0d2ad4ec96 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 9 May 2007 02:33:05 -0700 Subject: dm io: remove old interface Remove old dm-io interface. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-io.c | 131 ++--------------------------------------------------- drivers/md/dm-io.h | 51 ++------------------- 2 files changed, 7 insertions(+), 175 deletions(-) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 0c63809ab70..352c6fbeac5 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -13,8 +13,6 @@ #include #include -static struct bio_set *_bios; - struct dm_io_client { mempool_t *pool; struct bio_set *bios; @@ -35,74 +33,12 @@ struct io { * io. Since async io is likely to be the majority of io we'll * have the same number of io contexts as bios! (FIXME: must reduce this). */ -static unsigned _num_ios; -static mempool_t *_io_pool; - -/* - * Temporary functions to allow old and new interfaces to co-exist. - */ -static struct bio_set *bios(struct dm_io_client *client) -{ - return client ? client->bios : _bios; -} - -static mempool_t *io_pool(struct dm_io_client *client) -{ - return client ? client->pool : _io_pool; -} static unsigned int pages_to_ios(unsigned int pages) { return 4 * pages; /* too many ? */ } -static int resize_pool(unsigned int new_ios) -{ - int r = 0; - - if (_io_pool) { - if (new_ios == 0) { - /* free off the pool */ - mempool_destroy(_io_pool); - _io_pool = NULL; - bioset_free(_bios); - - } else { - /* resize the pool */ - r = mempool_resize(_io_pool, new_ios, GFP_KERNEL); - } - - } else { - /* create new pool */ - _io_pool = mempool_create_kmalloc_pool(new_ios, - sizeof(struct io)); - if (!_io_pool) - return -ENOMEM; - - _bios = bioset_create(16, 16); - if (!_bios) { - mempool_destroy(_io_pool); - _io_pool = NULL; - return -ENOMEM; - } - } - - if (!r) - _num_ios = new_ios; - - return r; -} - -int dm_io_get(unsigned int num_pages) -{ - return resize_pool(_num_ios + pages_to_ios(num_pages)); -} - -void dm_io_put(unsigned int num_pages) -{ - resize_pool(_num_ios - pages_to_ios(num_pages)); -} - /* * Create a client with mempool and bioset. */ @@ -182,7 +118,7 @@ static void dec_count(struct io *io, unsigned int region, int error) io_notify_fn fn = io->callback; void *context = io->context; - mempool_free(io, io_pool(io->client)); + mempool_free(io, io->client->pool); fn(r, context); } } @@ -310,7 +246,7 @@ static void dm_bio_destructor(struct bio *bio) { struct io *io = bio->bi_private; - bio_free(bio, bios(io->client)); + bio_free(bio, io->client->bios); } /* @@ -358,7 +294,7 @@ static void do_region(int rw, unsigned int region, struct io_region *where, * to hide it from bio_add_page(). */ num_bvecs = (remaining / (PAGE_SIZE >> SECTOR_SHIFT)) + 2; - bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, bios(io->client)); + bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, io->client->bios); bio->bi_sector = where->sector + (where->count - remaining); bio->bi_bdev = where->bdev; bio->bi_end_io = endio; @@ -462,7 +398,7 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions, return -EIO; } - io = mempool_alloc(io_pool(client), GFP_NOIO); + io = mempool_alloc(client->pool, GFP_NOIO); io->error = 0; atomic_set(&io->count, 1); /* see dispatch_io() */ io->sleeper = NULL; @@ -474,56 +410,6 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions, return 0; } -int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw, - struct page_list *pl, unsigned int offset, - unsigned long *error_bits) -{ - struct dpages dp; - list_dp_init(&dp, pl, offset); - return sync_io(NULL, num_regions, where, rw, &dp, error_bits); -} - -int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw, - struct bio_vec *bvec, unsigned long *error_bits) -{ - struct dpages dp; - bvec_dp_init(&dp, bvec); - return sync_io(NULL, num_regions, where, rw, &dp, error_bits); -} - -int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw, - void *data, unsigned long *error_bits) -{ - struct dpages dp; - vm_dp_init(&dp, data); - return sync_io(NULL, num_regions, where, rw, &dp, error_bits); -} - -int dm_io_async(unsigned int num_regions, struct io_region *where, int rw, - struct page_list *pl, unsigned int offset, - io_notify_fn fn, void *context) -{ - struct dpages dp; - list_dp_init(&dp, pl, offset); - return async_io(NULL, num_regions, where, rw, &dp, fn, context); -} - -int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw, - struct bio_vec *bvec, io_notify_fn fn, void *context) -{ - struct dpages dp; - bvec_dp_init(&dp, bvec); - return async_io(NULL, num_regions, where, rw, &dp, fn, context); -} - -int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, - void *data, io_notify_fn fn, void *context) -{ - struct dpages dp; - vm_dp_init(&dp, data); - return async_io(NULL, num_regions, where, rw, &dp, fn, context); -} - static int dp_init(struct dm_io_request *io_req, struct dpages *dp) { /* Set up dpages based on memory type */ @@ -572,12 +458,3 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions, &dp, io_req->notify.fn, io_req->notify.context); } EXPORT_SYMBOL(dm_io); - -EXPORT_SYMBOL(dm_io_get); -EXPORT_SYMBOL(dm_io_put); -EXPORT_SYMBOL(dm_io_sync); -EXPORT_SYMBOL(dm_io_async); -EXPORT_SYMBOL(dm_io_sync_bvec); -EXPORT_SYMBOL(dm_io_async_bvec); -EXPORT_SYMBOL(dm_io_sync_vm); -EXPORT_SYMBOL(dm_io_async_vm); diff --git a/drivers/md/dm-io.h b/drivers/md/dm-io.h index 05b13382558..f647e2cceaa 100644 --- a/drivers/md/dm-io.h +++ b/drivers/md/dm-io.h @@ -12,7 +12,7 @@ struct io_region { struct block_device *bdev; sector_t sector; - sector_t count; + sector_t count; /* If this is zero the region is ignored. */ }; struct page_list { @@ -20,10 +20,6 @@ struct page_list { struct page *page; }; -/* - * 'error' is a bitset, with each bit indicating whether an error - * occurred doing io to the corresponding region. - */ typedef void (*io_notify_fn)(unsigned long error, void *context); enum dm_io_mem_type { @@ -62,16 +58,6 @@ struct dm_io_request { struct dm_io_client *client; /* Client memory handler */ }; -/* - * Before anyone uses the IO interface they should call - * dm_io_get(), specifying roughly how many pages they are - * expecting to perform io on concurrently. - * - * This function may block. - */ -int dm_io_get(unsigned int num_pages); -void dm_io_put(unsigned int num_pages); - /* * For async io calls, users can alternatively use the dm_io() function below * and dm_io_client_create() to create private mempools for the client. @@ -82,41 +68,10 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages); int dm_io_client_resize(unsigned num_pages, struct dm_io_client *client); void dm_io_client_destroy(struct dm_io_client *client); -/* - * Synchronous IO. - * - * Please ensure that the rw flag in the next two functions is - * either READ or WRITE, ie. we don't take READA. Any - * regions with a zero count field will be ignored. - */ -int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw, - struct page_list *pl, unsigned int offset, - unsigned long *error_bits); - -int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw, - struct bio_vec *bvec, unsigned long *error_bits); - -int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw, - void *data, unsigned long *error_bits); - -/* - * Aynchronous IO. - * - * The 'where' array may be safely allocated on the stack since - * the function takes a copy. - */ -int dm_io_async(unsigned int num_regions, struct io_region *where, int rw, - struct page_list *pl, unsigned int offset, - io_notify_fn fn, void *context); - -int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw, - struct bio_vec *bvec, io_notify_fn fn, void *context); - -int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw, - void *data, io_notify_fn fn, void *context); - /* * IO interface using private per-client pools. + * Each bit in the optional 'sync_error_bits' bitset indicates whether an + * error occurred doing io to the corresponding region. */ int dm_io(struct dm_io_request *io_req, unsigned num_regions, struct io_region *region, unsigned long *sync_error_bits); -- cgit v1.2.3 From 0ba699347e96b5468b42b3decf1f381abbf99652 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:05 -0700 Subject: dm: bio list helpers More bio_list helper functions for new targets (including dm-delay and dm-loop) to manipulate lists of bios. Signed-off-by: Heinz Mauelshagen Signed-off-by: Bryn Reeves Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-bio-list.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/md/dm-bio-list.h b/drivers/md/dm-bio-list.h index da4349649f7..c6be88826fa 100644 --- a/drivers/md/dm-bio-list.h +++ b/drivers/md/dm-bio-list.h @@ -8,17 +8,43 @@ #define DM_BIO_LIST_H #include +#include struct bio_list { struct bio *head; struct bio *tail; }; +static inline int bio_list_empty(const struct bio_list *bl) +{ + return bl->head == NULL; +} + +#define BIO_LIST_INIT { .head = NULL, .tail = NULL } + +#define BIO_LIST(bl) \ + struct bio_list bl = BIO_LIST_INIT + static inline void bio_list_init(struct bio_list *bl) { bl->head = bl->tail = NULL; } +#define bio_list_for_each(bio, bl) \ + for (bio = (bl)->head; bio && ({ prefetch(bio->bi_next); 1; }); \ + bio = bio->bi_next) + +static inline unsigned bio_list_size(const struct bio_list *bl) +{ + unsigned sz = 0; + struct bio *bio; + + bio_list_for_each(bio, bl) + sz++; + + return sz; +} + static inline void bio_list_add(struct bio_list *bl, struct bio *bio) { bio->bi_next = NULL; -- cgit v1.2.3 From 26b9f228703f0518a90e7513d6fe7b6abeed5138 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Wed, 9 May 2007 02:33:06 -0700 Subject: dm: delay target New device-mapper target that can delay I/O (for testing). Reads can be separated from writes, redirected to different underlying devices and delayed by differing amounts of time. Signed-off-by: Heinz Mauelshagen Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/device-mapper/delay.txt | 26 +++ drivers/md/Kconfig | 9 + drivers/md/Makefile | 1 + drivers/md/dm-delay.c | 383 ++++++++++++++++++++++++++++++++++ 4 files changed, 419 insertions(+) create mode 100644 Documentation/device-mapper/delay.txt create mode 100644 drivers/md/dm-delay.c diff --git a/Documentation/device-mapper/delay.txt b/Documentation/device-mapper/delay.txt new file mode 100644 index 00000000000..15adc55359e --- /dev/null +++ b/Documentation/device-mapper/delay.txt @@ -0,0 +1,26 @@ +dm-delay +======== + +Device-Mapper's "delay" target delays reads and/or writes +and maps them to different devices. + +Parameters: + [ ] + +With separate write parameters, the first set is only used for reads. +Delays are specified in milliseconds. + +Example scripts +=============== +[[ +#!/bin/sh +# Create device delaying rw operation for 500ms +echo "0 `blockdev --getsize $1` delay $1 0 500" | dmsetup create delayed +]] + +[[ +#!/bin/sh +# Create device delaying only write operation for 500ms and +# splitting reads and writes to different devices $1 $2 +echo "0 `blockdev --getsize $1` delay $1 0 0 $2 0 500" | dmsetup create delayed +]] diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 4540ade6b6b..7df934d6913 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -262,6 +262,15 @@ config DM_MULTIPATH_EMC ---help--- Multipath support for EMC CX/AX series hardware. +config DM_DELAY + tristate "I/O delaying target (EXPERIMENTAL)" + depends on BLK_DEV_DM && EXPERIMENTAL + ---help--- + A target that delays reads and/or writes and can send + them to different devices. Useful for testing. + + If unsure, say N. + endmenu endif diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 34957a68d92..38754084eac 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MD_FAULTY) += faulty.o obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o +obj-$(CONFIG_DM_DELAY) += dm-delay.o obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o obj-$(CONFIG_DM_MULTIPATH_EMC) += dm-emc.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c new file mode 100644 index 00000000000..52c7cf9e580 --- /dev/null +++ b/drivers/md/dm-delay.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2005-2007 Red Hat GmbH + * + * A target that delays reads and/or writes and can send + * them to different devices. + * + * This file is released under the GPL. + */ + +#include +#include +#include +#include +#include + +#include "dm.h" +#include "dm-bio-list.h" + +#define DM_MSG_PREFIX "delay" + +struct delay_c { + struct timer_list delay_timer; + struct semaphore timer_lock; + struct work_struct flush_expired_bios; + struct list_head delayed_bios; + atomic_t may_delay; + mempool_t *delayed_pool; + + struct dm_dev *dev_read; + sector_t start_read; + unsigned read_delay; + unsigned reads; + + struct dm_dev *dev_write; + sector_t start_write; + unsigned write_delay; + unsigned writes; +}; + +struct delay_info { + struct delay_c *context; + struct list_head list; + struct bio *bio; + unsigned long expires; +}; + +static DEFINE_MUTEX(delayed_bios_lock); + +static struct workqueue_struct *kdelayd_wq; +static struct kmem_cache *delayed_cache; + +static void handle_delayed_timer(unsigned long data) +{ + struct delay_c *dc = (struct delay_c *)data; + + queue_work(kdelayd_wq, &dc->flush_expired_bios); +} + +static void queue_timeout(struct delay_c *dc, unsigned long expires) +{ + down(&dc->timer_lock); + + if (!timer_pending(&dc->delay_timer) || expires < dc->delay_timer.expires) + mod_timer(&dc->delay_timer, expires); + + up(&dc->timer_lock); +} + +static void flush_bios(struct bio *bio) +{ + struct bio *n; + + while (bio) { + n = bio->bi_next; + bio->bi_next = NULL; + generic_make_request(bio); + bio = n; + } +} + +static struct bio *flush_delayed_bios(struct delay_c *dc, int flush_all) +{ + struct delay_info *delayed, *next; + unsigned long next_expires = 0; + int start_timer = 0; + BIO_LIST(flush_bios); + + mutex_lock(&delayed_bios_lock); + list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) { + if (flush_all || time_after_eq(jiffies, delayed->expires)) { + list_del(&delayed->list); + bio_list_add(&flush_bios, delayed->bio); + if ((bio_data_dir(delayed->bio) == WRITE)) + delayed->context->writes--; + else + delayed->context->reads--; + mempool_free(delayed, dc->delayed_pool); + continue; + } + + if (!start_timer) { + start_timer = 1; + next_expires = delayed->expires; + } else + next_expires = min(next_expires, delayed->expires); + } + + mutex_unlock(&delayed_bios_lock); + + if (start_timer) + queue_timeout(dc, next_expires); + + return bio_list_get(&flush_bios); +} + +static void flush_expired_bios(struct work_struct *work) +{ + struct delay_c *dc; + + dc = container_of(work, struct delay_c, flush_expired_bios); + flush_bios(flush_delayed_bios(dc, 0)); +} + +/* + * Mapping parameters: + * [ ] + * + * With separate write parameters, the first set is only used for reads. + * Delays are specified in milliseconds. + */ +static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct delay_c *dc; + unsigned long long tmpll; + + if (argc != 3 && argc != 6) { + ti->error = "requires exactly 3 or 6 arguments"; + return -EINVAL; + } + + dc = kmalloc(sizeof(*dc), GFP_KERNEL); + if (!dc) { + ti->error = "Cannot allocate context"; + return -ENOMEM; + } + + dc->reads = dc->writes = 0; + + if (sscanf(argv[1], "%llu", &tmpll) != 1) { + ti->error = "Invalid device sector"; + goto bad; + } + dc->start_read = tmpll; + + if (sscanf(argv[2], "%u", &dc->read_delay) != 1) { + ti->error = "Invalid delay"; + goto bad; + } + + if (dm_get_device(ti, argv[0], dc->start_read, ti->len, + dm_table_get_mode(ti->table), &dc->dev_read)) { + ti->error = "Device lookup failed"; + goto bad; + } + + if (argc == 3) { + dc->dev_write = NULL; + goto out; + } + + if (sscanf(argv[4], "%llu", &tmpll) != 1) { + ti->error = "Invalid write device sector"; + goto bad; + } + dc->start_write = tmpll; + + if (sscanf(argv[5], "%u", &dc->write_delay) != 1) { + ti->error = "Invalid write delay"; + goto bad; + } + + if (dm_get_device(ti, argv[3], dc->start_write, ti->len, + dm_table_get_mode(ti->table), &dc->dev_write)) { + ti->error = "Write device lookup failed"; + dm_put_device(ti, dc->dev_read); + goto bad; + } + +out: + dc->delayed_pool = mempool_create_slab_pool(128, delayed_cache); + if (!dc->delayed_pool) { + DMERR("Couldn't create delayed bio pool."); + goto bad; + } + + init_timer(&dc->delay_timer); + dc->delay_timer.function = handle_delayed_timer; + dc->delay_timer.data = (unsigned long)dc; + + INIT_WORK(&dc->flush_expired_bios, flush_expired_bios); + INIT_LIST_HEAD(&dc->delayed_bios); + init_MUTEX(&dc->timer_lock); + atomic_set(&dc->may_delay, 1); + + ti->private = dc; + return 0; + +bad: + kfree(dc); + return -EINVAL; +} + +static void delay_dtr(struct dm_target *ti) +{ + struct delay_c *dc = ti->private; + + flush_workqueue(kdelayd_wq); + + dm_put_device(ti, dc->dev_read); + + if (dc->dev_write) + dm_put_device(ti, dc->dev_write); + + mempool_destroy(dc->delayed_pool); + kfree(dc); +} + +static int delay_bio(struct delay_c *dc, int delay, struct bio *bio) +{ + struct delay_info *delayed; + unsigned long expires = 0; + + if (!delay || !atomic_read(&dc->may_delay)) + return 1; + + delayed = mempool_alloc(dc->delayed_pool, GFP_NOIO); + + delayed->context = dc; + delayed->bio = bio; + delayed->expires = expires = jiffies + (delay * HZ / 1000); + + mutex_lock(&delayed_bios_lock); + + if (bio_data_dir(bio) == WRITE) + dc->writes++; + else + dc->reads++; + + list_add_tail(&delayed->list, &dc->delayed_bios); + + mutex_unlock(&delayed_bios_lock); + + queue_timeout(dc, expires); + + return 0; +} + +static void delay_presuspend(struct dm_target *ti) +{ + struct delay_c *dc = ti->private; + + atomic_set(&dc->may_delay, 0); + del_timer_sync(&dc->delay_timer); + flush_bios(flush_delayed_bios(dc, 1)); +} + +static void delay_resume(struct dm_target *ti) +{ + struct delay_c *dc = ti->private; + + atomic_set(&dc->may_delay, 1); +} + +static int delay_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) +{ + struct delay_c *dc = ti->private; + + if ((bio_data_dir(bio) == WRITE) && (dc->dev_write)) { + bio->bi_bdev = dc->dev_write->bdev; + bio->bi_sector = dc->start_write + + (bio->bi_sector - ti->begin); + + return delay_bio(dc, dc->write_delay, bio); + } + + bio->bi_bdev = dc->dev_read->bdev; + bio->bi_sector = dc->start_read + + (bio->bi_sector - ti->begin); + + return delay_bio(dc, dc->read_delay, bio); +} + +static int delay_status(struct dm_target *ti, status_type_t type, + char *result, unsigned maxlen) +{ + struct delay_c *dc = ti->private; + int sz = 0; + + switch (type) { + case STATUSTYPE_INFO: + DMEMIT("%u %u", dc->reads, dc->writes); + break; + + case STATUSTYPE_TABLE: + DMEMIT("%s %llu %u", dc->dev_read->name, + (unsigned long long) dc->start_read, + dc->read_delay); + if (dc->dev_write) + DMEMIT("%s %llu %u", dc->dev_write->name, + (unsigned long long) dc->start_write, + dc->write_delay); + break; + } + + return 0; +} + +static struct target_type delay_target = { + .name = "delay", + .version = {1, 0, 2}, + .module = THIS_MODULE, + .ctr = delay_ctr, + .dtr = delay_dtr, + .map = delay_map, + .presuspend = delay_presuspend, + .resume = delay_resume, + .status = delay_status, +}; + +static int __init dm_delay_init(void) +{ + int r = -ENOMEM; + + kdelayd_wq = create_workqueue("kdelayd"); + if (!kdelayd_wq) { + DMERR("Couldn't start kdelayd"); + goto bad_queue; + } + + delayed_cache = kmem_cache_create("dm-delay", + sizeof(struct delay_info), + __alignof__(struct delay_info), + 0, NULL, NULL); + if (!delayed_cache) { + DMERR("Couldn't create delayed bio cache."); + goto bad_memcache; + } + + r = dm_register_target(&delay_target); + if (r < 0) { + DMERR("register failed %d", r); + goto bad_register; + } + + return 0; + +bad_register: + kmem_cache_destroy(delayed_cache); +bad_memcache: + destroy_workqueue(kdelayd_wq); +bad_queue: + return r; +} + +static void __exit dm_delay_exit(void) +{ + int r = dm_unregister_target(&delay_target); + + if (r < 0) + DMERR("unregister failed %d", r); + + kmem_cache_destroy(delayed_cache); + destroy_workqueue(kdelayd_wq); +} + +/* Module hooks */ +module_init(dm_delay_init); +module_exit(dm_delay_exit); + +MODULE_DESCRIPTION(DM_NAME " delay target"); +MODULE_AUTHOR("Heinz Mauelshagen "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f5353cd7c9cd10cdf9d62a5487f3db77b7b68105 Mon Sep 17 00:00:00 2001 From: Jonathan Brassow Date: Wed, 9 May 2007 02:33:07 -0700 Subject: dm raid1: fix to commit pending clear region requests With the code as it is, it is possible for oustanding clear region requests never to get flushed when a mirror is deactivated or suspended. This means there will always be some resync work required when a mirror is activated, even though it may very well be in-sync. Always requesting the flush doesn't hurt us. This is because the log tracks whether any changes occurred and, if not, no flush is performed. Signed-off-by: Jonathan Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-raid1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index bde90460609..85d254edd9b 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -405,8 +405,7 @@ static void rh_update_states(struct region_hash *rh) mempool_free(reg, rh->region_pool); } - if (!list_empty(&recovered)) - rh->log->type->flush(rh->log); + rh->log->type->flush(rh->log); list_for_each_entry_safe (reg, next, &clean, list) mempool_free(reg, rh->region_pool); -- cgit v1.2.3 From b997b82d266c9fb910fc2ad95b9bb93b3bccf9be Mon Sep 17 00:00:00 2001 From: Jonathan Brassow Date: Wed, 9 May 2007 02:33:08 -0700 Subject: dm raid1: switch rh_in_sync to blocking in do_reads The call to rh_in_sync() in do_reads() should be allowed to block. It is in the mirror worker thread which already permits blocking operations. This will be needed to support clustered mirroring which will perform network operations. Signed-off-by: Jonathan Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-raid1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 85d254edd9b..ef124b71ccc 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -741,7 +741,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads) /* * We can only read balance if the region is in sync. */ - if (rh_in_sync(&ms->rh, region, 0)) + if (rh_in_sync(&ms->rh, region, 1)) m = choose_mirror(ms, bio->bi_sector); else m = ms->default_mirror; -- cgit v1.2.3 From ba8b45cea5f632540d561d37d94c71c07f6af1aa Mon Sep 17 00:00:00 2001 From: Jonathan Brassow Date: Wed, 9 May 2007 02:33:08 -0700 Subject: dm log: fix resume failed log device This patch removes the possibility of having uninitialized log state if the log device has failed. When a mirror resumes operation, it calls 'resume' on the logging module. If disk based logging is being used, the log device is read to fill in the log state. If the log device has failed, we cannot simply return, because this would leave the in-memory log state uninitialized. Instead, we assume all regions are out-of-sync and reset the log state. Failure to do this could result in the logging code reporting a region as in-sync, even though it isn't; which could result in a corrupted mirror. Signed-off-by: Jonathan Brassow Signed-off-by: Alasdair G Kergon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/md/dm-log.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index a60acf8d385..a66428d860f 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -478,7 +478,14 @@ static int disk_resume(struct dirty_log *log) DMWARN("%s: Failed to read header on mirror log device", lc->log_dev->name); fail_log_device(lc); - return r; + /* + * If the log device cannot be read, we must assume + * all regions are out-of-sync. If we simply return + * here, the state will be uninitialized and could + * lead us to return 'in-sync' status for regions + * that are actually 'out-of-sync'. + */ + lc->header.nr_regions = 0; } /* set or clear any new bits -- device has grown */ -- cgit v1.2.3 From ace4bd29c248b51db3f8a97e9b59740dc6caa074 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Wed, 9 May 2007 02:33:09 -0700 Subject: fix leaky resv_huge_pages when cpuset is in use The internal hugetlb resv_huge_pages variable can permanently leak nonzero value in the error path of hugetlb page fault handler when hugetlb page is used in combination of cpuset. The leaked count can permanently trap N number of hugetlb pages in unusable "reserved" state. Steps to reproduce the bug: (1) create two cpuset, user1 and user2 (2) reserve 50 htlb pages in cpuset user1 (3) attempt to shmget/shmat 50 htlb page inside cpuset user2 (4) kernel oom the user process in step 3 (5) ipcrm the shm segment At this point resv_huge_pages will have a count of 49, even though there are no active hugetlbfs file nor hugetlb shared memory segment in the system. The leak is permanent and there is no recovery method other than system reboot. The leaked count will hold up all future use of that many htlb pages in all cpusets. The culprit is that the error path of alloc_huge_page() did not properly undo the change it made to resv_huge_page, causing inconsistent state. Signed-off-by: Ken Chen Cc: David Gibson Cc: Adam Litke Cc: Martin Bligh Acked-by: David Gibson Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/hugetlb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 36db012b38d..88e708be1f6 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -140,6 +140,8 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return page; fail: + if (vma->vm_flags & VM_MAYSHARE) + resv_huge_pages++; spin_unlock(&hugetlb_lock); return NULL; } -- cgit v1.2.3 From d60846c4d16f9518b098b905af2b87cb6bf6dc42 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:17 -0700 Subject: swsusp: clean up printk Remove an inexplicable / Cc: "Rafael J. Wysocki" Cc: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/power/snapshot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index b7039772b05..48383ea7229 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1227,7 +1227,7 @@ asmlinkage int swsusp_save(void) nr_copy_pages = nr_pages; nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE); - printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages); + printk("swsusp: critical section: done (%d pages copied)\n", nr_pages); return 0; } -- cgit v1.2.3 From a3d25c275d383975504dc53c25b691df59bd3c48 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 May 2007 02:33:18 -0700 Subject: PM: Separate hibernation code from suspend code [ With Johannes Berg ] Separate the hibernation (aka suspend to disk code) from the other suspend code. In particular: * Remove the definitions related to hibernation from include/linux/pm.h * Introduce struct hibernation_ops and a new hibernate() function to hibernate the system, defined in include/linux/suspend.h * Separate suspend code in kernel/power/main.c from hibernation-related code in kernel/power/disk.c and kernel/power/user.c (with the help of hibernation_ops) * Switch ACPI (the only user of pm_ops.pm_disk_mode) to hibernation_ops Signed-off-by: Rafael J. Wysocki Cc: Greg KH Cc: Pavel Machek Cc: Nigel Cunningham Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/power/userland-swsusp.txt | 26 +++-- drivers/acpi/sleep/main.c | 67 ++++++++--- drivers/acpi/sleep/proc.c | 2 +- drivers/i2c/chips/tps65010.c | 2 +- include/linux/pm.h | 31 +---- include/linux/suspend.h | 24 ++++ kernel/power/disk.c | 195 ++++++++++++++++++-------------- kernel/power/main.c | 42 +++---- kernel/power/power.h | 7 +- kernel/power/user.c | 13 +-- kernel/sys.c | 2 +- 11 files changed, 226 insertions(+), 185 deletions(-) diff --git a/Documentation/power/userland-swsusp.txt b/Documentation/power/userland-swsusp.txt index 000556c932e..e00c6cf09e8 100644 --- a/Documentation/power/userland-swsusp.txt +++ b/Documentation/power/userland-swsusp.txt @@ -93,21 +93,23 @@ SNAPSHOT_S2RAM - suspend to RAM; using this call causes the kernel to to resume the system from RAM if there's enough battery power or restore its state on the basis of the saved suspend image otherwise) -SNAPSHOT_PMOPS - enable the usage of the pmops->prepare, pmops->enter and - pmops->finish methods (the in-kernel swsusp knows these as the "platform - method") which are needed on many machines to (among others) speed up - the resume by letting the BIOS skip some steps or to let the system - recognise the correct state of the hardware after the resume (in - particular on many machines this ensures that unplugged AC - adapters get correctly detected and that kacpid does not run wild after - the resume). The last ioctl() argument can take one of the three - values, defined in kernel/power/power.h: +SNAPSHOT_PMOPS - enable the usage of the hibernation_ops->prepare, + hibernate_ops->enter and hibernation_ops->finish methods (the in-kernel + swsusp knows these as the "platform method") which are needed on many + machines to (among others) speed up the resume by letting the BIOS skip + some steps or to let the system recognise the correct state of the + hardware after the resume (in particular on many machines this ensures + that unplugged AC adapters get correctly detected and that kacpid does + not run wild after the resume). The last ioctl() argument can take one + of the three values, defined in kernel/power/power.h: PMOPS_PREPARE - make the kernel carry out the - pm_ops->prepare(PM_SUSPEND_DISK) operation + hibernation_ops->prepare() operation PMOPS_ENTER - make the kernel power off the system by calling - pm_ops->enter(PM_SUSPEND_DISK) + hibernation_ops->enter() PMOPS_FINISH - make the kernel carry out the - pm_ops->finish(PM_SUSPEND_DISK) operation + hibernation_ops->finish() operation + Note that the actual constants are misnamed because they surface + internal kernel implementation details that have changed. The device's read() operation can be used to transfer the snapshot image from the kernel. It has the following limitations: diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index f8c63410bcb..52b23471dd6 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -29,7 +29,6 @@ static u32 acpi_suspend_states[] = { [PM_SUSPEND_ON] = ACPI_STATE_S0, [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, [PM_SUSPEND_MEM] = ACPI_STATE_S3, - [PM_SUSPEND_DISK] = ACPI_STATE_S4, [PM_SUSPEND_MAX] = ACPI_STATE_S5 }; @@ -94,14 +93,6 @@ static int acpi_pm_enter(suspend_state_t pm_state) do_suspend_lowlevel(); break; - case PM_SUSPEND_DISK: - if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM) - status = acpi_enter_sleep_state(acpi_state); - break; - case PM_SUSPEND_MAX: - acpi_power_off(); - break; - default: return -EINVAL; } @@ -157,12 +148,13 @@ int acpi_suspend(u32 acpi_state) suspend_state_t states[] = { [1] = PM_SUSPEND_STANDBY, [3] = PM_SUSPEND_MEM, - [4] = PM_SUSPEND_DISK, [5] = PM_SUSPEND_MAX }; if (acpi_state < 6 && states[acpi_state]) return pm_suspend(states[acpi_state]); + if (acpi_state == 4) + return hibernate(); return -EINVAL; } @@ -189,6 +181,49 @@ static struct pm_ops acpi_pm_ops = { .finish = acpi_pm_finish, }; +#ifdef CONFIG_SOFTWARE_SUSPEND +static int acpi_hibernation_prepare(void) +{ + return acpi_sleep_prepare(ACPI_STATE_S4); +} + +static int acpi_hibernation_enter(void) +{ + acpi_status status = AE_OK; + unsigned long flags = 0; + + ACPI_FLUSH_CPU_CACHE(); + + local_irq_save(flags); + acpi_enable_wakeup_device(ACPI_STATE_S4); + /* This shouldn't return. If it returns, we have a problem */ + status = acpi_enter_sleep_state(ACPI_STATE_S4); + local_irq_restore(flags); + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; +} + +static void acpi_hibernation_finish(void) +{ + acpi_leave_sleep_state(ACPI_STATE_S4); + acpi_disable_wakeup_device(ACPI_STATE_S4); + + /* reset firmware waking vector */ + acpi_set_firmware_waking_vector((acpi_physical_address) 0); + + if (init_8259A_after_S1) { + printk("Broken toshiba laptop -> kicking interrupts\n"); + init_8259A(0); + } +} + +static struct hibernation_ops acpi_hibernation_ops = { + .prepare = acpi_hibernation_prepare, + .enter = acpi_hibernation_enter, + .finish = acpi_hibernation_finish, +}; +#endif /* CONFIG_SOFTWARE_SUSPEND */ + /* * Toshiba fails to preserve interrupts over S1, reinitialization * of 8259 is needed after S1 resume. @@ -227,14 +262,18 @@ int __init acpi_sleep_init(void) sleep_states[i] = 1; printk(" S%d", i); } - if (i == ACPI_STATE_S4) { - if (sleep_states[i]) - acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM; - } } printk(")\n"); pm_set_ops(&acpi_pm_ops); + +#ifdef CONFIG_SOFTWARE_SUSPEND + if (sleep_states[ACPI_STATE_S4]) + hibernation_set_ops(&acpi_hibernation_ops); +#else + sleep_states[ACPI_STATE_S4] = 0; +#endif + return 0; } diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 5a76e5be61d..76b45f0b834 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -60,7 +60,7 @@ acpi_system_write_sleep(struct file *file, state = simple_strtoul(str, NULL, 0); #ifdef CONFIG_SOFTWARE_SUSPEND if (state == 4) { - error = pm_suspend(PM_SUSPEND_DISK); + error = hibernate(); goto Done; } #endif diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 7ed92dc3d83..3c3f2ebf3fc 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -354,7 +354,7 @@ static void tps65010_interrupt(struct tps65010 *tps) * also needs to get error handling and probably * an #ifdef CONFIG_SOFTWARE_SUSPEND */ - pm_suspend(PM_SUSPEND_DISK); + hibernate(); #endif poll = 1; } diff --git a/include/linux/pm.h b/include/linux/pm.h index 6e8fa3049e5..87545e0f0b5 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -107,26 +107,11 @@ typedef int __bitwise suspend_state_t; #define PM_SUSPEND_ON ((__force suspend_state_t) 0) #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1) #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) -#define PM_SUSPEND_DISK ((__force suspend_state_t) 4) -#define PM_SUSPEND_MAX ((__force suspend_state_t) 5) - -typedef int __bitwise suspend_disk_method_t; - -/* invalid must be 0 so struct pm_ops initialisers can leave it out */ -#define PM_DISK_INVALID ((__force suspend_disk_method_t) 0) -#define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 1) -#define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 2) -#define PM_DISK_REBOOT ((__force suspend_disk_method_t) 3) -#define PM_DISK_TEST ((__force suspend_disk_method_t) 4) -#define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 5) -#define PM_DISK_MAX ((__force suspend_disk_method_t) 6) +#define PM_SUSPEND_MAX ((__force suspend_state_t) 4) /** * struct pm_ops - Callbacks for managing platform dependent suspend states. * @valid: Callback to determine whether the given state can be entered. - * If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is - * always valid and never passed to this call. If not assigned, - * no suspend states are valid. * Valid states are advertised in /sys/power/state but can still * be rejected by prepare or enter if the conditions aren't right. * There is a %pm_valid_only_mem function available that can be assigned @@ -140,24 +125,12 @@ typedef int __bitwise suspend_disk_method_t; * * @finish: Called when the system has left the given state and all devices * are resumed. The return value is ignored. - * - * @pm_disk_mode: The generic code always allows one of the shutdown methods - * %PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and - * %PM_DISK_TESTPROC. If this variable is set, the mode it is set - * to is allowed in addition to those modes and is also made default. - * When this mode is sent selected, the @prepare call will be called - * before suspending to disk (if present), the @enter call should be - * present and will be called after all state has been saved and the - * machine is ready to be powered off; the @finish callback is called - * after state has been restored. All these calls are called with - * %PM_SUSPEND_DISK as the state. */ struct pm_ops { int (*valid)(suspend_state_t state); int (*prepare)(suspend_state_t state); int (*enter)(suspend_state_t state); int (*finish)(suspend_state_t state); - suspend_disk_method_t pm_disk_mode; }; /** @@ -276,8 +249,6 @@ extern void device_power_up(void); extern void device_resume(void); #ifdef CONFIG_PM -extern suspend_disk_method_t pm_disk_mode; - extern int device_suspend(pm_message_t state); extern int device_prepare_suspend(pm_message_t state); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 9d2aa1a12aa..d74da9122b6 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -32,6 +32,24 @@ static inline int pm_prepare_console(void) { return 0; } static inline void pm_restore_console(void) {} #endif +/** + * struct hibernation_ops - hibernation platform support + * + * The methods in this structure allow a platform to override the default + * mechanism of shutting down the machine during a hibernation transition. + * + * All three methods must be assigned. + * + * @prepare: prepare system for hibernation + * @enter: shut down system after state has been saved to disk + * @finish: finish/clean up after state has been reloaded + */ +struct hibernation_ops { + int (*prepare)(void); + int (*enter)(void); + void (*finish)(void); +}; + #if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) /* kernel/power/snapshot.c */ extern void __init register_nosave_region(unsigned long, unsigned long); @@ -39,11 +57,17 @@ extern int swsusp_page_is_forbidden(struct page *); extern void swsusp_set_page_free(struct page *); extern void swsusp_unset_page_free(struct page *); extern unsigned long get_safe_page(gfp_t gfp_mask); + +extern void hibernation_set_ops(struct hibernation_ops *ops); +extern int hibernate(void); #else static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } static inline void swsusp_set_page_free(struct page *p) {} static inline void swsusp_unset_page_free(struct page *p) {} + +static inline void hibernation_set_ops(struct hibernation_ops *ops) {} +static inline int hibernate(void) { return -ENOSYS; } #endif /* defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) */ void save_processor_state(void); diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 06331374d86..b5f0543ed84 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -30,30 +30,69 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION; dev_t swsusp_resume_device; sector_t swsusp_resume_block; +enum { + HIBERNATION_INVALID, + HIBERNATION_PLATFORM, + HIBERNATION_TEST, + HIBERNATION_TESTPROC, + HIBERNATION_SHUTDOWN, + HIBERNATION_REBOOT, + /* keep last */ + __HIBERNATION_AFTER_LAST +}; +#define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) +#define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) + +static int hibernation_mode = HIBERNATION_SHUTDOWN; + +struct hibernation_ops *hibernation_ops; + +/** + * hibernation_set_ops - set the global hibernate operations + * @ops: the hibernation operations to use in subsequent hibernation transitions + */ + +void hibernation_set_ops(struct hibernation_ops *ops) +{ + if (ops && !(ops->prepare && ops->enter && ops->finish)) { + WARN_ON(1); + return; + } + mutex_lock(&pm_mutex); + hibernation_ops = ops; + if (ops) + hibernation_mode = HIBERNATION_PLATFORM; + else if (hibernation_mode == HIBERNATION_PLATFORM) + hibernation_mode = HIBERNATION_SHUTDOWN; + + mutex_unlock(&pm_mutex); +} + + /** * platform_prepare - prepare the machine for hibernation using the * platform driver if so configured and return an error code if it fails */ -static inline int platform_prepare(void) +static int platform_prepare(void) { - int error = 0; + return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ? + hibernation_ops->prepare() : 0; +} - switch (pm_disk_mode) { - case PM_DISK_TEST: - case PM_DISK_TESTPROC: - case PM_DISK_SHUTDOWN: - case PM_DISK_REBOOT: - break; - default: - if (pm_ops && pm_ops->prepare) - error = pm_ops->prepare(PM_SUSPEND_DISK); - } - return error; +/** + * platform_finish - switch the machine to the normal mode of operation + * using the platform driver (must be called after platform_prepare()) + */ + +static void platform_finish(void) +{ + if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) + hibernation_ops->finish(); } /** - * power_down - Shut machine down for hibernate. + * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured so; otherwise try * to power off or reboot. @@ -61,20 +100,20 @@ static inline int platform_prepare(void) static void power_down(void) { - switch (pm_disk_mode) { - case PM_DISK_TEST: - case PM_DISK_TESTPROC: + switch (hibernation_mode) { + case HIBERNATION_TEST: + case HIBERNATION_TESTPROC: break; - case PM_DISK_SHUTDOWN: + case HIBERNATION_SHUTDOWN: kernel_power_off(); break; - case PM_DISK_REBOOT: + case HIBERNATION_REBOOT: kernel_restart(NULL); break; - default: - if (pm_ops && pm_ops->enter) { + case HIBERNATION_PLATFORM: + if (hibernation_ops) { kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); - pm_ops->enter(PM_SUSPEND_DISK); + hibernation_ops->enter(); break; } } @@ -87,20 +126,6 @@ static void power_down(void) while(1); } -static inline void platform_finish(void) -{ - switch (pm_disk_mode) { - case PM_DISK_TEST: - case PM_DISK_TESTPROC: - case PM_DISK_SHUTDOWN: - case PM_DISK_REBOOT: - break; - default: - if (pm_ops && pm_ops->finish) - pm_ops->finish(PM_SUSPEND_DISK); - } -} - static void unprepare_processes(void) { thaw_processes(); @@ -120,13 +145,10 @@ static int prepare_processes(void) } /** - * pm_suspend_disk - The granpappy of hibernation power management. - * - * If not, then call swsusp to do its thing, then figure out how - * to power down the system. + * hibernate - The granpappy of the built-in hibernation management */ -int pm_suspend_disk(void) +int hibernate(void) { int error; @@ -143,7 +165,8 @@ int pm_suspend_disk(void) if (error) goto Finish; - if (pm_disk_mode == PM_DISK_TESTPROC) { + mutex_lock(&pm_mutex); + if (hibernation_mode == HIBERNATION_TESTPROC) { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); goto Thaw; @@ -168,7 +191,7 @@ int pm_suspend_disk(void) if (error) goto Enable_cpus; - if (pm_disk_mode == PM_DISK_TEST) { + if (hibernation_mode == HIBERNATION_TEST) { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); goto Enable_cpus; @@ -205,6 +228,7 @@ int pm_suspend_disk(void) device_resume(); resume_console(); Thaw: + mutex_unlock(&pm_mutex); unprepare_processes(); Finish: free_basic_memory_bitmaps(); @@ -220,7 +244,7 @@ int pm_suspend_disk(void) * Called as a late_initcall (so all devices are discovered and * initialized), we call swsusp to see if we have a saved image or not. * If so, we quiesce devices, the restore the saved image. We will - * return above (in pm_suspend_disk() ) if everything goes well. + * return above (in hibernate() ) if everything goes well. * Otherwise, we fail gracefully and return to the normally * scheduled program. * @@ -315,25 +339,26 @@ static int software_resume(void) late_initcall(software_resume); -static const char * const pm_disk_modes[] = { - [PM_DISK_PLATFORM] = "platform", - [PM_DISK_SHUTDOWN] = "shutdown", - [PM_DISK_REBOOT] = "reboot", - [PM_DISK_TEST] = "test", - [PM_DISK_TESTPROC] = "testproc", +static const char * const hibernation_modes[] = { + [HIBERNATION_PLATFORM] = "platform", + [HIBERNATION_SHUTDOWN] = "shutdown", + [HIBERNATION_REBOOT] = "reboot", + [HIBERNATION_TEST] = "test", + [HIBERNATION_TESTPROC] = "testproc", }; /** - * disk - Control suspend-to-disk mode + * disk - Control hibernation mode * * Suspend-to-disk can be handled in several ways. We have a few options * for putting the system to sleep - using the platform driver (e.g. ACPI - * or other pm_ops), powering off the system or rebooting the system - * (for testing) as well as the two test modes. + * or other hibernation_ops), powering off the system or rebooting the + * system (for testing) as well as the two test modes. * * The system can support 'platform', and that is known a priori (and - * encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' - * as alternatives, as well as the test modes 'test' and 'testproc'. + * encoded by the presence of hibernation_ops). However, the user may + * choose 'shutdown' or 'reboot' as alternatives, as well as one fo the + * test modes, 'test' or 'testproc'. * * show() will display what the mode is currently set to. * store() will accept one of @@ -345,7 +370,7 @@ static const char * const pm_disk_modes[] = { * 'testproc' * * It will only change to 'platform' if the system - * supports it (as determined from pm_ops->pm_disk_mode). + * supports it (as determined by having hibernation_ops). */ static ssize_t disk_show(struct kset *kset, char *buf) @@ -353,28 +378,25 @@ static ssize_t disk_show(struct kset *kset, char *buf) int i; char *start = buf; - for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { - if (!pm_disk_modes[i]) + for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { + if (!hibernation_modes[i]) continue; switch (i) { - case PM_DISK_SHUTDOWN: - case PM_DISK_REBOOT: - case PM_DISK_TEST: - case PM_DISK_TESTPROC: + case HIBERNATION_SHUTDOWN: + case HIBERNATION_REBOOT: + case HIBERNATION_TEST: + case HIBERNATION_TESTPROC: break; - default: - if (pm_ops && pm_ops->enter && - (i == pm_ops->pm_disk_mode)) + case HIBERNATION_PLATFORM: + if (hibernation_ops) break; /* not a valid mode, continue with loop */ continue; } - if (i == pm_disk_mode) - buf += sprintf(buf, "[%s]", pm_disk_modes[i]); + if (i == hibernation_mode) + buf += sprintf(buf, "[%s] ", hibernation_modes[i]); else - buf += sprintf(buf, "%s", pm_disk_modes[i]); - if (i+1 != PM_DISK_MAX) - buf += sprintf(buf, " "); + buf += sprintf(buf, "%s ", hibernation_modes[i]); } buf += sprintf(buf, "\n"); return buf-start; @@ -387,39 +409,38 @@ static ssize_t disk_store(struct kset *kset, const char *buf, size_t n) int i; int len; char *p; - suspend_disk_method_t mode = 0; + int mode = HIBERNATION_INVALID; p = memchr(buf, '\n', n); len = p ? p - buf : n; mutex_lock(&pm_mutex); - for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { - if (!strncmp(buf, pm_disk_modes[i], len)) { + for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { + if (!strncmp(buf, hibernation_modes[i], len)) { mode = i; break; } } - if (mode) { + if (mode != HIBERNATION_INVALID) { switch (mode) { - case PM_DISK_SHUTDOWN: - case PM_DISK_REBOOT: - case PM_DISK_TEST: - case PM_DISK_TESTPROC: - pm_disk_mode = mode; + case HIBERNATION_SHUTDOWN: + case HIBERNATION_REBOOT: + case HIBERNATION_TEST: + case HIBERNATION_TESTPROC: + hibernation_mode = mode; break; - default: - if (pm_ops && pm_ops->enter && - (mode == pm_ops->pm_disk_mode)) - pm_disk_mode = mode; + case HIBERNATION_PLATFORM: + if (hibernation_ops) + hibernation_mode = mode; else error = -EINVAL; } - } else { + } else error = -EINVAL; - } - pr_debug("PM: suspend-to-disk mode set to '%s'\n", - pm_disk_modes[mode]); + if (!error) + pr_debug("PM: suspend-to-disk mode set to '%s'\n", + hibernation_modes[mode]); mutex_unlock(&pm_mutex); return error ? error : n; } diff --git a/kernel/power/main.c b/kernel/power/main.c index f6dda685e7e..40d56a31245 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -30,7 +30,6 @@ DEFINE_MUTEX(pm_mutex); struct pm_ops *pm_ops; -suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; /** * pm_set_ops - Set the global power method table. @@ -41,10 +40,6 @@ void pm_set_ops(struct pm_ops * ops) { mutex_lock(&pm_mutex); pm_ops = ops; - if (ops && ops->pm_disk_mode != PM_DISK_INVALID) { - pm_disk_mode = ops->pm_disk_mode; - } else - pm_disk_mode = PM_DISK_SHUTDOWN; mutex_unlock(&pm_mutex); } @@ -184,24 +179,12 @@ static void suspend_finish(suspend_state_t state) static const char * const pm_states[PM_SUSPEND_MAX] = { [PM_SUSPEND_STANDBY] = "standby", [PM_SUSPEND_MEM] = "mem", - [PM_SUSPEND_DISK] = "disk", }; static inline int valid_state(suspend_state_t state) { - /* Suspend-to-disk does not really need low-level support. - * It can work with shutdown/reboot if needed. If it isn't - * configured, then it cannot be supported. - */ - if (state == PM_SUSPEND_DISK) -#ifdef CONFIG_SOFTWARE_SUSPEND - return 1; -#else - return 0; -#endif - - /* all other states need lowlevel support and need to be - * valid to the lowlevel implementation, no valid callback + /* All states need lowlevel support and need to be valid + * to the lowlevel implementation, no valid callback * implies that none are valid. */ if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state)) return 0; @@ -229,11 +212,6 @@ static int enter_state(suspend_state_t state) if (!mutex_trylock(&pm_mutex)) return -EBUSY; - if (state == PM_SUSPEND_DISK) { - error = pm_suspend_disk(); - goto Unlock; - } - pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); if ((error = suspend_prepare(state))) goto Unlock; @@ -251,7 +229,7 @@ static int enter_state(suspend_state_t state) /** * pm_suspend - Externally visible function for suspending system. - * @state: Enumarted value of state to enter. + * @state: Enumerated value of state to enter. * * Determine whether or not value is within range, get state * structure, and enter (above). @@ -289,7 +267,13 @@ static ssize_t state_show(struct kset *kset, char *buf) if (pm_states[i] && valid_state(i)) s += sprintf(s,"%s ", pm_states[i]); } - s += sprintf(s,"\n"); +#ifdef CONFIG_SOFTWARE_SUSPEND + s += sprintf(s, "%s\n", "disk"); +#else + if (s != buf) + /* convert the last space to a newline */ + *(s-1) = '\n'; +#endif return (s - buf); } @@ -304,6 +288,12 @@ static ssize_t state_store(struct kset *kset, const char *buf, size_t n) p = memchr(buf, '\n', n); len = p ? p - buf : n; + /* First, check if we are requested to hibernate */ + if (!strncmp(buf, "disk", len)) { + error = hibernate(); + return error ? error : n; + } + for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { if (*s && !strncmp(buf, *s, len)) break; diff --git a/kernel/power/power.h b/kernel/power/power.h index 34b43542785..51381487103 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -25,12 +25,7 @@ struct swsusp_info { */ #define SPARE_PAGES ((1024 * 1024) >> PAGE_SHIFT) -extern int pm_suspend_disk(void); -#else -static inline int pm_suspend_disk(void) -{ - return -EPERM; -} +extern struct hibernation_ops *hibernation_ops; #endif extern int pfn_is_nosave(unsigned long); diff --git a/kernel/power/user.c b/kernel/power/user.c index 040560d9c31..24d7d78e6f4 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -130,16 +130,16 @@ static inline int platform_prepare(void) { int error = 0; - if (pm_ops && pm_ops->prepare) - error = pm_ops->prepare(PM_SUSPEND_DISK); + if (hibernation_ops) + error = hibernation_ops->prepare(); return error; } static inline void platform_finish(void) { - if (pm_ops && pm_ops->finish) - pm_ops->finish(PM_SUSPEND_DISK); + if (hibernation_ops) + hibernation_ops->finish(); } static inline int snapshot_suspend(int platform_suspend) @@ -384,7 +384,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, switch (arg) { case PMOPS_PREPARE: - if (pm_ops && pm_ops->enter) { + if (hibernation_ops) { data->platform_suspend = 1; error = 0; } else { @@ -395,8 +395,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, case PMOPS_ENTER: if (data->platform_suspend) { kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); - error = pm_ops->enter(PM_SUSPEND_DISK); - error = 0; + error = hibernation_ops->enter(); } break; diff --git a/kernel/sys.c b/kernel/sys.c index 926bf9d7ac4..8ecfe347377 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -881,7 +881,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user #ifdef CONFIG_SOFTWARE_SUSPEND case LINUX_REBOOT_CMD_SW_SUSPEND: { - int ret = pm_suspend(PM_SUSPEND_DISK); + int ret = hibernate(); unlock_kernel(); return ret; } -- cgit v1.2.3 From 1e0cb0c3bf04850fa6fb300293d9e85ba81b605f Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Wed, 9 May 2007 02:33:19 -0700 Subject: uml: fix build breakage UML now needs required-features.h to build - an empty one suffices. Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-um/required-features.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 include/asm-um/required-features.h diff --git a/include/asm-um/required-features.h b/include/asm-um/required-features.h new file mode 100644 index 00000000000..dfb967b2d2f --- /dev/null +++ b/include/asm-um/required-features.h @@ -0,0 +1,9 @@ +#ifndef __UM_REQUIRED_FEATURES_H +#define __UM_REQUIRED_FEATURES_H + +/* + * Nothing to see, just need something for the i386 and x86_64 asm + * headers to include. + */ + +#endif -- cgit v1.2.3 From 0ff563830209d2f5ea2ece071f7ea71aff934544 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 May 2007 02:33:20 -0700 Subject: uml: turn on SCSI support Enable (i)SCSI on UML, dunno why SCSI was deemed broken, it works like a charm. Signed-off-by: Peter Zijlstra Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/Kconfig | 16 +-------------- arch/um/Kconfig.scsi | 58 ---------------------------------------------------- 2 files changed, 1 insertion(+), 73 deletions(-) delete mode 100644 arch/um/Kconfig.scsi diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 354cc6b7053..b9c0f307a8f 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -320,21 +320,7 @@ source "crypto/Kconfig" source "lib/Kconfig" -menu "SCSI support" -depends on BROKEN - -config SCSI - tristate "SCSI support" - -# This gives us free_dma, which scsi.c wants. -config GENERIC_ISA_DMA - bool - depends on SCSI - default y - -source "arch/um/Kconfig.scsi" - -endmenu +source "drivers/scsi/Kconfig" source "drivers/md/Kconfig" diff --git a/arch/um/Kconfig.scsi b/arch/um/Kconfig.scsi deleted file mode 100644 index c291c942b1a..00000000000 --- a/arch/um/Kconfig.scsi +++ /dev/null @@ -1,58 +0,0 @@ -comment "SCSI support type (disk, tape, CD-ROM)" - depends on SCSI - -config BLK_DEV_SD - tristate "SCSI disk support" - depends on SCSI - -config SD_EXTRA_DEVS - int "Maximum number of SCSI disks that can be loaded as modules" - depends on BLK_DEV_SD - default "40" - -config CHR_DEV_ST - tristate "SCSI tape support" - depends on SCSI - -config BLK_DEV_SR - tristate "SCSI CD-ROM support" - depends on SCSI - -config BLK_DEV_SR_VENDOR - bool "Enable vendor-specific extensions (for SCSI CDROM)" - depends on BLK_DEV_SR - -config SR_EXTRA_DEVS - int "Maximum number of CDROM devices that can be loaded as modules" - depends on BLK_DEV_SR - default "2" - -config CHR_DEV_SG - tristate "SCSI generic support" - depends on SCSI - -comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs" - depends on SCSI - -#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -config SCSI_DEBUG_QUEUES - bool "Enable extra checks in new queueing code" - depends on SCSI - -#fi -config SCSI_MULTI_LUN - bool "Probe all LUNs on each SCSI device" - depends on SCSI - -config SCSI_CONSTANTS - bool "Verbose SCSI error reporting (kernel size +=12K)" - depends on SCSI - -config SCSI_LOGGING - bool "SCSI logging facility" - depends on SCSI - -config SCSI_DEBUG - tristate "SCSI debugging host simulator (EXPERIMENTAL)" - depends on SCSI - -- cgit v1.2.3 From 231f7e9d029a710c14352bff3b8d3753cb9bfde8 Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Wed, 9 May 2007 02:33:21 -0700 Subject: uml: mark a tt-only function Mark another function as tt-mode only. Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/os-Linux/process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index 92a7b59120d..2d9d2ca3929 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -239,6 +239,7 @@ out: return ok; } +#ifdef UML_CONFIG_MODE_TT void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) { int flags = 0, pages; @@ -260,6 +261,7 @@ void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) "errno = %d\n", errno); } } +#endif void init_new_thread_signals(void) { -- cgit v1.2.3 From cb0c78cc94a63871cad051674516b38e4a8aef95 Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Wed, 9 May 2007 02:33:22 -0700 Subject: Fix Linuxdoc comment A linuxdoc comment had fallen out of date - it refers to an argument which no longer exists. Signed-off-by: Jeff Dike Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/irq/handle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 32e1ab1477d..e391cbb1f56 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -22,7 +22,6 @@ * handle_bad_irq - handle spurious and unhandled irqs * @irq: the interrupt number * @desc: description of the interrupt - * @regs: pointer to a register structure * * Handles spurious and unhandled IRQ's. It also prints a debugmessage. */ -- cgit v1.2.3 From 0e7d18b57c39bedcbd181e3c06d13572b33e5380 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 9 May 2007 02:33:22 -0700 Subject: uml: turn build warnings into comments These haven't been fixed for ages. Just make comments out of them. arch/um/kernel/skas/process.c:181:2: warning: #warning Need to look up +userspace_pid by cpu arch/um/kernel/skas/process.c:187:2: warning: #warning Need to look up +userspace_pid by cpu arch/um/kernel/skas/process.c:194:2: warning: #warning need to loop over +userspace_pids in kill_off_processes_skas Signed-off-by: Miklos Szeredi Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/kernel/skas/process.c | 9 ++++++--- arch/um/os-Linux/skas/mem.c | 2 +- arch/um/os-Linux/skas/process.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index ef36facd8fe..a96ae1a0610 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -178,20 +178,23 @@ int start_uml_skas(void) int external_pid_skas(struct task_struct *task) { -#warning Need to look up userspace_pid by cpu + /* FIXME: Need to look up userspace_pid by cpu */ return(userspace_pid[0]); } int thread_pid_skas(struct task_struct *task) { -#warning Need to look up userspace_pid by cpu + /* FIXME: Need to look up userspace_pid by cpu */ return(userspace_pid[0]); } void kill_off_processes_skas(void) { if(proc_mm) -#warning need to loop over userspace_pids in kill_off_processes_skas + /* + * FIXME: need to loop over userspace_pids in + * kill_off_processes_skas + */ os_kill_ptraced_process(userspace_pid[0], 1); else { struct task_struct *p; diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index 8e490fff3d4..5c894632079 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -68,7 +68,7 @@ static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr) int err, pid = mm_idp->u.pid; if(proc_mm) -#warning Need to look up userspace_pid by cpu + /* FIXME: Need to look up userspace_pid by cpu */ pid = userspace_pid[0]; multi_count++; diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 5c088a55396..6a0e466d01e 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -586,7 +586,7 @@ void switch_mm_skas(struct mm_id *mm_idp) { int err; -#warning need cpu pid in switch_mm_skas + /* FIXME: need cpu pid in switch_mm_skas */ if(proc_mm){ err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_idp->u.mm_fd); -- cgit v1.2.3 From dd2a345f8f002845636dbf5d2d768bb5cd8a5f59 Mon Sep 17 00:00:00 2001 From: Dave Gilbert Date: Wed, 9 May 2007 02:33:24 -0700 Subject: Display all possible partitions when the root filesystem failed to mount Display all possible partitions when the root filesystem is not mounted. This helps to track spell'o's and missing drivers. Updated to work with newer kernels. Example output: VFS: Cannot open root device "foobar" or unknown-block(0,0) Please append a correct "root=" boot option; here are the available partitions: 0800 8388608 sda driver: sd 0801 192748 sda1 0802 8193150 sda2 0810 4194304 sdb driver: sd Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) [akpm@linux-foundation.org: cleanups, fix printk warnings] Signed-off-by: Jan Engelhardt Cc: Dave Gilbert Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/genhd.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/genhd.h | 1 + init/do_mounts.c | 7 ++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/block/genhd.c b/block/genhd.c index b5664440896..93a2cf65459 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -213,6 +213,59 @@ struct gendisk *get_gendisk(dev_t dev, int *part) return kobj ? to_disk(kobj) : NULL; } +/* + * print a full list of all partitions - intended for places where the root + * filesystem can't be mounted and thus to give the victim some idea of what + * went wrong + */ +void __init printk_all_partitions(void) +{ + int n; + struct gendisk *sgp; + + mutex_lock(&block_subsys_lock); + /* For each block device... */ + list_for_each_entry(sgp, &block_subsys.list, kobj.entry) { + char buf[BDEVNAME_SIZE]; + /* + * Don't show empty devices or things that have been surpressed + */ + if (get_capacity(sgp) == 0 || + (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) + continue; + + /* + * Note, unlike /proc/partitions, I am showing the numbers in + * hex - the same format as the root= option takes. + */ + printk("%02x%02x %10llu %s", + sgp->major, sgp->first_minor, + (unsigned long long)get_capacity(sgp) >> 1, + disk_name(sgp, 0, buf)); + if (sgp->driverfs_dev != NULL && + sgp->driverfs_dev->driver != NULL) + printk(" driver: %s\n", + sgp->driverfs_dev->driver->name); + else + printk(" (driver?)\n"); + + /* now show the partitions */ + for (n = 0; n < sgp->minors - 1; ++n) { + if (sgp->part[n] == NULL) + continue; + if (sgp->part[n]->nr_sects == 0) + continue; + printk(" %02x%02x %10llu %s\n", + sgp->major, n + 1 + sgp->first_minor, + (unsigned long long)sgp->part[n]->nr_sects >> 1, + disk_name(sgp, n + 1, buf)); + } /* partition subloop */ + } /* Block device loop */ + + mutex_unlock(&block_subsys_lock); + return; +} + #ifdef CONFIG_PROC_FS /* iterator */ static void *part_start(struct seq_file *part, loff_t *pos) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 2c65da7cabb..f589559cf07 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -413,6 +413,7 @@ char *disk_name (struct gendisk *hd, int part, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); extern void add_partition(struct gendisk *, int, sector_t, sector_t, int); extern void delete_partition(struct gendisk *, int); +extern void printk_all_partitions(void); extern struct gendisk *alloc_disk_node(int minors, int node_id); extern struct gendisk *alloc_disk(int minors); diff --git a/init/do_mounts.c b/init/do_mounts.c index 3f57ed4599d..46fe407fb03 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -308,17 +309,21 @@ retry: /* * Allow the user to distinguish between failed sys_open * and bad superblock on root device. + * and give them a list of the available devices */ #ifdef CONFIG_BLOCK __bdevname(ROOT_DEV, b); #endif printk("VFS: Cannot open root device \"%s\" or %s\n", root_device_name, b); - printk("Please append a correct \"root=\" boot option\n"); + printk("Please append a correct \"root=\" boot option; here are the available partitions:\n"); + printk_all_partitions(); panic("VFS: Unable to mount root fs on %s", b); } + printk("List of all partitions:\n"); + printk_all_partitions(); printk("No filesystem could mount root, tried: "); for (p = fs_names; *p; p += strlen(p)+1) printk(" %s", p); -- cgit v1.2.3 From 2f4dfe206a2fc07099dfad77a8ea2f4b4ae2140f Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Wed, 9 May 2007 02:33:25 -0700 Subject: Remove hardcoding of hard_smp_processor_id on UP systems With the advent of kdump, the assumption that the boot CPU when booting an UP kernel is always the CPU with a particular hardware ID (often 0) (usually referred to as BSP on some architectures) is not valid anymore. The reason being that the dump capture kernel boots on the crashed CPU (the CPU that invoked crash_kexec), which may be or may not be that particular CPU. Move definition of hard_smp_processor_id for the UP case to architecture-specific code ("asm/smp.h") where it belongs, so that each architecture can provide its own implementation. Signed-off-by: Fernando Luis Vazquez Cao Cc: "Luck, Tony" Acked-by: Andi Kleen Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc64/kernel/traps.c | 1 + include/asm-alpha/smp.h | 1 + include/asm-i386/smp.h | 3 ++- include/asm-ia64/smp.h | 3 ++- include/asm-m32r/smp.h | 6 +++++- include/asm-powerpc/smp.h | 1 + include/asm-s390/smp.h | 1 + include/asm-sparc/smp.h | 1 + include/asm-sparc64/smp.h | 1 + include/asm-um/smp.h | 4 ++++ include/asm-x86_64/smp.h | 4 +++- include/linux/smp.h | 1 - 12 files changed, 22 insertions(+), 5 deletions(-) diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index dc652f21029..d0fde36395b 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h index a1a1eca6be4..286e1d844f6 100644 --- a/include/asm-alpha/smp.h +++ b/include/asm-alpha/smp.h @@ -51,6 +51,7 @@ int smp_call_function_on_cpu(void (*func) (void *info), void *info,int retry, in #else /* CONFIG_SMP */ +#define hard_smp_processor_id() 0 #define smp_call_function_on_cpu(func,info,retry,wait,cpu) ({ 0; }) #endif /* CONFIG_SMP */ diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 090abc1da32..3243fa6f455 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -147,12 +147,13 @@ extern unsigned int num_processors; #else /* CONFIG_SMP */ +#define hard_smp_processor_id() 0 #define safe_smp_processor_id() 0 #define cpu_physical_id(cpu) boot_cpu_physical_apicid #define NO_PROC_ID 0xFF /* No processor magic marker */ -#endif +#endif /* CONFIG_SMP */ #ifndef __ASSEMBLY__ diff --git a/include/asm-ia64/smp.h b/include/asm-ia64/smp.h index 60fd4ae014f..62014b643ec 100644 --- a/include/asm-ia64/smp.h +++ b/include/asm-ia64/smp.h @@ -128,8 +128,9 @@ extern void unlock_ipi_calllock(void); extern void identify_siblings (struct cpuinfo_ia64 *); extern int is_multithreading_enabled(void); -#else +#else /* CONFIG_SMP */ +#define hard_smp_processor_id() 0 #define cpu_logical_id(i) 0 #define cpu_physical_id(i) ia64_get_lid() diff --git a/include/asm-m32r/smp.h b/include/asm-m32r/smp.h index abd937ac523..078e1a51a04 100644 --- a/include/asm-m32r/smp.h +++ b/include/asm-m32r/smp.h @@ -108,6 +108,10 @@ extern unsigned long send_IPI_mask_phys(cpumask_t, int, int); #define IPI_SHIFT (0) #define NR_IPIS (8) -#endif /* CONFIG_SMP */ +#else /* CONFIG_SMP */ + +#define hard_smp_processor_id() 0 + +#endif /* CONFIG_SMP */ #endif /* _ASM_M32R_SMP_H */ diff --git a/include/asm-powerpc/smp.h b/include/asm-powerpc/smp.h index 01717f266dc..d037f50580e 100644 --- a/include/asm-powerpc/smp.h +++ b/include/asm-powerpc/smp.h @@ -83,6 +83,7 @@ extern void __cpu_die(unsigned int cpu); #else /* for UP */ +#define hard_smp_processor_id() 0 #define smp_setup_cpu_maps() #endif /* CONFIG_SMP */ diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 0a28e6d6ef4..76e424f718c 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -110,6 +110,7 @@ static inline void smp_send_stop(void) __load_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK); } +#define hard_smp_processor_id() 0 #define smp_cpu_not_running(cpu) 1 #define smp_setup_cpu_possible_map() do { } while (0) #endif diff --git a/include/asm-sparc/smp.h b/include/asm-sparc/smp.h index b9da9a600e3..b3f492208fd 100644 --- a/include/asm-sparc/smp.h +++ b/include/asm-sparc/smp.h @@ -165,6 +165,7 @@ void smp_setup_cpu_possible_map(void); #else /* SMP */ +#define hard_smp_processor_id() 0 #define smp_setup_cpu_possible_map() do { } while (0) #endif /* !(SMP) */ diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h index cca54804b72..869d16fb907 100644 --- a/include/asm-sparc64/smp.h +++ b/include/asm-sparc64/smp.h @@ -48,6 +48,7 @@ extern unsigned char boot_cpu_id; #else +#define hard_smp_processor_id() 0 #define smp_setup_cpu_possible_map() do { } while (0) #define boot_cpu_id (0) diff --git a/include/asm-um/smp.h b/include/asm-um/smp.h index ca552261ed1..84f8cf29324 100644 --- a/include/asm-um/smp.h +++ b/include/asm-um/smp.h @@ -24,6 +24,10 @@ extern inline void smp_cpus_done(unsigned int maxcpus) extern struct task_struct *idle_threads[NR_CPUS]; +#else + +#define hard_smp_processor_id() 0 + #endif #endif diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h index d5704421456..f62fda52743 100644 --- a/include/asm-x86_64/smp.h +++ b/include/asm-x86_64/smp.h @@ -71,7 +71,9 @@ extern unsigned __cpuinitdata disabled_cpus; #define NO_PROC_ID 0xFF /* No processor magic marker */ -#endif +#else /* CONFIG_SMP */ +#define hard_smp_processor_id() 0 +#endif /* CONFIG_SMP */ /* * Some lowlevel functions might want to know about diff --git a/include/linux/smp.h b/include/linux/smp.h index 7ba23ec8211..3f70149eabb 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -83,7 +83,6 @@ void smp_prepare_boot_cpu(void); * These macros fold the SMP functionality into a single CPU system */ #define raw_smp_processor_id() 0 -#define hard_smp_processor_id() 0 static inline int up_smp_call_function(void) { return 0; -- cgit v1.2.3 From a36166c6ef45081fea6eeaf5ca785d7ed786b6e2 Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Wed, 9 May 2007 02:33:27 -0700 Subject: Use the APIC to determine the hardware processor id - i386 hard_smp_processor_id used to be just a macro that hard-coded hard_smp_processor_id to 0 in the non SMP case. When booting non SMP kernels on hardware where the boot ioapic id is not 0 this turns out to be a problem. This is happens frequently in the case of kdump and once in a great while in the case of real hardware. Use the APIC to determine the hardware processor id in both UP and SMP kernels to fix this issue. Notice that hard_smp_processor_id is only used by SMP code or by code that works with apics so we do not need to handle the case when apics are not present and hard_smp_processor_id should never be called there. Signed-off-by: Fernando Luis Vazquez Cao Cc: "Luck, Tony" Acked-by: Andi Kleen Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/mach-generic/probe.c | 2 -- include/asm-i386/smp.h | 36 +++++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/arch/i386/mach-generic/probe.c b/arch/i386/mach-generic/probe.c index a7b3999bb37..74f3da63442 100644 --- a/arch/i386/mach-generic/probe.c +++ b/arch/i386/mach-generic/probe.c @@ -119,9 +119,7 @@ int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) return 0; } -#ifdef CONFIG_SMP int hard_smp_processor_id(void) { return genapic->get_apic_id(*(unsigned long *)(APIC_BASE+APIC_ID)); } -#endif diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 3243fa6f455..0c713278706 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -124,20 +124,6 @@ static inline int num_booting_cpus(void) return cpus_weight(cpu_callout_map); } -#ifdef CONFIG_X86_LOCAL_APIC - -#ifdef APIC_DEFINITION -extern int hard_smp_processor_id(void); -#else -#include -static inline int hard_smp_processor_id(void) -{ - /* we don't want to mark this access volatile - bad code generation */ - return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID)); -} -#endif -#endif - extern int safe_smp_processor_id(void); extern int __cpu_disable(void); extern void __cpu_die(unsigned int cpu); @@ -147,7 +133,6 @@ extern unsigned int num_processors; #else /* CONFIG_SMP */ -#define hard_smp_processor_id() 0 #define safe_smp_processor_id() 0 #define cpu_physical_id(cpu) boot_cpu_physical_apicid @@ -157,6 +142,27 @@ extern unsigned int num_processors; #ifndef __ASSEMBLY__ +#ifdef CONFIG_X86_LOCAL_APIC + +#ifdef APIC_DEFINITION +extern int hard_smp_processor_id(void); +#else +#include +static inline int hard_smp_processor_id(void) +{ + /* we don't want to mark this access volatile - bad code generation */ + return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID)); +} +#endif /* APIC_DEFINITION */ + +#else /* CONFIG_X86_LOCAL_APIC */ + +#ifndef CONFIG_SMP +#define hard_smp_processor_id() 0 +#endif + +#endif /* CONFIG_X86_LOCAL_APIC */ + extern u8 apicid_2_node[]; #ifdef CONFIG_X86_LOCAL_APIC -- cgit v1.2.3 From dd988528f4a7d64908b427c251d727f3c3e88add Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Wed, 9 May 2007 02:33:28 -0700 Subject: Use the APIC to determine the hardware processor id - x86_64 hard_smp_processor_id used to be just a macro that hard-coded hard_smp_processor_id to 0 in the non SMP case. When booting non SMP kernels on hardware where the boot ioapic id is not 0 this turns out to be a problem. This is happens frequently in the case of kdump and once in a great while in the case of real hardware. Use the APIC to determine the hardware processor id in both UP and SMP kernels to fix this issue. Notice that hard_smp_processor_id is only used by SMP code or by code that works with apics so we do not need to handle the case when apics are not present and hard_smp_processor_id should never be called there. Signed-off-by: Fernando Luis Vazquez Cao Cc: "Luck, Tony" Acked-by: Andi Kleen Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-x86_64/smp.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h index f62fda52743..3f303d2365e 100644 --- a/include/asm-x86_64/smp.h +++ b/include/asm-x86_64/smp.h @@ -57,12 +57,6 @@ static inline int num_booting_cpus(void) #define raw_smp_processor_id() read_pda(cpunumber) -static inline int hard_smp_processor_id(void) -{ - /* we don't want to mark this access volatile - bad code generation */ - return GET_APIC_ID(*(unsigned int *)(APIC_BASE+APIC_ID)); -} - extern int __cpu_disable(void); extern void __cpu_die(unsigned int cpu); extern void prefill_possible_map(void); @@ -71,10 +65,14 @@ extern unsigned __cpuinitdata disabled_cpus; #define NO_PROC_ID 0xFF /* No processor magic marker */ -#else /* CONFIG_SMP */ -#define hard_smp_processor_id() 0 #endif /* CONFIG_SMP */ +static inline int hard_smp_processor_id(void) +{ + /* we don't want to mark this access volatile - bad code generation */ + return GET_APIC_ID(*(unsigned int *)(APIC_BASE+APIC_ID)); +} + /* * Some lowlevel functions might want to know about * the real APIC ID <-> CPU # mapping. -- cgit v1.2.3 From 818563dceccf695a71f8bd683a249e7bb09e3240 Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Wed, 9 May 2007 02:33:28 -0700 Subject: Always ask the hardware to obtain hardware processor id - ia64 Always ask the hardware to determine the hardware processor id in both UP and SMP kernels. Signed-off-by: Fernando Luis Vazquez Cao Cc: "Luck, Tony" Cc: Andi Kleen Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-ia64/smp.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/asm-ia64/smp.h b/include/asm-ia64/smp.h index 62014b643ec..c60024989eb 100644 --- a/include/asm-ia64/smp.h +++ b/include/asm-ia64/smp.h @@ -38,6 +38,8 @@ ia64_get_lid (void) return lid.f.id << 8 | lid.f.eid; } +#define hard_smp_processor_id() ia64_get_lid() + #ifdef CONFIG_SMP #define XTP_OFFSET 0x1e0008 @@ -110,8 +112,6 @@ max_xtp (void) writeb(0x0f, ipi_base_addr + XTP_OFFSET); /* Set XTP to max */ } -#define hard_smp_processor_id() ia64_get_lid() - /* Upping and downing of CPUs */ extern int __cpu_disable (void); extern void __cpu_die (unsigned int cpu); @@ -130,7 +130,6 @@ extern int is_multithreading_enabled(void); #else /* CONFIG_SMP */ -#define hard_smp_processor_id() 0 #define cpu_logical_id(i) 0 #define cpu_physical_id(i) ia64_get_lid() -- cgit v1.2.3 From 809aa5048fb7e7fd3bf0aa1fb169c42db0f63b08 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 9 May 2007 02:33:29 -0700 Subject: mca: fix bus matching There's a bug in the MCA bus matching algorithm in that it promotes from signed short to int before comparing with the actual id and does sign extension on anything > 0x7fff (which means that pos ids > 0x7fff never get correctly matched). Signed-off-by: James Bottomley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mca/mca-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c index da862e4632d..a70fe00aea1 100644 --- a/drivers/mca/mca-bus.c +++ b/drivers/mca/mca-bus.c @@ -47,7 +47,7 @@ static int mca_bus_match (struct device *dev, struct device_driver *drv) { struct mca_device *mca_dev = to_mca_device (dev); struct mca_driver *mca_drv = to_mca_driver (drv); - const short *mca_ids = mca_drv->id_table; + const unsigned short *mca_ids = mca_drv->id_table; int i; if (!mca_ids) -- cgit v1.2.3 From 8813d1c00ca923c1683da625ff85959be1db9a49 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 9 May 2007 02:33:30 -0700 Subject: mca: add integrated device bus matching The MCA bus has a few "integrated" functions, which are effectively virtual slots on the bus. The problem is that these special functions don't have dedicated pos IDs, so we have to manufacture ids for them outside the pos space ... and these ids can't be matched by the standard matching function, so add a special registration that requests a list of pos ids or a particular integrated function. Signed-off-by: James Bottomley Signed-off-by: Linus Torvalds --- drivers/mca/mca-bus.c | 26 ++++++++++++++++---------- drivers/mca/mca-driver.c | 13 +++++++++++++ include/linux/mca.h | 2 ++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c index a70fe00aea1..67b8e9453b1 100644 --- a/drivers/mca/mca-bus.c +++ b/drivers/mca/mca-bus.c @@ -48,18 +48,24 @@ static int mca_bus_match (struct device *dev, struct device_driver *drv) struct mca_device *mca_dev = to_mca_device (dev); struct mca_driver *mca_drv = to_mca_driver (drv); const unsigned short *mca_ids = mca_drv->id_table; - int i; - - if (!mca_ids) - return 0; - - for(i = 0; mca_ids[i]; i++) { - if (mca_ids[i] == mca_dev->pos_id) { - mca_dev->index = i; - return 1; + int i = 0; + + if (mca_ids) { + for(i = 0; mca_ids[i]; i++) { + if (mca_ids[i] == mca_dev->pos_id) { + mca_dev->index = i; + return 1; + } } } - + /* If the integrated id is present, treat it as though it were an + * additional id in the id_table (it can't be because by definition, + * integrated id's overflow a short */ + if (mca_drv->integrated_id && mca_dev->pos_id == + mca_drv->integrated_id) { + mca_dev->index = i; + return 1; + } return 0; } diff --git a/drivers/mca/mca-driver.c b/drivers/mca/mca-driver.c index 2223466b3d8..32cd39bcc71 100644 --- a/drivers/mca/mca-driver.c +++ b/drivers/mca/mca-driver.c @@ -36,12 +36,25 @@ int mca_register_driver(struct mca_driver *mca_drv) mca_drv->driver.bus = &mca_bus_type; if ((r = driver_register(&mca_drv->driver)) < 0) return r; + mca_drv->integrated_id = 0; } return 0; } EXPORT_SYMBOL(mca_register_driver); +int mca_register_driver_integrated(struct mca_driver *mca_driver, + int integrated_id) +{ + int r = mca_register_driver(mca_driver); + + if (!r) + mca_driver->integrated_id = integrated_id; + + return r; +} +EXPORT_SYMBOL(mca_register_driver_integrated); + void mca_unregister_driver(struct mca_driver *mca_drv) { if (MCA_bus) diff --git a/include/linux/mca.h b/include/linux/mca.h index 5cff2923092..37972704617 100644 --- a/include/linux/mca.h +++ b/include/linux/mca.h @@ -94,6 +94,7 @@ struct mca_bus { struct mca_driver { const short *id_table; void *driver_data; + int integrated_id; struct device_driver driver; }; #define to_mca_driver(mdriver) container_of(mdriver, struct mca_driver, driver) @@ -125,6 +126,7 @@ extern enum MCA_AdapterStatus mca_device_status(struct mca_device *mca_dev); extern struct bus_type mca_bus_type; extern int mca_register_driver(struct mca_driver *drv); +extern int mca_register_driver_integrated(struct mca_driver *, int); extern void mca_unregister_driver(struct mca_driver *drv); /* WARNING: only called by the boot time device setup */ -- cgit v1.2.3 From 54493c10069741a02cd34c2b44a6bfdb85e7de6a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 May 2007 02:33:31 -0700 Subject: cm4000_cs: fix error paths This patch fixes error paths in module_init and probe functions in cm4000_cs and cm4040_cs drivers. Cc: Harald Welte Signed-off-by: Akinobu Mita Cc: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/pcmcia/cm4000_cs.c | 9 +++++++-- drivers/char/pcmcia/cm4040_cs.c | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index e91b43a014b..8cbc64fe0fe 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1881,8 +1881,11 @@ static int cm4000_probe(struct pcmcia_device *link) init_waitqueue_head(&dev->readq); ret = cm4000_config(link, i); - if (ret) + if (ret) { + dev_table[i] = NULL; + kfree(dev); return ret; + } class_device_create(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i); @@ -1907,7 +1910,7 @@ static void cm4000_detach(struct pcmcia_device *link) cm4000_release(link); dev_table[devno] = NULL; - kfree(dev); + kfree(dev); class_device_destroy(cmm_class, MKDEV(major, devno)); @@ -1956,12 +1959,14 @@ static int __init cmm_init(void) if (major < 0) { printk(KERN_WARNING MODULE_NAME ": could not get major number\n"); + class_destroy(cmm_class); return major; } rc = pcmcia_register_driver(&cm4000_driver); if (rc < 0) { unregister_chrdev(major, DEVICE_NAME); + class_destroy(cmm_class); return rc; } diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index f2e4ec4fd40..af88181a17f 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -636,8 +636,11 @@ static int reader_probe(struct pcmcia_device *link) setup_timer(&dev->poll_timer, cm4040_do_poll, 0); ret = reader_config(link, i); - if (ret) + if (ret) { + dev_table[i] = NULL; + kfree(dev); return ret; + } class_device_create(cmx_class, NULL, MKDEV(major, i), NULL, "cmx%d", i); @@ -708,12 +711,14 @@ static int __init cm4040_init(void) if (major < 0) { printk(KERN_WARNING MODULE_NAME ": could not get major number\n"); + class_destroy(cmx_class); return major; } rc = pcmcia_register_driver(&reader_driver); if (rc < 0) { unregister_chrdev(major, DEVICE_NAME); + class_destroy(cmx_class); return rc; } -- cgit v1.2.3 From 884c3d751093446918c2f7a4b2c745f28cf91c39 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 May 2007 02:33:32 -0700 Subject: cm4000_cs: use bitrev Cleanup using bitrev8 in cm4000_cs driver. Cc: Harald Welte Signed-off-by: Akinobu Mita Cc: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/pcmcia/Kconfig | 1 + drivers/char/pcmcia/cm4000_cs.c | 35 ++++++----------------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig index 27c1179ee52..f25facd97bb 100644 --- a/drivers/char/pcmcia/Kconfig +++ b/drivers/char/pcmcia/Kconfig @@ -21,6 +21,7 @@ config SYNCLINK_CS config CARDMAN_4000 tristate "Omnikey Cardman 4000 support" depends on PCMCIA + select BITREVERSE help Enable support for the Omnikey Cardman 4000 PCMCIA Smartcard reader. diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 8cbc64fe0fe..561d0e151d0 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -194,41 +195,17 @@ static inline unsigned char xinb(unsigned short port) } #endif -#define b_0000 15 -#define b_0001 14 -#define b_0010 13 -#define b_0011 12 -#define b_0100 11 -#define b_0101 10 -#define b_0110 9 -#define b_0111 8 -#define b_1000 7 -#define b_1001 6 -#define b_1010 5 -#define b_1011 4 -#define b_1100 3 -#define b_1101 2 -#define b_1110 1 -#define b_1111 0 - -static unsigned char irtab[16] = { - b_0000, b_1000, b_0100, b_1100, - b_0010, b_1010, b_0110, b_1110, - b_0001, b_1001, b_0101, b_1101, - b_0011, b_1011, b_0111, b_1111 -}; +static inline unsigned char invert_revert(unsigned char ch) +{ + return bitrev8(~ch); +} static void str_invert_revert(unsigned char *b, int len) { int i; for (i = 0; i < len; i++) - b[i] = (irtab[b[i] & 0x0f] << 4) | irtab[b[i] >> 4]; -} - -static unsigned char invert_revert(unsigned char ch) -{ - return (irtab[ch & 0x0f] << 4) | irtab[ch >> 4]; + b[i] = invert_revert(b[i]); } #define ATRLENCK(dev,pos) \ -- cgit v1.2.3 From 92f4c701aad794de9e4cf7341d0a486aed027c46 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 May 2007 02:33:32 -0700 Subject: use simple_read_from_buffer() in fs/ Cleanup using simple_read_from_buffer() in binfmt_misc, configfs, and sysfs. Cc: Greg Kroah-Hartman Cc: Joel Becker Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_misc.c | 13 +------------ fs/configfs/file.c | 33 ++------------------------------- fs/sysfs/file.c | 33 ++------------------------------- 3 files changed, 5 insertions(+), 74 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 18657f001b4..72d0b412c37 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -675,19 +675,8 @@ static ssize_t bm_status_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { char *s = enabled ? "enabled" : "disabled"; - int len = strlen(s); - loff_t pos = *ppos; - if (pos < 0) - return -EINVAL; - if (pos >= len) - return 0; - if (len < pos + nbytes) - nbytes = len - pos; - if (copy_to_user(buf, s + pos, nbytes)) - return -EFAULT; - *ppos = pos + nbytes; - return nbytes; + return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s)); } static ssize_t bm_status_write(struct file * file, const char __user * buffer, diff --git a/fs/configfs/file.c b/fs/configfs/file.c index d98be5e0132..3527c7c6def 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -77,36 +77,6 @@ static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buf return ret; } - -/** - * flush_read_buffer - push buffer to userspace. - * @buffer: data buffer for file. - * @userbuf: user-passed buffer. - * @count: number of bytes requested. - * @ppos: file position. - * - * Copy the buffer we filled in fill_read_buffer() to userspace. - * This is done at the reader's leisure, copying and advancing - * the amount they specify each time. - * This may be called continuously until the buffer is empty. - */ -static int flush_read_buffer(struct configfs_buffer * buffer, char __user * buf, - size_t count, loff_t * ppos) -{ - int error; - - if (*ppos > buffer->count) - return 0; - - if (count > (buffer->count - *ppos)) - count = buffer->count - *ppos; - - error = copy_to_user(buf,buffer->page + *ppos,count); - if (!error) - *ppos += count; - return error ? -EFAULT : count; -} - /** * configfs_read_file - read an attribute. * @file: file pointer. @@ -139,7 +109,8 @@ configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *pp } pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", __FUNCTION__, count, *ppos, buffer->page); - retval = flush_read_buffer(buffer,buf,count,ppos); + retval = simple_read_from_buffer(buf, count, ppos, buffer->page, + buffer->count); out: up(&buffer->sem); return retval; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 0e637adc2b8..b502c7197ec 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -111,36 +111,6 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer return ret; } - -/** - * flush_read_buffer - push buffer to userspace. - * @buffer: data buffer for file. - * @buf: user-passed buffer. - * @count: number of bytes requested. - * @ppos: file position. - * - * Copy the buffer we filled in fill_read_buffer() to userspace. - * This is done at the reader's leisure, copying and advancing - * the amount they specify each time. - * This may be called continuously until the buffer is empty. - */ -static int flush_read_buffer(struct sysfs_buffer * buffer, char __user * buf, - size_t count, loff_t * ppos) -{ - int error; - - if (*ppos > buffer->count) - return 0; - - if (count > (buffer->count - *ppos)) - count = buffer->count - *ppos; - - error = copy_to_user(buf,buffer->page + *ppos,count); - if (!error) - *ppos += count; - return error ? -EFAULT : count; -} - /** * sysfs_read_file - read an attribute. * @file: file pointer. @@ -177,7 +147,8 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) } pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", __FUNCTION__, count, *ppos, buffer->page); - retval = flush_read_buffer(buffer,buf,count,ppos); + retval = simple_read_from_buffer(buf, count, ppos, buffer->page, + buffer->count); out: up(&buffer->sem); return retval; -- cgit v1.2.3 From 85badbdf5120d246ce2bb3f1a7689a805f9c9006 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 May 2007 02:33:33 -0700 Subject: use simple_read_from_buffer in kernel/ Cleanup using simple_read_from_buffer() for /dev/cpuset/tasks and /proc/config.gz. Cc: Paul Jackson Cc: Randy Dunlap Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/configs.c | 15 +++------------ kernel/cpuset.c | 7 +------ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/kernel/configs.c b/kernel/configs.c index 8fa1fb28f8a..e84d3f9c6c7 100644 --- a/kernel/configs.c +++ b/kernel/configs.c @@ -61,18 +61,9 @@ static ssize_t ikconfig_read_current(struct file *file, char __user *buf, size_t len, loff_t * offset) { - loff_t pos = *offset; - ssize_t count; - - if (pos >= kernel_config_data_size) - return 0; - - count = min(len, (size_t)(kernel_config_data_size - pos)); - if (copy_to_user(buf, kernel_config_data + MAGIC_SIZE + pos, count)) - return -EFAULT; - - *offset += count; - return count; + return simple_read_from_buffer(buf, len, offset, + kernel_config_data + MAGIC_SIZE, + kernel_config_data_size); } static const struct file_operations ikconfig_file_ops = { diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 88b416dfbc7..f57854b0892 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1772,12 +1772,7 @@ static ssize_t cpuset_tasks_read(struct file *file, char __user *buf, { struct ctr_struct *ctr = file->private_data; - if (*ppos + nbytes > ctr->bufsz) - nbytes = ctr->bufsz - *ppos; - if (copy_to_user(buf, ctr->buf + *ppos, nbytes)) - return -EFAULT; - *ppos += nbytes; - return nbytes; + return simple_read_from_buffer(buf, nbytes, ppos, ctr->buf, ctr->bufsz); } static int cpuset_tasks_release(struct inode *unused_inode, struct file *file) -- cgit v1.2.3 From 8a63011275e1a0ec9389e8c7d9b08caab8957ca0 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Wed, 9 May 2007 02:33:34 -0700 Subject: pretend cpuset has some form of hugetlb page reservation When cpuset is configured, it breaks the strict hugetlb page reservation as the accounting is done on a global variable. Such reservation is completely rubbish in the presence of cpuset because the reservation is not checked against page availability for the current cpuset. Application can still potentially OOM'ed by kernel with lack of free htlb page in cpuset that the task is in. Attempt to enforce strict accounting with cpuset is almost impossible (or too ugly) because cpuset is too fluid that task or memory node can be dynamically moved between cpusets. The change of semantics for shared hugetlb mapping with cpuset is undesirable. However, in order to preserve some of the semantics, we fall back to check against current free page availability as a best attempt and hopefully to minimize the impact of changing semantics that cpuset has on hugetlb. Signed-off-by: Ken Chen Cc: Paul Jackson Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/hugetlb.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 88e708be1f6..eb7180db303 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -174,6 +174,17 @@ static int __init hugetlb_setup(char *s) } __setup("hugepages=", hugetlb_setup); +static unsigned int cpuset_mems_nr(unsigned int *array) +{ + int node; + unsigned int nr = 0; + + for_each_node_mask(node, cpuset_current_mems_allowed) + nr += array[node]; + + return nr; +} + #ifdef CONFIG_SYSCTL static void update_and_free_page(struct page *page) { @@ -819,6 +830,26 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to) chg = region_chg(&inode->i_mapping->private_list, from, to); if (chg < 0) return chg; + /* + * When cpuset is configured, it breaks the strict hugetlb page + * reservation as the accounting is done on a global variable. Such + * reservation is completely rubbish in the presence of cpuset because + * the reservation is not checked against page availability for the + * current cpuset. Application can still potentially OOM'ed by kernel + * with lack of free htlb page in cpuset that the task is in. + * Attempt to enforce strict accounting with cpuset is almost + * impossible (or too ugly) because cpuset is too fluid that + * task or memory node can be dynamically moved between cpusets. + * + * The change of semantics for shared hugetlb mapping with cpuset is + * undesirable. However, in order to preserve some of the semantics, + * we fall back to check against current free page availability as + * a best attempt and hopefully to minimize the impact of changing + * semantics that cpuset has. + */ + if (chg > cpuset_mems_nr(free_huge_pages_node)) + return -ENOMEM; + ret = hugetlb_acct_memory(chg); if (ret < 0) return ret; -- cgit v1.2.3 From 4d3556329926bde7c1e80cd6c650525326652ea4 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 9 May 2007 02:33:35 -0700 Subject: Add info about whitespace to SubmitChecklist Signed-off-by: Roland Dreier Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SubmitChecklist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/SubmitChecklist b/Documentation/SubmitChecklist index bd23dc0bc0c..6491b2c45dd 100644 --- a/Documentation/SubmitChecklist +++ b/Documentation/SubmitChecklist @@ -80,3 +80,7 @@ kernel patches. 23: Tested after it has been merged into the -mm patchset to make sure that it still works with all of the other queued patches and various changes in the VM, VFS, and other subsystems. + +24: Avoid whitespace damage such as indenting with spaces or whitespace + at the end of lines. You can test this by feeding the patch to + "git apply --check --whitespace=error-all" -- cgit v1.2.3 From b8cb34481edfee6692c83d3b283e29820e840280 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Wed, 9 May 2007 02:33:35 -0700 Subject: pasemi: hardware rng driver Driver for the on-chip hardware random number generator on PA Semi PA6T-1682M. Signed-off-by: Egor Martovetsky Signed-off-by: Olof Johansson Signed-off-by: Michael Buesch Cc: Paul Mackerras Cc: Kumar Gala Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/hw_random/Kconfig | 14 ++++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/pasemi-rng.c | 156 ++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/char/hw_random/pasemi-rng.c diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 5f3acd8e64b..7cda04b3353 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -91,3 +91,17 @@ config HW_RANDOM_OMAP module will be called omap-rng. If unsure, say Y. + +config HW_RANDOM_PASEMI + tristate "PA Semi HW Random Number Generator support" + depends on HW_RANDOM && PPC_PASEMI + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on PA6T-1682M processor. + + To compile this driver as a module, choose M here: the + module will be called pasemi-rng. + + If unsure, say Y. + diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index c41fa19454e..c8b7300e2fb 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o +obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c new file mode 100644 index 00000000000..fa6040b6c8f --- /dev/null +++ b/drivers/char/hw_random/pasemi-rng.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Maintained by: Olof Johansson + * + * Driver for the PWRficient onchip rng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define SDCRNG_CTL_REG 0x00 +#define SDCRNG_CTL_FVLD_M 0x0000f000 +#define SDCRNG_CTL_FVLD_S 12 +#define SDCRNG_CTL_KSZ 0x00000800 +#define SDCRNG_CTL_RSRC_CRG 0x00000010 +#define SDCRNG_CTL_RSRC_RRG 0x00000000 +#define SDCRNG_CTL_CE 0x00000004 +#define SDCRNG_CTL_RE 0x00000002 +#define SDCRNG_CTL_DR 0x00000001 +#define SDCRNG_CTL_SELECT_RRG_RNG (SDCRNG_CTL_RE | SDCRNG_CTL_RSRC_RRG) +#define SDCRNG_CTL_SELECT_CRG_RNG (SDCRNG_CTL_CE | SDCRNG_CTL_RSRC_CRG) +#define SDCRNG_VAL_REG 0x20 + +#define MODULE_NAME "pasemi_rng" + +static int pasemi_rng_data_present(struct hwrng *rng) +{ + void __iomem *rng_regs = (void __iomem *)rng->priv; + + return (in_le32(rng_regs + SDCRNG_CTL_REG) + & SDCRNG_CTL_FVLD_M) ? 1 : 0; +} + +static int pasemi_rng_data_read(struct hwrng *rng, u32 *data) +{ + void __iomem *rng_regs = (void __iomem *)rng->priv; + *data = in_le32(rng_regs + SDCRNG_VAL_REG); + return 4; +} + +static int pasemi_rng_init(struct hwrng *rng) +{ + void __iomem *rng_regs = (void __iomem *)rng->priv; + u32 ctl; + + ctl = SDCRNG_CTL_DR | SDCRNG_CTL_SELECT_RRG_RNG | SDCRNG_CTL_KSZ; + out_le32(rng_regs + SDCRNG_CTL_REG, ctl); + out_le32(rng_regs + SDCRNG_CTL_REG, ctl & ~SDCRNG_CTL_DR); + + return 0; +} + +static void pasemi_rng_cleanup(struct hwrng *rng) +{ + void __iomem *rng_regs = (void __iomem *)rng->priv; + u32 ctl; + + ctl = SDCRNG_CTL_RE | SDCRNG_CTL_CE; + out_le32(rng_regs + SDCRNG_CTL_REG, + in_le32(rng_regs + SDCRNG_CTL_REG) & ~ctl); +} + +static struct hwrng pasemi_rng = { + .name = MODULE_NAME, + .init = pasemi_rng_init, + .cleanup = pasemi_rng_cleanup, + .data_present = pasemi_rng_data_present, + .data_read = pasemi_rng_data_read, +}; + +static int __devinit rng_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + void __iomem *rng_regs; + struct device_node *rng_np = ofdev->node; + struct resource res; + int err = 0; + + err = of_address_to_resource(rng_np, 0, &res); + if (err) + return -ENODEV; + + rng_regs = ioremap(res.start, 0x100); + + if (!rng_regs) + return -ENOMEM; + + pasemi_rng.priv = (unsigned long)rng_regs; + + printk(KERN_INFO "Registering PA Semi RNG\n"); + + err = hwrng_register(&pasemi_rng); + + if (err) + iounmap(rng_regs); + + return err; +} + +static int __devexit rng_remove(struct of_device *dev) +{ + void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv; + + hwrng_unregister(&pasemi_rng); + iounmap(rng_regs); + + return 0; +} + +static struct of_device_id rng_match[] = { + { + .compatible = "1682m-rng", + }, + {}, +}; + +static struct of_platform_driver rng_driver = { + .name = "pasemi-rng", + .match_table = rng_match, + .probe = rng_probe, + .remove = rng_remove, +}; + +static int __init rng_init(void) +{ + return of_register_platform_driver(&rng_driver); +} +module_init(rng_init); + +static void __exit rng_exit(void) +{ + of_unregister_platform_driver(&rng_driver); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Egor Martovetsky "); +MODULE_DESCRIPTION("H/W RNG driver for PA Semi processor"); -- cgit v1.2.3 From 84963048ca8093e0aa71ac90c2a5fe7af5f617c3 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 9 May 2007 02:33:36 -0700 Subject: nbd: check the return value of sysfs_create_file [akpm@linux-foundation.org: fix it] Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 090796bef78..069ae39a9cd 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -366,20 +366,25 @@ static struct disk_attribute pid_attr = { .show = pid_show, }; -static void nbd_do_it(struct nbd_device *lo) +static int nbd_do_it(struct nbd_device *lo) { struct request *req; + int ret; BUG_ON(lo->magic != LO_MAGIC); lo->pid = current->pid; - sysfs_create_file(&lo->disk->kobj, &pid_attr.attr); + ret = sysfs_create_file(&lo->disk->kobj, &pid_attr.attr); + if (ret) { + printk(KERN_ERR "nbd: sysfs_create_file failed!"); + return ret; + } while ((req = nbd_read_stat(lo)) != NULL) nbd_end_request(req); sysfs_remove_file(&lo->disk->kobj, &pid_attr.attr); - return; + return 0; } static void nbd_clear_que(struct nbd_device *lo) @@ -569,7 +574,9 @@ static int nbd_ioctl(struct inode *inode, struct file *file, case NBD_DO_IT: if (!lo->file) return -EINVAL; - nbd_do_it(lo); + error = nbd_do_it(lo); + if (error) + return error; /* on return tidy up in case we have a signal */ /* Forcibly shutdown the socket causing all listeners * to error -- cgit v1.2.3 From 55c0d1f83e481dd6c77f52f7dcfeb043b8b740fa Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 9 May 2007 02:33:37 -0700 Subject: Move sig_kernel_* et al macros to linux/signal.h This patch moves the sig_kernel_* and related macros from kernel/signal.c to linux/signal.h, and cleans them up slightly. I need the sig_kernel_* macros for default signal behavior in the utrace code, and want to avoid duplication or overhead to share the knowledge. Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/signal.h | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/signal.c | 119 ---------------------------------------------- 2 files changed, 125 insertions(+), 119 deletions(-) diff --git a/include/linux/signal.h b/include/linux/signal.h index 14749056dd6..3fa0fab4a04 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -243,6 +243,131 @@ extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, extern struct kmem_cache *sighand_cachep; +/* + * In POSIX a signal is sent either to a specific thread (Linux task) + * or to the process as a whole (Linux thread group). How the signal + * is sent determines whether it's to one thread or the whole group, + * which determines which signal mask(s) are involved in blocking it + * from being delivered until later. When the signal is delivered, + * either it's caught or ignored by a user handler or it has a default + * effect that applies to the whole thread group (POSIX process). + * + * The possible effects an unblocked signal set to SIG_DFL can have are: + * ignore - Nothing Happens + * terminate - kill the process, i.e. all threads in the group, + * similar to exit_group. The group leader (only) reports + * WIFSIGNALED status to its parent. + * coredump - write a core dump file describing all threads using + * the same mm and then kill all those threads + * stop - stop all the threads in the group, i.e. TASK_STOPPED state + * + * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. + * Other signals when not blocked and set to SIG_DFL behaves as follows. + * The job control signals also have other special effects. + * + * +--------------------+------------------+ + * | POSIX signal | default action | + * +--------------------+------------------+ + * | SIGHUP | terminate | + * | SIGINT | terminate | + * | SIGQUIT | coredump | + * | SIGILL | coredump | + * | SIGTRAP | coredump | + * | SIGABRT/SIGIOT | coredump | + * | SIGBUS | coredump | + * | SIGFPE | coredump | + * | SIGKILL | terminate(+) | + * | SIGUSR1 | terminate | + * | SIGSEGV | coredump | + * | SIGUSR2 | terminate | + * | SIGPIPE | terminate | + * | SIGALRM | terminate | + * | SIGTERM | terminate | + * | SIGCHLD | ignore | + * | SIGCONT | ignore(*) | + * | SIGSTOP | stop(*)(+) | + * | SIGTSTP | stop(*) | + * | SIGTTIN | stop(*) | + * | SIGTTOU | stop(*) | + * | SIGURG | ignore | + * | SIGXCPU | coredump | + * | SIGXFSZ | coredump | + * | SIGVTALRM | terminate | + * | SIGPROF | terminate | + * | SIGPOLL/SIGIO | terminate | + * | SIGSYS/SIGUNUSED | coredump | + * | SIGSTKFLT | terminate | + * | SIGWINCH | ignore | + * | SIGPWR | terminate | + * | SIGRTMIN-SIGRTMAX | terminate | + * +--------------------+------------------+ + * | non-POSIX signal | default action | + * +--------------------+------------------+ + * | SIGEMT | coredump | + * +--------------------+------------------+ + * + * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". + * (*) Special job control effects: + * When SIGCONT is sent, it resumes the process (all threads in the group) + * from TASK_STOPPED state and also clears any pending/queued stop signals + * (any of those marked with "stop(*)"). This happens regardless of blocking, + * catching, or ignoring SIGCONT. When any stop signal is sent, it clears + * any pending/queued SIGCONT signals; this happens regardless of blocking, + * catching, or ignored the stop signal, though (except for SIGSTOP) the + * default action of stopping the process may happen later or never. + */ + +#ifdef SIGEMT +#define SIGEMT_MASK rt_sigmask(SIGEMT) +#else +#define SIGEMT_MASK 0 +#endif + +#if SIGRTMIN > BITS_PER_LONG +#define rt_sigmask(sig) (1ULL << ((sig)-1)) +#else +#define rt_sigmask(sig) sigmask(sig) +#endif +#define siginmask(sig, mask) (rt_sigmask(sig) & (mask)) + +#define SIG_KERNEL_ONLY_MASK (\ + rt_sigmask(SIGKILL) | rt_sigmask(SIGSTOP)) + +#define SIG_KERNEL_STOP_MASK (\ + rt_sigmask(SIGSTOP) | rt_sigmask(SIGTSTP) | \ + rt_sigmask(SIGTTIN) | rt_sigmask(SIGTTOU) ) + +#define SIG_KERNEL_COREDUMP_MASK (\ + rt_sigmask(SIGQUIT) | rt_sigmask(SIGILL) | \ + rt_sigmask(SIGTRAP) | rt_sigmask(SIGABRT) | \ + rt_sigmask(SIGFPE) | rt_sigmask(SIGSEGV) | \ + rt_sigmask(SIGBUS) | rt_sigmask(SIGSYS) | \ + rt_sigmask(SIGXCPU) | rt_sigmask(SIGXFSZ) | \ + SIGEMT_MASK ) + +#define SIG_KERNEL_IGNORE_MASK (\ + rt_sigmask(SIGCONT) | rt_sigmask(SIGCHLD) | \ + rt_sigmask(SIGWINCH) | rt_sigmask(SIGURG) ) + +#define sig_kernel_only(sig) \ + (((sig) < SIGRTMIN) && siginmask(sig, SIG_KERNEL_ONLY_MASK)) +#define sig_kernel_coredump(sig) \ + (((sig) < SIGRTMIN) && siginmask(sig, SIG_KERNEL_COREDUMP_MASK)) +#define sig_kernel_ignore(sig) \ + (((sig) < SIGRTMIN) && siginmask(sig, SIG_KERNEL_IGNORE_MASK)) +#define sig_kernel_stop(sig) \ + (((sig) < SIGRTMIN) && siginmask(sig, SIG_KERNEL_STOP_MASK)) + +#define sig_needs_tasklist(sig) ((sig) == SIGCONT) + +#define sig_user_defined(t, signr) \ + (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \ + ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) + +#define sig_fatal(t, signr) \ + (!siginmask(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ + (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) + #endif /* __KERNEL__ */ #endif /* _LINUX_SIGNAL_H */ diff --git a/kernel/signal.c b/kernel/signal.c index 1368e67c848..4c8f49eadf7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -38,125 +38,6 @@ static struct kmem_cache *sigqueue_cachep; -/* - * In POSIX a signal is sent either to a specific thread (Linux task) - * or to the process as a whole (Linux thread group). How the signal - * is sent determines whether it's to one thread or the whole group, - * which determines which signal mask(s) are involved in blocking it - * from being delivered until later. When the signal is delivered, - * either it's caught or ignored by a user handler or it has a default - * effect that applies to the whole thread group (POSIX process). - * - * The possible effects an unblocked signal set to SIG_DFL can have are: - * ignore - Nothing Happens - * terminate - kill the process, i.e. all threads in the group, - * similar to exit_group. The group leader (only) reports - * WIFSIGNALED status to its parent. - * coredump - write a core dump file describing all threads using - * the same mm and then kill all those threads - * stop - stop all the threads in the group, i.e. TASK_STOPPED state - * - * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. - * Other signals when not blocked and set to SIG_DFL behaves as follows. - * The job control signals also have other special effects. - * - * +--------------------+------------------+ - * | POSIX signal | default action | - * +--------------------+------------------+ - * | SIGHUP | terminate | - * | SIGINT | terminate | - * | SIGQUIT | coredump | - * | SIGILL | coredump | - * | SIGTRAP | coredump | - * | SIGABRT/SIGIOT | coredump | - * | SIGBUS | coredump | - * | SIGFPE | coredump | - * | SIGKILL | terminate(+) | - * | SIGUSR1 | terminate | - * | SIGSEGV | coredump | - * | SIGUSR2 | terminate | - * | SIGPIPE | terminate | - * | SIGALRM | terminate | - * | SIGTERM | terminate | - * | SIGCHLD | ignore | - * | SIGCONT | ignore(*) | - * | SIGSTOP | stop(*)(+) | - * | SIGTSTP | stop(*) | - * | SIGTTIN | stop(*) | - * | SIGTTOU | stop(*) | - * | SIGURG | ignore | - * | SIGXCPU | coredump | - * | SIGXFSZ | coredump | - * | SIGVTALRM | terminate | - * | SIGPROF | terminate | - * | SIGPOLL/SIGIO | terminate | - * | SIGSYS/SIGUNUSED | coredump | - * | SIGSTKFLT | terminate | - * | SIGWINCH | ignore | - * | SIGPWR | terminate | - * | SIGRTMIN-SIGRTMAX | terminate | - * +--------------------+------------------+ - * | non-POSIX signal | default action | - * +--------------------+------------------+ - * | SIGEMT | coredump | - * +--------------------+------------------+ - * - * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". - * (*) Special job control effects: - * When SIGCONT is sent, it resumes the process (all threads in the group) - * from TASK_STOPPED state and also clears any pending/queued stop signals - * (any of those marked with "stop(*)"). This happens regardless of blocking, - * catching, or ignoring SIGCONT. When any stop signal is sent, it clears - * any pending/queued SIGCONT signals; this happens regardless of blocking, - * catching, or ignored the stop signal, though (except for SIGSTOP) the - * default action of stopping the process may happen later or never. - */ - -#ifdef SIGEMT -#define M_SIGEMT M(SIGEMT) -#else -#define M_SIGEMT 0 -#endif - -#if SIGRTMIN > BITS_PER_LONG -#define M(sig) (1ULL << ((sig)-1)) -#else -#define M(sig) (1UL << ((sig)-1)) -#endif -#define T(sig, mask) (M(sig) & (mask)) - -#define SIG_KERNEL_ONLY_MASK (\ - M(SIGKILL) | M(SIGSTOP) ) - -#define SIG_KERNEL_STOP_MASK (\ - M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) ) - -#define SIG_KERNEL_COREDUMP_MASK (\ - M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \ - M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \ - M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT ) - -#define SIG_KERNEL_IGNORE_MASK (\ - M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) ) - -#define sig_kernel_only(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK)) -#define sig_kernel_coredump(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK)) -#define sig_kernel_ignore(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK)) -#define sig_kernel_stop(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK)) - -#define sig_needs_tasklist(sig) ((sig) == SIGCONT) - -#define sig_user_defined(t, signr) \ - (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \ - ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) - -#define sig_fatal(t, signr) \ - (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ - (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) static int sig_ignored(struct task_struct *t, int sig) { -- cgit v1.2.3 From ae030e435f5400cff77c52506a8d3d7278f0947c Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Wed, 9 May 2007 02:33:38 -0700 Subject: tty_set_ldisc() receive_room fix Fix tty_set_ldisc in tty_io.c so that tty->receive_room is only cleared if actually changing line disciplines. Without this fix a problem occurs when requesting the line discipline to change to the same line discipline. In this case tty->receive_room is cleared but ldisc->open() is not called to set tty->receive_room back to a sane value. The result is that tty->receive_room is stuck at 0 preventing the tty flip buffer from passing receive data to the line discipline. For example: a switch from N_TTY to N_TTY followed by a select() call for read input results in data never being received because tty->receive_room is stuck at zero. A switch from N_TTY to N_TTY followed by a read() call works because the read() call itself sets tty->receive_room correctly (but select does not). Previously (< 2.6.18) this was not a problem because the tty flip buffer pushed data to the line discipline without regard for tty->receive room. Signed-off-by: Paul Fulghum Acked-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 7710a6a77d9..bf5a00145c0 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -933,13 +933,6 @@ restart: if (ld == NULL) return -EINVAL; - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - /* * Problem: What do we do if this blocks ? */ @@ -951,6 +944,13 @@ restart: return 0; } + /* + * No more input please, we are switching. The new ldisc + * will update this value in the ldisc open function + */ + + tty->receive_room = 0; + o_ldisc = tty->ldisc; o_tty = tty->link; -- cgit v1.2.3 From 18d8362d517cb2bd97761294924fe6c2a6ee5e3c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:39 -0700 Subject: mutex_lock_interruptible(): add __must_check It's not sane to use mutex_lock_interruptible() and to then ignore the result. Ditto down_interruptible(), but I'm lazy. Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mutex.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/linux/mutex.h b/include/linux/mutex.h index b81bc2adaef..0d50ea3df68 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -121,11 +121,12 @@ static inline int fastcall mutex_is_locked(struct mutex *lock) * Also see Documentation/mutex-design.txt. */ extern void fastcall mutex_lock(struct mutex *lock); -extern int fastcall mutex_lock_interruptible(struct mutex *lock); +extern int __must_check fastcall mutex_lock_interruptible(struct mutex *lock); #ifdef CONFIG_DEBUG_LOCK_ALLOC extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass); -extern int mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass); +extern int __must_check mutex_lock_interruptible_nested(struct mutex *lock, + unsigned int subclass); #else # define mutex_lock_nested(lock, subclass) mutex_lock(lock) # define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock) -- cgit v1.2.3 From 35c35d1afa8f9afe01d922ff9668a14c5d879b02 Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Wed, 9 May 2007 02:33:40 -0700 Subject: clocksource: spelling error in watchdog code There's more that need fixing, and fix my own subject spelling error too. Signed-off-by: Daniel Walker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/time/clocksource.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index fe5c7db2424..db0c725de5e 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -75,14 +75,14 @@ static struct timer_list watchdog_timer; static DEFINE_SPINLOCK(watchdog_lock); static cycle_t watchdog_last; /* - * Interval: 0.5sec Treshold: 0.0625s + * Interval: 0.5sec Threshold: 0.0625s */ #define WATCHDOG_INTERVAL (HZ >> 1) -#define WATCHDOG_TRESHOLD (NSEC_PER_SEC >> 4) +#define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 4) static void clocksource_ratewd(struct clocksource *cs, int64_t delta) { - if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD) + if (delta > -WATCHDOG_THRESHOLD && delta < WATCHDOG_THRESHOLD) return; printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n", -- cgit v1.2.3 From 62ce39c531860aee6c12128c629b0a82f656a306 Mon Sep 17 00:00:00 2001 From: Josef 'Jeff' Sipek Date: Wed, 9 May 2007 02:33:41 -0700 Subject: fs: fix indentation in do_path_lookup Signed-off-by: Josef 'Jeff' Sipek Acked-by: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 856b2f5da51..19f178ec574 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1155,11 +1155,9 @@ static int fastcall do_path_lookup(int dfd, const char *name, current->total_link_count = 0; retval = link_path_walk(name, nd); out: - if (likely(retval == 0)) { - if (unlikely(!audit_dummy_context() && nd && nd->dentry && + if (unlikely(!retval && !audit_dummy_context() && nd->dentry && nd->dentry->d_inode)) audit_inode(name, nd->dentry->d_inode); - } out_fail: return retval; -- cgit v1.2.3 From 2dfdd266b9a2f2d93a3fdbee89969f6ea9ec5377 Mon Sep 17 00:00:00 2001 From: Josef 'Jeff' Sipek Date: Wed, 9 May 2007 02:33:41 -0700 Subject: fs: use path_walk in do_path_lookup Since path_walk sets the total_link_count to 0 and calls link_path_walk, we can just call path_walk directly. Signed-off-by: Josef 'Jeff' Sipek Acked-by: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 19f178ec574..b3780e3fc88 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1152,8 +1152,8 @@ static int fastcall do_path_lookup(int dfd, const char *name, fput_light(file, fput_needed); } - current->total_link_count = 0; - retval = link_path_walk(name, nd); + + retval = path_walk(name, nd); out: if (unlikely(!retval && !audit_dummy_context() && nd->dentry && nd->dentry->d_inode)) -- cgit v1.2.3 From 66effdc6a6a79c11f1c0d607d0de299108d69507 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 9 May 2007 02:33:42 -0700 Subject: doc: what a patch series is It seems that we need to clarify that a patch series is a series of related patches rather than "here are some of my patches as multiple (numbered) emails." Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SubmittingPatches | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index b0d0043f7c4..a417b25fb1a 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -363,7 +363,8 @@ area or subsystem of the kernel is being patched. The "summary phrase" in the email's Subject should concisely describe the patch which that email contains. The "summary phrase" should not be a filename. Do not use the same "summary -phrase" for every patch in a whole patch series. +phrase" for every patch in a whole patch series (where a "patch +series" is an ordered sequence of multiple, related patches). Bear in mind that the "summary phrase" of your email becomes a globally-unique identifier for that patch. It propagates -- cgit v1.2.3 From 0366299bd2de283091f4394c0f446e58b5f94815 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 9 May 2007 02:33:43 -0700 Subject: kernel-doc: small kernel-doc optimization Get the kernel version string only once from the environment, thus slightly speeding up kernel-doc. Signed-off-by: Borislav Petkov Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/kernel-doc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index a325a0c890d..e5bf649e516 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -337,6 +337,7 @@ sub get_kernel_version() { } return $version; } +my $kernelversion = get_kernel_version(); # generate a sequence of code that will splice in highlighting information # using the s// operator. @@ -610,7 +611,7 @@ sub output_function_xml(%) { print "\n"; print " ".$args{'function'}."\n"; print " 9\n"; - print " " . get_kernel_version() . "\n"; + print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " ".$args{'function'}."\n"; @@ -687,7 +688,7 @@ sub output_struct_xml(%) { print "\n"; print " ".$args{'type'}." ".$args{'struct'}."\n"; print " 9\n"; - print " " . get_kernel_version() . "\n"; + print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " ".$args{'type'}." ".$args{'struct'}."\n"; @@ -772,7 +773,7 @@ sub output_enum_xml(%) { print "\n"; print " enum ".$args{'enum'}."\n"; print " 9\n"; - print " " . get_kernel_version() . "\n"; + print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " enum ".$args{'enum'}."\n"; -- cgit v1.2.3 From 9b04bd27564cfd7224e0135ba37df778f1d490bf Mon Sep 17 00:00:00 2001 From: David Miller Date: Wed, 9 May 2007 02:33:43 -0700 Subject: Fix printk format warnings in timer_list.c u64 and s64 are not necessarily 'long long' on some 64-bit platforms, so explicit the type to kill the compiler warnings. Also consistently use '%Lu' which is unsigned. Signed-off-by: David S. Miller Cc: Thomas Gleixner Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/time/timer_list.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index b734ca4bc75..8bbcfb77f7d 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -65,7 +65,7 @@ print_timer(struct seq_file *m, struct hrtimer *timer, int idx, u64 now) SEQ_printf(m, ", %s/%d", tmp, timer->start_pid); #endif SEQ_printf(m, "\n"); - SEQ_printf(m, " # expires at %Ld nsecs [in %Ld nsecs]\n", + SEQ_printf(m, " # expires at %Lu nsecs [in %Lu nsecs]\n", (unsigned long long)ktime_to_ns(timer->expires), (unsigned long long)(ktime_to_ns(timer->expires) - now)); } @@ -111,14 +111,14 @@ print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now) { SEQ_printf(m, " .index: %d\n", base->index); - SEQ_printf(m, " .resolution: %Ld nsecs\n", + SEQ_printf(m, " .resolution: %Lu nsecs\n", (unsigned long long)ktime_to_ns(base->resolution)); SEQ_printf(m, " .get_time: "); print_name_offset(m, base->get_time); SEQ_printf(m, "\n"); #ifdef CONFIG_HIGH_RES_TIMERS - SEQ_printf(m, " .offset: %Ld nsecs\n", - ktime_to_ns(base->offset)); + SEQ_printf(m, " .offset: %Lu nsecs\n", + (unsigned long long) ktime_to_ns(base->offset)); #endif SEQ_printf(m, "active timers:\n"); print_active_timers(m, base, now); @@ -135,10 +135,11 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) print_base(m, cpu_base->clock_base + i, now); } #define P(x) \ - SEQ_printf(m, " .%-15s: %Ld\n", #x, (u64)(cpu_base->x)) + SEQ_printf(m, " .%-15s: %Lu\n", #x, \ + (unsigned long long)(cpu_base->x)) #define P_ns(x) \ - SEQ_printf(m, " .%-15s: %Ld nsecs\n", #x, \ - (u64)(ktime_to_ns(cpu_base->x))) + SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \ + (unsigned long long)(ktime_to_ns(cpu_base->x))) #ifdef CONFIG_HIGH_RES_TIMERS P_ns(expires_next); @@ -150,10 +151,11 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) #ifdef CONFIG_TICK_ONESHOT # define P(x) \ - SEQ_printf(m, " .%-15s: %Ld\n", #x, (u64)(ts->x)) + SEQ_printf(m, " .%-15s: %Lu\n", #x, \ + (unsigned long long)(ts->x)) # define P_ns(x) \ - SEQ_printf(m, " .%-15s: %Ld nsecs\n", #x, \ - (u64)(ktime_to_ns(ts->x))) + SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \ + (unsigned long long)(ktime_to_ns(ts->x))) { struct tick_sched *ts = tick_get_tick_sched(cpu); P(nohz_mode); @@ -167,7 +169,8 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) P(last_jiffies); P(next_jiffies); P_ns(idle_expires); - SEQ_printf(m, "jiffies: %Ld\n", (u64)jiffies); + SEQ_printf(m, "jiffies: %Lu\n", + (unsigned long long)jiffies); } #endif -- cgit v1.2.3 From ef71c15c46a053818f7e69098ebb052b31ffa56b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 9 May 2007 02:33:44 -0700 Subject: AFS: export a couple of core functions for AFS write support Export a couple of core functions for AFS write support to use: find_get_pages_contig() find_get_pages_tag() Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/filemap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/filemap.c b/mm/filemap.c index 9cbf4fea4a5..9e56fd158fa 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -750,6 +750,7 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index, read_unlock_irq(&mapping->tree_lock); return i; } +EXPORT_SYMBOL(find_get_pages_contig); /** * find_get_pages_tag - find and return pages that match @tag @@ -778,6 +779,7 @@ unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, read_unlock_irq(&mapping->tree_lock); return ret; } +EXPORT_SYMBOL(find_get_pages_tag); /** * grab_cache_page_nowait - returns locked page at given index in given cache -- cgit v1.2.3 From 416351f28d2b31d15ff73e9aff699b2163704c95 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 9 May 2007 02:33:45 -0700 Subject: AFS: AFS fixups Make some miscellaneous changes to the AFS filesystem: (1) Assert RCU barriers on module exit to make sure RCU has finished with callbacks in this module. (2) Correctly handle the AFS server returning a zero-length read. (3) Split out data zapping calls into one function (afs_zap_data). (4) Rename some afs_file_*() functions to afs_*() where they apply to non-regular files too. (5) Be consistent about the presentation of volume ID:vnode ID in debugging output. Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/callback.c | 9 +++---- fs/afs/dir.c | 18 ++++++------- fs/afs/file.c | 75 +++++++++++++++++++------------------------------------ fs/afs/fsclient.c | 37 ++++++++++++++------------- fs/afs/inode.c | 25 +++++++++++++------ fs/afs/internal.h | 29 +++++++++++++++------ fs/afs/main.c | 2 ++ fs/afs/misc.c | 1 + fs/afs/mntpt.c | 5 ++-- fs/afs/security.c | 12 +++++---- fs/afs/server.c | 3 +++ fs/afs/vnode.c | 14 +++++------ 12 files changed, 121 insertions(+), 109 deletions(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 9bdbf36a9aa..f64e40fefc0 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -44,7 +44,7 @@ void afs_init_callback_state(struct afs_server *server) while (!RB_EMPTY_ROOT(&server->cb_promises)) { vnode = rb_entry(server->cb_promises.rb_node, struct afs_vnode, cb_promise); - _debug("UNPROMISE { vid=%x vn=%u uq=%u}", + _debug("UNPROMISE { vid=%x:%u uq=%u}", vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); rb_erase(&vnode->cb_promise, &server->cb_promises); vnode->cb_promised = false; @@ -84,11 +84,8 @@ void afs_broken_callback_work(struct work_struct *work) /* if the vnode's data version number changed then its contents * are different */ - if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { - _debug("zap data {%x:%u}", - vnode->fid.vid, vnode->fid.vnode); - invalidate_remote_inode(&vnode->vfs_inode); - } + if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) + afs_zap_data(vnode); } out: diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 0c1e902f17a..51f177af5dd 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -55,7 +55,7 @@ const struct inode_operations afs_dir_inode_operations = { .rmdir = afs_rmdir, .rename = afs_rename, .permission = afs_permission, - .getattr = afs_inode_getattr, + .getattr = afs_getattr, }; static struct dentry_operations afs_fs_dentry_operations = { @@ -491,7 +491,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, vnode = AFS_FS_I(dir); - _enter("{%x:%d},%p{%s},", + _enter("{%x:%u},%p{%s},", vnode->fid.vid, vnode->fid.vnode, dentry, dentry->d_name.name); ASSERTCMP(dentry->d_inode, ==, NULL); @@ -731,7 +731,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode) dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%s},%o", + _enter("{%x:%u},{%s},%o", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); ret = -ENAMETOOLONG; @@ -796,7 +796,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%s}", + _enter("{%x:%u},{%s}", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); ret = -ENAMETOOLONG; @@ -842,7 +842,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%s}", + _enter("{%x:%u},{%s}", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); ret = -ENAMETOOLONG; @@ -916,7 +916,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, int mode, dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%s},%o,", + _enter("{%x:%u},{%s},%o,", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); ret = -ENAMETOOLONG; @@ -983,7 +983,7 @@ static int afs_link(struct dentry *from, struct inode *dir, vnode = AFS_FS_I(from->d_inode); dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%x:%d},{%s}", + _enter("{%x:%u},{%x:%u},{%s}", vnode->fid.vid, vnode->fid.vnode, dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); @@ -1032,7 +1032,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, dvnode = AFS_FS_I(dir); - _enter("{%x:%d},{%s},%s", + _enter("{%x:%u},{%s},%s", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, content); @@ -1104,7 +1104,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, orig_dvnode = AFS_FS_I(old_dir); new_dvnode = AFS_FS_I(new_dir); - _enter("{%x:%d},{%x:%d},{%x:%d},{%s}", + _enter("{%x:%u},{%x:%u},{%x:%u},{%s}", orig_dvnode->fid.vid, orig_dvnode->fid.vnode, vnode->fid.vid, vnode->fid.vnode, new_dvnode->fid.vid, new_dvnode->fid.vnode, diff --git a/fs/afs/file.c b/fs/afs/file.c index ae256498f4f..3eb3fc7b36b 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -17,9 +17,9 @@ #include #include "internal.h" -static int afs_file_readpage(struct file *file, struct page *page); -static void afs_file_invalidatepage(struct page *page, unsigned long offset); -static int afs_file_releasepage(struct page *page, gfp_t gfp_flags); +static int afs_readpage(struct file *file, struct page *page); +static void afs_invalidatepage(struct page *page, unsigned long offset); +static int afs_releasepage(struct page *page, gfp_t gfp_flags); const struct file_operations afs_file_operations = { .open = afs_open, @@ -32,15 +32,15 @@ const struct file_operations afs_file_operations = { }; const struct inode_operations afs_file_inode_operations = { - .getattr = afs_inode_getattr, + .getattr = afs_getattr, .permission = afs_permission, }; const struct address_space_operations afs_fs_aops = { - .readpage = afs_file_readpage, + .readpage = afs_readpage, .set_page_dirty = __set_page_dirty_nobuffers, - .releasepage = afs_file_releasepage, - .invalidatepage = afs_file_invalidatepage, + .releasepage = afs_releasepage, + .invalidatepage = afs_invalidatepage, }; /* @@ -52,7 +52,7 @@ int afs_open(struct inode *inode, struct file *file) struct key *key; int ret; - _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); + _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { @@ -78,7 +78,7 @@ int afs_release(struct inode *inode, struct file *file) { struct afs_vnode *vnode = AFS_FS_I(inode); - _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); + _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); key_put(file->private_data); _leave(" = 0"); @@ -89,10 +89,10 @@ int afs_release(struct inode *inode, struct file *file) * deal with notification that a page was read from the cache */ #ifdef AFS_CACHING_SUPPORT -static void afs_file_readpage_read_complete(void *cookie_data, - struct page *page, - void *data, - int error) +static void afs_readpage_read_complete(void *cookie_data, + struct page *page, + void *data, + int error) { _enter("%p,%p,%p,%d", cookie_data, page, data, error); @@ -109,10 +109,10 @@ static void afs_file_readpage_read_complete(void *cookie_data, * deal with notification that a page was written to the cache */ #ifdef AFS_CACHING_SUPPORT -static void afs_file_readpage_write_complete(void *cookie_data, - struct page *page, - void *data, - int error) +static void afs_readpage_write_complete(void *cookie_data, + struct page *page, + void *data, + int error) { _enter("%p,%p,%p,%d", cookie_data, page, data, error); @@ -121,9 +121,9 @@ static void afs_file_readpage_write_complete(void *cookie_data, #endif /* - * AFS read page from file (or symlink) + * AFS read page from file, directory or symlink */ -static int afs_file_readpage(struct file *file, struct page *page) +static int afs_readpage(struct file *file, struct page *page) { struct afs_vnode *vnode; struct inode *inode; @@ -218,31 +218,14 @@ error: return ret; } -/* - * get a page cookie for the specified page - */ -#ifdef AFS_CACHING_SUPPORT -int afs_cache_get_page_cookie(struct page *page, - struct cachefs_page **_page_cookie) -{ - int ret; - - _enter(""); - ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO); - - _leave(" = %d", ret); - return ret; -} -#endif - /* * invalidate part or all of a page */ -static void afs_file_invalidatepage(struct page *page, unsigned long offset) +static void afs_invalidatepage(struct page *page, unsigned long offset) { int ret = 1; - _enter("{%lu},%lu", page->index, offset); + kenter("{%lu},%lu", page->index, offset); BUG_ON(!PageLocked(page)); @@ -274,23 +257,17 @@ static void afs_file_invalidatepage(struct page *page, unsigned long offset) /* * release a page and cleanup its private data */ -static int afs_file_releasepage(struct page *page, gfp_t gfp_flags) +static int afs_releasepage(struct page *page, gfp_t gfp_flags) { - struct cachefs_page *pageio; + struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); - _enter("{%lu},%x", page->index, gfp_flags); + _enter("{{%x:%u}[%lu],%lx},%x", + vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, + gfp_flags); if (PagePrivate(page)) { -#ifdef AFS_CACHING_SUPPORT - struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); - cachefs_uncache_page(vnode->cache, page); -#endif - - pageio = (struct cachefs_page *) page_private(page); set_page_private(page, 0); ClearPagePrivate(page); - - kfree(pageio); } _leave(" = 0"); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index e54e6c2ad34..1e65fee3641 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -206,7 +206,7 @@ int afs_fs_fetch_file_status(struct afs_server *server, struct afs_call *call; __be32 *bp; - _enter(",%x,{%x:%d},,", + _enter(",%x,{%x:%u},,", key_serial(key), vnode->fid.vid, vnode->fid.vnode); call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); @@ -265,25 +265,20 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call, call->offset = 0; call->unmarshall++; - if (call->count < PAGE_SIZE) { - page = call->reply3; - buffer = kmap_atomic(page, KM_USER0); - memset(buffer + PAGE_SIZE - call->count, 0, - call->count); - kunmap_atomic(buffer, KM_USER0); - } - /* extract the returned data */ case 2: _debug("extract data"); - page = call->reply3; - buffer = kmap_atomic(page, KM_USER0); - ret = afs_extract_data(call, skb, last, buffer, call->count); - kunmap_atomic(buffer, KM_USER0); - switch (ret) { - case 0: break; - case -EAGAIN: return 0; - default: return ret; + if (call->count > 0) { + page = call->reply3; + buffer = kmap_atomic(page, KM_USER0); + ret = afs_extract_data(call, skb, last, buffer, + call->count); + kunmap_atomic(buffer, KM_USER0); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } } call->offset = 0; @@ -318,6 +313,14 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call, if (!last) return 0; + if (call->count < PAGE_SIZE) { + _debug("clear"); + page = call->reply3; + buffer = kmap_atomic(page, KM_USER0); + memset(buffer + call->count, 0, PAGE_SIZE - call->count); + kunmap_atomic(buffer, KM_USER0); + } + _leave(" = 0 [done]"); return 0; } diff --git a/fs/afs/inode.c b/fs/afs/inode.c index c184a4ee599..9c984cc42cc 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -125,7 +125,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, struct inode *inode; int ret; - _enter(",{%u,%u,%u},,", fid->vid, fid->vnode, fid->unique); + _enter(",{%x:%u.%u},,", fid->vid, fid->vnode, fid->unique); as = sb->s_fs_info; data.volume = as->volume; @@ -203,6 +203,19 @@ bad_inode: return ERR_PTR(ret); } +/* + * mark the data attached to an inode as obsolete due to a write on the server + * - might also want to ditch all the outstanding writes and dirty pages + */ +void afs_zap_data(struct afs_vnode *vnode) +{ + kenter("zap data {%x:%u}", vnode->fid.vid, vnode->fid.vnode); + + /* nuke all the non-dirty pages that aren't locked, mapped or being + * written back */ + invalidate_remote_inode(&vnode->vfs_inode); +} + /* * validate a vnode/inode * - there are several things we need to check @@ -258,10 +271,8 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) /* if the vnode's data version number changed then its contents are * different */ - if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { - _debug("zap data {%x:%d}", vnode->fid.vid, vnode->fid.vnode); - invalidate_remote_inode(&vnode->vfs_inode); - } + if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) + afs_zap_data(vnode); clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); mutex_unlock(&vnode->validate_lock); @@ -278,7 +289,7 @@ error_unlock: /* * read the attributes of an inode */ -int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, +int afs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode; @@ -301,7 +312,7 @@ void afs_clear_inode(struct inode *inode) vnode = AFS_FS_I(inode); - _enter("{%x:%d.%d} v=%u x=%u t=%u }", + _enter("{%x:%u.%d} v=%u x=%u t=%u }", vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, diff --git a/fs/afs/internal.h b/fs/afs/internal.h index d90c158cd93..9feb5c59d8f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -433,10 +433,6 @@ extern const struct file_operations afs_file_operations; extern int afs_open(struct inode *, struct file *); extern int afs_release(struct inode *, struct file *); -#ifdef AFS_CACHING_SUPPORT -extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **); -#endif - /* * fsclient.c */ @@ -474,10 +470,9 @@ extern int afs_fs_rename(struct afs_server *, struct key *, extern struct inode *afs_iget(struct super_block *, struct key *, struct afs_fid *, struct afs_file_status *, struct afs_callback *); +extern void afs_zap_data(struct afs_vnode *); extern int afs_validate(struct afs_vnode *, struct key *); -extern int afs_inode_getattr(struct vfsmount *, struct dentry *, - struct kstat *); -extern void afs_zap_permits(struct rcu_head *); +extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern void afs_clear_inode(struct inode *); /* @@ -533,6 +528,7 @@ extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *, */ extern void afs_clear_permits(struct afs_vnode *); extern void afs_cache_permit(struct afs_vnode *, struct key *, long); +extern void afs_zap_permits(struct rcu_head *); extern struct key *afs_request_key(struct afs_cell *); extern int afs_permission(struct inode *, int, struct nameidata *); @@ -726,6 +722,21 @@ do { \ } \ } while(0) +#define ASSERTRANGE(L, OP1, N, OP2, H) \ +do { \ + if (unlikely(!((L) OP1 (N)) || !((N) OP2 (H)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "AFS: Assertion failed\n"); \ + printk(KERN_ERR "%lu "#OP1" %lu "#OP2" %lu is false\n", \ + (unsigned long)(L), (unsigned long)(N), \ + (unsigned long)(H)); \ + printk(KERN_ERR "0x%lx "#OP1" 0x%lx "#OP2" 0x%lx is false\n", \ + (unsigned long)(L), (unsigned long)(N), \ + (unsigned long)(H)); \ + BUG(); \ + } \ +} while(0) + #define ASSERTIF(C, X) \ do { \ if (unlikely((C) && !(X))) { \ @@ -758,6 +769,10 @@ do { \ do { \ } while(0) +#define ASSERTRANGE(L, OP1, N, OP2, H) \ +do { \ +} while(0) + #define ASSERTIF(C, X) \ do { \ } while(0) diff --git a/fs/afs/main.c b/fs/afs/main.c index 80ec6fd19a7..f1f71ff7d5c 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -149,6 +149,7 @@ error_cache: afs_vlocation_purge(); afs_cell_purge(); afs_proc_cleanup(); + rcu_barrier(); printk(KERN_ERR "kAFS: failed to register: %d\n", ret); return ret; } @@ -176,6 +177,7 @@ static void __exit afs_exit(void) cachefs_unregister_netfs(&afs_cache_netfs); #endif afs_proc_cleanup(); + rcu_barrier(); } module_exit(afs_exit); diff --git a/fs/afs/misc.c b/fs/afs/misc.c index cdb9792d816..d1a889c4074 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -22,6 +22,7 @@ int afs_abort_to_error(u32 abort_code) { switch (abort_code) { case 13: return -EACCES; + case 27: return -EFBIG; case 30: return -EROFS; case VSALVAGE: return -EIO; case VNOVNODE: return -ENOENT; diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 034fcfd4e33..a3684dcc76e 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -36,7 +36,7 @@ const struct inode_operations afs_mntpt_inode_operations = { .lookup = afs_mntpt_lookup, .follow_link = afs_mntpt_follow_link, .readlink = page_readlink, - .getattr = afs_inode_getattr, + .getattr = afs_getattr, }; static LIST_HEAD(afs_vfsmounts); @@ -58,7 +58,8 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) char *buf; int ret; - _enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique); + _enter("{%x:%u,%u}", + vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); /* read the contents of the symlink into the pagecache */ page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file); diff --git a/fs/afs/security.c b/fs/afs/security.c index f9f424d8045..e0ea88b63eb 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -109,7 +109,7 @@ void afs_clear_permits(struct afs_vnode *vnode) { struct afs_permits *permits; - _enter("{%x}", vnode->fid.vnode); + _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); mutex_lock(&vnode->permits_lock); permits = vnode->permits; @@ -132,7 +132,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order) struct afs_vnode *auth_vnode; int count, loop; - _enter("{%x},%x,%lx", vnode->fid.vnode, key_serial(key), acl_order); + _enter("{%x:%u},%x,%lx", + vnode->fid.vid, vnode->fid.vnode, key_serial(key), acl_order); auth_vnode = afs_get_auth_inode(vnode, key); if (IS_ERR(auth_vnode)) { @@ -220,7 +221,8 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key, bool valid; int loop, ret; - _enter(""); + _enter("{%x:%u},%x", + vnode->fid.vid, vnode->fid.vnode, key_serial(key)); auth_vnode = afs_get_auth_inode(vnode, key); if (IS_ERR(auth_vnode)) { @@ -268,9 +270,9 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key, _leave(" = %d", ret); return ret; } + *_access = vnode->status.caller_access; } - *_access = vnode->status.caller_access; iput(&auth_vnode->vfs_inode); _leave(" = 0 [access %x]", *_access); return 0; @@ -288,7 +290,7 @@ int afs_permission(struct inode *inode, int mask, struct nameidata *nd) struct key *key; int ret; - _enter("{{%x:%x},%lx},%x,", + _enter("{{%x:%u},%lx},%x,", vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); key = afs_request_key(vnode->volume->cell); diff --git a/fs/afs/server.c b/fs/afs/server.c index 96bb23b476a..231ae415027 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -252,6 +252,9 @@ static void afs_destroy_server(struct afs_server *server) { _enter("%p", server); + ASSERTIF(server->cb_break_head != server->cb_break_tail, + delayed_work_pending(&server->cb_break_work)); + ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL); ASSERTCMP(server->cb_promises.rb_node, ==, NULL); ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail); diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index a1904ab8426..0e37f9949cb 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -261,7 +261,7 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode, DECLARE_WAITQUEUE(myself, current); - _enter("%s,{%u,%u,%u}", + _enter("%s,{%x:%u.%u}", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); @@ -389,7 +389,7 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%x,,,", + _enter("%s{%x:%u.%u},%x,,,", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, @@ -446,7 +446,7 @@ int afs_vnode_create(struct afs_vnode *vnode, struct key *key, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%x,%s,,", + _enter("%s{%x:%u.%u},%x,%s,,", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, @@ -502,7 +502,7 @@ int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%x,%s", + _enter("%s{%x:%u.%u},%x,%s", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, @@ -557,7 +557,7 @@ extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s", + _enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s", dvnode->volume->vlocation->vldb.name, dvnode->fid.vid, dvnode->fid.vnode, @@ -628,7 +628,7 @@ int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%x,%s,%s,,,", + _enter("%s{%x:%u.%u},%x,%s,%s,,,", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, @@ -687,7 +687,7 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode, struct afs_server *server; int ret; - _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s,%s", + _enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s", orig_dvnode->volume->vlocation->vldb.name, orig_dvnode->fid.vid, orig_dvnode->fid.vnode, -- cgit v1.2.3 From 31143d5d515ece617ffccb7df5ff75e4d1dfa120 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 9 May 2007 02:33:46 -0700 Subject: AFS: implement basic file write support Implement support for writing to regular AFS files, including: (1) write (2) truncate (3) fsync, fdatasync (4) chmod, chown, chgrp, utime. AFS writeback attempts to batch writes into as chunks as large as it can manage up to the point that it writes back 65535 pages in one chunk or it meets a locked page. Furthermore, if a page has been written to using a particular key, then should another write to that page use some other key, the first write will be flushed before the second is allowed to take place. If the first write fails due to a security error, then the page will be scrapped and reread before the second write takes place. If a page is dirty and the callback on it is broken by the server, then the dirty data is not discarded (same behaviour as NFS). Shared-writable mappings are not supported by this patch. [akpm@linux-foundation.org: fix a bunch of warnings] Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/Makefile | 3 +- fs/afs/afs_fs.h | 2 + fs/afs/dir.c | 1 + fs/afs/file.c | 32 ++- fs/afs/fsclient.c | 332 +++++++++++++++++++++- fs/afs/inode.c | 47 ++- fs/afs/internal.h | 66 ++++- fs/afs/rxrpc.c | 80 +++++- fs/afs/super.c | 5 +- fs/afs/vnode.c | 107 +++++++ fs/afs/write.c | 835 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1483 insertions(+), 27 deletions(-) create mode 100644 fs/afs/write.c diff --git a/fs/afs/Makefile b/fs/afs/Makefile index cf83e5d6351..73ce561f3ea 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -22,6 +22,7 @@ kafs-objs := \ vlclient.o \ vlocation.o \ vnode.o \ - volume.o + volume.o \ + write.o obj-$(CONFIG_AFS_FS) := kafs.o diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h index 89e0d1650a7..2198006d2d0 100644 --- a/fs/afs/afs_fs.h +++ b/fs/afs/afs_fs.h @@ -18,6 +18,8 @@ enum AFS_FS_Operations { FSFETCHDATA = 130, /* AFS Fetch file data */ FSFETCHSTATUS = 132, /* AFS Fetch file status */ + FSSTOREDATA = 133, /* AFS Store file data */ + FSSTORESTATUS = 135, /* AFS Store file status */ FSREMOVEFILE = 136, /* AFS Remove a file */ FSCREATEFILE = 137, /* AFS Create a file */ FSRENAME = 138, /* AFS Rename or move a file or directory */ diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 51f177af5dd..2fb31276196 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -56,6 +56,7 @@ const struct inode_operations afs_dir_inode_operations = { .rename = afs_rename, .permission = afs_permission, .getattr = afs_getattr, + .setattr = afs_setattr, }; static struct dentry_operations afs_fs_dentry_operations = { diff --git a/fs/afs/file.c b/fs/afs/file.c index 3eb3fc7b36b..3e25795e5a4 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -15,32 +15,43 @@ #include #include #include +#include #include "internal.h" static int afs_readpage(struct file *file, struct page *page); static void afs_invalidatepage(struct page *page, unsigned long offset); static int afs_releasepage(struct page *page, gfp_t gfp_flags); +static int afs_launder_page(struct page *page); const struct file_operations afs_file_operations = { .open = afs_open, .release = afs_release, .llseek = generic_file_llseek, .read = do_sync_read, + .write = do_sync_write, .aio_read = generic_file_aio_read, + .aio_write = afs_file_write, .mmap = generic_file_readonly_mmap, .sendfile = generic_file_sendfile, + .fsync = afs_fsync, }; const struct inode_operations afs_file_inode_operations = { .getattr = afs_getattr, + .setattr = afs_setattr, .permission = afs_permission, }; const struct address_space_operations afs_fs_aops = { .readpage = afs_readpage, - .set_page_dirty = __set_page_dirty_nobuffers, + .set_page_dirty = afs_set_page_dirty, + .launder_page = afs_launder_page, .releasepage = afs_releasepage, .invalidatepage = afs_invalidatepage, + .prepare_write = afs_prepare_write, + .commit_write = afs_commit_write, + .writepage = afs_writepage, + .writepages = afs_writepages, }; /* @@ -230,11 +241,6 @@ static void afs_invalidatepage(struct page *page, unsigned long offset) BUG_ON(!PageLocked(page)); if (PagePrivate(page)) { -#ifdef AFS_CACHING_SUPPORT - struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); - cachefs_uncache_page(vnode->cache,page); -#endif - /* We release buffers only if the entire page is being * invalidated. * The get_block cached value has been unconditionally @@ -254,20 +260,34 @@ static void afs_invalidatepage(struct page *page, unsigned long offset) _leave(" = %d", ret); } +/* + * write back a dirty page + */ +static int afs_launder_page(struct page *page) +{ + _enter("{%lu}", page->index); + + return 0; +} + /* * release a page and cleanup its private data */ static int afs_releasepage(struct page *page, gfp_t gfp_flags) { struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); + struct afs_writeback *wb; _enter("{{%x:%u}[%lu],%lx},%x", vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, gfp_flags); if (PagePrivate(page)) { + wb = (struct afs_writeback *) page_private(page); + ASSERT(wb != NULL); set_page_private(page, 0); ClearPagePrivate(page); + afs_put_writeback(wb); } _leave(" = 0"); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 1e65fee3641..025b1903d9e 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -33,8 +33,10 @@ static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) */ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, struct afs_file_status *status, - struct afs_vnode *vnode) + struct afs_vnode *vnode, + afs_dataversion_t *store_version) { + afs_dataversion_t expected_version; const __be32 *bp = *_bp; umode_t mode; u64 data_version, size; @@ -101,7 +103,11 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; } - if (status->data_version != data_version) { + expected_version = status->data_version; + if (store_version) + expected_version = *store_version; + + if (expected_version != data_version) { status->data_version = data_version; if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { _debug("vnode modified %llx on {%x:%u}", @@ -110,6 +116,8 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, set_bit(AFS_VNODE_MODIFIED, &vnode->flags); set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); } + } else if (store_version) { + status->data_version = data_version; } } @@ -155,6 +163,44 @@ static void xdr_decode_AFSVolSync(const __be32 **_bp, *_bp = bp; } +/* + * encode the requested attributes into an AFSStoreStatus block + */ +static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr) +{ + __be32 *bp = *_bp; + u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0; + + mask = 0; + if (attr->ia_valid & ATTR_MTIME) { + mask |= AFS_SET_MTIME; + mtime = attr->ia_mtime.tv_sec; + } + + if (attr->ia_valid & ATTR_UID) { + mask |= AFS_SET_OWNER; + owner = attr->ia_uid; + } + + if (attr->ia_valid & ATTR_GID) { + mask |= AFS_SET_GROUP; + group = attr->ia_gid; + } + + if (attr->ia_valid & ATTR_MODE) { + mask |= AFS_SET_MODE; + mode = attr->ia_mode & S_IALLUGO; + } + + *bp++ = htonl(mask); + *bp++ = htonl(mtime); + *bp++ = htonl(owner); + *bp++ = htonl(group); + *bp++ = htonl(mode); + *bp++ = 0; /* segment size */ + *_bp = bp; +} + /* * deliver reply data to an FS.FetchStatus */ @@ -175,7 +221,7 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack(&bp, vnode); if (call->reply2) xdr_decode_AFSVolSync(&bp, call->reply2); @@ -295,7 +341,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call, } bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack(&bp, vnode); if (call->reply2) xdr_decode_AFSVolSync(&bp, call->reply2); @@ -479,8 +525,8 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFid(&bp, call->reply2); - xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack_raw(&bp, call->reply4); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ @@ -577,7 +623,7 @@ static int afs_deliver_fs_remove(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ _leave(" = 0 [done]"); @@ -660,8 +706,8 @@ static int afs_deliver_fs_link(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); - xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); + xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ _leave(" = 0 [done]"); @@ -749,8 +795,8 @@ static int afs_deliver_fs_symlink(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFid(&bp, call->reply2); - xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); - xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ _leave(" = 0 [done]"); @@ -855,9 +901,10 @@ static int afs_deliver_fs_rename(struct afs_call *call, /* unmarshall the reply once we've received all of it */ bp = call->buffer; - xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode); + xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL); if (new_dvnode != orig_dvnode) - xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode); + xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode, + NULL); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ _leave(" = 0 [done]"); @@ -939,3 +986,262 @@ int afs_fs_rename(struct afs_server *server, return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); } + +/* + * deliver reply data to an FS.StoreData + */ +static int afs_deliver_fs_store_data(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *vnode = call->reply; + const __be32 *bp; + + _enter(",,%u", last); + + afs_transfer_reply(call, skb); + if (!last) { + _leave(" = 0 [more]"); + return 0; + } + + if (call->reply_size != call->reply_max) { + _leave(" = -EBADMSG [%u != %u]", + call->reply_size, call->reply_max); + return -EBADMSG; + } + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, + &call->store_version); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + afs_pages_written_back(vnode, call); + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.StoreData operation type + */ +static const struct afs_call_type afs_RXFSStoreData = { + .name = "FS.StoreData", + .deliver = afs_deliver_fs_store_data, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * store a set of pages + */ +int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb, + pgoff_t first, pgoff_t last, + unsigned offset, unsigned to, + const struct afs_wait_mode *wait_mode) +{ + struct afs_vnode *vnode = wb->vnode; + struct afs_call *call; + loff_t size, pos, i_size; + __be32 *bp; + + _enter(",%x,{%x:%u},,", + key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode); + + size = to - offset; + if (first != last) + size += (loff_t)(last - first) << PAGE_SHIFT; + pos = (loff_t)first << PAGE_SHIFT; + pos += offset; + + i_size = i_size_read(&vnode->vfs_inode); + if (pos + size > i_size) + i_size = size + pos; + + _debug("size %llx, at %llx, i_size %llx", + (unsigned long long) size, (unsigned long long) pos, + (unsigned long long) i_size); + + BUG_ON(i_size > 0xffffffff); // TODO: use 64-bit store + + call = afs_alloc_flat_call(&afs_RXFSStoreData, + (4 + 6 + 3) * 4, + (21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->wb = wb; + call->key = wb->key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + call->mapping = vnode->vfs_inode.i_mapping; + call->first = first; + call->last = last; + call->first_offset = offset; + call->last_to = to; + call->send_pages = true; + call->store_version = vnode->status.data_version + 1; + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSSTOREDATA); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + *bp++ = 0; /* mask */ + *bp++ = 0; /* mtime */ + *bp++ = 0; /* owner */ + *bp++ = 0; /* group */ + *bp++ = 0; /* unix mode */ + *bp++ = 0; /* segment size */ + + *bp++ = htonl(pos); + *bp++ = htonl(size); + *bp++ = htonl(i_size); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} + +/* + * deliver reply data to an FS.StoreStatus + */ +static int afs_deliver_fs_store_status(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + afs_dataversion_t *store_version; + struct afs_vnode *vnode = call->reply; + const __be32 *bp; + + _enter(",,%u", last); + + afs_transfer_reply(call, skb); + if (!last) { + _leave(" = 0 [more]"); + return 0; + } + + if (call->reply_size != call->reply_max) { + _leave(" = -EBADMSG [%u != %u]", + call->reply_size, call->reply_max); + return -EBADMSG; + } + + /* unmarshall the reply once we've received all of it */ + store_version = NULL; + if (call->operation_ID == FSSTOREDATA) + store_version = &call->store_version; + + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.StoreStatus operation type + */ +static const struct afs_call_type afs_RXFSStoreStatus = { + .name = "FS.StoreStatus", + .deliver = afs_deliver_fs_store_status, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +static const struct afs_call_type afs_RXFSStoreData_as_Status = { + .name = "FS.StoreData", + .deliver = afs_deliver_fs_store_status, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus + * so as to alter the file size also + */ +static int afs_fs_setattr_size(struct afs_server *server, struct key *key, + struct afs_vnode *vnode, struct iattr *attr, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; + + _enter(",%x,{%x:%u},,", + key_serial(key), vnode->fid.vid, vnode->fid.vnode); + + ASSERT(attr->ia_valid & ATTR_SIZE); + ASSERTCMP(attr->ia_size, <=, 0xffffffff); // TODO: use 64-bit store + + call = afs_alloc_flat_call(&afs_RXFSStoreData_as_Status, + (4 + 6 + 3) * 4, + (21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + call->store_version = vnode->status.data_version + 1; + call->operation_ID = FSSTOREDATA; + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSSTOREDATA); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + xdr_encode_AFS_StoreStatus(&bp, attr); + + *bp++ = 0; /* position of start of write */ + *bp++ = 0; /* size of write */ + *bp++ = htonl(attr->ia_size); /* new file length */ + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} + +/* + * set the attributes on a file, using FS.StoreData if there's a change in file + * size, and FS.StoreStatus otherwise + */ +int afs_fs_setattr(struct afs_server *server, struct key *key, + struct afs_vnode *vnode, struct iattr *attr, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; + + if (attr->ia_valid & ATTR_SIZE) + return afs_fs_setattr_size(server, key, vnode, attr, + wait_mode); + + _enter(",%x,{%x:%u},,", + key_serial(key), vnode->fid.vid, vnode->fid.vnode); + + call = afs_alloc_flat_call(&afs_RXFSStoreStatus, + (4 + 6) * 4, + (21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + call->operation_ID = FSSTORESTATUS; + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSSTORESTATUS); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + xdr_encode_AFS_StoreStatus(&bp, attr); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 9c984cc42cc..515a5d12d8f 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -209,7 +209,7 @@ bad_inode: */ void afs_zap_data(struct afs_vnode *vnode) { - kenter("zap data {%x:%u}", vnode->fid.vid, vnode->fid.vnode); + _enter("zap data {%x:%u}", vnode->fid.vid, vnode->fid.vnode); /* nuke all the non-dirty pages that aren't locked, mapped or being * written back */ @@ -334,6 +334,7 @@ void afs_clear_inode(struct inode *inode) vnode->server = NULL; } + ASSERT(list_empty(&vnode->writebacks)); ASSERT(!vnode->cb_promised); #ifdef AFS_CACHING_SUPPORT @@ -350,3 +351,47 @@ void afs_clear_inode(struct inode *inode) _leave(""); } + +/* + * set the attributes of an inode + */ +int afs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); + struct key *key; + int ret; + + _enter("{%x:%u},{n=%s},%x", + vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, + attr->ia_valid); + + if (!(attr->ia_valid & (ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID | + ATTR_MTIME))) { + _leave(" = 0 [unsupported]"); + return 0; + } + + /* flush any dirty data outstanding on a regular file */ + if (S_ISREG(vnode->vfs_inode.i_mode)) { + filemap_write_and_wait(vnode->vfs_inode.i_mapping); + afs_writeback_all(vnode); + } + + if (attr->ia_valid & ATTR_FILE) { + key = attr->ia_file->private_data; + } else { + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + } + + ret = afs_vnode_setattr(vnode, key, attr); + if (!(attr->ia_valid & ATTR_FILE)) + key_put(key); + +error: + _leave(" = %d", ret); + return ret; +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 9feb5c59d8f..a30d4fa768e 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -21,6 +21,7 @@ #define AFS_CELL_MAX_ADDRS 15 +struct pagevec; struct afs_call; typedef enum { @@ -75,12 +76,15 @@ struct afs_call { struct key *key; /* security for this call */ struct afs_server *server; /* server affected by incoming CM call */ void *request; /* request data (first part) */ - void *request2; /* request data (second part) */ + struct address_space *mapping; /* page set */ + struct afs_writeback *wb; /* writeback being performed */ void *buffer; /* reply receive buffer */ void *reply; /* reply buffer (first part) */ void *reply2; /* reply buffer (second part) */ void *reply3; /* reply buffer (third part) */ void *reply4; /* reply buffer (fourth part) */ + pgoff_t first; /* first page in mapping to deal with */ + pgoff_t last; /* last page in mapping to deal with */ enum { /* call state */ AFS_CALL_REQUESTING, /* request is being sent for outgoing call */ AFS_CALL_AWAIT_REPLY, /* awaiting reply to outgoing call */ @@ -97,14 +101,18 @@ struct afs_call { unsigned request_size; /* size of request data */ unsigned reply_max; /* maximum size of reply */ unsigned reply_size; /* current size of reply */ + unsigned first_offset; /* offset into mapping[first] */ + unsigned last_to; /* amount of mapping[last] */ unsigned short offset; /* offset into received data store */ unsigned char unmarshall; /* unmarshalling phase */ bool incoming; /* T if incoming call */ + bool send_pages; /* T if data from mapping should be sent */ u16 service_id; /* RxRPC service ID to call */ __be16 port; /* target UDP port */ __be32 operation_ID; /* operation ID for an incoming call */ u32 count; /* count for use in unmarshalling */ __be32 tmp; /* place to extract temporary data */ + afs_dataversion_t store_version; /* updated version expected from store */ }; struct afs_call_type { @@ -123,6 +131,32 @@ struct afs_call_type { void (*destructor)(struct afs_call *call); }; +/* + * record of an outstanding writeback on a vnode + */ +struct afs_writeback { + struct list_head link; /* link in vnode->writebacks */ + struct work_struct writer; /* work item to perform the writeback */ + struct afs_vnode *vnode; /* vnode to which this write applies */ + struct key *key; /* owner of this write */ + wait_queue_head_t waitq; /* completion and ready wait queue */ + pgoff_t first; /* first page in batch */ + pgoff_t point; /* last page in current store op */ + pgoff_t last; /* last page in batch (inclusive) */ + unsigned offset_first; /* offset into first page of start of write */ + unsigned to_last; /* offset into last page of end of write */ + int num_conflicts; /* count of conflicting writes in list */ + int usage; + bool conflicts; /* T if has dependent conflicts */ + enum { + AFS_WBACK_SYNCING, /* synchronisation being performed */ + AFS_WBACK_PENDING, /* write pending */ + AFS_WBACK_CONFLICTING, /* conflicting writes posted */ + AFS_WBACK_WRITING, /* writing back */ + AFS_WBACK_COMPLETE /* the writeback record has been unlinked */ + } state __attribute__((packed)); +}; + /* * AFS superblock private data * - there's one superblock per volume @@ -305,6 +339,7 @@ struct afs_vnode { wait_queue_head_t update_waitq; /* status fetch waitqueue */ int update_cnt; /* number of outstanding ops that will update the * status */ + spinlock_t writeback_lock; /* lock for writebacks */ spinlock_t lock; /* waitqueue/flags lock */ unsigned long flags; #define AFS_VNODE_CB_BROKEN 0 /* set if vnode's callback was broken */ @@ -316,6 +351,8 @@ struct afs_vnode { long acl_order; /* ACL check count (callback break count) */ + struct list_head writebacks; /* alterations in pagecache that need writing */ + /* outstanding callback notification on this file */ struct rb_node server_rb; /* link in server->fs_vnodes */ struct rb_node cb_promise; /* link in server->cb_promises */ @@ -463,6 +500,12 @@ extern int afs_fs_rename(struct afs_server *, struct key *, struct afs_vnode *, const char *, struct afs_vnode *, const char *, const struct afs_wait_mode *); +extern int afs_fs_store_data(struct afs_server *, struct afs_writeback *, + pgoff_t, pgoff_t, unsigned, unsigned, + const struct afs_wait_mode *); +extern int afs_fs_setattr(struct afs_server *, struct key *, + struct afs_vnode *, struct iattr *, + const struct afs_wait_mode *); /* * inode.c @@ -473,6 +516,7 @@ extern struct inode *afs_iget(struct super_block *, struct key *, extern void afs_zap_data(struct afs_vnode *); extern int afs_validate(struct afs_vnode *, struct key *); extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); +extern int afs_setattr(struct dentry *, struct iattr *); extern void afs_clear_inode(struct inode *); /* @@ -625,6 +669,9 @@ extern int afs_vnode_symlink(struct afs_vnode *, struct key *, const char *, struct afs_file_status *, struct afs_server **); extern int afs_vnode_rename(struct afs_vnode *, struct afs_vnode *, struct key *, const char *, const char *); +extern int afs_vnode_store_data(struct afs_writeback *, pgoff_t, pgoff_t, + unsigned, unsigned); +extern int afs_vnode_setattr(struct afs_vnode *, struct key *, struct iattr *); /* * volume.c @@ -641,6 +688,23 @@ extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *); extern int afs_volume_release_fileserver(struct afs_vnode *, struct afs_server *, int); +/* + * write.c + */ +extern int afs_set_page_dirty(struct page *); +extern void afs_put_writeback(struct afs_writeback *); +extern int afs_prepare_write(struct file *, struct page *, unsigned, unsigned); +extern int afs_commit_write(struct file *, struct page *, unsigned, unsigned); +extern int afs_writepage(struct page *, struct writeback_control *); +extern int afs_writepages(struct address_space *, struct writeback_control *); +extern int afs_write_inode(struct inode *, int); +extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); +extern ssize_t afs_file_write(struct kiocb *, const struct iovec *, + unsigned long, loff_t); +extern int afs_writeback_all(struct afs_vnode *); +extern int afs_fsync(struct file *, struct dentry *, int); + + /*****************************************************************************/ /* * debug tracing diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 222c1a3abbb..04189c47d6a 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -236,6 +236,70 @@ void afs_flat_call_destructor(struct afs_call *call) call->buffer = NULL; } +/* + * attach the data from a bunch of pages on an inode to a call + */ +int afs_send_pages(struct afs_call *call, struct msghdr *msg, struct kvec *iov) +{ + struct page *pages[8]; + unsigned count, n, loop, offset, to; + pgoff_t first = call->first, last = call->last; + int ret; + + _enter(""); + + offset = call->first_offset; + call->first_offset = 0; + + do { + _debug("attach %lx-%lx", first, last); + + count = last - first + 1; + if (count > ARRAY_SIZE(pages)) + count = ARRAY_SIZE(pages); + n = find_get_pages_contig(call->mapping, first, count, pages); + ASSERTCMP(n, ==, count); + + loop = 0; + do { + msg->msg_flags = 0; + to = PAGE_SIZE; + if (first + loop >= last) + to = call->last_to; + else + msg->msg_flags = MSG_MORE; + iov->iov_base = kmap(pages[loop]) + offset; + iov->iov_len = to - offset; + offset = 0; + + _debug("- range %u-%u%s", + offset, to, msg->msg_flags ? " [more]" : ""); + msg->msg_iov = (struct iovec *) iov; + msg->msg_iovlen = 1; + + /* have to change the state *before* sending the last + * packet as RxRPC might give us the reply before it + * returns from sending the request */ + if (first + loop >= last) + call->state = AFS_CALL_AWAIT_REPLY; + ret = rxrpc_kernel_send_data(call->rxcall, msg, + to - offset); + kunmap(pages[loop]); + if (ret < 0) + break; + } while (++loop < count); + first += count; + + for (loop = 0; loop < count; loop++) + put_page(pages[loop]); + if (ret < 0) + break; + } while (first < last); + + _leave(" = %d", ret); + return ret; +} + /* * initiate a call */ @@ -253,8 +317,9 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, ASSERT(call->type != NULL); ASSERT(call->type->name != NULL); - _debug("MAKE %p{%s} [%d]", - call, call->type->name, atomic_read(&afs_outstanding_calls)); + _debug("____MAKE %p{%s,%x} [%d]____", + call, call->type->name, key_serial(call->key), + atomic_read(&afs_outstanding_calls)); call->wait_mode = wait_mode; INIT_WORK(&call->async_work, afs_process_async_call); @@ -289,16 +354,23 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; - msg.msg_flags = 0; + msg.msg_flags = (call->send_pages ? MSG_MORE : 0); /* have to change the state *before* sending the last packet as RxRPC * might give us the reply before it returns from sending the * request */ - call->state = AFS_CALL_AWAIT_REPLY; + if (!call->send_pages) + call->state = AFS_CALL_AWAIT_REPLY; ret = rxrpc_kernel_send_data(rxcall, &msg, call->request_size); if (ret < 0) goto error_do_abort; + if (call->send_pages) { + ret = afs_send_pages(call, &msg, iov); + if (ret < 0) + goto error_do_abort; + } + /* at this point, an async call may no longer exist as it may have * already completed */ return wait_mode->wait(call); diff --git a/fs/afs/super.c b/fs/afs/super.c index 7030d76155f..d24be334b60 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -50,6 +50,7 @@ static const struct super_operations afs_super_ops = { .statfs = simple_statfs, .alloc_inode = afs_alloc_inode, .drop_inode = generic_delete_inode, + .write_inode = afs_write_inode, .destroy_inode = afs_destroy_inode, .clear_inode = afs_clear_inode, .umount_begin = afs_umount_begin, @@ -66,7 +67,7 @@ enum { afs_opt_vol, }; -static const match_table_t afs_options_list = { +static match_table_t afs_options_list = { { afs_opt_cell, "cell=%s" }, { afs_opt_rwpath, "rwpath" }, { afs_opt_vol, "vol=%s" }, @@ -459,7 +460,9 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, init_waitqueue_head(&vnode->update_waitq); mutex_init(&vnode->permits_lock); mutex_init(&vnode->validate_lock); + spin_lock_init(&vnode->writeback_lock); spin_lock_init(&vnode->lock); + INIT_LIST_HEAD(&vnode->writebacks); INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); } } diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index 0e37f9949cb..ec814660209 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -753,3 +753,110 @@ no_server: _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt); return PTR_ERR(server); } + +/* + * write to a file + */ +int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last, + unsigned offset, unsigned to) +{ + struct afs_server *server; + struct afs_vnode *vnode = wb->vnode; + int ret; + + _enter("%s{%x:%u.%u},%x,%lx,%lx,%x,%x", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(wb->key), + first, last, offset, to); + + /* this op will fetch the status */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_store_data(server, wb, first, last, offset, to, + &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + } else { + afs_vnode_status_update_failed(vnode, ret); + } + + _leave(" = %d", ret); + return ret; + +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + return PTR_ERR(server); +} + +/* + * set the attributes on a file + */ +int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key, + struct iattr *attr) +{ + struct afs_server *server; + int ret; + + _enter("%s{%x:%u.%u},%x", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key)); + + /* this op will fetch the status */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_setattr(server, key, vnode, attr, &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + } else { + afs_vnode_status_update_failed(vnode, ret); + } + + _leave(" = %d", ret); + return ret; + +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + return PTR_ERR(server); +} diff --git a/fs/afs/write.c b/fs/afs/write.c new file mode 100644 index 00000000000..83ff2926281 --- /dev/null +++ b/fs/afs/write.c @@ -0,0 +1,835 @@ +/* handling of writes to regular files and writing back to the server + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "internal.h" + +static int afs_write_back_from_locked_page(struct afs_writeback *wb, + struct page *page); + +/* + * mark a page as having been made dirty and thus needing writeback + */ +int afs_set_page_dirty(struct page *page) +{ + _enter(""); + return __set_page_dirty_nobuffers(page); +} + +/* + * unlink a writeback record because its usage has reached zero + * - must be called with the wb->vnode->writeback_lock held + */ +static void afs_unlink_writeback(struct afs_writeback *wb) +{ + struct afs_writeback *front; + struct afs_vnode *vnode = wb->vnode; + + list_del_init(&wb->link); + if (!list_empty(&vnode->writebacks)) { + /* if an fsync rises to the front of the queue then wake it + * up */ + front = list_entry(vnode->writebacks.next, + struct afs_writeback, link); + if (front->state == AFS_WBACK_SYNCING) { + _debug("wake up sync"); + front->state = AFS_WBACK_COMPLETE; + wake_up(&front->waitq); + } + } +} + +/* + * free a writeback record + */ +static void afs_free_writeback(struct afs_writeback *wb) +{ + _enter(""); + key_put(wb->key); + kfree(wb); +} + +/* + * dispose of a reference to a writeback record + */ +void afs_put_writeback(struct afs_writeback *wb) +{ + struct afs_vnode *vnode = wb->vnode; + + _enter("{%d}", wb->usage); + + spin_lock(&vnode->writeback_lock); + if (--wb->usage == 0) + afs_unlink_writeback(wb); + else + wb = NULL; + spin_unlock(&vnode->writeback_lock); + if (wb) + afs_free_writeback(wb); +} + +/* + * partly or wholly fill a page that's under preparation for writing + */ +static int afs_fill_page(struct afs_vnode *vnode, struct key *key, + unsigned start, unsigned len, struct page *page) +{ + int ret; + + _enter(",,%u,%u", start, len); + + ASSERTCMP(start + len, <=, PAGE_SIZE); + + ret = afs_vnode_fetch_data(vnode, key, start, len, page); + if (ret < 0) { + if (ret == -ENOENT) { + _debug("got NOENT from server" + " - marking file deleted and stale"); + set_bit(AFS_VNODE_DELETED, &vnode->flags); + ret = -ESTALE; + } + } + + _leave(" = %d", ret); + return ret; +} + +/* + * prepare a page for being written to + */ +static int afs_prepare_page(struct afs_vnode *vnode, struct page *page, + struct key *key, unsigned offset, unsigned to) +{ + unsigned eof, tail, start, stop, len; + loff_t i_size, pos; + void *p; + int ret; + + _enter(""); + + if (offset == 0 && to == PAGE_SIZE) + return 0; + + p = kmap(page); + + i_size = i_size_read(&vnode->vfs_inode); + pos = (loff_t) page->index << PAGE_SHIFT; + if (pos >= i_size) { + /* partial write, page beyond EOF */ + _debug("beyond"); + if (offset > 0) + memset(p, 0, offset); + if (to < PAGE_SIZE) + memset(p + to, 0, PAGE_SIZE - to); + kunmap(page); + return 0; + } + + if (i_size - pos >= PAGE_SIZE) { + /* partial write, page entirely before EOF */ + _debug("before"); + tail = eof = PAGE_SIZE; + } else { + /* partial write, page overlaps EOF */ + eof = i_size - pos; + _debug("overlap %u", eof); + tail = max(eof, to); + if (tail < PAGE_SIZE) + memset(p + tail, 0, PAGE_SIZE - tail); + if (offset > eof) + memset(p + eof, 0, PAGE_SIZE - eof); + } + + kunmap(p); + + ret = 0; + if (offset > 0 || eof > to) { + /* need to fill one or two bits that aren't going to be written + * (cover both fillers in one read if there are two) */ + start = (offset > 0) ? 0 : to; + stop = (eof > to) ? eof : offset; + len = stop - start; + _debug("wr=%u-%u av=0-%u rd=%u@%u", + offset, to, eof, start, len); + ret = afs_fill_page(vnode, key, start, len, page); + } + + _leave(" = %d", ret); + return ret; +} + +/* + * prepare to perform part of a write to a page + * - the caller holds the page locked, preventing it from being written out or + * modified by anyone else + */ +int afs_prepare_write(struct file *file, struct page *page, + unsigned offset, unsigned to) +{ + struct afs_writeback *candidate, *wb; + struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); + struct key *key = file->private_data; + pgoff_t index; + int ret; + + _enter("{%x:%u},{%lx},%u,%u", + vnode->fid.vid, vnode->fid.vnode, page->index, offset, to); + + candidate = kzalloc(sizeof(*candidate), GFP_KERNEL); + if (!candidate) + return -ENOMEM; + candidate->vnode = vnode; + candidate->first = candidate->last = page->index; + candidate->offset_first = offset; + candidate->to_last = to; + candidate->usage = 1; + candidate->state = AFS_WBACK_PENDING; + init_waitqueue_head(&candidate->waitq); + + if (!PageUptodate(page)) { + _debug("not up to date"); + ret = afs_prepare_page(vnode, page, key, offset, to); + if (ret < 0) { + kfree(candidate); + _leave(" = %d [prep]", ret); + return ret; + } + SetPageUptodate(page); + } + +try_again: + index = page->index; + spin_lock(&vnode->writeback_lock); + + /* see if this page is already pending a writeback under a suitable key + * - if so we can just join onto that one */ + wb = (struct afs_writeback *) page_private(page); + if (wb) { + if (wb->key == key && wb->state == AFS_WBACK_PENDING) + goto subsume_in_current_wb; + goto flush_conflicting_wb; + } + + if (index > 0) { + /* see if we can find an already pending writeback that we can + * append this page to */ + list_for_each_entry(wb, &vnode->writebacks, link) { + if (wb->last == index - 1 && wb->key == key && + wb->state == AFS_WBACK_PENDING) + goto append_to_previous_wb; + } + } + + list_add_tail(&candidate->link, &vnode->writebacks); + candidate->key = key_get(key); + spin_unlock(&vnode->writeback_lock); + SetPagePrivate(page); + set_page_private(page, (unsigned long) candidate); + _leave(" = 0 [new]"); + return 0; + +subsume_in_current_wb: + _debug("subsume"); + ASSERTRANGE(wb->first, <=, index, <=, wb->last); + if (index == wb->first && offset < wb->offset_first) + wb->offset_first = offset; + if (index == wb->last && to > wb->to_last) + wb->to_last = to; + spin_unlock(&vnode->writeback_lock); + kfree(candidate); + _leave(" = 0 [sub]"); + return 0; + +append_to_previous_wb: + _debug("append into %lx-%lx", wb->first, wb->last); + wb->usage++; + wb->last++; + wb->to_last = to; + spin_unlock(&vnode->writeback_lock); + SetPagePrivate(page); + set_page_private(page, (unsigned long) wb); + kfree(candidate); + _leave(" = 0 [app]"); + return 0; + + /* the page is currently bound to another context, so if it's dirty we + * need to flush it before we can use the new context */ +flush_conflicting_wb: + _debug("flush conflict"); + if (wb->state == AFS_WBACK_PENDING) + wb->state = AFS_WBACK_CONFLICTING; + spin_unlock(&vnode->writeback_lock); + if (PageDirty(page)) { + ret = afs_write_back_from_locked_page(wb, page); + if (ret < 0) { + afs_put_writeback(candidate); + _leave(" = %d", ret); + return ret; + } + } + + /* the page holds a ref on the writeback record */ + afs_put_writeback(wb); + set_page_private(page, 0); + ClearPagePrivate(page); + goto try_again; +} + +/* + * finalise part of a write to a page + */ +int afs_commit_write(struct file *file, struct page *page, + unsigned offset, unsigned to) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); + loff_t i_size, maybe_i_size; + + _enter("{%x:%u},{%lx},%u,%u", + vnode->fid.vid, vnode->fid.vnode, page->index, offset, to); + + maybe_i_size = (loff_t) page->index << PAGE_SHIFT; + maybe_i_size += to; + + i_size = i_size_read(&vnode->vfs_inode); + if (maybe_i_size > i_size) { + spin_lock(&vnode->writeback_lock); + i_size = i_size_read(&vnode->vfs_inode); + if (maybe_i_size > i_size) + i_size_write(&vnode->vfs_inode, maybe_i_size); + spin_unlock(&vnode->writeback_lock); + } + + set_page_dirty(page); + + if (PageDirty(page)) + _debug("dirtied"); + + return 0; +} + +/* + * kill all the pages in the given range + */ +static void afs_kill_pages(struct afs_vnode *vnode, bool error, + pgoff_t first, pgoff_t last) +{ + struct pagevec pv; + unsigned count, loop; + + _enter("{%x:%u},%lx-%lx", + vnode->fid.vid, vnode->fid.vnode, first, last); + + pagevec_init(&pv, 0); + + do { + _debug("kill %lx-%lx", first, last); + + count = last - first + 1; + if (count > PAGEVEC_SIZE) + count = PAGEVEC_SIZE; + pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping, + first, count, pv.pages); + ASSERTCMP(pv.nr, ==, count); + + for (loop = 0; loop < count; loop++) { + ClearPageUptodate(pv.pages[loop]); + if (error) + SetPageError(pv.pages[loop]); + end_page_writeback(pv.pages[loop]); + } + + __pagevec_release(&pv); + } while (first < last); + + _leave(""); +} + +/* + * synchronously write back the locked page and any subsequent non-locked dirty + * pages also covered by the same writeback record + */ +static int afs_write_back_from_locked_page(struct afs_writeback *wb, + struct page *primary_page) +{ + struct page *pages[8], *page; + unsigned long count; + unsigned n, offset, to; + pgoff_t start, first, last; + int loop, ret; + + _enter(",%lx", primary_page->index); + + count = 1; + if (!clear_page_dirty_for_io(primary_page)) + BUG(); + if (test_set_page_writeback(primary_page)) + BUG(); + + /* find all consecutive lockable dirty pages, stopping when we find a + * page that is not immediately lockable, is not dirty or is missing, + * or we reach the end of the range */ + start = primary_page->index; + if (start >= wb->last) + goto no_more; + start++; + do { + _debug("more %lx [%lx]", start, count); + n = wb->last - start + 1; + if (n > ARRAY_SIZE(pages)) + n = ARRAY_SIZE(pages); + n = find_get_pages_contig(wb->vnode->vfs_inode.i_mapping, + start, n, pages); + _debug("fgpc %u", n); + if (n == 0) + goto no_more; + if (pages[0]->index != start) { + for (n--; n >= 0; n--) + put_page(pages[n]); + goto no_more; + } + + for (loop = 0; loop < n; loop++) { + page = pages[loop]; + if (page->index > wb->last) + break; + if (TestSetPageLocked(page)) + break; + if (!PageDirty(page) || + page_private(page) != (unsigned long) wb) { + unlock_page(page); + break; + } + if (!clear_page_dirty_for_io(page)) + BUG(); + if (test_set_page_writeback(page)) + BUG(); + unlock_page(page); + put_page(page); + } + count += loop; + if (loop < n) { + for (; loop < n; loop++) + put_page(pages[loop]); + goto no_more; + } + + start += loop; + } while (start <= wb->last && count < 65536); + +no_more: + /* we now have a contiguous set of dirty pages, each with writeback set + * and the dirty mark cleared; the first page is locked and must remain + * so, all the rest are unlocked */ + first = primary_page->index; + last = first + count - 1; + + offset = (first == wb->first) ? wb->offset_first : 0; + to = (last == wb->last) ? wb->to_last : PAGE_SIZE; + + _debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to); + + ret = afs_vnode_store_data(wb, first, last, offset, to); + if (ret < 0) { + switch (ret) { + case -EDQUOT: + case -ENOSPC: + set_bit(AS_ENOSPC, + &wb->vnode->vfs_inode.i_mapping->flags); + break; + case -EROFS: + case -EIO: + case -EREMOTEIO: + case -EFBIG: + case -ENOENT: + case -ENOMEDIUM: + case -ENXIO: + afs_kill_pages(wb->vnode, true, first, last); + set_bit(AS_EIO, &wb->vnode->vfs_inode.i_mapping->flags); + break; + case -EACCES: + case -EPERM: + case -ENOKEY: + case -EKEYEXPIRED: + case -EKEYREJECTED: + case -EKEYREVOKED: + afs_kill_pages(wb->vnode, false, first, last); + break; + default: + break; + } + } else { + ret = count; + } + + _leave(" = %d", ret); + return ret; +} + +/* + * write a page back to the server + * - the caller locked the page for us + */ +int afs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct backing_dev_info *bdi = page->mapping->backing_dev_info; + struct afs_writeback *wb; + int ret; + + _enter("{%lx},", page->index); + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || !PageDirty(page)) { + unlock_page(page); + return 0; + } + + wb = (struct afs_writeback *) page_private(page); + ASSERT(wb != NULL); + + ret = afs_write_back_from_locked_page(wb, page); + unlock_page(page); + if (ret < 0) { + _leave(" = %d", ret); + return 0; + } + + wbc->nr_to_write -= ret; + if (wbc->nonblocking && bdi_write_congested(bdi)) + wbc->encountered_congestion = 1; + + _leave(" = 0"); + return 0; +} + +/* + * write a region of pages back to the server + */ +int afs_writepages_region(struct address_space *mapping, + struct writeback_control *wbc, + pgoff_t index, pgoff_t end, pgoff_t *_next) +{ + struct backing_dev_info *bdi = mapping->backing_dev_info; + struct afs_writeback *wb; + struct page *page; + int ret, n; + + _enter(",,%lx,%lx,", index, end); + + do { + n = find_get_pages_tag(mapping, &index, PAGECACHE_TAG_DIRTY, + 1, &page); + if (!n) + break; + + _debug("wback %lx", page->index); + + if (page->index > end) { + *_next = index; + page_cache_release(page); + _leave(" = 0 [%lx]", *_next); + return 0; + } + + /* at this point we hold neither mapping->tree_lock nor lock on + * the page itself: the page may be truncated or invalidated + * (changing page->mapping to NULL), or even swizzled back from + * swapper_space to tmpfs file mapping + */ + lock_page(page); + + if (page->mapping != mapping) { + unlock_page(page); + page_cache_release(page); + continue; + } + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || !PageDirty(page)) { + unlock_page(page); + continue; + } + + wb = (struct afs_writeback *) page_private(page); + ASSERT(wb != NULL); + + spin_lock(&wb->vnode->writeback_lock); + wb->state = AFS_WBACK_WRITING; + spin_unlock(&wb->vnode->writeback_lock); + + ret = afs_write_back_from_locked_page(wb, page); + unlock_page(page); + page_cache_release(page); + if (ret < 0) { + _leave(" = %d", ret); + return ret; + } + + wbc->nr_to_write -= ret; + + if (wbc->nonblocking && bdi_write_congested(bdi)) { + wbc->encountered_congestion = 1; + break; + } + + cond_resched(); + } while (index < end && wbc->nr_to_write > 0); + + *_next = index; + _leave(" = 0 [%lx]", *_next); + return 0; +} + +/* + * write some of the pending data back to the server + */ +int afs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct backing_dev_info *bdi = mapping->backing_dev_info; + pgoff_t start, end, next; + int ret; + + _enter(""); + + if (wbc->nonblocking && bdi_write_congested(bdi)) { + wbc->encountered_congestion = 1; + _leave(" = 0 [congest]"); + return 0; + } + + if (wbc->range_cyclic) { + start = mapping->writeback_index; + end = -1; + ret = afs_writepages_region(mapping, wbc, start, end, &next); + if (start > 0 && wbc->nr_to_write > 0 && ret == 0 && + !(wbc->nonblocking && wbc->encountered_congestion)) + ret = afs_writepages_region(mapping, wbc, 0, start, + &next); + mapping->writeback_index = next; + } else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) { + end = (pgoff_t)(LLONG_MAX >> PAGE_CACHE_SHIFT); + ret = afs_writepages_region(mapping, wbc, 0, end, &next); + if (wbc->nr_to_write > 0) + mapping->writeback_index = next; + } else { + start = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + ret = afs_writepages_region(mapping, wbc, start, end, &next); + } + + _leave(" = %d", ret); + return ret; +} + +/* + * write an inode back + */ +int afs_write_inode(struct inode *inode, int sync) +{ + struct afs_vnode *vnode = AFS_FS_I(inode); + int ret; + + _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); + + ret = 0; + if (sync) { + ret = filemap_fdatawait(inode->i_mapping); + if (ret < 0) + __mark_inode_dirty(inode, I_DIRTY_DATASYNC); + } + + _leave(" = %d", ret); + return ret; +} + +/* + * completion of write to server + */ +void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call) +{ + struct afs_writeback *wb = call->wb; + struct pagevec pv; + unsigned count, loop; + pgoff_t first = call->first, last = call->last; + bool free_wb; + + _enter("{%x:%u},{%lx-%lx}", + vnode->fid.vid, vnode->fid.vnode, first, last); + + ASSERT(wb != NULL); + + pagevec_init(&pv, 0); + + do { + _debug("attach %lx-%lx", first, last); + + count = last - first + 1; + if (count > PAGEVEC_SIZE) + count = PAGEVEC_SIZE; + pv.nr = find_get_pages_contig(call->mapping, first, count, + pv.pages); + ASSERTCMP(pv.nr, ==, count); + + spin_lock(&vnode->writeback_lock); + for (loop = 0; loop < count; loop++) { + struct page *page = pv.pages[loop]; + end_page_writeback(page); + if (page_private(page) == (unsigned long) wb) { + set_page_private(page, 0); + ClearPagePrivate(page); + wb->usage--; + } + } + free_wb = false; + if (wb->usage == 0) { + afs_unlink_writeback(wb); + free_wb = true; + } + spin_unlock(&vnode->writeback_lock); + first += count; + if (free_wb) { + afs_free_writeback(wb); + wb = NULL; + } + + __pagevec_release(&pv); + } while (first < last); + + _leave(""); +} + +/* + * write to an AFS file + */ +ssize_t afs_file_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct dentry *dentry = iocb->ki_filp->f_path.dentry; + struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); + ssize_t result; + size_t count = iov_length(iov, nr_segs); + int ret; + + _enter("{%x.%u},{%zu},%lu,", + vnode->fid.vid, vnode->fid.vnode, count, nr_segs); + + if (IS_SWAPFILE(&vnode->vfs_inode)) { + printk(KERN_INFO + "AFS: Attempt to write to active swap file!\n"); + return -EBUSY; + } + + if (!count) + return 0; + + result = generic_file_aio_write(iocb, iov, nr_segs, pos); + if (IS_ERR_VALUE(result)) { + _leave(" = %zd", result); + return result; + } + + /* return error values for O_SYNC and IS_SYNC() */ + if (IS_SYNC(&vnode->vfs_inode) || iocb->ki_filp->f_flags & O_SYNC) { + ret = afs_fsync(iocb->ki_filp, dentry, 1); + if (ret < 0) + result = ret; + } + + _leave(" = %zd", result); + return result; +} + +/* + * flush the vnode to the fileserver + */ +int afs_writeback_all(struct afs_vnode *vnode) +{ + struct address_space *mapping = vnode->vfs_inode.i_mapping; + struct writeback_control wbc = { + .bdi = mapping->backing_dev_info, + .sync_mode = WB_SYNC_ALL, + .nr_to_write = LONG_MAX, + .for_writepages = 1, + .range_cyclic = 1, + }; + int ret; + + _enter(""); + + ret = mapping->a_ops->writepages(mapping, &wbc); + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + + _leave(" = %d", ret); + return ret; +} + +/* + * flush any dirty pages for this process, and check for write errors. + * - the return status from this call provides a reliable indication of + * whether any write errors occurred for this process. + */ +int afs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct afs_writeback *wb, *xwb; + struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); + int ret; + + _enter("{%x:%u},{n=%s},%d", + vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, + datasync); + + /* use a writeback record as a marker in the queue - when this reaches + * the front of the queue, all the outstanding writes are either + * completed or rejected */ + wb = kzalloc(sizeof(*wb), GFP_KERNEL); + if (!wb) + return -ENOMEM; + wb->vnode = vnode; + wb->first = 0; + wb->last = -1; + wb->offset_first = 0; + wb->to_last = PAGE_SIZE; + wb->usage = 1; + wb->state = AFS_WBACK_SYNCING; + init_waitqueue_head(&wb->waitq); + + spin_lock(&vnode->writeback_lock); + list_for_each_entry(xwb, &vnode->writebacks, link) { + if (xwb->state == AFS_WBACK_PENDING) + xwb->state = AFS_WBACK_CONFLICTING; + } + list_add_tail(&wb->link, &vnode->writebacks); + spin_unlock(&vnode->writeback_lock); + + /* push all the outstanding writebacks to the server */ + ret = afs_writeback_all(vnode); + if (ret < 0) { + afs_put_writeback(wb); + _leave(" = %d [wb]", ret); + return ret; + } + + /* wait for the preceding writes to actually complete */ + ret = wait_event_interruptible(wb->waitq, + wb->state == AFS_WBACK_COMPLETE || + vnode->writebacks.next == &wb->link); + afs_put_writeback(wb); + _leave(" = %d", ret); + return ret; +} -- cgit v1.2.3 From e7498281d3caec1141d7542ec494c4e4a1d404c9 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 9 May 2007 02:33:47 -0700 Subject: Use common cpu_is_xxx() macros on AT91 and AVR32 Several drivers shared between AT91 and AVR32 chips use cpu_is_xxx() to handle CPU-specific differences. Currently, such code needs to be inside #ifdef CONFIG_ARCH_AT91 because the macros don't exist on AVR32. By defining the same macros on both AT91 and AVR32, these #ifdefs can be eliminated. Since the macros will evaluate to a constant value for CPUs that aren't supported by the current architecture, any code that is only needed on AT91 will be optimized away on AVR32 and vice versa. Signed-off-by: Haavard Skinnemoen Cc: David Brownell Acked-by: Andrew Victor Cc: Nicolas Ferre Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm/arch-at91/cpu.h | 6 ++++++ include/asm-avr32/arch-at32ap/cpu.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 include/asm-avr32/arch-at32ap/cpu.h diff --git a/include/asm-arm/arch-at91/cpu.h b/include/asm-arm/arch-at91/cpu.h index d464ca58cdb..7ef4eebe9f8 100644 --- a/include/asm-arm/arch-at91/cpu.h +++ b/include/asm-arm/arch-at91/cpu.h @@ -68,4 +68,10 @@ static inline unsigned long at91_arch_identify(void) #define cpu_is_at91sam9263() (0) #endif +/* + * Since this is ARM, we will never run on any AVR32 CPU. But these + * definitions may reduce clutter in common drivers. + */ +#define cpu_is_at32ap7000() (0) + #endif diff --git a/include/asm-avr32/arch-at32ap/cpu.h b/include/asm-avr32/arch-at32ap/cpu.h new file mode 100644 index 00000000000..2bdc5bd6f79 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/cpu.h @@ -0,0 +1,33 @@ +/* + * AVR32 and (fake) AT91 CPU identification + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARCH_CPU_H +#define __ASM_ARCH_CPU_H + +/* + * Only AT32AP7000 is defined for now. We can identify the specific + * chip at runtime, but I'm not sure if it's really worth it. + */ +#ifdef CONFIG_CPU_AT32AP7000 +# define cpu_is_at32ap7000() (1) +#else +# define cpu_is_at32ap7000() (0) +#endif + +/* + * Since this is AVR32, we will never run on any AT91 CPU. But these + * definitions may reduce clutter in common drivers. + */ +#define cpu_is_at91rm9200() (0) +#define cpu_is_at91sam9xe() (0) +#define cpu_is_at91sam9260() (0) +#define cpu_is_at91sam9261() (0) +#define cpu_is_at91sam9263() (0) + +#endif /* __ASM_ARCH_CPU_H */ -- cgit v1.2.3 From a1e3cf418fc1e6b13bdc472ffb60bd02735e41a6 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 9 May 2007 02:33:50 -0700 Subject: atmel_spi: remove unnecessary (and wrong) #ifdefs Now that the cpu_is_xxx() macros are available both on AVR32 and AT91, we can remove a couple of #ifdefs from this driver. One of them is actually wrong -- new_1 should be set on AVR32 but isn't. This causes the bus clock to run at twice the speed it is configured to. Signed-off-by: Haavard Skinnemoen Cc: David Brownell Acked-by: Andrew Victor Cc: Nicolas Ferre Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/atmel_spi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 66e7bc98579..1d8a2f6bb8e 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -22,10 +22,7 @@ #include #include #include - -#ifdef CONFIG_ARCH_AT91 #include -#endif #include "atmel_spi.h" @@ -552,10 +549,8 @@ static int __init atmel_spi_probe(struct platform_device *pdev) goto out_free_buffer; as->irq = irq; as->clk = clk; -#ifdef CONFIG_ARCH_AT91 if (!cpu_is_at91rm9200()) as->new_1 = 1; -#endif ret = request_irq(irq, atmel_spi_interrupt, 0, pdev->dev.bus_id, master); -- cgit v1.2.3 From e18f3ffb9c3ddfc1b4ad8f38f5f2acae8c16f0c9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:50 -0700 Subject: schedule_on_each_cpu(): use preempt_disable() We take workqueue_mutex in there to keep CPU hotplug away. But preempt_disable() will suffice for that. Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b6fa5e63085..1ea4bcb8697 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -638,7 +638,7 @@ int schedule_on_each_cpu(work_func_t func) if (!works) return -ENOMEM; - mutex_lock(&workqueue_mutex); + preempt_disable(); /* CPU hotplug */ for_each_online_cpu(cpu) { struct work_struct *work = per_cpu_ptr(works, cpu); @@ -646,7 +646,7 @@ int schedule_on_each_cpu(work_func_t func) set_bit(WORK_STRUCT_PENDING, work_data_bits(work)); __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), work); } - mutex_unlock(&workqueue_mutex); + preempt_enable(); flush_workqueue(keventd_wq); free_percpu(works); return 0; -- cgit v1.2.3 From fc2e4d70410546307344821eed6fd23803a45286 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:33:51 -0700 Subject: reimplement flush_workqueue() Remove ->remove_sequence, ->insert_sequence, and ->work_done from struct cpu_workqueue_struct. To implement flush_workqueue() we can queue a barrier work on each CPU and wait for its completition. The barrier is queued under workqueue_mutex to ensure that per cpu wq->cpu_wq is alive, we drop this mutex before going to sleep. If CPU goes down while we are waiting for completition, take_over_work() will move the barrier on another CPU, and the handler will wake up us eventually. Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 70 ++++++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 1ea4bcb8697..b7bb37ab03b 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -36,23 +36,13 @@ /* * The per-CPU workqueue (if single thread, we always use the first * possible cpu). - * - * The sequence counters are for flush_scheduled_work(). It wants to wait - * until all currently-scheduled works are completed, but it doesn't - * want to be livelocked by new, incoming ones. So it waits until - * remove_sequence is >= the insert_sequence which pertained when - * flush_scheduled_work() was called. */ struct cpu_workqueue_struct { spinlock_t lock; - long remove_sequence; /* Least-recently added (next to run) */ - long insert_sequence; /* Next to add */ - struct list_head worklist; wait_queue_head_t more_work; - wait_queue_head_t work_done; struct workqueue_struct *wq; struct task_struct *thread; @@ -138,8 +128,6 @@ static int __run_work(struct cpu_workqueue_struct *cwq, struct work_struct *work f(work); spin_lock_irqsave(&cwq->lock, flags); - cwq->remove_sequence++; - wake_up(&cwq->work_done); ret = 1; } spin_unlock_irqrestore(&cwq->lock, flags); @@ -187,7 +175,6 @@ static void __queue_work(struct cpu_workqueue_struct *cwq, spin_lock_irqsave(&cwq->lock, flags); set_wq_data(work, cwq); list_add_tail(&work->entry, &cwq->worklist); - cwq->insert_sequence++; wake_up(&cwq->more_work); spin_unlock_irqrestore(&cwq->lock, flags); } @@ -338,8 +325,6 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) } spin_lock_irqsave(&cwq->lock, flags); - cwq->remove_sequence++; - wake_up(&cwq->work_done); } cwq->run_depth--; spin_unlock_irqrestore(&cwq->lock, flags); @@ -394,6 +379,25 @@ static int worker_thread(void *__cwq) return 0; } +struct wq_barrier { + struct work_struct work; + struct completion done; +}; + +static void wq_barrier_func(struct work_struct *work) +{ + struct wq_barrier *barr = container_of(work, struct wq_barrier, work); + complete(&barr->done); +} + +static inline void init_wq_barrier(struct wq_barrier *barr) +{ + INIT_WORK(&barr->work, wq_barrier_func); + __set_bit(WORK_STRUCT_PENDING, work_data_bits(&barr->work)); + + init_completion(&barr->done); +} + static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) { if (cwq->thread == current) { @@ -401,23 +405,18 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) * Probably keventd trying to flush its own queue. So simply run * it by hand rather than deadlocking. */ + mutex_unlock(&workqueue_mutex); run_workqueue(cwq); + mutex_lock(&workqueue_mutex); } else { - DEFINE_WAIT(wait); - long sequence_needed; + struct wq_barrier barr; - spin_lock_irq(&cwq->lock); - sequence_needed = cwq->insert_sequence; + init_wq_barrier(&barr); + __queue_work(cwq, &barr.work); - while (sequence_needed - cwq->remove_sequence > 0) { - prepare_to_wait(&cwq->work_done, &wait, - TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&cwq->lock); - schedule(); - spin_lock_irq(&cwq->lock); - } - finish_wait(&cwq->work_done, &wait); - spin_unlock_irq(&cwq->lock); + mutex_unlock(&workqueue_mutex); + wait_for_completion(&barr.done); + mutex_lock(&workqueue_mutex); } } @@ -428,29 +427,25 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) * Forces execution of the workqueue and blocks until its completion. * This is typically used in driver shutdown handlers. * - * This function will sample each workqueue's current insert_sequence number and - * will sleep until the head sequence is greater than or equal to that. This - * means that we sleep until all works which were queued on entry have been - * handled, but we are not livelocked by new incoming ones. + * We sleep until all works which were queued on entry have been handled, + * but we are not livelocked by new incoming ones. * * This function used to run the workqueues itself. Now we just wait for the * helper threads to do it. */ void fastcall flush_workqueue(struct workqueue_struct *wq) { - might_sleep(); - + mutex_lock(&workqueue_mutex); if (is_single_threaded(wq)) { /* Always use first cpu's area. */ flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); } else { int cpu; - mutex_lock(&workqueue_mutex); for_each_online_cpu(cpu) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); - mutex_unlock(&workqueue_mutex); } + mutex_unlock(&workqueue_mutex); } EXPORT_SYMBOL_GPL(flush_workqueue); @@ -463,12 +458,9 @@ static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, spin_lock_init(&cwq->lock); cwq->wq = wq; cwq->thread = NULL; - cwq->insert_sequence = 0; - cwq->remove_sequence = 0; cwq->freezeable = freezeable; INIT_LIST_HEAD(&cwq->worklist); init_waitqueue_head(&cwq->more_work); - init_waitqueue_head(&cwq->work_done); if (is_single_threaded(wq)) p = kthread_create(worker_thread, cwq, "%s", wq->name); -- cgit v1.2.3 From b89deed32ccc96098bd6bc953c64bba6b847774f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:33:52 -0700 Subject: implement flush_work() A basic problem with flush_scheduled_work() is that it blocks behind _all_ presently-queued works, rather than just the work whcih the caller wants to flush. If the caller holds some lock, and if one of the queued work happens to want that lock as well then accidental deadlocks can occur. One example of this is the phy layer: it wants to flush work while holding rtnl_lock(). But if a linkwatch event happens to be queued, the phy code will deadlock because the linkwatch callback function takes rtnl_lock. So we implement a new function which will flush a *single* work - just the one which the caller wants to free up. Thus we avoid the accidental deadlocks which can arise from unrelated subsystems' callbacks taking shared locks. flush_work() non-blockingly dequeues the work_struct which we want to kill, then it waits for its handler to complete on all CPUs. Add ->current_work to the "struct cpu_workqueue_struct", it points to currently running "struct work_struct". When flush_work(work) detects ->current_work == work, it inserts a barrier at the _head_ of ->worklist (and thus right _after_ that work) and waits for completition. This means that the next work fired on that CPU will be this barrier, or another barrier queued by concurrent flush_work(), so the caller of flush_work() will be woken before any "regular" work has a chance to run. When wait_on_work() unlocks workqueue_mutex (or whatever we choose to protect against CPU hotplug), CPU may go away. But in that case take_over_work() will move a barrier we queued to another CPU, it will be fired sometime, and wait_on_work() will be woken. Actually, we are doing cleanup_workqueue_thread()->kthread_stop() before take_over_work(), so cwq->thread should complete its ->worklist (and thus the barrier), because currently we don't check kthread_should_stop() in run_workqueue(). But even if we did, everything should be ok. [akpm@osdl.org: cleanup] [akpm@osdl.org: add flush_work_keventd() wrapper] Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 4 +- kernel/workqueue.c | 95 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f16ba1e0687..26a70992dec 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -178,6 +178,8 @@ extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, struct delay extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); +extern void flush_work(struct workqueue_struct *wq, struct work_struct *work); +extern void flush_work_keventd(struct work_struct *work); extern int FASTCALL(schedule_work(struct work_struct *work)); extern int FASTCALL(run_scheduled_work(struct work_struct *work)); @@ -199,7 +201,7 @@ int execute_in_process_context(work_func_t fn, struct execute_work *); * Kill off a pending schedule_delayed_work(). Note that the work callback * function may still be running on return from cancel_delayed_work(), unless * it returns 1 and the work doesn't re-arm itself. Run flush_workqueue() or - * cancel_work_sync() to wait on it. + * flush_work() or cancel_work_sync() to wait on it. */ static inline int cancel_delayed_work(struct delayed_work *work) { diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b7bb37ab03b..918d55267a1 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -46,6 +46,7 @@ struct cpu_workqueue_struct { struct workqueue_struct *wq; struct task_struct *thread; + struct work_struct *current_work; int run_depth; /* Detect run_workqueue() recursion depth */ @@ -120,6 +121,7 @@ static int __run_work(struct cpu_workqueue_struct *cwq, struct work_struct *work && work_pending(work) && !list_empty(&work->entry)) { work_func_t f = work->func; + cwq->current_work = work; list_del_init(&work->entry); spin_unlock_irqrestore(&cwq->lock, flags); @@ -128,6 +130,7 @@ static int __run_work(struct cpu_workqueue_struct *cwq, struct work_struct *work f(work); spin_lock_irqsave(&cwq->lock, flags); + cwq->current_work = NULL; ret = 1; } spin_unlock_irqrestore(&cwq->lock, flags); @@ -166,6 +169,17 @@ int fastcall run_scheduled_work(struct work_struct *work) } EXPORT_SYMBOL(run_scheduled_work); +static void insert_work(struct cpu_workqueue_struct *cwq, + struct work_struct *work, int tail) +{ + set_wq_data(work, cwq); + if (tail) + list_add_tail(&work->entry, &cwq->worklist); + else + list_add(&work->entry, &cwq->worklist); + wake_up(&cwq->more_work); +} + /* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) @@ -173,9 +187,7 @@ static void __queue_work(struct cpu_workqueue_struct *cwq, unsigned long flags; spin_lock_irqsave(&cwq->lock, flags); - set_wq_data(work, cwq); - list_add_tail(&work->entry, &cwq->worklist); - wake_up(&cwq->more_work); + insert_work(cwq, work, 1); spin_unlock_irqrestore(&cwq->lock, flags); } @@ -305,6 +317,7 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) struct work_struct, entry); work_func_t f = work->func; + cwq->current_work = work; list_del_init(cwq->worklist.next); spin_unlock_irqrestore(&cwq->lock, flags); @@ -325,6 +338,7 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) } spin_lock_irqsave(&cwq->lock, flags); + cwq->current_work = NULL; } cwq->run_depth--; spin_unlock_irqrestore(&cwq->lock, flags); @@ -449,6 +463,75 @@ void fastcall flush_workqueue(struct workqueue_struct *wq) } EXPORT_SYMBOL_GPL(flush_workqueue); +static void wait_on_work(struct cpu_workqueue_struct *cwq, + struct work_struct *work) +{ + struct wq_barrier barr; + int running = 0; + + spin_lock_irq(&cwq->lock); + if (unlikely(cwq->current_work == work)) { + init_wq_barrier(&barr); + insert_work(cwq, &barr.work, 0); + running = 1; + } + spin_unlock_irq(&cwq->lock); + + if (unlikely(running)) { + mutex_unlock(&workqueue_mutex); + wait_for_completion(&barr.done); + mutex_lock(&workqueue_mutex); + } +} + +/** + * flush_work - block until a work_struct's callback has terminated + * @wq: the workqueue on which the work is queued + * @work: the work which is to be flushed + * + * flush_work() will attempt to cancel the work if it is queued. If the work's + * callback appears to be running, flush_work() will block until it has + * completed. + * + * flush_work() is designed to be used when the caller is tearing down data + * structures which the callback function operates upon. It is expected that, + * prior to calling flush_work(), the caller has arranged for the work to not + * be requeued. + */ +void flush_work(struct workqueue_struct *wq, struct work_struct *work) +{ + struct cpu_workqueue_struct *cwq; + + mutex_lock(&workqueue_mutex); + cwq = get_wq_data(work); + /* Was it ever queued ? */ + if (!cwq) + goto out; + + /* + * This work can't be re-queued, and the lock above protects us + * from take_over_work(), no need to re-check that get_wq_data() + * is still the same when we take cwq->lock. + */ + spin_lock_irq(&cwq->lock); + list_del_init(&work->entry); + work_release(work); + spin_unlock_irq(&cwq->lock); + + if (is_single_threaded(wq)) { + /* Always use first cpu's area. */ + wait_on_work(per_cpu_ptr(wq->cpu_wq, singlethread_cpu), work); + } else { + int cpu; + + for_each_online_cpu(cpu) + wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); + } +out: + mutex_unlock(&workqueue_mutex); +} +EXPORT_SYMBOL_GPL(flush_work); + static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, int cpu, int freezeable) { @@ -650,6 +733,12 @@ void flush_scheduled_work(void) } EXPORT_SYMBOL(flush_scheduled_work); +void flush_work_keventd(struct work_struct *work) +{ + flush_work(keventd_wq, work); +} +EXPORT_SYMBOL(flush_work_keventd); + /** * cancel_rearming_delayed_workqueue - reliably kill off a delayed work whose handler rearms the delayed work. * @wq: the controlling workqueue structure -- cgit v1.2.3 From edab2516a6c1752e8e5e3d55727cabf12346e5df Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:53 -0700 Subject: flush_workqueue(): use preempt_disable to hold off cpu hotplug Cc: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: Gautham Shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 918d55267a1..5176d51bcc2 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -419,18 +419,22 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) * Probably keventd trying to flush its own queue. So simply run * it by hand rather than deadlocking. */ - mutex_unlock(&workqueue_mutex); + preempt_enable(); + /* + * We can still touch *cwq here because we are keventd, and + * hot-unplug will be waiting us to exit. + */ run_workqueue(cwq); - mutex_lock(&workqueue_mutex); + preempt_disable(); } else { struct wq_barrier barr; init_wq_barrier(&barr); __queue_work(cwq, &barr.work); - mutex_unlock(&workqueue_mutex); + preempt_enable(); /* Can no longer touch *cwq */ wait_for_completion(&barr.done); - mutex_lock(&workqueue_mutex); + preempt_disable(); } } @@ -449,7 +453,7 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) */ void fastcall flush_workqueue(struct workqueue_struct *wq) { - mutex_lock(&workqueue_mutex); + preempt_disable(); /* CPU hotplug */ if (is_single_threaded(wq)) { /* Always use first cpu's area. */ flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); @@ -459,7 +463,7 @@ void fastcall flush_workqueue(struct workqueue_struct *wq) for_each_online_cpu(cpu) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); } - mutex_unlock(&workqueue_mutex); + preempt_enable(); } EXPORT_SYMBOL_GPL(flush_workqueue); -- cgit v1.2.3 From 83c22520c51bf67529367e8237f95c03fe44e2da Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:33:54 -0700 Subject: flush_cpu_workqueue: don't flush an empty ->worklist Now when we have ->current_work we can avoid adding a barrier and waiting for its completition when cwq's queue is empty. Note: this change is also useful if we change flush_workqueue() to also check the dead CPUs. Signed-off-by: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: Gautham Shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5176d51bcc2..5ecf4984e38 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -404,12 +404,15 @@ static void wq_barrier_func(struct work_struct *work) complete(&barr->done); } -static inline void init_wq_barrier(struct wq_barrier *barr) +static void insert_wq_barrier(struct cpu_workqueue_struct *cwq, + struct wq_barrier *barr, int tail) { INIT_WORK(&barr->work, wq_barrier_func); __set_bit(WORK_STRUCT_PENDING, work_data_bits(&barr->work)); init_completion(&barr->done); + + insert_work(cwq, &barr->work, tail); } static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) @@ -428,13 +431,20 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) preempt_disable(); } else { struct wq_barrier barr; + int active = 0; - init_wq_barrier(&barr); - __queue_work(cwq, &barr.work); + spin_lock_irq(&cwq->lock); + if (!list_empty(&cwq->worklist) || cwq->current_work != NULL) { + insert_wq_barrier(cwq, &barr, 1); + active = 1; + } + spin_unlock_irq(&cwq->lock); - preempt_enable(); /* Can no longer touch *cwq */ - wait_for_completion(&barr.done); - preempt_disable(); + if (active) { + preempt_enable(); + wait_for_completion(&barr.done); + preempt_disable(); + } } } @@ -475,8 +485,7 @@ static void wait_on_work(struct cpu_workqueue_struct *cwq, spin_lock_irq(&cwq->lock); if (unlikely(cwq->current_work == work)) { - init_wq_barrier(&barr); - insert_work(cwq, &barr.work, 0); + insert_wq_barrier(cwq, &barr, 0); running = 1; } spin_unlock_irq(&cwq->lock); -- cgit v1.2.3 From a9df62c7585e6caa1e7d2425b2b14460ec3afc20 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:54 -0700 Subject: aio: use flush_work() Migrate AIO over to use flush_work(). Cc: "Maciej W. Rozycki" Cc: David Howells Cc: Zach Brown Cc: Benjamin LaHaise Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/aio.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index b97ab8028b6..d18690bb03e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -346,10 +346,9 @@ void fastcall exit_aio(struct mm_struct *mm) wait_for_all_aios(ctx); /* - * this is an overkill, but ensures we don't leave - * the ctx on the aio_wq + * Ensure we don't leave the ctx on the aio_wq */ - flush_workqueue(aio_wq); + flush_work(aio_wq, &ctx->wq.work); if (1 != atomic_read(&ctx->users)) printk(KERN_DEBUG @@ -372,7 +371,7 @@ void fastcall __put_ioctx(struct kioctx *ctx) BUG_ON(ctx->reqs_active); cancel_delayed_work(&ctx->wq); - flush_workqueue(aio_wq); + flush_work(aio_wq, &ctx->wq.work); aio_free_ring(ctx); mmdrop(ctx->mm); ctx->mm = NULL; -- cgit v1.2.3 From 19a75d83ffeab004cfcfac64024ad3997bac7220 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:56 -0700 Subject: kblockd: use flush_work Switch the kblockd flushing from a global flush to a more specific flush_work(). (akpm: bypassed maintainers, sorry. There are other patches which depend on this) Cc: "Maciej W. Rozycki" Cc: David Howells Cc: Jens Axboe Cc: Nick Piggin Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/as-iosched.c | 2 +- block/ll_rw_blk.c | 7 +++---- include/linux/blkdev.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/block/as-iosched.c b/block/as-iosched.c index 640aa839d63..109e91b91ff 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -1306,7 +1306,7 @@ static void as_exit_queue(elevator_t *e) struct as_data *ad = e->elevator_data; del_timer_sync(&ad->antic_timer); - kblockd_flush(); + kblockd_flush_work(&ad->antic_work); BUG_ON(!list_empty(&ad->fifo_list[REQ_SYNC])); BUG_ON(!list_empty(&ad->fifo_list[REQ_ASYNC])); diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index d99d402953a..c059767c552 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -1712,7 +1712,6 @@ EXPORT_SYMBOL(blk_stop_queue); void blk_sync_queue(struct request_queue *q) { del_timer_sync(&q->unplug_timer); - kblockd_flush(); } EXPORT_SYMBOL(blk_sync_queue); @@ -3632,11 +3631,11 @@ int kblockd_schedule_work(struct work_struct *work) EXPORT_SYMBOL(kblockd_schedule_work); -void kblockd_flush(void) +void kblockd_flush_work(struct work_struct *work) { - flush_workqueue(kblockd_workqueue); + flush_work(kblockd_workqueue, work); } -EXPORT_SYMBOL(kblockd_flush); +EXPORT_SYMBOL(kblockd_flush_work); int __init blk_dev_init(void) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index a686eabe22d..db5b00a792f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -854,7 +854,7 @@ static inline void put_dev_sector(Sector p) struct work_struct; int kblockd_schedule_work(struct work_struct *work); -void kblockd_flush(void); +void kblockd_flush_work(struct work_struct *work); #define MODULE_ALIAS_BLOCKDEV(major,minor) \ MODULE_ALIAS("block-major-" __stringify(major) "-" __stringify(minor)) -- cgit v1.2.3 From 2b3cb2e778811a1df99e37fd7c359837501ab103 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:57 -0700 Subject: tg3: use flush_work_keventd() Convert tg3 over to flush_work_keventd(). Remove nasty now-unneeded deadlock avoidance logic. (akpm: bypassed maintainers, sorry. There are other patches which depend on this) Cc: "Maciej W. Rozycki" Cc: David Howells Cc: "David S. Miller" Cc: Michael Chan Cc: Jeff Garzik Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/tg3.c | 11 +---------- drivers/net/tg3.h | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index e5e901ecd80..0c0f9c81732 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -3716,10 +3716,8 @@ static void tg3_reset_task(struct work_struct *work) unsigned int restart_timer; tg3_full_lock(tp, 0); - tp->tg3_flags |= TG3_FLAG_IN_RESET_TASK; if (!netif_running(tp->dev)) { - tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK; tg3_full_unlock(tp); return; } @@ -3750,8 +3748,6 @@ static void tg3_reset_task(struct work_struct *work) mod_timer(&tp->timer, jiffies + 1); out: - tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK; - tg3_full_unlock(tp); } @@ -7390,12 +7386,7 @@ static int tg3_close(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); - /* Calling flush_scheduled_work() may deadlock because - * linkwatch_event() may be on the workqueue and it will try to get - * the rtnl_lock which we are holding. - */ - while (tp->tg3_flags & TG3_FLAG_IN_RESET_TASK) - msleep(1); + flush_work_keventd(&tp->reset_task); netif_stop_queue(dev); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 4d334cf5a24..bd9f4f428e5 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2228,7 +2228,7 @@ struct tg3 { #define TG3_FLAG_JUMBO_RING_ENABLE 0x00800000 #define TG3_FLAG_10_100_ONLY 0x01000000 #define TG3_FLAG_PAUSE_AUTONEG 0x02000000 -#define TG3_FLAG_IN_RESET_TASK 0x04000000 + #define TG3_FLAG_40BIT_DMA_BUG 0x08000000 #define TG3_FLAG_BROKEN_CHECKSUMS 0x10000000 #define TG3_FLAG_SUPPORT_MSI 0x20000000 -- cgit v1.2.3 From d9ef8b92887c35f113cb749270530f87961f7a0a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:58 -0700 Subject: e1000: use flush_work_keventd() Switch e1000 over to flush_work_keventd(). This probably fixes a netdev-close versus linkwatch rtnl_lock() deadlock which nobody knew about. (akpm: bypassed maintainers, sorry. There are other patches which depend on this) Cc: "Maciej W. Rozycki" Cc: David Howells Cc: "David S. Miller" Cc: Jeff Garzik Acked-by: Auke Kok Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 3a03a74c060..397e25bdbfe 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1214,7 +1214,7 @@ e1000_remove(struct pci_dev *pdev) int i; #endif - flush_scheduled_work(); + flush_work_keventd(&adapter->reset_task); e1000_release_manageability(adapter); -- cgit v1.2.3 From 67ac58edf771b10f8e89bf9fe1ccf6c9b92ce063 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:33:59 -0700 Subject: libata: use flush_work() (akpm: bypassed maintainers, sorry. There are other patches which depend on this) Cc: "Maciej W. Rozycki" Cc: David Howells Cc: Jeff Garzik Cc: Oleg Nesterov Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/ata/libata-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index a7950885d18..b74e56caba6 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1316,7 +1316,7 @@ void ata_port_flush_task(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); DPRINTK("flush #1\n"); - flush_workqueue(ata_wq); + flush_work(ata_wq, &ap->port_task.work); /* akpm: seems unneeded */ /* * At this point, if a task is running, it's guaranteed to see @@ -1327,7 +1327,7 @@ void ata_port_flush_task(struct ata_port *ap) if (ata_msg_ctl(ap)) ata_port_printk(ap, KERN_DEBUG, "%s: flush #2\n", __FUNCTION__); - flush_workqueue(ata_wq); + flush_work(ata_wq, &ap->port_task.work); } spin_lock_irqsave(ap->lock, flags); @@ -6475,9 +6475,9 @@ void ata_port_detach(struct ata_port *ap) /* Flush hotplug task. The sequence is similar to * ata_port_flush_task(). */ - flush_workqueue(ata_aux_wq); + flush_work(ata_aux_wq, &ap->hotplug_task.work); /* akpm: why? */ cancel_delayed_work(&ap->hotplug_task); - flush_workqueue(ata_aux_wq); + flush_work(ata_aux_wq, &ap->hotplug_task.work); skip_eh: /* remove the associated SCSI host */ -- cgit v1.2.3 From d0758bc334780d70266c1d1b974ed26f740a0819 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:34:00 -0700 Subject: phy: use flush_work_keventd() (akpm: bypassed maintainers, sorry. There are other patches which depend on this) Cc: "Maciej W. Rozycki" Cc: David Howells Cc: Jeff Garzik Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/phy/phy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index eed433d6056..f445c465b14 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -662,10 +662,10 @@ int phy_stop_interrupts(struct phy_device *phydev) phy_error(phydev); /* - * Finish any pending work; we might have been scheduled - * to be called from keventd ourselves, though. + * Finish any pending work; we might have been scheduled to be called + * from keventd ourselves, but flush_work_keventd() handles that. */ - run_scheduled_work(&phydev->phy_queue); + flush_work_keventd(&phydev->phy_queue); free_irq(phydev->irq, phydev); -- cgit v1.2.3 From 7c9cb38302e78d24e37f7d8a2ea7eed4ae5f2fa7 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Wed, 9 May 2007 02:34:01 -0700 Subject: relay: use plain timer instead of delayed work relay doesn't need to use schedule_delayed_work() for waking readers when a simple timer will do. Signed-off-by: Tom Zanussi Cc: Satyam Sharma Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/relay.h | 3 ++- kernel/relay.c | 35 ++++++++++++++++------------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/include/linux/relay.h b/include/linux/relay.h index 759a0f97bec..6cd8c4425fc 100644 --- a/include/linux/relay.h +++ b/include/linux/relay.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -38,7 +39,7 @@ struct rchan_buf size_t subbufs_consumed; /* count of sub-buffers consumed */ struct rchan *chan; /* associated channel */ wait_queue_head_t read_wait; /* reader wait queue */ - struct delayed_work wake_readers; /* reader wake-up work struct */ + struct timer_list timer; /* reader wake-up timer */ struct dentry *dentry; /* channel file dentry */ struct kref kref; /* channel buffer refcount */ struct page **page_array; /* array of current buffer pages */ diff --git a/kernel/relay.c b/kernel/relay.c index 577f251c7e2..e804589c863 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -310,16 +310,13 @@ static struct rchan_callbacks default_channel_callbacks = { /** * wakeup_readers - wake up readers waiting on a channel - * @work: work struct that contains the the channel buffer + * @data: contains the the channel buffer * - * This is the work function used to defer reader waking. The - * reason waking is deferred is that calling directly from write - * causes problems if you're writing from say the scheduler. + * This is the timer function used to defer reader waking. */ -static void wakeup_readers(struct work_struct *work) +static void wakeup_readers(unsigned long data) { - struct rchan_buf *buf = - container_of(work, struct rchan_buf, wake_readers.work); + struct rchan_buf *buf = (struct rchan_buf *)data; wake_up_interruptible(&buf->read_wait); } @@ -337,11 +334,9 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init) if (init) { init_waitqueue_head(&buf->read_wait); kref_init(&buf->kref); - INIT_DELAYED_WORK(&buf->wake_readers, NULL); - } else { - cancel_delayed_work(&buf->wake_readers); - flush_scheduled_work(); - } + setup_timer(&buf->timer, wakeup_readers, (unsigned long)buf); + } else + del_timer_sync(&buf->timer); buf->subbufs_produced = 0; buf->subbufs_consumed = 0; @@ -447,8 +442,7 @@ end: static void relay_close_buf(struct rchan_buf *buf) { buf->finalized = 1; - cancel_delayed_work(&buf->wake_readers); - flush_scheduled_work(); + del_timer_sync(&buf->timer); kref_put(&buf->kref, relay_remove_buf); } @@ -608,11 +602,14 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) buf->dentry->d_inode->i_size += buf->chan->subbuf_size - buf->padding[old_subbuf]; smp_mb(); - if (waitqueue_active(&buf->read_wait)) { - PREPARE_DELAYED_WORK(&buf->wake_readers, - wakeup_readers); - schedule_delayed_work(&buf->wake_readers, 1); - } + if (waitqueue_active(&buf->read_wait)) + /* + * Calling wake_up_interruptible() from here + * will deadlock if we happen to be logging + * from the scheduler (trying to re-grab + * rq->lock), so defer it. + */ + __mod_timer(&buf->timer, jiffies + 1); } old = buf->data; -- cgit v1.2.3 From 6f7cc11aa6c7d5002e16096c7590944daece70ed Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Wed, 9 May 2007 02:34:02 -0700 Subject: Extend notifier_call_chain to count nr_calls made Since 2.6.18-something, the community has been bugged by the problem to provide a clean and a stable mechanism to postpone a cpu-hotplug event as lock_cpu_hotplug was badly broken. This is another proposal towards solving that problem. This one is along the lines of the solution provided in kernel/workqueue.c Instead of having a global mechanism like lock_cpu_hotplug, we allow the subsytems to define their own per-subsystem hot cpu mutexes. These would be taken(released) where ever we are currently calling lock_cpu_hotplug(unlock_cpu_hotplug). Also, in the per-subsystem hotcpu callback function,we take this mutex before we handle any pre-cpu-hotplug events and release it once we finish handling the post-cpu-hotplug events. A standard means for doing this has been provided in [PATCH 2/4] and demonstrated in [PATCH 3/4]. The ordering of these per-subsystem mutexes might still prove to be a problem, but hopefully lockdep should help us get out of that muddle. The patch set to be applied against linux-2.6.19-rc5 is as follows: [PATCH 1/4] : Extend notifier_call_chain with an option to specify the number of notifications to be sent and also count the number of notifications actually sent. [PATCH 2/4] : Define events CPU_LOCK_ACQUIRE and CPU_LOCK_RELEASE and send out notifications for these in _cpu_up and _cpu_down. This would help us standardise the acquire and release of the subsystem locks in the hotcpu callback functions of these subsystems. [PATCH 3/4] : Eliminate lock_cpu_hotplug from kernel/sched.c. [PATCH 4/4] : In workqueue_cpu_callback function, acquire(release) the workqueue_mutex while handling CPU_LOCK_ACQUIRE(CPU_LOCK_RELEASE). If the per-subsystem-locking approach survives the test of time, we can expect a slow phasing out of lock_cpu_hotplug, which has not yet been eliminated in these patches :) This patch: Provide notifier_call_chain with an option to call only a specified number of notifiers and also record the number of call to notifiers made. The need for this enhancement was identified in the post entitled "Slab - Eliminate lock_cpu_hotplug from slab" (http://lkml.org/lkml/2006/10/28/92) by Ravikiran G Thirumalai and Andrew Morton. This patch adds two additional parameters to notifier_call_chain API namely - int nr_to_calls : Number of notifier_functions to be called. The don't care value is -1. - unsigned int *nr_calls : Records the total number of notifier_funtions called by notifier_call_chain. The don't care value is NULL. [michal.k.k.piotrowski@gmail.com: build fix] Credit: Andrew Morton Signed-off-by: Gautham R Shenoy Signed-off-by: Michal Piotrowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/notifier.h | 52 +++++++++++++++------------ kernel/sys.c | 94 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 39 deletions(-) diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 10a43ed0527..e34221bf894 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -112,32 +112,40 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); #ifdef __KERNEL__ -extern int atomic_notifier_chain_register(struct atomic_notifier_head *, - struct notifier_block *); -extern int blocking_notifier_chain_register(struct blocking_notifier_head *, - struct notifier_block *); -extern int raw_notifier_chain_register(struct raw_notifier_head *, - struct notifier_block *); -extern int srcu_notifier_chain_register(struct srcu_notifier_head *, - struct notifier_block *); - -extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, - struct notifier_block *); -extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, - struct notifier_block *); -extern int raw_notifier_chain_unregister(struct raw_notifier_head *, - struct notifier_block *); -extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, - struct notifier_block *); - -extern int atomic_notifier_call_chain(struct atomic_notifier_head *, +extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, + struct notifier_block *nb); +extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh, + struct notifier_block *nb); +extern int raw_notifier_chain_register(struct raw_notifier_head *nh, + struct notifier_block *nb); +extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, + struct notifier_block *nb); + +extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, + struct notifier_block *nb); +extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, + struct notifier_block *nb); +extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh, + struct notifier_block *nb); +extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, + struct notifier_block *nb); + +extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v); -extern int blocking_notifier_call_chain(struct blocking_notifier_head *, +extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, + unsigned long val, void *v, int nr_to_call, int *nr_calls); +extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v); -extern int raw_notifier_call_chain(struct raw_notifier_head *, +extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, + unsigned long val, void *v, int nr_to_call, int *nr_calls); +extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v); -extern int srcu_notifier_call_chain(struct srcu_notifier_head *, +extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, + unsigned long val, void *v, int nr_to_call, int *nr_calls); +extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v); +extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, + unsigned long val, void *v, int nr_to_call, int *nr_calls); #define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_OK 0x0001 /* Suits me */ diff --git a/kernel/sys.c b/kernel/sys.c index 8ecfe347377..d4985df21b6 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -134,19 +134,39 @@ static int notifier_chain_unregister(struct notifier_block **nl, return -ENOENT; } +/** + * notifier_call_chain - Informs the registered notifiers about an event. + * @nl: Pointer to head of the blocking notifier chain + * @val: Value passed unmodified to notifier function + * @v: Pointer passed unmodified to notifier function + * @nr_to_call: Number of notifier functions to be called. Don't care + * value of this parameter is -1. + * @nr_calls: Records the number of notifications sent. Don't care + * value of this field is NULL. + * @returns: notifier_call_chain returns the value returned by the + * last notifier function called. + */ + static int __kprobes notifier_call_chain(struct notifier_block **nl, - unsigned long val, void *v) + unsigned long val, void *v, + int nr_to_call, int *nr_calls) { int ret = NOTIFY_DONE; struct notifier_block *nb, *next_nb; nb = rcu_dereference(*nl); - while (nb) { + + while (nb && nr_to_call) { next_nb = rcu_dereference(nb->next); ret = nb->notifier_call(nb, val, v); + + if (nr_calls) + (*nr_calls)++; + if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; nb = next_nb; + nr_to_call--; } return ret; } @@ -205,10 +225,12 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); /** - * atomic_notifier_call_chain - Call functions in an atomic notifier chain + * __atomic_notifier_call_chain - Call functions in an atomic notifier chain * @nh: Pointer to head of the atomic notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function + * @nr_to_call: See the comment for notifier_call_chain. + * @nr_calls: See the comment for notifier_call_chain. * * Calls each function in a notifier chain in turn. The functions * run in an atomic context, so they must not block. @@ -222,19 +244,27 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); * of the last notifier function called. */ -int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, - unsigned long val, void *v) +int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh, + unsigned long val, void *v, + int nr_to_call, int *nr_calls) { int ret; rcu_read_lock(); - ret = notifier_call_chain(&nh->head, val, v); + ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); rcu_read_unlock(); return ret; } -EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); +EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain); + +int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, + unsigned long val, void *v) +{ + return __atomic_notifier_call_chain(nh, val, v, -1, NULL); +} +EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); /* * Blocking notifier chain routines. All access to the chain is * synchronized by an rwsem. @@ -304,10 +334,12 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); /** - * blocking_notifier_call_chain - Call functions in a blocking notifier chain + * __blocking_notifier_call_chain - Call functions in a blocking notifier chain * @nh: Pointer to head of the blocking notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function + * @nr_to_call: See comment for notifier_call_chain. + * @nr_calls: See comment for notifier_call_chain. * * Calls each function in a notifier chain in turn. The functions * run in a process context, so they are allowed to block. @@ -320,8 +352,9 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); * of the last notifier function called. */ -int blocking_notifier_call_chain(struct blocking_notifier_head *nh, - unsigned long val, void *v) +int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, + unsigned long val, void *v, + int nr_to_call, int *nr_calls) { int ret = NOTIFY_DONE; @@ -332,12 +365,19 @@ int blocking_notifier_call_chain(struct blocking_notifier_head *nh, */ if (rcu_dereference(nh->head)) { down_read(&nh->rwsem); - ret = notifier_call_chain(&nh->head, val, v); + ret = notifier_call_chain(&nh->head, val, v, nr_to_call, + nr_calls); up_read(&nh->rwsem); } return ret; } +EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain); +int blocking_notifier_call_chain(struct blocking_notifier_head *nh, + unsigned long val, void *v) +{ + return __blocking_notifier_call_chain(nh, val, v, -1, NULL); +} EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); /* @@ -383,10 +423,12 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh, EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); /** - * raw_notifier_call_chain - Call functions in a raw notifier chain + * __raw_notifier_call_chain - Call functions in a raw notifier chain * @nh: Pointer to head of the raw notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function + * @nr_to_call: See comment for notifier_call_chain. + * @nr_calls: See comment for notifier_call_chain * * Calls each function in a notifier chain in turn. The functions * run in an undefined context. @@ -400,10 +442,19 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); * of the last notifier function called. */ +int __raw_notifier_call_chain(struct raw_notifier_head *nh, + unsigned long val, void *v, + int nr_to_call, int *nr_calls) +{ + return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); +} + +EXPORT_SYMBOL_GPL(__raw_notifier_call_chain); + int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v) { - return notifier_call_chain(&nh->head, val, v); + return __raw_notifier_call_chain(nh, val, v, -1, NULL); } EXPORT_SYMBOL_GPL(raw_notifier_call_chain); @@ -478,10 +529,12 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); /** - * srcu_notifier_call_chain - Call functions in an SRCU notifier chain + * __srcu_notifier_call_chain - Call functions in an SRCU notifier chain * @nh: Pointer to head of the SRCU notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function + * @nr_to_call: See comment for notifier_call_chain. + * @nr_calls: See comment for notifier_call_chain * * Calls each function in a notifier chain in turn. The functions * run in a process context, so they are allowed to block. @@ -494,18 +547,25 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); * of the last notifier function called. */ -int srcu_notifier_call_chain(struct srcu_notifier_head *nh, - unsigned long val, void *v) +int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, + unsigned long val, void *v, + int nr_to_call, int *nr_calls) { int ret; int idx; idx = srcu_read_lock(&nh->srcu); - ret = notifier_call_chain(&nh->head, val, v); + ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); srcu_read_unlock(&nh->srcu, idx); return ret; } +EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain); +int srcu_notifier_call_chain(struct srcu_notifier_head *nh, + unsigned long val, void *v) +{ + return __srcu_notifier_call_chain(nh, val, v, -1, NULL); +} EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); /** -- cgit v1.2.3 From baaca49f415b25fdbe2a8f3c22b39929e450fbfd Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Wed, 9 May 2007 02:34:03 -0700 Subject: Define and use new events,CPU_LOCK_ACQUIRE and CPU_LOCK_RELEASE This is an attempt to provide an alternate mechanism for postponing a hotplug event instead of using a global mechanism like lock_cpu_hotplug. The proposal is to add two new events namely CPU_LOCK_ACQUIRE and CPU_LOCK_RELEASE. The notification for these two events would be sent out before and after a cpu_hotplug event respectively. During the CPU_LOCK_ACQUIRE event, a cpu-hotplug-aware subsystem is supposed to acquire any per-subsystem hotcpu mutex ( Eg. workqueue_mutex in kernel/workqueue.c ). During the CPU_LOCK_RELEASE release event the cpu-hotplug-aware subsystem is supposed to release the per-subsystem hotcpu mutex. The reasons for defining new events as opposed to reusing the existing events like CPU_UP_PREPARE/CPU_UP_FAILED/CPU_ONLINE for locking/unlocking of per-subsystem hotcpu mutexes are as follow: - CPU_LOCK_ACQUIRE: All hotcpu mutexes are taken before subsystems start handling pre-hotplug events like CPU_UP_PREPARE/CPU_DOWN_PREPARE etc, thus ensuring a clean handling of these events. - CPU_LOCK_RELEASE: The hotcpu mutexes will be released only after all subsystems have handled post-hotplug events like CPU_DOWN_FAILED, CPU_DEAD,CPU_ONLINE etc thereby ensuring that there are no subsequent clashes amongst the interdependent subsystems after a cpu hotplugs. This patch also uses __raw_notifier_call chain in _cpu_up to take care of the dependency between the two consequetive calls to raw_notifier_call_chain. [akpm@linux-foundation.org: fix a bug] Signed-off-by: Gautham R Shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/notifier.h | 2 ++ kernel/cpu.c | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/linux/notifier.h b/include/linux/notifier.h index e34221bf894..1903e5490c0 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -194,6 +194,8 @@ extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, #define CPU_DOWN_PREPARE 0x0005 /* CPU (unsigned)v going down */ #define CPU_DOWN_FAILED 0x0006 /* CPU (unsigned)v NOT going down */ #define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ +#define CPU_LOCK_ACQUIRE 0x0008 /* Acquire all hotcpu locks */ +#define CPU_LOCK_RELEASE 0x0009 /* Release all hotcpu locks */ #endif /* __KERNEL__ */ #endif /* _LINUX_NOTIFIER_H */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 36e70845cfc..48810498b35 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -132,12 +132,15 @@ static int _cpu_down(unsigned int cpu) if (!cpu_online(cpu)) return -EINVAL; + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, + (void *)(long)cpu); err = raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, (void *)(long)cpu); if (err == NOTIFY_BAD) { printk("%s: attempt to take down CPU %u failed\n", __FUNCTION__, cpu); - return -EINVAL; + err = -EINVAL; + goto out_release; } /* Ensure that we are not runnable on dying cpu */ @@ -185,6 +188,9 @@ out_thread: err = kthread_stop(p); out_allowed: set_cpus_allowed(current, old_allowed); +out_release: + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_RELEASE, + (void *)(long)cpu); return err; } @@ -206,13 +212,15 @@ int cpu_down(unsigned int cpu) /* Requires cpu_add_remove_lock to be held */ static int __cpuinit _cpu_up(unsigned int cpu) { - int ret; + int ret, nr_calls = 0; void *hcpu = (void *)(long)cpu; if (cpu_online(cpu) || !cpu_present(cpu)) return -EINVAL; - ret = raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, hcpu); + ret = __raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu, + -1, &nr_calls); if (ret == NOTIFY_BAD) { printk("%s: attempt to bring up CPU %u failed\n", __FUNCTION__, cpu); @@ -233,8 +241,9 @@ static int __cpuinit _cpu_up(unsigned int cpu) out_notify: if (ret != 0) - raw_notifier_call_chain(&cpu_chain, - CPU_UP_CANCELED, hcpu); + __raw_notifier_call_chain(&cpu_chain, + CPU_UP_CANCELED, hcpu, nr_calls, NULL); + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_RELEASE, hcpu); return ret; } -- cgit v1.2.3 From 5be9361cdff17fc76fa0c3e262ead94158555f16 Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Wed, 9 May 2007 02:34:04 -0700 Subject: Eliminate lock_cpu_hotplug in kernel/schedc Eliminate lock_cpu_hotplug from kernel/sched.c and use sched_hotcpu_mutex instead to postpone a hotplug event. In the migration_call hotcpu callback function, take sched_hotcpu_mutex while handling the event CPU_LOCK_ACQUIRE and release it while handling CPU_LOCK_RELEASE event. [akpm@linux-foundation.org: fix deadlock] Signed-off-by: Gautham R Shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sched.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/kernel/sched.c b/kernel/sched.c index 66bd7ff23f1..fe1a9c2b855 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -305,6 +305,7 @@ struct rq { }; static DEFINE_PER_CPU(struct rq, runqueues) ____cacheline_aligned_in_smp; +static DEFINE_MUTEX(sched_hotcpu_mutex); static inline int cpu_of(struct rq *rq) { @@ -4520,13 +4521,13 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask) struct task_struct *p; int retval; - lock_cpu_hotplug(); + mutex_lock(&sched_hotcpu_mutex); read_lock(&tasklist_lock); p = find_process_by_pid(pid); if (!p) { read_unlock(&tasklist_lock); - unlock_cpu_hotplug(); + mutex_unlock(&sched_hotcpu_mutex); return -ESRCH; } @@ -4553,7 +4554,7 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask) out_unlock: put_task_struct(p); - unlock_cpu_hotplug(); + mutex_unlock(&sched_hotcpu_mutex); return retval; } @@ -4610,7 +4611,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask) struct task_struct *p; int retval; - lock_cpu_hotplug(); + mutex_lock(&sched_hotcpu_mutex); read_lock(&tasklist_lock); retval = -ESRCH; @@ -4626,7 +4627,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask) out_unlock: read_unlock(&tasklist_lock); - unlock_cpu_hotplug(); + mutex_unlock(&sched_hotcpu_mutex); if (retval) return retval; @@ -5388,6 +5389,10 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) struct rq *rq; switch (action) { + case CPU_LOCK_ACQUIRE: + mutex_lock(&sched_hotcpu_mutex); + break; + case CPU_UP_PREPARE: p = kthread_create(migration_thread, hcpu, "migration/%d",cpu); if (IS_ERR(p)) @@ -5433,7 +5438,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) BUG_ON(rq->nr_running != 0); /* No need to migrate the tasks: it was best-effort if - * they didn't do lock_cpu_hotplug(). Just wake up + * they didn't take sched_hotcpu_mutex. Just wake up * the requestors. */ spin_lock_irq(&rq->lock); while (!list_empty(&rq->migration_queue)) { @@ -5447,6 +5452,9 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) spin_unlock_irq(&rq->lock); break; #endif + case CPU_LOCK_RELEASE: + mutex_unlock(&sched_hotcpu_mutex); + break; } return NOTIFY_OK; } @@ -6822,10 +6830,10 @@ int arch_reinit_sched_domains(void) { int err; - lock_cpu_hotplug(); + mutex_lock(&sched_hotcpu_mutex); detach_destroy_domains(&cpu_online_map); err = arch_init_sched_domains(&cpu_online_map); - unlock_cpu_hotplug(); + mutex_unlock(&sched_hotcpu_mutex); return err; } @@ -6930,12 +6938,12 @@ void __init sched_init_smp(void) { cpumask_t non_isolated_cpus; - lock_cpu_hotplug(); + mutex_lock(&sched_hotcpu_mutex); arch_init_sched_domains(&cpu_online_map); cpus_andnot(non_isolated_cpus, cpu_possible_map, cpu_isolated_map); if (cpus_empty(non_isolated_cpus)) cpu_set(smp_processor_id(), non_isolated_cpus); - unlock_cpu_hotplug(); + mutex_unlock(&sched_hotcpu_mutex); /* XXX: Theoretical race here - CPU may be hotplugged now */ hotcpu_notifier(update_sched_domains, 0); -- cgit v1.2.3 From e7407dcc69e077ac34a527842db916abfbc458df Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 9 May 2007 02:34:04 -0700 Subject: call cpu_chain with CPU_DOWN_FAILED if CPU_DOWN_PREPARE failed This makes cpu hotplug symmetrical: if CPU_UP_PREPARE fails we get CPU_UP_CANCELED, so we can undo what ever happened on PREPARE. The same should happen for CPU_DOWN_PREPARE. [akpm@linux-foundation.org: fix for reduce-size-of-task_struct-on-64-bit-machines] Cc: Srivatsa Vaddagiri Cc: Gautham Shenoy Signed-off-by: Heiko Carstens Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/cpu.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 48810498b35..1a823944e97 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -97,7 +97,7 @@ static inline void check_for_tasks(int cpu) (!cputime_eq(p->utime, cputime_zero) || !cputime_eq(p->stime, cputime_zero))) printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d\ - (state = %ld, flags = %lx) \n", + (state = %ld, flags = %x) \n", p->comm, p->pid, cpu, p->state, p->flags); } write_unlock_irq(&tasklist_lock); @@ -122,9 +122,10 @@ static int take_cpu_down(void *unused) /* Requires cpu_add_remove_lock to be held */ static int _cpu_down(unsigned int cpu) { - int err; + int err, nr_calls = 0; struct task_struct *p; cpumask_t old_allowed, tmp; + void *hcpu = (void *)(long)cpu; if (num_online_cpus() == 1) return -EBUSY; @@ -132,11 +133,12 @@ static int _cpu_down(unsigned int cpu) if (!cpu_online(cpu)) return -EINVAL; - raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, - (void *)(long)cpu); - err = raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, - (void *)(long)cpu); + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, hcpu); + err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, + hcpu, -1, &nr_calls); if (err == NOTIFY_BAD) { + __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, hcpu, + nr_calls, NULL); printk("%s: attempt to take down CPU %u failed\n", __FUNCTION__, cpu); err = -EINVAL; @@ -156,7 +158,7 @@ static int _cpu_down(unsigned int cpu) if (IS_ERR(p) || cpu_online(cpu)) { /* CPU didn't die: tell everyone. Can't complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, - (void *)(long)cpu) == NOTIFY_BAD) + hcpu) == NOTIFY_BAD) BUG(); if (IS_ERR(p)) { @@ -178,8 +180,7 @@ static int _cpu_down(unsigned int cpu) put_cpu(); /* CPU is completely dead: tell everyone. Too late to complain. */ - if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD, - (void *)(long)cpu) == NOTIFY_BAD) + if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD, hcpu) == NOTIFY_BAD) BUG(); check_for_tasks(cpu); -- cgit v1.2.3 From 38c3bd96a0a7d375e12f173c34fbebf9f153cb56 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 9 May 2007 02:34:05 -0700 Subject: slab: use CPU_LOCK_[ACQUIRE|RELEASE] Looks like this was forgotten when CPU_LOCK_[ACQUIRE|RELEASE] was introduced. Cc: Pekka Enberg Cc: Srivatsa Vaddagiri Cc: Gautham Shenoy Signed-off-by: Heiko Carstens Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slab.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 01262d26e19..cceb347a354 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1186,8 +1186,10 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, int memsize = sizeof(struct kmem_list3); switch (action) { - case CPU_UP_PREPARE: + case CPU_LOCK_ACQUIRE: mutex_lock(&cache_chain_mutex); + break; + case CPU_UP_PREPARE: /* * We need to do this right in the beginning since * alloc_arraycache's are going to use this list. @@ -1274,16 +1276,9 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, } break; case CPU_ONLINE: - mutex_unlock(&cache_chain_mutex); start_cpu_timer(cpu); break; #ifdef CONFIG_HOTPLUG_CPU - case CPU_DOWN_PREPARE: - mutex_lock(&cache_chain_mutex); - break; - case CPU_DOWN_FAILED: - mutex_unlock(&cache_chain_mutex); - break; case CPU_DEAD: /* * Even if all the cpus of a node are down, we don't free the @@ -1354,6 +1349,8 @@ free_array_cache: continue; drain_freelist(cachep, l3, l3->free_objects); } + break; + case CPU_LOCK_RELEASE: mutex_unlock(&cache_chain_mutex); break; } -- cgit v1.2.3 From 319c2a986eb45989690c955d9667b814ef0ed56f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:06 -0700 Subject: workqueue: fix freezeable workqueues implementation Currently ->freezeable is per-cpu, this is wrong. CPU_UP_PREPARE creates cwq->thread which is not freezeable. Move ->freezeable to workqueue_struct. Signed-off-by: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: "Pallipadi, Venkatesh" Cc: Gautham shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5ecf4984e38..d80dbdceadb 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -49,8 +49,6 @@ struct cpu_workqueue_struct { struct work_struct *current_work; int run_depth; /* Detect run_workqueue() recursion depth */ - - int freezeable; /* Freeze the thread during suspend */ } ____cacheline_aligned; /* @@ -61,6 +59,7 @@ struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; const char *name; struct list_head list; /* Empty if single thread */ + int freezeable; /* Freeze threads during suspend */ }; /* All the per-cpu workqueues on the system, for hotplug cpu to add/remove @@ -351,7 +350,7 @@ static int worker_thread(void *__cwq) struct k_sigaction sa; sigset_t blocked; - if (!cwq->freezeable) + if (!cwq->wq->freezeable) current->flags |= PF_NOFREEZE; set_user_nice(current, -5); @@ -375,7 +374,7 @@ static int worker_thread(void *__cwq) set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { - if (cwq->freezeable) + if (cwq->wq->freezeable) try_to_freeze(); add_wait_queue(&cwq->more_work, &wait); @@ -546,7 +545,7 @@ out: EXPORT_SYMBOL_GPL(flush_work); static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, - int cpu, int freezeable) + int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); struct task_struct *p; @@ -554,7 +553,6 @@ static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, spin_lock_init(&cwq->lock); cwq->wq = wq; cwq->thread = NULL; - cwq->freezeable = freezeable; INIT_LIST_HEAD(&cwq->worklist); init_waitqueue_head(&cwq->more_work); @@ -586,10 +584,12 @@ struct workqueue_struct *__create_workqueue(const char *name, } wq->name = name; + wq->freezeable = freezeable; + mutex_lock(&workqueue_mutex); if (singlethread) { INIT_LIST_HEAD(&wq->list); - p = create_workqueue_thread(wq, singlethread_cpu, freezeable); + p = create_workqueue_thread(wq, singlethread_cpu); if (!p) destroy = 1; else @@ -597,7 +597,7 @@ struct workqueue_struct *__create_workqueue(const char *name, } else { list_add(&wq->list, &workqueues); for_each_online_cpu(cpu) { - p = create_workqueue_thread(wq, cpu, freezeable); + p = create_workqueue_thread(wq, cpu); if (p) { kthread_bind(p, cpu); wake_up_process(p); @@ -854,7 +854,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, mutex_lock(&workqueue_mutex); /* Create a new workqueue thread for it. */ list_for_each_entry(wq, &workqueues, list) { - if (!create_workqueue_thread(wq, hotcpu, 0)) { + if (!create_workqueue_thread(wq, hotcpu)) { printk("workqueue for %i failed\n", hotcpu); return NOTIFY_BAD; } -- cgit v1.2.3 From d721304dce0ced0b3b0366996cc02929669708a8 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:07 -0700 Subject: workqueue: fix flush_workqueue() vs CPU_DEAD race Many thanks to Srivatsa Vaddagiri for the helpful discussion and for spotting the bug in my previous attempt. work->func() (and thus flush_workqueue()) must not use workqueue_mutex, this leads to deadlock when CPU_DEAD does kthread_stop(). However without this mutex held we can't detect CPU_DEAD in progress, which can move pending works to another CPU while the dead one is not on cpu_online_map. Change flush_workqueue() to use for_each_possible_cpu(). This means that flush_cpu_workqueue() may hit CPU which is already dead. However in that case !list_empty(&cwq->worklist) || cwq->current_work != NULL means that CPU_DEAD in progress, it will do kthread_stop() + take_over_work() so we can proceed and insert a barrier. We hold cwq->lock, so we are safe. Also, add migrate_sequence incremented by take_over_work() under cwq->lock. If take_over_work() happened before we checked this CPU, we should see the new value after spin_unlock(). Further possible changes: remove CPU_DEAD handling (along with take_over_work, migrate_sequence) from workqueue.c. CPU_DEAD just sets cwq->please_exit_after_flush flag. CPU_UP_PREPARE->create_workqueue_thread() clears this flag, and creates the new thread if cwq->thread == NULL. This way the workqueue/cpu-hotplug interaction is almost zero, workqueue_mutex just protects "workqueues" list, CPU_LOCK_ACQUIRE/CPU_LOCK_RELEASE go away. Signed-off-by: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: "Pallipadi, Venkatesh" Cc: Gautham shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index d80dbdceadb..1d1933cf377 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -64,6 +64,7 @@ struct workqueue_struct { /* All the per-cpu workqueues on the system, for hotplug cpu to add/remove threads to each one as cpus come/go. */ +static long migrate_sequence __read_mostly; static DEFINE_MUTEX(workqueue_mutex); static LIST_HEAD(workqueues); @@ -421,13 +422,7 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) * Probably keventd trying to flush its own queue. So simply run * it by hand rather than deadlocking. */ - preempt_enable(); - /* - * We can still touch *cwq here because we are keventd, and - * hot-unplug will be waiting us to exit. - */ run_workqueue(cwq); - preempt_disable(); } else { struct wq_barrier barr; int active = 0; @@ -439,11 +434,8 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) } spin_unlock_irq(&cwq->lock); - if (active) { - preempt_enable(); + if (active) wait_for_completion(&barr.done); - preempt_disable(); - } } } @@ -462,17 +454,21 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) */ void fastcall flush_workqueue(struct workqueue_struct *wq) { - preempt_disable(); /* CPU hotplug */ if (is_single_threaded(wq)) { /* Always use first cpu's area. */ flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); } else { + long sequence; int cpu; +again: + sequence = migrate_sequence; - for_each_online_cpu(cpu) + for_each_possible_cpu(cpu) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); + + if (unlikely(sequence != migrate_sequence)) + goto again; } - preempt_enable(); } EXPORT_SYMBOL_GPL(flush_workqueue); @@ -544,17 +540,21 @@ out: } EXPORT_SYMBOL_GPL(flush_work); -static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, - int cpu) +static void init_cpu_workqueue(struct workqueue_struct *wq, int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - struct task_struct *p; - spin_lock_init(&cwq->lock); cwq->wq = wq; - cwq->thread = NULL; + spin_lock_init(&cwq->lock); INIT_LIST_HEAD(&cwq->worklist); init_waitqueue_head(&cwq->more_work); +} + +static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, + int cpu) +{ + struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); + struct task_struct *p; if (is_single_threaded(wq)) p = kthread_create(worker_thread, cwq, "%s", wq->name); @@ -589,6 +589,7 @@ struct workqueue_struct *__create_workqueue(const char *name, mutex_lock(&workqueue_mutex); if (singlethread) { INIT_LIST_HEAD(&wq->list); + init_cpu_workqueue(wq, singlethread_cpu); p = create_workqueue_thread(wq, singlethread_cpu); if (!p) destroy = 1; @@ -596,7 +597,11 @@ struct workqueue_struct *__create_workqueue(const char *name, wake_up_process(p); } else { list_add(&wq->list, &workqueues); - for_each_online_cpu(cpu) { + for_each_possible_cpu(cpu) { + init_cpu_workqueue(wq, cpu); + if (!cpu_online(cpu)) + continue; + p = create_workqueue_thread(wq, cpu); if (p) { kthread_bind(p, cpu); @@ -831,6 +836,7 @@ static void take_over_work(struct workqueue_struct *wq, unsigned int cpu) spin_lock_irq(&cwq->lock); list_replace_init(&cwq->worklist, &list); + migrate_sequence++; while (!list_empty(&list)) { printk("Taking work for %s\n", wq->name); -- cgit v1.2.3 From 36aa9dfc39bf473780439f5629c30f59d677e793 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:08 -0700 Subject: workqueue: don't clear cwq->thread until it exits Pointed out by Srivatsa Vaddagiri. cleanup_workqueue_thread() sets cwq->thread = NULL and does kthread_stop(). This breaks the "if (cwq->thread == current)" logic in flush_cpu_workqueue() and leads to deadlock. Kill the thead first, then clear cwq->thread. workqueue_mutex protects us from create_workqueue_thread() so we don't need cwq->lock. Signed-off-by: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: "Pallipadi, Venkatesh" Cc: Gautham shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 1d1933cf377..398c34ff6a5 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -625,17 +625,12 @@ EXPORT_SYMBOL_GPL(__create_workqueue); static void cleanup_workqueue_thread(struct workqueue_struct *wq, int cpu) { - struct cpu_workqueue_struct *cwq; - unsigned long flags; - struct task_struct *p; + struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - cwq = per_cpu_ptr(wq->cpu_wq, cpu); - spin_lock_irqsave(&cwq->lock, flags); - p = cwq->thread; - cwq->thread = NULL; - spin_unlock_irqrestore(&cwq->lock, flags); - if (p) - kthread_stop(p); + if (cwq->thread) { + kthread_stop(cwq->thread); + cwq->thread = NULL; + } } /** -- cgit v1.2.3 From 3af24433efac62f451bfdb1cf1edb7181fb73645 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:09 -0700 Subject: workqueue: don't migrate pending works from the dead CPU Currently CPU_DEAD uses kthread_stop() to stop cwq->thread and then transfers cwq->worklist to another CPU. However, it is very unlikely that worker_thread() will notice kthread_should_stop() before flushing cwq->worklist. It is only possible if worker_thread() was preempted after run_workqueue(cwq), a new work_struct was added, and CPU_DEAD happened before cwq->thread has a chance to run. This means that take_over_work() mostly adds unneeded complications. Note also that kthread_stop() is not good per se, wake_up_process() may confuse work->func() if it sleeps waiting for some event. Remove take_over_work() and migrate_sequence complications. CPU_DEAD sets the cwq->should_stop flag (introduced by this patch) and waits for cwq->thread to flush cwq->worklist and exit. Because the dead CPU is not on cpu_online_map, no more works can be added to that cwq. cpu_populated_map was introduced to optimize for_each_possible_cpu(), it is not strictly needed, and it is more a documentation in fact. Saves 418 bytes. Signed-off-by: Oleg Nesterov Cc: Srivatsa Vaddagiri Cc: "Pallipadi, Venkatesh" Cc: Gautham shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 430 ++++++++++++++++++++++++++--------------------------- 1 file changed, 211 insertions(+), 219 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 398c34ff6a5..a981add58fb 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -43,10 +43,11 @@ struct cpu_workqueue_struct { struct list_head worklist; wait_queue_head_t more_work; + struct work_struct *current_work; struct workqueue_struct *wq; struct task_struct *thread; - struct work_struct *current_work; + int should_stop; int run_depth; /* Detect run_workqueue() recursion depth */ } ____cacheline_aligned; @@ -64,11 +65,12 @@ struct workqueue_struct { /* All the per-cpu workqueues on the system, for hotplug cpu to add/remove threads to each one as cpus come/go. */ -static long migrate_sequence __read_mostly; static DEFINE_MUTEX(workqueue_mutex); static LIST_HEAD(workqueues); -static int singlethread_cpu; +static int singlethread_cpu __read_mostly; +/* optimization, we could use cpu_possible_map */ +static cpumask_t cpu_populated_map __read_mostly; /* If it's single threaded, it isn't in the list of workqueues. */ static inline int is_single_threaded(struct workqueue_struct *wq) @@ -344,10 +346,28 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) spin_unlock_irqrestore(&cwq->lock, flags); } +/* + * NOTE: the caller must not touch *cwq if this func returns true + */ +static int cwq_should_stop(struct cpu_workqueue_struct *cwq) +{ + int should_stop = cwq->should_stop; + + if (unlikely(should_stop)) { + spin_lock_irq(&cwq->lock); + should_stop = cwq->should_stop && list_empty(&cwq->worklist); + if (should_stop) + cwq->thread = NULL; + spin_unlock_irq(&cwq->lock); + } + + return should_stop; +} + static int worker_thread(void *__cwq) { struct cpu_workqueue_struct *cwq = __cwq; - DECLARE_WAITQUEUE(wait, current); + DEFINE_WAIT(wait); struct k_sigaction sa; sigset_t blocked; @@ -373,23 +393,21 @@ static int worker_thread(void *__cwq) siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); - set_current_state(TASK_INTERRUPTIBLE); - while (!kthread_should_stop()) { + for (;;) { if (cwq->wq->freezeable) try_to_freeze(); - add_wait_queue(&cwq->more_work, &wait); - if (list_empty(&cwq->worklist)) + prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE); + if (!cwq->should_stop && list_empty(&cwq->worklist)) schedule(); - else - __set_current_state(TASK_RUNNING); - remove_wait_queue(&cwq->more_work, &wait); + finish_wait(&cwq->more_work, &wait); + + if (cwq_should_stop(cwq)) + break; - if (!list_empty(&cwq->worklist)) - run_workqueue(cwq); - set_current_state(TASK_INTERRUPTIBLE); + run_workqueue(cwq); } - __set_current_state(TASK_RUNNING); + return 0; } @@ -454,20 +472,13 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) */ void fastcall flush_workqueue(struct workqueue_struct *wq) { - if (is_single_threaded(wq)) { - /* Always use first cpu's area. */ + if (is_single_threaded(wq)) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); - } else { - long sequence; + else { int cpu; -again: - sequence = migrate_sequence; - for_each_possible_cpu(cpu) + for_each_cpu_mask(cpu, cpu_populated_map) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); - - if (unlikely(sequence != migrate_sequence)) - goto again; } } EXPORT_SYMBOL_GPL(flush_workqueue); @@ -485,11 +496,8 @@ static void wait_on_work(struct cpu_workqueue_struct *cwq, } spin_unlock_irq(&cwq->lock); - if (unlikely(running)) { - mutex_unlock(&workqueue_mutex); + if (unlikely(running)) wait_for_completion(&barr.done); - mutex_lock(&workqueue_mutex); - } } /** @@ -510,155 +518,31 @@ void flush_work(struct workqueue_struct *wq, struct work_struct *work) { struct cpu_workqueue_struct *cwq; - mutex_lock(&workqueue_mutex); cwq = get_wq_data(work); /* Was it ever queued ? */ if (!cwq) - goto out; + return; /* - * This work can't be re-queued, and the lock above protects us - * from take_over_work(), no need to re-check that get_wq_data() - * is still the same when we take cwq->lock. + * This work can't be re-queued, no need to re-check that + * get_wq_data() is still the same when we take cwq->lock. */ spin_lock_irq(&cwq->lock); list_del_init(&work->entry); work_release(work); spin_unlock_irq(&cwq->lock); - if (is_single_threaded(wq)) { - /* Always use first cpu's area. */ + if (is_single_threaded(wq)) wait_on_work(per_cpu_ptr(wq->cpu_wq, singlethread_cpu), work); - } else { + else { int cpu; - for_each_online_cpu(cpu) + for_each_cpu_mask(cpu, cpu_populated_map) wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); } -out: - mutex_unlock(&workqueue_mutex); } EXPORT_SYMBOL_GPL(flush_work); -static void init_cpu_workqueue(struct workqueue_struct *wq, int cpu) -{ - struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - - cwq->wq = wq; - spin_lock_init(&cwq->lock); - INIT_LIST_HEAD(&cwq->worklist); - init_waitqueue_head(&cwq->more_work); -} - -static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, - int cpu) -{ - struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - struct task_struct *p; - - if (is_single_threaded(wq)) - p = kthread_create(worker_thread, cwq, "%s", wq->name); - else - p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu); - if (IS_ERR(p)) - return NULL; - cwq->thread = p; - return p; -} - -struct workqueue_struct *__create_workqueue(const char *name, - int singlethread, int freezeable) -{ - int cpu, destroy = 0; - struct workqueue_struct *wq; - struct task_struct *p; - - wq = kzalloc(sizeof(*wq), GFP_KERNEL); - if (!wq) - return NULL; - - wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct); - if (!wq->cpu_wq) { - kfree(wq); - return NULL; - } - - wq->name = name; - wq->freezeable = freezeable; - - mutex_lock(&workqueue_mutex); - if (singlethread) { - INIT_LIST_HEAD(&wq->list); - init_cpu_workqueue(wq, singlethread_cpu); - p = create_workqueue_thread(wq, singlethread_cpu); - if (!p) - destroy = 1; - else - wake_up_process(p); - } else { - list_add(&wq->list, &workqueues); - for_each_possible_cpu(cpu) { - init_cpu_workqueue(wq, cpu); - if (!cpu_online(cpu)) - continue; - - p = create_workqueue_thread(wq, cpu); - if (p) { - kthread_bind(p, cpu); - wake_up_process(p); - } else - destroy = 1; - } - } - mutex_unlock(&workqueue_mutex); - - /* - * Was there any error during startup? If yes then clean up: - */ - if (destroy) { - destroy_workqueue(wq); - wq = NULL; - } - return wq; -} -EXPORT_SYMBOL_GPL(__create_workqueue); - -static void cleanup_workqueue_thread(struct workqueue_struct *wq, int cpu) -{ - struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - - if (cwq->thread) { - kthread_stop(cwq->thread); - cwq->thread = NULL; - } -} - -/** - * destroy_workqueue - safely terminate a workqueue - * @wq: target workqueue - * - * Safely destroy a workqueue. All work currently pending will be done first. - */ -void destroy_workqueue(struct workqueue_struct *wq) -{ - int cpu; - - flush_workqueue(wq); - - /* We don't need the distraction of CPUs appearing and vanishing. */ - mutex_lock(&workqueue_mutex); - if (is_single_threaded(wq)) - cleanup_workqueue_thread(wq, singlethread_cpu); - else { - for_each_online_cpu(cpu) - cleanup_workqueue_thread(wq, cpu); - list_del(&wq->list); - } - mutex_unlock(&workqueue_mutex); - free_percpu(wq->cpu_wq); - kfree(wq); -} -EXPORT_SYMBOL_GPL(destroy_workqueue); static struct workqueue_struct *keventd_wq; @@ -822,85 +706,193 @@ int current_is_keventd(void) } -/* Take the work from this (downed) CPU. */ -static void take_over_work(struct workqueue_struct *wq, unsigned int cpu) +static struct cpu_workqueue_struct * +init_cpu_workqueue(struct workqueue_struct *wq, int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - struct list_head list; - struct work_struct *work; - spin_lock_irq(&cwq->lock); - list_replace_init(&cwq->worklist, &list); - migrate_sequence++; - - while (!list_empty(&list)) { - printk("Taking work for %s\n", wq->name); - work = list_entry(list.next,struct work_struct,entry); - list_del(&work->entry); - __queue_work(per_cpu_ptr(wq->cpu_wq, smp_processor_id()), work); - } - spin_unlock_irq(&cwq->lock); + cwq->wq = wq; + spin_lock_init(&cwq->lock); + INIT_LIST_HEAD(&cwq->worklist); + init_waitqueue_head(&cwq->more_work); + + return cwq; } -/* We're holding the cpucontrol mutex here */ -static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, - unsigned long action, - void *hcpu) +static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) +{ + struct workqueue_struct *wq = cwq->wq; + const char *fmt = is_single_threaded(wq) ? "%s" : "%s/%d"; + struct task_struct *p; + + p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu); + /* + * Nobody can add the work_struct to this cwq, + * if (caller is __create_workqueue) + * nobody should see this wq + * else // caller is CPU_UP_PREPARE + * cpu is not on cpu_online_map + * so we can abort safely. + */ + if (IS_ERR(p)) + return PTR_ERR(p); + + cwq->thread = p; + cwq->should_stop = 0; + if (!is_single_threaded(wq)) + kthread_bind(p, cpu); + + if (is_single_threaded(wq) || cpu_online(cpu)) + wake_up_process(p); + + return 0; +} + +struct workqueue_struct *__create_workqueue(const char *name, + int singlethread, int freezeable) { - unsigned int hotcpu = (unsigned long)hcpu; struct workqueue_struct *wq; + struct cpu_workqueue_struct *cwq; + int err = 0, cpu; - switch (action) { - case CPU_UP_PREPARE: + wq = kzalloc(sizeof(*wq), GFP_KERNEL); + if (!wq) + return NULL; + + wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct); + if (!wq->cpu_wq) { + kfree(wq); + return NULL; + } + + wq->name = name; + wq->freezeable = freezeable; + + if (singlethread) { + INIT_LIST_HEAD(&wq->list); + cwq = init_cpu_workqueue(wq, singlethread_cpu); + err = create_workqueue_thread(cwq, singlethread_cpu); + } else { mutex_lock(&workqueue_mutex); - /* Create a new workqueue thread for it. */ - list_for_each_entry(wq, &workqueues, list) { - if (!create_workqueue_thread(wq, hotcpu)) { - printk("workqueue for %i failed\n", hotcpu); - return NOTIFY_BAD; - } + list_add(&wq->list, &workqueues); + + for_each_possible_cpu(cpu) { + cwq = init_cpu_workqueue(wq, cpu); + if (err || !cpu_online(cpu)) + continue; + err = create_workqueue_thread(cwq, cpu); } - break; + mutex_unlock(&workqueue_mutex); + } + + if (err) { + destroy_workqueue(wq); + wq = NULL; + } + return wq; +} +EXPORT_SYMBOL_GPL(__create_workqueue); - case CPU_ONLINE: - /* Kick off worker threads. */ - list_for_each_entry(wq, &workqueues, list) { - struct cpu_workqueue_struct *cwq; +static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) +{ + struct wq_barrier barr; + int alive = 0; - cwq = per_cpu_ptr(wq->cpu_wq, hotcpu); - kthread_bind(cwq->thread, hotcpu); - wake_up_process(cwq->thread); - } + spin_lock_irq(&cwq->lock); + if (cwq->thread != NULL) { + insert_wq_barrier(cwq, &barr, 1); + cwq->should_stop = 1; + alive = 1; + } + spin_unlock_irq(&cwq->lock); + + if (alive) { + wait_for_completion(&barr.done); + + while (unlikely(cwq->thread != NULL)) + cpu_relax(); + /* + * Wait until cwq->thread unlocks cwq->lock, + * it won't touch *cwq after that. + */ + smp_rmb(); + spin_unlock_wait(&cwq->lock); + } +} + +/** + * destroy_workqueue - safely terminate a workqueue + * @wq: target workqueue + * + * Safely destroy a workqueue. All work currently pending will be done first. + */ +void destroy_workqueue(struct workqueue_struct *wq) +{ + struct cpu_workqueue_struct *cwq; + + if (is_single_threaded(wq)) { + cwq = per_cpu_ptr(wq->cpu_wq, singlethread_cpu); + cleanup_workqueue_thread(cwq, singlethread_cpu); + } else { + int cpu; + + mutex_lock(&workqueue_mutex); + list_del(&wq->list); mutex_unlock(&workqueue_mutex); - break; - case CPU_UP_CANCELED: - list_for_each_entry(wq, &workqueues, list) { - if (!per_cpu_ptr(wq->cpu_wq, hotcpu)->thread) - continue; - /* Unbind so it can run. */ - kthread_bind(per_cpu_ptr(wq->cpu_wq, hotcpu)->thread, - any_online_cpu(cpu_online_map)); - cleanup_workqueue_thread(wq, hotcpu); + for_each_cpu_mask(cpu, cpu_populated_map) { + cwq = per_cpu_ptr(wq->cpu_wq, cpu); + cleanup_workqueue_thread(cwq, cpu); } - mutex_unlock(&workqueue_mutex); - break; + } - case CPU_DOWN_PREPARE: + free_percpu(wq->cpu_wq); + kfree(wq); +} +EXPORT_SYMBOL_GPL(destroy_workqueue); + +static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + struct cpu_workqueue_struct *cwq; + struct workqueue_struct *wq; + + switch (action) { + case CPU_LOCK_ACQUIRE: mutex_lock(&workqueue_mutex); - break; + return NOTIFY_OK; - case CPU_DOWN_FAILED: + case CPU_LOCK_RELEASE: mutex_unlock(&workqueue_mutex); - break; + return NOTIFY_OK; - case CPU_DEAD: - list_for_each_entry(wq, &workqueues, list) - cleanup_workqueue_thread(wq, hotcpu); - list_for_each_entry(wq, &workqueues, list) - take_over_work(wq, hotcpu); - mutex_unlock(&workqueue_mutex); - break; + case CPU_UP_PREPARE: + cpu_set(cpu, cpu_populated_map); + } + + list_for_each_entry(wq, &workqueues, list) { + cwq = per_cpu_ptr(wq->cpu_wq, cpu); + + switch (action) { + case CPU_UP_PREPARE: + if (!create_workqueue_thread(cwq, cpu)) + break; + printk(KERN_ERR "workqueue for %i failed\n", cpu); + return NOTIFY_BAD; + + case CPU_ONLINE: + wake_up_process(cwq->thread); + break; + + case CPU_UP_CANCELED: + if (cwq->thread) + wake_up_process(cwq->thread); + case CPU_DEAD: + cleanup_workqueue_thread(cwq, cpu); + break; + } } return NOTIFY_OK; @@ -908,9 +900,9 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, void init_workqueues(void) { + cpu_populated_map = cpu_online_map; singlethread_cpu = first_cpu(cpu_possible_map); hotcpu_notifier(workqueue_cpu_callback, 0); keventd_wq = create_workqueue("events"); BUG_ON(!keventd_wq); } - -- cgit v1.2.3 From 7097a87afe937a5879528d52880c2d95f089e96c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:10 -0700 Subject: workqueue: kill run_scheduled_work() Because it has no callers. Actually, I think the whole idea of run_scheduled_work() was not right, not good to mix "unqueue this work and execute its ->func()" in one function. Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 1 - kernel/workqueue.c | 73 ----------------------------------------------- 2 files changed, 74 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 26a70992dec..2a58f16e196 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -182,7 +182,6 @@ extern void flush_work(struct workqueue_struct *wq, struct work_struct *work); extern void flush_work_keventd(struct work_struct *work); extern int FASTCALL(schedule_work(struct work_struct *work)); -extern int FASTCALL(run_scheduled_work(struct work_struct *work)); extern int FASTCALL(schedule_delayed_work(struct delayed_work *work, unsigned long delay)); extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a981add58fb..ea422254f8b 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -98,79 +98,6 @@ static inline void *get_wq_data(struct work_struct *work) return (void *) (atomic_long_read(&work->data) & WORK_STRUCT_WQ_DATA_MASK); } -static int __run_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&cwq->lock, flags); - /* - * We need to re-validate the work info after we've gotten - * the cpu_workqueue lock. We can run the work now iff: - * - * - the wq_data still matches the cpu_workqueue_struct - * - AND the work is still marked pending - * - AND the work is still on a list (which will be this - * workqueue_struct list) - * - * All these conditions are important, because we - * need to protect against the work being run right - * now on another CPU (all but the last one might be - * true if it's currently running and has not been - * released yet, for example). - */ - if (get_wq_data(work) == cwq - && work_pending(work) - && !list_empty(&work->entry)) { - work_func_t f = work->func; - cwq->current_work = work; - list_del_init(&work->entry); - spin_unlock_irqrestore(&cwq->lock, flags); - - if (!test_bit(WORK_STRUCT_NOAUTOREL, work_data_bits(work))) - work_release(work); - f(work); - - spin_lock_irqsave(&cwq->lock, flags); - cwq->current_work = NULL; - ret = 1; - } - spin_unlock_irqrestore(&cwq->lock, flags); - return ret; -} - -/** - * run_scheduled_work - run scheduled work synchronously - * @work: work to run - * - * This checks if the work was pending, and runs it - * synchronously if so. It returns a boolean to indicate - * whether it had any scheduled work to run or not. - * - * NOTE! This _only_ works for normal work_structs. You - * CANNOT use this for delayed work, because the wq data - * for delayed work will not point properly to the per- - * CPU workqueue struct, but will change! - */ -int fastcall run_scheduled_work(struct work_struct *work) -{ - for (;;) { - struct cpu_workqueue_struct *cwq; - - if (!work_pending(work)) - return 0; - if (list_empty(&work->entry)) - return 0; - /* NOTE! This depends intimately on __queue_work! */ - cwq = get_wq_data(work); - if (!cwq) - return 0; - if (__run_work(cwq, work)) - return 1; - } -} -EXPORT_SYMBOL(run_scheduled_work); - static void insert_work(struct cpu_workqueue_struct *cwq, struct work_struct *work, int tail) { -- cgit v1.2.3 From f293ea92007419e4f9c52db0cf57af17f45b9f94 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:10 -0700 Subject: workqueue: don't save interrupts in run_workqueue() work->func() may sleep, it's a bug to call run_workqueue() with irqs disabled. Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ea422254f8b..74f3f782522 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -227,13 +227,7 @@ EXPORT_SYMBOL_GPL(queue_delayed_work_on); static void run_workqueue(struct cpu_workqueue_struct *cwq) { - unsigned long flags; - - /* - * Keep taking off work from the queue until - * done. - */ - spin_lock_irqsave(&cwq->lock, flags); + spin_lock_irq(&cwq->lock); cwq->run_depth++; if (cwq->run_depth > 3) { /* morton gets to eat his hat */ @@ -248,7 +242,7 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) cwq->current_work = work; list_del_init(cwq->worklist.next); - spin_unlock_irqrestore(&cwq->lock, flags); + spin_unlock_irq(&cwq->lock); BUG_ON(get_wq_data(work) != cwq); if (!test_bit(WORK_STRUCT_NOAUTOREL, work_data_bits(work))) @@ -266,11 +260,11 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) dump_stack(); } - spin_lock_irqsave(&cwq->lock, flags); + spin_lock_irq(&cwq->lock); cwq->current_work = NULL; } cwq->run_depth--; - spin_unlock_irqrestore(&cwq->lock, flags); + spin_unlock_irq(&cwq->lock); } /* @@ -399,6 +393,8 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) */ void fastcall flush_workqueue(struct workqueue_struct *wq) { + might_sleep(); + if (is_single_threaded(wq)) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); else { @@ -445,6 +441,8 @@ void flush_work(struct workqueue_struct *wq, struct work_struct *work) { struct cpu_workqueue_struct *cwq; + might_sleep(); + cwq = get_wq_data(work); /* Was it ever queued ? */ if (!cwq) -- cgit v1.2.3 From dfb4b82e1c631b1a6057e77212996a890aa515b7 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:11 -0700 Subject: workqueue: make cancel_rearming_delayed_workqueue() work on idle dwork cancel_rearming_delayed_workqueue(dwork) will hang forever if dwork was not scheduled, because in that case cancel_delayed_work()->del_timer_sync() never returns true. I don't know if there are any callers which may have problems, but this is not so convenient, and the fix is very simple. Q: looks like we don't need "struct workqueue_struct *wq" parameter. If the timer was aborted successfully, get_wq_data() == wq. Is it worth to add the new function? Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 74f3f782522..ce72d45c7fd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -569,6 +569,10 @@ EXPORT_SYMBOL(flush_work_keventd); void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, struct delayed_work *dwork) { + /* Was it ever queued ? */ + if (!get_wq_data(&dwork->work)) + return; + while (!cancel_delayed_work(dwork)) flush_workqueue(wq); } -- cgit v1.2.3 From b1f4ec172f75bc2f5cc4f4be69b5587660a955d2 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:12 -0700 Subject: workqueue: introduce cpu_singlethread_map The code like if (is_single_threaded(wq)) do_something(singlethread_cpu); else { for_each_cpu_mask(cpu, cpu_populated_map) do_something(cpu); } looks very annoying. We can add "static cpumask_t cpu_singlethread_map" and simplify the code. Lessens .text a bit, and imho makes the code more readable. Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 55 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ce72d45c7fd..6308a4bc6a8 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -69,6 +69,7 @@ static DEFINE_MUTEX(workqueue_mutex); static LIST_HEAD(workqueues); static int singlethread_cpu __read_mostly; +static cpumask_t cpu_singlethread_map __read_mostly; /* optimization, we could use cpu_possible_map */ static cpumask_t cpu_populated_map __read_mostly; @@ -78,6 +79,12 @@ static inline int is_single_threaded(struct workqueue_struct *wq) return list_empty(&wq->list); } +static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq) +{ + return is_single_threaded(wq) + ? &cpu_singlethread_map : &cpu_populated_map; +} + /* * Set the workqueue on which a work item is to be run * - Must *only* be called if the pending flag is set @@ -393,16 +400,12 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) */ void fastcall flush_workqueue(struct workqueue_struct *wq) { - might_sleep(); - - if (is_single_threaded(wq)) - flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu)); - else { - int cpu; + const cpumask_t *cpu_map = wq_cpu_map(wq); + int cpu - for_each_cpu_mask(cpu, cpu_populated_map) - flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); - } + might_sleep(); + for_each_cpu_mask(cpu, *cpu_map) + flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); } EXPORT_SYMBOL_GPL(flush_workqueue); @@ -439,7 +442,9 @@ static void wait_on_work(struct cpu_workqueue_struct *cwq, */ void flush_work(struct workqueue_struct *wq, struct work_struct *work) { + const cpumask_t *cpu_map = wq_cpu_map(wq); struct cpu_workqueue_struct *cwq; + int cpu; might_sleep(); @@ -457,14 +462,8 @@ void flush_work(struct workqueue_struct *wq, struct work_struct *work) work_release(work); spin_unlock_irq(&cwq->lock); - if (is_single_threaded(wq)) - wait_on_work(per_cpu_ptr(wq->cpu_wq, singlethread_cpu), work); - else { - int cpu; - - for_each_cpu_mask(cpu, cpu_populated_map) - wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); - } + for_each_cpu_mask(cpu, *cpu_map) + wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); } EXPORT_SYMBOL_GPL(flush_work); @@ -757,22 +756,17 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) */ void destroy_workqueue(struct workqueue_struct *wq) { + const cpumask_t *cpu_map = wq_cpu_map(wq); struct cpu_workqueue_struct *cwq; + int cpu; - if (is_single_threaded(wq)) { - cwq = per_cpu_ptr(wq->cpu_wq, singlethread_cpu); - cleanup_workqueue_thread(cwq, singlethread_cpu); - } else { - int cpu; + mutex_lock(&workqueue_mutex); + list_del(&wq->list); + mutex_unlock(&workqueue_mutex); - mutex_lock(&workqueue_mutex); - list_del(&wq->list); - mutex_unlock(&workqueue_mutex); - - for_each_cpu_mask(cpu, cpu_populated_map) { - cwq = per_cpu_ptr(wq->cpu_wq, cpu); - cleanup_workqueue_thread(cwq, cpu); - } + for_each_cpu_mask(cpu, *cpu_map) { + cwq = per_cpu_ptr(wq->cpu_wq, cpu); + cleanup_workqueue_thread(cwq, cpu); } free_percpu(wq->cpu_wq); @@ -831,6 +825,7 @@ void init_workqueues(void) { cpu_populated_map = cpu_online_map; singlethread_cpu = first_cpu(cpu_possible_map); + cpu_singlethread_map = cpumask_of_cpu(singlethread_cpu); hotcpu_notifier(workqueue_cpu_callback, 0); keventd_wq = create_workqueue("events"); BUG_ON(!keventd_wq); -- cgit v1.2.3 From cce1a1656c9a3fdc6c6c1029b576e4ab6ecaac37 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:13 -0700 Subject: workqueue: introduce workqueue_struct->singlethread Add explicit workqueue_struct->singlethread flag. This lessens .text a little, but most importantly this allows us to manipulate wq->list without changine the meaning of is_single_threaded(). Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 6308a4bc6a8..32b1091f21e 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -58,8 +58,9 @@ struct cpu_workqueue_struct { */ struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; + struct list_head list; const char *name; - struct list_head list; /* Empty if single thread */ + int singlethread; int freezeable; /* Freeze threads during suspend */ }; @@ -76,7 +77,7 @@ static cpumask_t cpu_populated_map __read_mostly; /* If it's single threaded, it isn't in the list of workqueues. */ static inline int is_single_threaded(struct workqueue_struct *wq) { - return list_empty(&wq->list); + return wq->singlethread; } static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq) @@ -401,7 +402,7 @@ static void flush_cpu_workqueue(struct cpu_workqueue_struct *cwq) void fastcall flush_workqueue(struct workqueue_struct *wq) { const cpumask_t *cpu_map = wq_cpu_map(wq); - int cpu + int cpu; might_sleep(); for_each_cpu_mask(cpu, *cpu_map) @@ -694,10 +695,11 @@ struct workqueue_struct *__create_workqueue(const char *name, } wq->name = name; + wq->singlethread = singlethread; wq->freezeable = freezeable; + INIT_LIST_HEAD(&wq->list); if (singlethread) { - INIT_LIST_HEAD(&wq->list); cwq = init_cpu_workqueue(wq, singlethread_cpu); err = create_workqueue_thread(cwq, singlethread_cpu); } else { -- cgit v1.2.3 From c12920d19078eb8fd99560ec232a6e05c6ff1aa8 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:14 -0700 Subject: workqueue: make init_workqueues() __init The only caller of init_workqueues() is do_basic_setup(). Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 32b1091f21e..e858e93886e 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -823,7 +823,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -void init_workqueues(void) +void __init init_workqueues(void) { cpu_populated_map = cpu_online_map; singlethread_cpu = first_cpu(cpu_possible_map); -- cgit v1.2.3 From 06ba38a9a0f6ceffe70343f684c5a690e3710ef4 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:15 -0700 Subject: workqueues: shift kthread_bind() from CPU_UP_PREPARE to CPU_ONLINE CPU_UP_PREPARE binds cwq->thread to the new CPU. So CPU_UP_CANCELED tries to wake up the task which is bound to the failed CPU. With this patch we don't bind cwq->thread until CPU becomes online. The first wake_up() after kthread_create() is a bit special, make a simple helper for that. Signed-off-by: Oleg Nesterov Cc: Gautham R Shenoy Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index e858e93886e..7d1ebfc1a99 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -668,15 +668,21 @@ static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) cwq->thread = p; cwq->should_stop = 0; - if (!is_single_threaded(wq)) - kthread_bind(p, cpu); - - if (is_single_threaded(wq) || cpu_online(cpu)) - wake_up_process(p); return 0; } +static void start_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) +{ + struct task_struct *p = cwq->thread; + + if (p != NULL) { + if (cpu >= 0) + kthread_bind(p, cpu); + wake_up_process(p); + } +} + struct workqueue_struct *__create_workqueue(const char *name, int singlethread, int freezeable) { @@ -702,6 +708,7 @@ struct workqueue_struct *__create_workqueue(const char *name, if (singlethread) { cwq = init_cpu_workqueue(wq, singlethread_cpu); err = create_workqueue_thread(cwq, singlethread_cpu); + start_workqueue_thread(cwq, -1); } else { mutex_lock(&workqueue_mutex); list_add(&wq->list, &workqueues); @@ -711,6 +718,7 @@ struct workqueue_struct *__create_workqueue(const char *name, if (err || !cpu_online(cpu)) continue; err = create_workqueue_thread(cwq, cpu); + start_workqueue_thread(cwq, cpu); } mutex_unlock(&workqueue_mutex); } @@ -808,12 +816,11 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, return NOTIFY_BAD; case CPU_ONLINE: - wake_up_process(cwq->thread); + start_workqueue_thread(cwq, cpu); break; case CPU_UP_CANCELED: - if (cwq->thread) - wake_up_process(cwq->thread); + start_workqueue_thread(cwq, -1); case CPU_DEAD: cleanup_workqueue_thread(cwq, cpu); break; -- cgit v1.2.3 From ed7c0feede39d70092d048ec30f59bb1df69eec6 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:16 -0700 Subject: make queue_delayed_work() friendly to flush_fork() Currently typeof(delayed_work->work.data) is "struct workqueue_struct" when the timer is pending "struct cpu_workqueue_struct" whe the work is queued This makes impossible to use flush_fork(delayed_work->work) in addition to cancel_delayed_work/cancel_rearming_delayed_work, not good. Change queue_delayed_work/delayed_work_timer_fn to use cwq, not wq. This complicates (and uglifies) these functions a little bit, but alows us to use flush_fork(dwork) and imho makes the whole code more consistent. Also, document the fact that cancel_rearming_delayed_work() doesn't garantee the completion of work->func() upon return. Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 7d1ebfc1a99..d107e1c3b07 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -90,18 +90,20 @@ static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq) * Set the workqueue on which a work item is to be run * - Must *only* be called if the pending flag is set */ -static inline void set_wq_data(struct work_struct *work, void *wq) +static inline void set_wq_data(struct work_struct *work, + struct cpu_workqueue_struct *cwq) { unsigned long new; BUG_ON(!work_pending(work)); - new = (unsigned long) wq | (1UL << WORK_STRUCT_PENDING); + new = (unsigned long) cwq | (1UL << WORK_STRUCT_PENDING); new |= WORK_STRUCT_FLAG_MASK & *work_data_bits(work); atomic_long_set(&work->data, new); } -static inline void *get_wq_data(struct work_struct *work) +static inline +struct cpu_workqueue_struct *get_wq_data(struct work_struct *work) { return (void *) (atomic_long_read(&work->data) & WORK_STRUCT_WQ_DATA_MASK); } @@ -157,7 +159,8 @@ EXPORT_SYMBOL_GPL(queue_work); void delayed_work_timer_fn(unsigned long __data) { struct delayed_work *dwork = (struct delayed_work *)__data; - struct workqueue_struct *wq = get_wq_data(&dwork->work); + struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work); + struct workqueue_struct *wq = cwq->wq; int cpu = smp_processor_id(); if (unlikely(is_single_threaded(wq))) @@ -189,8 +192,9 @@ int fastcall queue_delayed_work(struct workqueue_struct *wq, BUG_ON(timer_pending(timer)); BUG_ON(!list_empty(&work->entry)); - /* This stores wq for the moment, for the timer_fn */ - set_wq_data(work, wq); + /* This stores cwq for the moment, for the timer_fn */ + set_wq_data(work, + per_cpu_ptr(wq->cpu_wq, raw_smp_processor_id())); timer->expires = jiffies + delay; timer->data = (unsigned long)dwork; timer->function = delayed_work_timer_fn; @@ -221,8 +225,9 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, BUG_ON(timer_pending(timer)); BUG_ON(!list_empty(&work->entry)); - /* This stores wq for the moment, for the timer_fn */ - set_wq_data(work, wq); + /* This stores cwq for the moment, for the timer_fn */ + set_wq_data(work, + per_cpu_ptr(wq->cpu_wq, raw_smp_processor_id())); timer->expires = jiffies + delay; timer->data = (unsigned long)dwork; timer->function = delayed_work_timer_fn; @@ -562,9 +567,12 @@ void flush_work_keventd(struct work_struct *work) EXPORT_SYMBOL(flush_work_keventd); /** - * cancel_rearming_delayed_workqueue - reliably kill off a delayed work whose handler rearms the delayed work. + * cancel_rearming_delayed_workqueue - kill off a delayed work whose handler rearms the delayed work. * @wq: the controlling workqueue structure * @dwork: the delayed work struct + * + * Note that the work callback function may still be running on return from + * cancel_delayed_work(). Run flush_workqueue() or flush_work() to wait on it. */ void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, struct delayed_work *dwork) @@ -579,7 +587,7 @@ void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, EXPORT_SYMBOL(cancel_rearming_delayed_workqueue); /** - * cancel_rearming_delayed_work - reliably kill off a delayed keventd work whose handler rearms the delayed work. + * cancel_rearming_delayed_work - kill off a delayed keventd work whose handler rearms the delayed work. * @dwork: the delayed work struct */ void cancel_rearming_delayed_work(struct delayed_work *dwork) -- cgit v1.2.3 From 63bc0362521cbaae3ed17b8de7b094f9492453f0 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:16 -0700 Subject: unify queue_delayed_work() and queue_delayed_work_on() Change queue_delayed_work() to use queue_delayed_work_on() to avoid the code duplication (saves 133 bytes). Q: queue_delayed_work() enqueues &dwork->work directly when delay == 0, why? [jirislaby@gmail.com: oops fix] Signed-off-by: Oleg Nesterov Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index d107e1c3b07..0eb9b33f1d9 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -180,28 +180,11 @@ void delayed_work_timer_fn(unsigned long __data) int fastcall queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay) { - int ret = 0; - struct timer_list *timer = &dwork->timer; - struct work_struct *work = &dwork->work; - - timer_stats_timer_set_start_info(timer); + timer_stats_timer_set_start_info(&dwork->timer); if (delay == 0) - return queue_work(wq, work); - - if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) { - BUG_ON(timer_pending(timer)); - BUG_ON(!list_empty(&work->entry)); + return queue_work(wq, &dwork->work); - /* This stores cwq for the moment, for the timer_fn */ - set_wq_data(work, - per_cpu_ptr(wq->cpu_wq, raw_smp_processor_id())); - timer->expires = jiffies + delay; - timer->data = (unsigned long)dwork; - timer->function = delayed_work_timer_fn; - add_timer(timer); - ret = 1; - } - return ret; + return queue_delayed_work_on(-1, wq, dwork, delay); } EXPORT_SYMBOL_GPL(queue_delayed_work); @@ -227,11 +210,16 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, /* This stores cwq for the moment, for the timer_fn */ set_wq_data(work, - per_cpu_ptr(wq->cpu_wq, raw_smp_processor_id())); + per_cpu_ptr(wq->cpu_wq, wq->singlethread ? + singlethread_cpu : raw_smp_processor_id())); timer->expires = jiffies + delay; timer->data = (unsigned long)dwork; timer->function = delayed_work_timer_fn; - add_timer_on(timer, cpu); + + if (unlikely(cpu >= 0)) + add_timer_on(timer, cpu); + else + add_timer(timer); ret = 1; } return ret; -- cgit v1.2.3 From a848e3b67c07ed79374bd0f9b82f9ce45a419643 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:17 -0700 Subject: workqueue: introduce wq_per_cpu() helper Cleanup. A number of per_cpu_ptr(wq->cpu_wq, cpu) users have to check that cpu is valid for this wq. Make a simple helper. Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 0eb9b33f1d9..985902e2e07 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -86,6 +86,14 @@ static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq) ? &cpu_singlethread_map : &cpu_populated_map; } +static +struct cpu_workqueue_struct *wq_per_cpu(struct workqueue_struct *wq, int cpu) +{ + if (unlikely(is_single_threaded(wq))) + cpu = singlethread_cpu; + return per_cpu_ptr(wq->cpu_wq, cpu); +} + /* * Set the workqueue on which a work item is to be run * - Must *only* be called if the pending flag is set @@ -142,16 +150,14 @@ static void __queue_work(struct cpu_workqueue_struct *cwq, */ int fastcall queue_work(struct workqueue_struct *wq, struct work_struct *work) { - int ret = 0, cpu = get_cpu(); + int ret = 0; if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) { - if (unlikely(is_single_threaded(wq))) - cpu = singlethread_cpu; BUG_ON(!list_empty(&work->entry)); - __queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work); + __queue_work(wq_per_cpu(wq, get_cpu()), work); + put_cpu(); ret = 1; } - put_cpu(); return ret; } EXPORT_SYMBOL_GPL(queue_work); @@ -161,12 +167,8 @@ void delayed_work_timer_fn(unsigned long __data) struct delayed_work *dwork = (struct delayed_work *)__data; struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work); struct workqueue_struct *wq = cwq->wq; - int cpu = smp_processor_id(); - - if (unlikely(is_single_threaded(wq))) - cpu = singlethread_cpu; - __queue_work(per_cpu_ptr(wq->cpu_wq, cpu), &dwork->work); + __queue_work(wq_per_cpu(wq, smp_processor_id()), &dwork->work); } /** @@ -209,9 +211,7 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, BUG_ON(!list_empty(&work->entry)); /* This stores cwq for the moment, for the timer_fn */ - set_wq_data(work, - per_cpu_ptr(wq->cpu_wq, wq->singlethread ? - singlethread_cpu : raw_smp_processor_id())); + set_wq_data(work, wq_per_cpu(wq, raw_smp_processor_id())); timer->expires = jiffies + delay; timer->data = (unsigned long)dwork; timer->function = delayed_work_timer_fn; -- cgit v1.2.3 From 1634c48f8b85dcb05101f1eb2eab9af40b5976da Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:18 -0700 Subject: make cancel_rearming_delayed_work() work on any workqueue, not just keventd_wq cancel_rearming_delayed_workqueue(wq, dwork) doesn't need the first parameter. We don't hang on un-queued dwork any longer, and work->data doesn't change its type. This means we can always figure out "wq" from dwork when it is needed. Remove this parameter, and rename the function to cancel_rearming_delayed_work(). Re-create an inline "obsolete" cancel_rearming_delayed_workqueue(wq) which just calls cancel_rearming_delayed_work(). Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 13 ++++++++++--- kernel/workqueue.c | 27 +++++++++------------------ 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 2a58f16e196..27110c04f21 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -191,9 +191,6 @@ extern int current_is_keventd(void); extern int keventd_up(void); extern void init_workqueues(void); -void cancel_rearming_delayed_work(struct delayed_work *work); -void cancel_rearming_delayed_workqueue(struct workqueue_struct *, - struct delayed_work *); int execute_in_process_context(work_func_t fn, struct execute_work *); /* @@ -212,4 +209,14 @@ static inline int cancel_delayed_work(struct delayed_work *work) return ret; } +extern void cancel_rearming_delayed_work(struct delayed_work *work); + +/* Obsolete. use cancel_rearming_delayed_work() */ +static inline +void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, + struct delayed_work *work) +{ + cancel_rearming_delayed_work(work); +} + #endif diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 985902e2e07..41eaffd125c 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -555,32 +555,23 @@ void flush_work_keventd(struct work_struct *work) EXPORT_SYMBOL(flush_work_keventd); /** - * cancel_rearming_delayed_workqueue - kill off a delayed work whose handler rearms the delayed work. - * @wq: the controlling workqueue structure + * cancel_rearming_delayed_work - kill off a delayed work whose handler rearms the delayed work. * @dwork: the delayed work struct * * Note that the work callback function may still be running on return from * cancel_delayed_work(). Run flush_workqueue() or flush_work() to wait on it. */ -void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, - struct delayed_work *dwork) +void cancel_rearming_delayed_work(struct delayed_work *dwork) { - /* Was it ever queued ? */ - if (!get_wq_data(&dwork->work)) - return; + struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work); - while (!cancel_delayed_work(dwork)) - flush_workqueue(wq); -} -EXPORT_SYMBOL(cancel_rearming_delayed_workqueue); + /* Was it ever queued ? */ + if (cwq != NULL) { + struct workqueue_struct *wq = cwq->wq; -/** - * cancel_rearming_delayed_work - kill off a delayed keventd work whose handler rearms the delayed work. - * @dwork: the delayed work struct - */ -void cancel_rearming_delayed_work(struct delayed_work *dwork) -{ - cancel_rearming_delayed_workqueue(keventd_wq, dwork); + while (!cancel_delayed_work(dwork)) + flush_workqueue(wq); + } } EXPORT_SYMBOL(cancel_rearming_delayed_work); -- cgit v1.2.3 From c214b2cc5f9be7c236f9b91acf524688ff0e3e72 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:18 -0700 Subject: ipvs: flush defense_work before module unload net/ipv4/ipvs/ip_vs_core.c module_exit ip_vs_cleanup ip_vs_control_cleanup cancel_rearming_delayed_work // done This is unsafe. The module may be unloaded and the memory may be freed while defense_work's handler is still running/preempted. Do flush_work(&defense_work.work) after cancel_rearming_delayed_work(). Alternatively, we could add flush_work() to cancel_rearming_delayed_work(), but note that we can't change cancel_delayed_work() in the same manner because it may be called from atomic context. Signed-off-by: Oleg Nesterov Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/ipv4/ipvs/ip_vs_ctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index b3050a6817e..342e836677a 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -2387,6 +2387,7 @@ void ip_vs_control_cleanup(void) EnterFunction(2); ip_vs_trash_cleanup(); cancel_rearming_delayed_work(&defense_work); + flush_work_keventd(&defense_work.work); ip_vs_kill_estimator(&ip_vs_stats); unregister_sysctl_table(sysctl_header); proc_net_remove("ip_vs_stats"); -- cgit v1.2.3 From 23b2e5991afde5af91a1a661d7f47ee56120759e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:19 -0700 Subject: workqueue: kill NOAUTOREL works We don't have any users, and it is not so trivial to use NOAUTOREL works correctly. It is better to simplify API. Delete NOAUTOREL support and rename work_release to work_clear_pending to avoid a confusion. Signed-off-by: Oleg Nesterov Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 64 +++++++---------------------------------------- kernel/workqueue.c | 5 ++-- 2 files changed, 11 insertions(+), 58 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 27110c04f21..e1581dce589 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -24,15 +24,13 @@ typedef void (*work_func_t)(struct work_struct *work); struct work_struct { atomic_long_t data; #define WORK_STRUCT_PENDING 0 /* T if work item pending execution */ -#define WORK_STRUCT_NOAUTOREL 1 /* F if work item automatically released on exec */ #define WORK_STRUCT_FLAG_MASK (3UL) #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) struct list_head entry; work_func_t func; }; -#define WORK_DATA_INIT(autorelease) \ - ATOMIC_LONG_INIT((autorelease) << WORK_STRUCT_NOAUTOREL) +#define WORK_DATA_INIT() ATOMIC_LONG_INIT(0) struct delayed_work { struct work_struct work; @@ -44,14 +42,8 @@ struct execute_work { }; #define __WORK_INITIALIZER(n, f) { \ - .data = WORK_DATA_INIT(0), \ - .entry = { &(n).entry, &(n).entry }, \ - .func = (f), \ - } - -#define __WORK_INITIALIZER_NAR(n, f) { \ - .data = WORK_DATA_INIT(1), \ - .entry = { &(n).entry, &(n).entry }, \ + .data = WORK_DATA_INIT(), \ + .entry = { &(n).entry, &(n).entry }, \ .func = (f), \ } @@ -60,23 +52,12 @@ struct execute_work { .timer = TIMER_INITIALIZER(NULL, 0, 0), \ } -#define __DELAYED_WORK_INITIALIZER_NAR(n, f) { \ - .work = __WORK_INITIALIZER_NAR((n).work, (f)), \ - .timer = TIMER_INITIALIZER(NULL, 0, 0), \ - } - #define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) -#define DECLARE_WORK_NAR(n, f) \ - struct work_struct n = __WORK_INITIALIZER_NAR(n, f) - #define DECLARE_DELAYED_WORK(n, f) \ struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) -#define DECLARE_DELAYED_WORK_NAR(n, f) \ - struct dwork_struct n = __DELAYED_WORK_INITIALIZER_NAR(n, f) - /* * initialize a work item's function pointer */ @@ -95,16 +76,9 @@ struct execute_work { * assignment of the work data initializer allows the compiler * to generate better code. */ -#define INIT_WORK(_work, _func) \ - do { \ - (_work)->data = (atomic_long_t) WORK_DATA_INIT(0); \ - INIT_LIST_HEAD(&(_work)->entry); \ - PREPARE_WORK((_work), (_func)); \ - } while (0) - -#define INIT_WORK_NAR(_work, _func) \ +#define INIT_WORK(_work, _func) \ do { \ - (_work)->data = (atomic_long_t) WORK_DATA_INIT(1); \ + (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ INIT_LIST_HEAD(&(_work)->entry); \ PREPARE_WORK((_work), (_func)); \ } while (0) @@ -115,12 +89,6 @@ struct execute_work { init_timer(&(_work)->timer); \ } while (0) -#define INIT_DELAYED_WORK_NAR(_work, _func) \ - do { \ - INIT_WORK_NAR(&(_work)->work, (_func)); \ - init_timer(&(_work)->timer); \ - } while (0) - #define INIT_DELAYED_WORK_DEFERRABLE(_work, _func) \ do { \ INIT_WORK(&(_work)->work, (_func)); \ @@ -143,24 +111,10 @@ struct execute_work { work_pending(&(w)->work) /** - * work_release - Release a work item under execution - * @work: The work item to release - * - * This is used to release a work item that has been initialised with automatic - * release mode disabled (WORK_STRUCT_NOAUTOREL is set). This gives the work - * function the opportunity to grab auxiliary data from the container of the - * work_struct before clearing the pending bit as the work_struct may be - * subject to deallocation the moment the pending bit is cleared. - * - * In such a case, this should be called in the work function after it has - * fetched any data it may require from the containter of the work_struct. - * After this function has been called, the work_struct may be scheduled for - * further execution or it may be deallocated unless other precautions are - * taken. - * - * This should also be used to release a delayed work item. + * work_clear_pending - for internal use only, mark a work item as not pending + * @work: The work item in question */ -#define work_release(work) \ +#define work_clear_pending(work) \ clear_bit(WORK_STRUCT_PENDING, work_data_bits(work)) @@ -205,7 +159,7 @@ static inline int cancel_delayed_work(struct delayed_work *work) ret = del_timer(&work->timer); if (ret) - work_release(&work->work); + work_clear_pending(&work->work); return ret; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 41eaffd125c..0611de815a8 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -246,8 +246,7 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq) spin_unlock_irq(&cwq->lock); BUG_ON(get_wq_data(work) != cwq); - if (!test_bit(WORK_STRUCT_NOAUTOREL, work_data_bits(work))) - work_release(work); + work_clear_pending(work); f(work); if (unlikely(in_atomic() || lockdep_depth(current) > 0)) { @@ -453,7 +452,7 @@ void flush_work(struct workqueue_struct *wq, struct work_struct *work) */ spin_lock_irq(&cwq->lock); list_del_init(&work->entry); - work_release(work); + work_clear_pending(work); spin_unlock_irq(&cwq->lock); for_each_cpu_mask(cpu, *cpu_map) -- cgit v1.2.3 From b9aac8e0d32499217417ff0b494731811f185b18 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:20 -0700 Subject: worker_thread: don't play with signals worker_thread() doesn't need to "Block and flush all signals", this was already done by its caller, kthread(). Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 0611de815a8..87693b37d01 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -290,18 +290,11 @@ static int worker_thread(void *__cwq) struct cpu_workqueue_struct *cwq = __cwq; DEFINE_WAIT(wait); struct k_sigaction sa; - sigset_t blocked; if (!cwq->wq->freezeable) current->flags |= PF_NOFREEZE; set_user_nice(current, -5); - - /* Block and flush all signals */ - sigfillset(&blocked); - sigprocmask(SIG_BLOCK, &blocked, NULL); - flush_signals(current); - /* * We inherited MPOL_INTERLEAVE from the booting kernel. * Set MPOL_DEFAULT to insure node local allocations. -- cgit v1.2.3 From 85f4186af944c1240c84934a9ab578743df2d69b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:20 -0700 Subject: worker_thread: fix racy try_to_freeze() usage worker_thread() can miss freeze_process()->signal_wake_up() if it happens between try_to_freeze() and prepare_to_wait(). We should check freezing() before entering schedule(). This race was introduced by me in [PATCH 1/1] workqueue: don't migrate pending works from the dead CPU Looks like mm/vmscan.c:kswapd() has the same race. Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 87693b37d01..63885abf1ba 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -308,14 +308,14 @@ static int worker_thread(void *__cwq) do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); for (;;) { - if (cwq->wq->freezeable) - try_to_freeze(); - prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE); - if (!cwq->should_stop && list_empty(&cwq->worklist)) + if (!freezing(current) && !cwq->should_stop + && list_empty(&cwq->worklist)) schedule(); finish_wait(&cwq->more_work, &wait); + try_to_freeze(); + if (cwq_should_stop(cwq)) break; -- cgit v1.2.3 From a4798833d26b293fd18b7bf102991426aa0b56fd Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:21 -0700 Subject: zap_other_threads: remove unneeded ->exit_signal change We already depend on fact that all sub-threads have ->exit_signal == -1, no need to set it in zap_other_threads(). Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/signal.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 4c8f49eadf7..23ae6d62fc4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -913,17 +913,6 @@ void zap_other_threads(struct task_struct *p) if (t->exit_state) continue; - /* - * We don't want to notify the parent, since we are - * killed as part of a thread group due to another - * thread doing an execve() or similar. So set the - * exit signal to -1 to allow immediate reaping of - * the process. But don't detach the thread group - * leader. - */ - if (t != p->group_leader) - t->exit_signal = -1; - /* SIGKILL will be handled before any pending SIGSTOP */ sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); -- cgit v1.2.3 From 5830c5902138f80b0a097b797200c739466beedd Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:34:22 -0700 Subject: slab: shut down cache_reaper when cpu goes down Shutdown the cache_reaper if the cpu is brought down and set the cache_reap.func to NULL. Otherwise hotplug shuts down the reaper for good. Signed-off-by: Christoph Lameter Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/slab.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mm/slab.c b/mm/slab.c index cceb347a354..1a7a10de2a4 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1279,6 +1279,20 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, start_cpu_timer(cpu); break; #ifdef CONFIG_HOTPLUG_CPU + case CPU_DOWN_PREPARE: + /* + * Shutdown cache reaper. Note that the cache_chain_mutex is + * held so that if cache_reap() is invoked it cannot do + * anything expensive but will only modify reap_work + * and reschedule the timer. + */ + cancel_rearming_delayed_work(&per_cpu(reap_work, cpu)); + /* Now the cache_reaper is guaranteed to be not running. */ + per_cpu(reap_work, cpu).work.func = NULL; + break; + case CPU_DOWN_FAILED: + start_cpu_timer(cpu); + break; case CPU_DEAD: /* * Even if all the cpus of a node are down, we don't free the -- cgit v1.2.3 From 28e53bddf814485699a4142bc056fd37d4e11dd4 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:22 -0700 Subject: unify flush_work/flush_work_keventd and rename it to cancel_work_sync flush_work(wq, work) doesn't need the first parameter, we can use cwq->wq (this was possible from the very beginnig, I missed this). So we can unify flush_work_keventd and flush_work. Also, rename flush_work() to cancel_work_sync() and fix all callers. Perhaps this is not the best name, but "flush_work" is really bad. (akpm: this is why the earlier patches bypassed maintainers) Signed-off-by: Oleg Nesterov Cc: Jeff Garzik Cc: "David S. Miller" Cc: Jens Axboe Cc: Tejun Heo Cc: Auke Kok , Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/ll_rw_blk.c | 2 +- drivers/ata/libata-core.c | 8 ++++---- drivers/net/e1000/e1000_main.c | 2 +- drivers/net/phy/phy.c | 4 ++-- drivers/net/tg3.c | 2 +- fs/aio.c | 4 ++-- include/linux/workqueue.h | 21 ++++++++++++--------- kernel/workqueue.c | 36 +++++++++++++++++------------------- net/ipv4/ipvs/ip_vs_ctl.c | 2 +- 9 files changed, 41 insertions(+), 40 deletions(-) diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index c059767c552..df506571ed6 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -3633,7 +3633,7 @@ EXPORT_SYMBOL(kblockd_schedule_work); void kblockd_flush_work(struct work_struct *work) { - flush_work(kblockd_workqueue, work); + cancel_work_sync(work); } EXPORT_SYMBOL(kblockd_flush_work); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b74e56caba6..fef87dd70d1 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1316,7 +1316,7 @@ void ata_port_flush_task(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); DPRINTK("flush #1\n"); - flush_work(ata_wq, &ap->port_task.work); /* akpm: seems unneeded */ + cancel_work_sync(&ap->port_task.work); /* akpm: seems unneeded */ /* * At this point, if a task is running, it's guaranteed to see @@ -1327,7 +1327,7 @@ void ata_port_flush_task(struct ata_port *ap) if (ata_msg_ctl(ap)) ata_port_printk(ap, KERN_DEBUG, "%s: flush #2\n", __FUNCTION__); - flush_work(ata_wq, &ap->port_task.work); + cancel_work_sync(&ap->port_task.work); } spin_lock_irqsave(ap->lock, flags); @@ -6475,9 +6475,9 @@ void ata_port_detach(struct ata_port *ap) /* Flush hotplug task. The sequence is similar to * ata_port_flush_task(). */ - flush_work(ata_aux_wq, &ap->hotplug_task.work); /* akpm: why? */ + cancel_work_sync(&ap->hotplug_task.work); /* akpm: why? */ cancel_delayed_work(&ap->hotplug_task); - flush_work(ata_aux_wq, &ap->hotplug_task.work); + cancel_work_sync(&ap->hotplug_task.work); skip_eh: /* remove the associated SCSI host */ diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 397e25bdbfe..637ae8f6879 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1214,7 +1214,7 @@ e1000_remove(struct pci_dev *pdev) int i; #endif - flush_work_keventd(&adapter->reset_task); + cancel_work_sync(&adapter->reset_task); e1000_release_manageability(adapter); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index f445c465b14..f71dab34766 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -663,9 +663,9 @@ int phy_stop_interrupts(struct phy_device *phydev) /* * Finish any pending work; we might have been scheduled to be called - * from keventd ourselves, but flush_work_keventd() handles that. + * from keventd ourselves, but cancel_work_sync() handles that. */ - flush_work_keventd(&phydev->phy_queue); + cancel_work_sync(&phydev->phy_queue); free_irq(phydev->irq, phydev); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 0c0f9c81732..923b9c725cc 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -7386,7 +7386,7 @@ static int tg3_close(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); - flush_work_keventd(&tp->reset_task); + cancel_work_sync(&tp->reset_task); netif_stop_queue(dev); diff --git a/fs/aio.c b/fs/aio.c index d18690bb03e..ac1c1587aa0 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -348,7 +348,7 @@ void fastcall exit_aio(struct mm_struct *mm) /* * Ensure we don't leave the ctx on the aio_wq */ - flush_work(aio_wq, &ctx->wq.work); + cancel_work_sync(&ctx->wq.work); if (1 != atomic_read(&ctx->users)) printk(KERN_DEBUG @@ -371,7 +371,7 @@ void fastcall __put_ioctx(struct kioctx *ctx) BUG_ON(ctx->reqs_active); cancel_delayed_work(&ctx->wq); - flush_work(aio_wq, &ctx->wq.work); + cancel_work_sync(&ctx->wq.work); aio_free_ring(ctx); mmdrop(ctx->mm); ctx->mm = NULL; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index e1581dce589..d555f31c074 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -128,30 +128,33 @@ extern struct workqueue_struct *__create_workqueue(const char *name, extern void destroy_workqueue(struct workqueue_struct *wq); extern int FASTCALL(queue_work(struct workqueue_struct *wq, struct work_struct *work)); -extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay)); +extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, + struct delayed_work *work, unsigned long delay)); extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, - struct delayed_work *work, unsigned long delay); + struct delayed_work *work, unsigned long delay); + extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); -extern void flush_work(struct workqueue_struct *wq, struct work_struct *work); -extern void flush_work_keventd(struct work_struct *work); +extern void flush_scheduled_work(void); extern int FASTCALL(schedule_work(struct work_struct *work)); -extern int FASTCALL(schedule_delayed_work(struct delayed_work *work, unsigned long delay)); - -extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); +extern int FASTCALL(schedule_delayed_work(struct delayed_work *work, + unsigned long delay)); +extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, + unsigned long delay); extern int schedule_on_each_cpu(work_func_t func); -extern void flush_scheduled_work(void); extern int current_is_keventd(void); extern int keventd_up(void); extern void init_workqueues(void); int execute_in_process_context(work_func_t fn, struct execute_work *); +extern void cancel_work_sync(struct work_struct *work); + /* * Kill off a pending schedule_delayed_work(). Note that the work callback * function may still be running on return from cancel_delayed_work(), unless * it returns 1 and the work doesn't re-arm itself. Run flush_workqueue() or - * flush_work() or cancel_work_sync() to wait on it. + * cancel_work_sync() to wait on it. */ static inline int cancel_delayed_work(struct delayed_work *work) { diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 63885abf1ba..c9ab4293904 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -413,23 +413,23 @@ static void wait_on_work(struct cpu_workqueue_struct *cwq, } /** - * flush_work - block until a work_struct's callback has terminated - * @wq: the workqueue on which the work is queued + * cancel_work_sync - block until a work_struct's callback has terminated * @work: the work which is to be flushed * - * flush_work() will attempt to cancel the work if it is queued. If the work's - * callback appears to be running, flush_work() will block until it has - * completed. + * cancel_work_sync() will attempt to cancel the work if it is queued. If the + * work's callback appears to be running, cancel_work_sync() will block until + * it has completed. * - * flush_work() is designed to be used when the caller is tearing down data - * structures which the callback function operates upon. It is expected that, - * prior to calling flush_work(), the caller has arranged for the work to not - * be requeued. + * cancel_work_sync() is designed to be used when the caller is tearing down + * data structures which the callback function operates upon. It is expected + * that, prior to calling cancel_work_sync(), the caller has arranged for the + * work to not be requeued. */ -void flush_work(struct workqueue_struct *wq, struct work_struct *work) +void cancel_work_sync(struct work_struct *work) { - const cpumask_t *cpu_map = wq_cpu_map(wq); struct cpu_workqueue_struct *cwq; + struct workqueue_struct *wq; + const cpumask_t *cpu_map; int cpu; might_sleep(); @@ -448,10 +448,13 @@ void flush_work(struct workqueue_struct *wq, struct work_struct *work) work_clear_pending(work); spin_unlock_irq(&cwq->lock); + wq = cwq->wq; + cpu_map = wq_cpu_map(wq); + for_each_cpu_mask(cpu, *cpu_map) wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); } -EXPORT_SYMBOL_GPL(flush_work); +EXPORT_SYMBOL_GPL(cancel_work_sync); static struct workqueue_struct *keventd_wq; @@ -540,18 +543,13 @@ void flush_scheduled_work(void) } EXPORT_SYMBOL(flush_scheduled_work); -void flush_work_keventd(struct work_struct *work) -{ - flush_work(keventd_wq, work); -} -EXPORT_SYMBOL(flush_work_keventd); - /** * cancel_rearming_delayed_work - kill off a delayed work whose handler rearms the delayed work. * @dwork: the delayed work struct * * Note that the work callback function may still be running on return from - * cancel_delayed_work(). Run flush_workqueue() or flush_work() to wait on it. + * cancel_delayed_work(). Run flush_workqueue() or cancel_work_sync() to wait + * on it. */ void cancel_rearming_delayed_work(struct delayed_work *dwork) { diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index 342e836677a..68fe1d4d021 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -2387,7 +2387,7 @@ void ip_vs_control_cleanup(void) EnterFunction(2); ip_vs_trash_cleanup(); cancel_rearming_delayed_work(&defense_work); - flush_work_keventd(&defense_work.work); + cancel_work_sync(&defense_work.work); ip_vs_kill_estimator(&ip_vs_stats); unregister_sysctl_table(sysctl_header); proc_net_remove("ip_vs_stats"); -- cgit v1.2.3 From c93465181fed0f8f5942a41108943dadea0aa345 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:23 -0700 Subject: ____call_usermodehelper: don't flush_signals() ____call_usermodehelper() has no reason for flush_signals(). It is a fresh forked process which is going to exec a user-space application or exit on failure. Signed-off-by: Oleg Nesterov Cc: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/kmod.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/kmod.c b/kernel/kmod.c index 49cc4b9c1a8..6cea9db25c3 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -135,7 +135,6 @@ static int ____call_usermodehelper(void *data) /* Unblock all signals and set the session keyring. */ new_session = key_get(sub_info->ring); - flush_signals(current); spin_lock_irq(¤t->sighand->siglock); old_session = __install_session_keyring(current, new_session); flush_signal_handlers(current, 1); -- cgit v1.2.3 From 73c279927f89561ecb45b2dfdf9314bafcfd9f67 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 9 May 2007 02:34:32 -0700 Subject: kthread: don't depend on work queues Currently there is a circular reference between work queue initialization and kthread initialization. This prevents the kthread infrastructure from initializing until after work queues have been initialized. We want the properties of tasks created with kthread_create to be as close as possible to the init_task and to not be contaminated by user processes. The later we start our kthreadd that creates these tasks the harder it is to avoid contamination from user processes and the more of a mess we have to clean up because the defaults have changed on us. So this patch modifies the kthread support to not use work queues but to instead use a simple list of structures, and to have kthreadd start from init_task immediately after our kernel thread that execs /sbin/init. By being a true child of init_task we only have to change those process settings that we want to have different from init_task, such as our process name, the cpus that are allowed, blocking all signals and setting SIGCHLD to SIG_IGN so that all of our children are reaped automatically. By being a true child of init_task we also naturally get our ppid set to 0 and do not wind up as a child of PID == 1. Ensuring that tasks generated by kthread_create will not slow down the functioning of the wait family of functions. [akpm@linux-foundation.org: use interruptible sleeps] Signed-off-by: Eric W. Biederman Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kthread.h | 3 ++ init/main.c | 5 ++ kernel/kthread.c | 124 ++++++++++++++++++++++++++---------------------- 3 files changed, 76 insertions(+), 56 deletions(-) diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 1c65e7a9f18..00dd957e245 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -30,4 +30,7 @@ void kthread_bind(struct task_struct *k, unsigned int cpu); int kthread_stop(struct task_struct *k); int kthread_should_stop(void); +int kthreadd(void *unused); +extern struct task_struct *kthreadd_task; + #endif /* _LINUX_KTHREAD_H */ diff --git a/init/main.c b/init/main.c index c1537e0ddce..e8d080cab44 100644 --- a/init/main.c +++ b/init/main.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -425,8 +426,12 @@ static void __init setup_command_line(char *command_line) static void noinline rest_init(void) __releases(kernel_lock) { + int pid; + kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); numa_default_policy(); + pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); + kthreadd_task = find_task_by_pid(pid); unlock_kernel(); /* diff --git a/kernel/kthread.c b/kernel/kthread.c index 87c50ccd1d4..0eb0070a3c5 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -1,7 +1,7 @@ /* Kernel thread helper functions. * Copyright (C) 2004 IBM Corporation, Rusty Russell. * - * Creation is done via keventd, so that we get a clean environment + * Creation is done via kthreadd, so that we get a clean environment * even if we're invoked from userspace (think modprobe, hotplug cpu, * etc.). */ @@ -15,24 +15,22 @@ #include #include -/* - * We dont want to execute off keventd since it might - * hold a semaphore our callers hold too: - */ -static struct workqueue_struct *helper_wq; +static DEFINE_SPINLOCK(kthread_create_lock); +static LIST_HEAD(kthread_create_list); +struct task_struct *kthreadd_task; struct kthread_create_info { - /* Information passed to kthread() from keventd. */ + /* Information passed to kthread() from kthreadd. */ int (*threadfn)(void *data); void *data; struct completion started; - /* Result passed back to kthread_create() from keventd. */ + /* Result passed back to kthread_create() from kthreadd. */ struct task_struct *result; struct completion done; - struct work_struct work; + struct list_head list; }; struct kthread_stop_info @@ -60,42 +58,17 @@ int kthread_should_stop(void) } EXPORT_SYMBOL(kthread_should_stop); -static void kthread_exit_files(void) -{ - struct fs_struct *fs; - struct task_struct *tsk = current; - - exit_fs(tsk); /* current->fs->count--; */ - fs = init_task.fs; - tsk->fs = fs; - atomic_inc(&fs->count); - exit_files(tsk); - current->files = init_task.files; - atomic_inc(&tsk->files->count); -} - static int kthread(void *_create) { struct kthread_create_info *create = _create; int (*threadfn)(void *data); void *data; - sigset_t blocked; int ret = -EINTR; - kthread_exit_files(); - - /* Copy data: it's on keventd's stack */ + /* Copy data: it's on kthread's stack */ threadfn = create->threadfn; data = create->data; - /* Block and flush all signals (in case we're not from keventd). */ - sigfillset(&blocked); - sigprocmask(SIG_BLOCK, &blocked, NULL); - flush_signals(current); - - /* By default we can run anywhere, unlike keventd. */ - set_cpus_allowed(current, CPU_MASK_ALL); - /* OK, tell user we're spawned, wait for stop or wakeup */ __set_current_state(TASK_INTERRUPTIBLE); complete(&create->started); @@ -112,11 +85,8 @@ static int kthread(void *_create) return 0; } -/* We are keventd: create a thread. */ -static void keventd_create_kthread(struct work_struct *work) +static void create_kthread(struct kthread_create_info *create) { - struct kthread_create_info *create = - container_of(work, struct kthread_create_info, work); int pid; /* We want our own signal handler (we take no signals by default). */ @@ -162,17 +132,14 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), create.data = data; init_completion(&create.started); init_completion(&create.done); - INIT_WORK(&create.work, keventd_create_kthread); - - /* - * The workqueue needs to start up first: - */ - if (!helper_wq) - create.work.func(&create.work); - else { - queue_work(helper_wq, &create.work); - wait_for_completion(&create.done); - } + + spin_lock(&kthread_create_lock); + list_add_tail(&create.list, &kthread_create_list); + wake_up_process(kthreadd_task); + spin_unlock(&kthread_create_lock); + + wait_for_completion(&create.done); + if (!IS_ERR(create.result)) { va_list args; va_start(args, namefmt); @@ -180,7 +147,6 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), namefmt, args); va_end(args); } - return create.result; } EXPORT_SYMBOL(kthread_create); @@ -245,12 +211,58 @@ int kthread_stop(struct task_struct *k) } EXPORT_SYMBOL(kthread_stop); -static __init int helper_init(void) + +static __init void kthreadd_setup(void) { - helper_wq = create_singlethread_workqueue("kthread"); - BUG_ON(!helper_wq); + struct task_struct *tsk = current; + struct k_sigaction sa; + sigset_t blocked; - return 0; + set_task_comm(tsk, "kthreadd"); + + /* Block and flush all signals */ + sigfillset(&blocked); + sigprocmask(SIG_BLOCK, &blocked, NULL); + flush_signals(tsk); + + /* SIG_IGN makes children autoreap: see do_notify_parent(). */ + sa.sa.sa_handler = SIG_IGN; + sa.sa.sa_flags = 0; + siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); + do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); + + set_user_nice(current, -5); + set_cpus_allowed(current, CPU_MASK_ALL); } -core_initcall(helper_init); +int kthreadd(void *unused) +{ + /* Setup a clean context for our children to inherit. */ + kthreadd_setup(); + + current->flags |= PF_NOFREEZE; + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (list_empty(&kthread_create_list)) + schedule(); + __set_current_state(TASK_RUNNING); + + spin_lock(&kthread_create_lock); + while (!list_empty(&kthread_create_list)) { + struct kthread_create_info *create; + + create = list_entry(kthread_create_list.next, + struct kthread_create_info, list); + list_del_init(&create->list); + spin_unlock(&kthread_create_lock); + + create_kthread(create); + + spin_lock(&kthread_create_lock); + } + spin_unlock(&kthread_create_lock); + } + + return 0; +} -- cgit v1.2.3 From 49d769d52e16efabd3ad47b7995522fff771371d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 9 May 2007 02:34:33 -0700 Subject: Change reparent_to_init to reparent_to_kthreadd When a kernel thread calls daemonize, instead of reparenting the thread to init reparent the thread to kthreadd next to the threads created by kthread_create. This is really just a stop gap until daemonize goes away, but it does ensure no kernel threads are under init and they are all in one place that is easy to find. Signed-off-by: Eric W. Biederman Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/exit.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index f5a7abb621f..bc982cd7274 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -254,26 +255,25 @@ static int has_stopped_jobs(struct pid *pgrp) } /** - * reparent_to_init - Reparent the calling kernel thread to the init task of the pid space that the thread belongs to. + * reparent_to_kthreadd - Reparent the calling kernel thread to kthreadd * * If a kernel thread is launched as a result of a system call, or if - * it ever exits, it should generally reparent itself to init so that - * it is correctly cleaned up on exit. + * it ever exits, it should generally reparent itself to kthreadd so it + * isn't in the way of other processes and is correctly cleaned up on exit. * * The various task state such as scheduling policy and priority may have * been inherited from a user process, so we reset them to sane values here. * - * NOTE that reparent_to_init() gives the caller full capabilities. + * NOTE that reparent_to_kthreadd() gives the caller full capabilities. */ -static void reparent_to_init(void) +static void reparent_to_kthreadd(void) { write_lock_irq(&tasklist_lock); ptrace_unlink(current); /* Reparent to init */ remove_parent(current); - current->parent = child_reaper(current); - current->real_parent = child_reaper(current); + current->real_parent = current->parent = kthreadd_task; add_parent(current); /* Set the exit signal to SIGCHLD so we signal init on exit */ @@ -400,7 +400,7 @@ void daemonize(const char *name, ...) current->files = init_task.files; atomic_inc(¤t->files->count); - reparent_to_init(); + reparent_to_kthreadd(); } EXPORT_SYMBOL(daemonize); -- cgit v1.2.3 From b8a124da9261873e3e3541898d5c46d273afee34 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:35 -0700 Subject: usbatm_heavy_init: don't use CLONE_SIGHAND usbatm_do_heavy_init() calls allow_signal() which plays with parent process's ->sighand. Signed-off-by: Oleg Nesterov Acked-by: Duncan Sands Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/usb/atm/usbatm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index b082d95bbba..11e9b15ca45 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1033,7 +1033,7 @@ static int usbatm_do_heavy_init(void *arg) static int usbatm_heavy_init(struct usbatm_data *instance) { - int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_KERNEL); + int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_FS | CLONE_FILES); if (ret < 0) { usb_err(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret); -- cgit v1.2.3 From 90cce03d9bfcb28600a56efef6b0a5a4fbf6c2b1 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:36 -0700 Subject: wait_for_helper: remove unneeded do_sigaction() allow_signal(SIGCHLD) does all necessary job, no need to call do_sigaction() prior to. Signed-off-by: Oleg Nesterov Cc: Rusty Russell Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/kmod.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kernel/kmod.c b/kernel/kmod.c index 6cea9db25c3..4d32eb07717 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -185,14 +185,9 @@ static int wait_for_helper(void *data) { struct subprocess_info *sub_info = data; pid_t pid; - struct k_sigaction sa; /* Install a handler: if SIGCLD isn't handled sys_wait4 won't * populate the status, but will return -ECHILD. */ - sa.sa.sa_handler = SIG_IGN; - sa.sa.sa_flags = 0; - siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); - do_sigaction(SIGCHLD, &sa, NULL); allow_signal(SIGCHLD); pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD); -- cgit v1.2.3 From 5de18d169739293e27e0cf9acfc75a2d2f4aa572 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:36 -0700 Subject: worker_thread: don't play with SIGCHLD and numa policy worker_thread() inherits ignored SIGCHLD and numa_default_policy() from its parent, kthreadd. No need to setup this again. Signed-off-by: Oleg Nesterov Acked-by: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index c9ab4293904..25cee1afe6f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -289,23 +289,11 @@ static int worker_thread(void *__cwq) { struct cpu_workqueue_struct *cwq = __cwq; DEFINE_WAIT(wait); - struct k_sigaction sa; if (!cwq->wq->freezeable) current->flags |= PF_NOFREEZE; set_user_nice(current, -5); - /* - * We inherited MPOL_INTERLEAVE from the booting kernel. - * Set MPOL_DEFAULT to insure node local allocations. - */ - numa_default_policy(); - - /* SIG_IGN makes children autoreap: see do_notify_parent(). */ - sa.sa.sa_handler = SIG_IGN; - sa.sa.sa_flags = 0; - siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); - do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); for (;;) { prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE); -- cgit v1.2.3 From 10ab825bdef8df510f99c703a5a2d9b13a4e31a5 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:37 -0700 Subject: change kernel threads to ignore signals instead of blocking them Currently kernel threads use sigprocmask(SIG_BLOCK) to protect against signals. This doesn't prevent the signal delivery, this only blocks signal_wake_up(). Every "killall -33 kthreadd" means a "struct siginfo" leak. Change kthreadd_setup() to set all handlers to SIG_IGN instead of blocking them (make a new helper ignore_signals() for that). If the kernel thread needs some signal, it should use allow_signal() anyway, and in that case it should not use CLONE_SIGHAND. Note that we can't change daemonize() (should die!) in the same way, because it can be used along with CLONE_SIGHAND. This means that allow_signal() still should unblock the signal to work correctly with daemonize()ed threads. However, disallow_signal() doesn't block the signal any longer but ignores it. NOTE: with or without this patch the kernel threads are not protected from handle_stop_signal(), this seems harmless, but not good. Signed-off-by: Oleg Nesterov Acked-by: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 1 + kernel/exit.c | 2 +- kernel/kthread.c | 17 +++-------------- kernel/signal.c | 10 ++++++++++ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 3d95c480f58..28000b1658f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1317,6 +1317,7 @@ extern int in_egroup_p(gid_t); extern void proc_caches_init(void); extern void flush_signals(struct task_struct *); +extern void ignore_signals(struct task_struct *); extern void flush_signal_handlers(struct task_struct *, int force_default); extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info); diff --git a/kernel/exit.c b/kernel/exit.c index bc982cd7274..b0c6f0c3a2d 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -347,7 +347,7 @@ int disallow_signal(int sig) return -EINVAL; spin_lock_irq(¤t->sighand->siglock); - sigaddset(¤t->blocked, sig); + current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); return 0; diff --git a/kernel/kthread.c b/kernel/kthread.c index 0eb0070a3c5..df8a8e8f6ca 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -215,24 +215,13 @@ EXPORT_SYMBOL(kthread_stop); static __init void kthreadd_setup(void) { struct task_struct *tsk = current; - struct k_sigaction sa; - sigset_t blocked; set_task_comm(tsk, "kthreadd"); - /* Block and flush all signals */ - sigfillset(&blocked); - sigprocmask(SIG_BLOCK, &blocked, NULL); - flush_signals(tsk); + ignore_signals(tsk); - /* SIG_IGN makes children autoreap: see do_notify_parent(). */ - sa.sa.sa_handler = SIG_IGN; - sa.sa.sa_flags = 0; - siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); - do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); - - set_user_nice(current, -5); - set_cpus_allowed(current, CPU_MASK_ALL); + set_user_nice(tsk, -5); + set_cpus_allowed(tsk, CPU_MASK_ALL); } int kthreadd(void *unused) diff --git a/kernel/signal.c b/kernel/signal.c index 23ae6d62fc4..2ac3a668d9d 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -209,6 +209,16 @@ void flush_signals(struct task_struct *t) spin_unlock_irqrestore(&t->sighand->siglock, flags); } +void ignore_signals(struct task_struct *t) +{ + int i; + + for (i = 0; i < _NSIG; ++i) + t->sighand->action[i].sa.sa_handler = SIG_IGN; + + flush_signals(t); +} + /* * Flush all handlers for a task. */ -- cgit v1.2.3 From 7b0834c26fd796c79dfcc3939ed2b9122b75246f Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Wed, 9 May 2007 02:34:41 -0700 Subject: Remove kthread_bind() call from _cpu_down() We are anyway kthread_stop()ping other per-cpu kernel threads after move_task_off_dead_cpu(), so we can do it with the stop_machine_run thread as well. I just checked with Vatsa if there was any subtle reason why they had put in the kthread_bind() in cpu.c. Vatsa cannot seem to recollect any and I can't see any. So let us just remove the kthread_bind. Signed-off-by: Gautham R Shenoy Cc: Oleg Nesterov Cc: "Eric W. Biederman" Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/cpu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 1a823944e97..28cb6c71a47 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -175,10 +175,6 @@ static int _cpu_down(unsigned int cpu) /* This actually kills the CPU. */ __cpu_die(cpu); - /* Move it here so it can run. */ - kthread_bind(p, get_cpu()); - put_cpu(); - /* CPU is completely dead: tell everyone. Too late to complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD, hcpu) == NOTIFY_BAD) BUG(); -- cgit v1.2.3 From 6e84d644b5929789398914b0ccf447355dec6fb0 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 9 May 2007 02:34:46 -0700 Subject: make cancel_rearming_delayed_work() reliable Thanks to Jarek Poplawski for the ideas and for spotting the bug in the initial draft patch. cancel_rearming_delayed_work() currently has many limitations, because it requires that dwork always re-arms itself via queue_delayed_work(). So it hangs forever if dwork doesn't do this, or cancel_rearming_delayed_work/ cancel_delayed_work was already called. It uses flush_workqueue() in a loop, so it can't be used if workqueue was freezed, and it is potentially live- lockable on busy system if delay is small. With this patch cancel_rearming_delayed_work() doesn't make any assumptions about dwork, it can re-arm itself via queue_delayed_work(), or queue_work(), or do nothing. As a "side effect", cancel_work_sync() was changed to handle re-arming works as well. Disadvantages: - this patch adds wmb() to insert_work(). - slowdowns the fast path (when del_timer() succeeds on entry) of cancel_rearming_delayed_work(), because wait_on_work() is called unconditionally. In that case, compared to the old version, we are doing "unneeded" lock/unlock for each online CPU. On the other hand, this means we don't need to use cancel_work_sync() after cancel_rearming_delayed_work(). - complicates the code (.text grows by 130 bytes). [akpm@linux-foundation.org: fix speling] Signed-off-by: Oleg Nesterov Cc: David Chinner Cc: David Howells Cc: Gautham Shenoy Acked-by: Jarek Poplawski Cc: Srivatsa Vaddagiri Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/workqueue.c | 140 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 49 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 25cee1afe6f..b976ed87dd3 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -120,6 +120,11 @@ static void insert_work(struct cpu_workqueue_struct *cwq, struct work_struct *work, int tail) { set_wq_data(work, cwq); + /* + * Ensure that we get the right work->data if we see the + * result of list_add() below, see try_to_grab_pending(). + */ + smp_wmb(); if (tail) list_add_tail(&work->entry, &cwq->worklist); else @@ -383,7 +388,46 @@ void fastcall flush_workqueue(struct workqueue_struct *wq) } EXPORT_SYMBOL_GPL(flush_workqueue); -static void wait_on_work(struct cpu_workqueue_struct *cwq, +/* + * Upon a successful return, the caller "owns" WORK_STRUCT_PENDING bit, + * so this work can't be re-armed in any way. + */ +static int try_to_grab_pending(struct work_struct *work) +{ + struct cpu_workqueue_struct *cwq; + int ret = 0; + + if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) + return 1; + + /* + * The queueing is in progress, or it is already queued. Try to + * steal it from ->worklist without clearing WORK_STRUCT_PENDING. + */ + + cwq = get_wq_data(work); + if (!cwq) + return ret; + + spin_lock_irq(&cwq->lock); + if (!list_empty(&work->entry)) { + /* + * This work is queued, but perhaps we locked the wrong cwq. + * In that case we must see the new value after rmb(), see + * insert_work()->wmb(). + */ + smp_rmb(); + if (cwq == get_wq_data(work)) { + list_del_init(&work->entry); + ret = 1; + } + } + spin_unlock_irq(&cwq->lock); + + return ret; +} + +static void wait_on_cpu_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) { struct wq_barrier barr; @@ -400,20 +444,7 @@ static void wait_on_work(struct cpu_workqueue_struct *cwq, wait_for_completion(&barr.done); } -/** - * cancel_work_sync - block until a work_struct's callback has terminated - * @work: the work which is to be flushed - * - * cancel_work_sync() will attempt to cancel the work if it is queued. If the - * work's callback appears to be running, cancel_work_sync() will block until - * it has completed. - * - * cancel_work_sync() is designed to be used when the caller is tearing down - * data structures which the callback function operates upon. It is expected - * that, prior to calling cancel_work_sync(), the caller has arranged for the - * work to not be requeued. - */ -void cancel_work_sync(struct work_struct *work) +static void wait_on_work(struct work_struct *work) { struct cpu_workqueue_struct *cwq; struct workqueue_struct *wq; @@ -423,29 +454,62 @@ void cancel_work_sync(struct work_struct *work) might_sleep(); cwq = get_wq_data(work); - /* Was it ever queued ? */ if (!cwq) return; - /* - * This work can't be re-queued, no need to re-check that - * get_wq_data() is still the same when we take cwq->lock. - */ - spin_lock_irq(&cwq->lock); - list_del_init(&work->entry); - work_clear_pending(work); - spin_unlock_irq(&cwq->lock); - wq = cwq->wq; cpu_map = wq_cpu_map(wq); for_each_cpu_mask(cpu, *cpu_map) - wait_on_work(per_cpu_ptr(wq->cpu_wq, cpu), work); + wait_on_cpu_work(per_cpu_ptr(wq->cpu_wq, cpu), work); +} + +/** + * cancel_work_sync - block until a work_struct's callback has terminated + * @work: the work which is to be flushed + * + * cancel_work_sync() will cancel the work if it is queued. If the work's + * callback appears to be running, cancel_work_sync() will block until it + * has completed. + * + * It is possible to use this function if the work re-queues itself. It can + * cancel the work even if it migrates to another workqueue, however in that + * case it only guarantees that work->func() has completed on the last queued + * workqueue. + * + * cancel_work_sync(&delayed_work->work) should be used only if ->timer is not + * pending, otherwise it goes into a busy-wait loop until the timer expires. + * + * The caller must ensure that workqueue_struct on which this work was last + * queued can't be destroyed before this function returns. + */ +void cancel_work_sync(struct work_struct *work) +{ + while (!try_to_grab_pending(work)) + cpu_relax(); + wait_on_work(work); + work_clear_pending(work); } EXPORT_SYMBOL_GPL(cancel_work_sync); +/** + * cancel_rearming_delayed_work - reliably kill off a delayed work. + * @dwork: the delayed work struct + * + * It is possible to use this function if @dwork rearms itself via queue_work() + * or queue_delayed_work(). See also the comment for cancel_work_sync(). + */ +void cancel_rearming_delayed_work(struct delayed_work *dwork) +{ + while (!del_timer(&dwork->timer) && + !try_to_grab_pending(&dwork->work)) + cpu_relax(); + wait_on_work(&dwork->work); + work_clear_pending(&dwork->work); +} +EXPORT_SYMBOL(cancel_rearming_delayed_work); -static struct workqueue_struct *keventd_wq; +static struct workqueue_struct *keventd_wq __read_mostly; /** * schedule_work - put work task in global workqueue @@ -531,28 +595,6 @@ void flush_scheduled_work(void) } EXPORT_SYMBOL(flush_scheduled_work); -/** - * cancel_rearming_delayed_work - kill off a delayed work whose handler rearms the delayed work. - * @dwork: the delayed work struct - * - * Note that the work callback function may still be running on return from - * cancel_delayed_work(). Run flush_workqueue() or cancel_work_sync() to wait - * on it. - */ -void cancel_rearming_delayed_work(struct delayed_work *dwork) -{ - struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work); - - /* Was it ever queued ? */ - if (cwq != NULL) { - struct workqueue_struct *wq = cwq->wq; - - while (!cancel_delayed_work(dwork)) - flush_workqueue(wq); - } -} -EXPORT_SYMBOL(cancel_rearming_delayed_work); - /** * execute_in_process_context - reliably execute the routine with user context * @fn: the function to execute -- cgit v1.2.3 From 8842c9655b2b7f0e8e6c50a773b649e5d8a57678 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 9 May 2007 02:34:46 -0700 Subject: remove nfs4_acl_add_ace() nfs4_acl_add_ace() can now be removed. Signed-off-by: Adrian Bunk Acked-by: Neil Brown Acked-by: J. Bruce Fields Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfs4acl.c | 17 ----------------- include/linux/nfs4_acl.h | 1 - 2 files changed, 18 deletions(-) diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 673a53c014a..cc3b7badd48 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -137,7 +137,6 @@ struct ace_container { static short ace2type(struct nfs4_ace *); static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); -void nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t); struct nfs4_acl * nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, @@ -785,21 +784,6 @@ nfs4_acl_new(int n) return acl; } -void -nfs4_acl_add_ace(struct nfs4_acl *acl, u32 type, u32 flag, u32 access_mask, - int whotype, uid_t who) -{ - struct nfs4_ace *ace = acl->aces + acl->naces; - - ace->type = type; - ace->flag = flag; - ace->access_mask = access_mask; - ace->whotype = whotype; - ace->who = who; - - acl->naces++; -} - static struct { char *string; int stringlen; @@ -851,6 +835,5 @@ nfs4_acl_write_who(int who, char *p) } EXPORT_SYMBOL(nfs4_acl_new); -EXPORT_SYMBOL(nfs4_acl_add_ace); EXPORT_SYMBOL(nfs4_acl_get_whotype); EXPORT_SYMBOL(nfs4_acl_write_who); diff --git a/include/linux/nfs4_acl.h b/include/linux/nfs4_acl.h index 409b6e02f33..c9c05a78e9b 100644 --- a/include/linux/nfs4_acl.h +++ b/include/linux/nfs4_acl.h @@ -44,7 +44,6 @@ #define NFS4_ACL_MAX 170 struct nfs4_acl *nfs4_acl_new(int); -void nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t); int nfs4_acl_get_whotype(char *, u32); int nfs4_acl_write_who(int who, char *p); int nfs4_acl_permission(struct nfs4_acl *acl, uid_t owner, gid_t group, -- cgit v1.2.3 From f34b95689d2ce001c157b1604289ff240b4bdee0 Mon Sep 17 00:00:00 2001 From: Peter Staubach Date: Wed, 9 May 2007 02:34:48 -0700 Subject: The NFSv2/NFSv3 server does not handle zero length WRITE requests correctly The NFSv2 and NFSv3 servers do not handle WRITE requests for 0 bytes correctly. The specifications indicate that the server should accept the request, but it should mostly turn into a no-op. Currently, the server will return an XDR decode error, which it should not. Attached is a patch which addresses this issue. It also adds some boundary checking to ensure that the request contains as much data as was requested to be written. It also correctly handles an NFSv3 request which requests to write more data than the server has stated that it is prepared to handle. Previously, there was some support which looked like it should work, but wasn't quite right. Signed-off-by: Peter Staubach Acked-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfs3xdr.c | 48 ++++++++++++++++++++++++++++++++++++++---------- fs/nfsd/nfsxdr.c | 46 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 7e4bb0af24d..43fb360784b 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -369,7 +369,7 @@ int nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_writeargs *args) { - unsigned int len, v, hdr; + unsigned int len, v, hdr, dlen; u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh)) @@ -379,18 +379,47 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, args->count = ntohl(*p++); args->stable = ntohl(*p++); len = args->len = ntohl(*p++); + /* + * The count must equal the amount of data passed. + */ + if (args->count != args->len) + return 0; + /* + * Check to make sure that we got the right number of + * bytes. + * + * If more than one page was used, then compute the length + * of the data in the request as the total size of the + * request minus the transport protocol headers minus the + * RPC protocol headers minus the NFS protocol fields + * already consumed. If the request fits into a single + * page, then compete the length of the data as the size + * of the NFS portion of the request minus the NFS + * protocol fields already consumed. + */ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; - if (rqstp->rq_arg.len < hdr || - rqstp->rq_arg.len - hdr < len) + if (rqstp->rq_respages != rqstp->rq_pages + 1) { + dlen = rqstp->rq_arg.len - + (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr; + } else { + dlen = rqstp->rq_arg.head[0].iov_len - hdr; + } + /* + * Round the length of the data which was specified up to + * the next multiple of XDR units and then compare that + * against the length which was actually received. + */ + if (dlen != ((len + 3) & ~0x3)) return 0; + if (args->count > max_blocksize) { + args->count = max_blocksize; + len = args->len = max_blocksize; + } rqstp->rq_vec[0].iov_base = (void*)p; rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; - - if (len > max_blocksize) - len = max_blocksize; - v= 0; + v = 0; while (len > rqstp->rq_vec[v].iov_len) { len -= rqstp->rq_vec[v].iov_len; v++; @@ -398,9 +427,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, rqstp->rq_vec[v].iov_len = PAGE_SIZE; } rqstp->rq_vec[v].iov_len = len; - args->vlen = v+1; - - return args->count == args->len && rqstp->rq_vec[0].iov_len > 0; + args->vlen = v + 1; + return 1; } int diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 0c24b9e24fe..6035e03655c 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -284,8 +284,9 @@ int nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_writeargs *args) { - unsigned int len; + unsigned int len, hdr, dlen; int v; + if (!(p = decode_fh(p, &args->fh))) return 0; @@ -293,11 +294,42 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, args->offset = ntohl(*p++); /* offset */ p++; /* totalcount */ len = args->len = ntohl(*p++); - rqstp->rq_vec[0].iov_base = (void*)p; - rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - - (((void*)p) - rqstp->rq_arg.head[0].iov_base); + /* + * The protocol specifies a maximum of 8192 bytes. + */ if (len > NFSSVC_MAXBLKSIZE_V2) - len = NFSSVC_MAXBLKSIZE_V2; + return 0; + + /* + * Check to make sure that we got the right number of + * bytes. + * + * If more than one page was used, then compute the length + * of the data in the request as the total size of the + * request minus the transport protocol headers minus the + * RPC protocol headers minus the NFS protocol fields + * already consumed. If the request fits into a single + * page, then compete the length of the data as the size + * of the NFS portion of the request minus the NFS + * protocol fields already consumed. + */ + hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; + if (rqstp->rq_respages != rqstp->rq_pages + 1) { + dlen = rqstp->rq_arg.len - + (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr; + } else { + dlen = rqstp->rq_arg.head[0].iov_len - hdr; + } + /* + * Round the length of the data which was specified up to + * the next multiple of XDR units and then compare that + * against the length which was actually received. + */ + if (dlen != ((len + 3) & ~0x3)) + return 0; + + rqstp->rq_vec[0].iov_base = (void*)p; + rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; v = 0; while (len > rqstp->rq_vec[v].iov_len) { len -= rqstp->rq_vec[v].iov_len; @@ -306,8 +338,8 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, rqstp->rq_vec[v].iov_len = PAGE_SIZE; } rqstp->rq_vec[v].iov_len = len; - args->vlen = v+1; - return rqstp->rq_vec[0].iov_len > 0; + args->vlen = v + 1; + return 1; } int -- cgit v1.2.3 From 7ac1bea5507218da03f6005d228789da5a831c3f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:48 -0700 Subject: knfsd: rename sk_defer_lock to sk_lock Now that sk_defer_lock protects two different things, make the name more generic. Also don't bother with disabling _bh as the lock is only ever taken from process context. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/svcsock.h | 3 ++- net/sunrpc/svcauth_unix.c | 10 +++++----- net/sunrpc/svcsock.c | 13 +++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 7909687557b..e21dd93ac4b 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -37,7 +37,8 @@ struct svc_sock { atomic_t sk_reserved; /* space on outq that is reserved */ - spinlock_t sk_defer_lock; /* protects sk_deferred */ + spinlock_t sk_lock; /* protects sk_deferred and + * sk_info_authunix */ struct list_head sk_deferred; /* deferred requests that need to * be revisted */ struct mutex sk_mutex; /* to serialize sending data */ diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 2bd23ea2aa8..07dcd20cbee 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -385,7 +385,7 @@ ip_map_cached_get(struct svc_rqst *rqstp) { struct ip_map *ipm; struct svc_sock *svsk = rqstp->rq_sock; - spin_lock_bh(&svsk->sk_defer_lock); + spin_lock(&svsk->sk_lock); ipm = svsk->sk_info_authunix; if (ipm != NULL) { if (!cache_valid(&ipm->h)) { @@ -395,13 +395,13 @@ ip_map_cached_get(struct svc_rqst *rqstp) * same IP address. */ svsk->sk_info_authunix = NULL; - spin_unlock_bh(&svsk->sk_defer_lock); + spin_unlock(&svsk->sk_lock); cache_put(&ipm->h, &ip_map_cache); return NULL; } cache_get(&ipm->h); } - spin_unlock_bh(&svsk->sk_defer_lock); + spin_unlock(&svsk->sk_lock); return ipm; } @@ -410,14 +410,14 @@ ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm) { struct svc_sock *svsk = rqstp->rq_sock; - spin_lock_bh(&svsk->sk_defer_lock); + spin_lock(&svsk->sk_lock); if (svsk->sk_sock->type == SOCK_STREAM && svsk->sk_info_authunix == NULL) { /* newly cached, keep the reference */ svsk->sk_info_authunix = ipm; ipm = NULL; } - spin_unlock_bh(&svsk->sk_defer_lock); + spin_unlock(&svsk->sk_lock); if (ipm) cache_put(&ipm->h, &ip_map_cache); } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 22f61aee482..fdb1386f5dc 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -53,7 +53,8 @@ * svc_serv->sv_lock protects sv_tempsocks, sv_permsocks, sv_tmpcnt. * when both need to be taken (rare), svc_serv->sv_lock is first. * BKL protects svc_serv->sv_nrthread. - * svc_sock->sk_defer_lock protects the svc_sock->sk_deferred list + * svc_sock->sk_lock protects the svc_sock->sk_deferred list + * and the ->sk_info_authunix cache. * svc_sock->sk_flags.SK_BUSY prevents a svc_sock being enqueued multiply. * * Some flags can be set to certain values at any time @@ -1633,7 +1634,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, svsk->sk_server = serv; atomic_set(&svsk->sk_inuse, 1); svsk->sk_lastrecv = get_seconds(); - spin_lock_init(&svsk->sk_defer_lock); + spin_lock_init(&svsk->sk_lock); INIT_LIST_HEAD(&svsk->sk_deferred); INIT_LIST_HEAD(&svsk->sk_ready); mutex_init(&svsk->sk_mutex); @@ -1857,9 +1858,9 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many) dprintk("revisit queued\n"); svsk = dr->svsk; dr->svsk = NULL; - spin_lock_bh(&svsk->sk_defer_lock); + spin_lock(&svsk->sk_lock); list_add(&dr->handle.recent, &svsk->sk_deferred); - spin_unlock_bh(&svsk->sk_defer_lock); + spin_unlock(&svsk->sk_lock); set_bit(SK_DEFERRED, &svsk->sk_flags); svc_sock_enqueue(svsk); svc_sock_put(svsk); @@ -1925,7 +1926,7 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_sock *svsk) if (!test_bit(SK_DEFERRED, &svsk->sk_flags)) return NULL; - spin_lock_bh(&svsk->sk_defer_lock); + spin_lock(&svsk->sk_lock); clear_bit(SK_DEFERRED, &svsk->sk_flags); if (!list_empty(&svsk->sk_deferred)) { dr = list_entry(svsk->sk_deferred.next, @@ -1934,6 +1935,6 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_sock *svsk) list_del_init(&dr->handle.recent); set_bit(SK_DEFERRED, &svsk->sk_flags); } - spin_unlock_bh(&svsk->sk_defer_lock); + spin_unlock(&svsk->sk_lock); return dr; } -- cgit v1.2.3 From 669716433598a1498049e75a84a5aaf69c8da173 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 9 May 2007 02:34:49 -0700 Subject: nfsd/nfs4state: remove unnecessary daemonize call Acked-by: Neil Brown Cc: Trond Myklebust Signed-off-by: Eric W. Biederman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfs4state.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 678f3be88ac..3cc8ce422ab 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1326,8 +1326,6 @@ do_recall(void *__dp) { struct nfs4_delegation *dp = __dp; - daemonize("nfsv4-recall"); - nfsd4_cb_recall(dp); return 0; } -- cgit v1.2.3 From cd123012d99fde4759500fee611e724e4f3016e3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 9 May 2007 02:34:50 -0700 Subject: RPC: add wrapper for svc_reserve to account for checksum When the kernel calls svc_reserve to downsize the expected size of an RPC reply, it fails to account for the possibility of a checksum at the end of the packet. If a client mounts a NFSv2/3 with sec=krb5i/p, and does I/O then you'll generally see messages similar to this in the server's ring buffer: RPC request reserved 164 but used 208 While I was never able to verify it, I suspect that this problem is also the root cause of some oopses I've seen under these conditions: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=227726 This is probably also a problem for other sec= types and for NFSv4. The large reserved size for NFSv4 compound packets seems to generally paper over the problem, however. This patch adds a wrapper for svc_reserve that accounts for the possibility of a checksum. It also fixes up the appropriate callers of svc_reserve to call the wrapper. For now, it just uses a hardcoded value that I determined via testing. That value may need to be revised upward as things change, or we may want to eventually add a new auth_op that attempts to calculate this somehow. Unfortunately, there doesn't seem to be a good way to reliably determine the expected checksum length prior to actually calculating it, particularly with schemes like spkm3. Signed-off-by: Jeff Layton Acked-by: Neil Brown Cc: Trond Myklebust Acked-by: J. Bruce Fields Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfs3proc.c | 2 +- fs/nfsd/nfsproc.c | 2 +- include/linux/sunrpc/svc.h | 19 +++++++++++++++++++ net/sunrpc/svc.c | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 7f5bad0393b..eac82830bfd 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -177,7 +177,7 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, if (max_blocksize < resp->count) resp->count = max_blocksize; - svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); + svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); fh_copy(&resp->fh, &argp->fh); nfserr = nfsd_read(rqstp, &resp->fh, NULL, diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 5cc2eec981b..b2c7147aa92 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -155,7 +155,7 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, argp->count); argp->count = NFSSVC_MAXBLKSIZE_V2; } - svc_reserve(rqstp, (19<<2) + argp->count + 4); + svc_reserve_auth(rqstp, (19<<2) + argp->count + 4); resp->count = argp->count; nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 35fa4d5aadd..4a7ae8ab6eb 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -396,4 +396,23 @@ char * svc_print_addr(struct svc_rqst *, char *, size_t); #define RPC_MAX_ADDRBUFLEN (63U) +/* + * When we want to reduce the size of the reserved space in the response + * buffer, we need to take into account the size of any checksum data that + * may be at the end of the packet. This is difficult to determine exactly + * for all cases without actually generating the checksum, so we just use a + * static value. + */ +static inline void +svc_reserve_auth(struct svc_rqst *rqstp, int space) +{ + int added_space = 0; + + switch(rqstp->rq_authop->flavour) { + case RPC_AUTH_GSS: + added_space = RPC_MAX_AUTH_SIZE; + } + return svc_reserve(rqstp, space + added_space); +} + #endif /* SUNRPC_SVC_H */ diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index b7503c103ae..e673ef99390 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -907,7 +907,7 @@ svc_process(struct svc_rqst *rqstp) * better idea of reply size */ if (procp->pc_xdrressize) - svc_reserve(rqstp, procp->pc_xdrressize<<2); + svc_reserve_auth(rqstp, procp->pc_xdrressize<<2); /* Call the function that processes the request. */ if (!versp->vs_dispatch) { -- cgit v1.2.3 From 5bd5f5812bfa753218e02cb773e06ede48055798 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 May 2007 02:34:51 -0700 Subject: sunrpc: fix error path in module_init register_rpc_pipefs() needs to clean up rpc_inode_cache by kmem_cache_destroy() on register_filesystem() failure. init_sunrpc() needs to unregister rpc_pipe_fs by unregister_rpc_pipefs() when rpc_init_mempool() returns error. Signed-off-by: Akinobu Mita Cc: Neil Brown Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/sunrpc/rpc_pipe.c | 9 ++++++++- net/sunrpc/sunrpc_syms.c | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index ad39b47e05b..a2f1893bde5 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -845,6 +845,8 @@ init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) int register_rpc_pipefs(void) { + int err; + rpc_inode_cachep = kmem_cache_create("rpc_inode_cache", sizeof(struct rpc_inode), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| @@ -852,7 +854,12 @@ int register_rpc_pipefs(void) init_once, NULL); if (!rpc_inode_cachep) return -ENOMEM; - register_filesystem(&rpc_pipe_fs_type); + err = register_filesystem(&rpc_pipe_fs_type); + if (err) { + kmem_cache_destroy(rpc_inode_cachep); + return err; + } + return 0; } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 43ecf62f12e..0d35bc796d0 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -146,9 +146,11 @@ init_sunrpc(void) int err = register_rpc_pipefs(); if (err) goto out; - err = rpc_init_mempool() != 0; - if (err) + err = rpc_init_mempool(); + if (err) { + unregister_rpc_pipefs(); goto out; + } #ifdef RPC_DEBUG rpc_register_sysctl(); #endif -- cgit v1.2.3 From 402acd29e552cb80109d1d5c0ada53f634465d87 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:52 -0700 Subject: knfsd: avoid use of unitialised variables on error path when nfs exports We need to zero various parts of 'exp' before any 'goto out', otherwise when we go to free the contents... we die. Signed-off-by: Neil Brown Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 6f24768272a..79bd03b8bbf 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -469,6 +469,13 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) nd.dentry = NULL; exp.ex_path = NULL; + /* fs locations */ + exp.ex_fslocs.locations = NULL; + exp.ex_fslocs.locations_count = 0; + exp.ex_fslocs.migrated = 0; + + exp.ex_uuid = NULL; + if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; @@ -509,13 +516,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (exp.h.expiry_time == 0) goto out; - /* fs locations */ - exp.ex_fslocs.locations = NULL; - exp.ex_fslocs.locations_count = 0; - exp.ex_fslocs.migrated = 0; - - exp.ex_uuid = NULL; - /* flags */ err = get_int(&mesg, &an_int); if (err == -ENOENT) -- cgit v1.2.3 From 153e44d22fb5f98198f90fbf56e89b345e48534d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:52 -0700 Subject: knfsd: rpc: fix server-side wrapping of krb5i replies It's not necessarily correct to assume that the xdr_buf used to hold the server's reply must have page data whenever it has tail data. And there's no need for us to deal with that case separately anyway. Acked-by: "J. Bruce Fields" Signed-off-by: Neil Brown Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/sunrpc/auth_gss/svcauth_gss.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index db298b501c8..c678f5f461c 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1196,13 +1196,7 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) BUG(); - if (resbuf->page_len == 0 - && resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE - < PAGE_SIZE) { - BUG_ON(resbuf->tail[0].iov_len); - /* Use head for everything */ - resv = &resbuf->head[0]; - } else if (resbuf->tail[0].iov_base == NULL) { + if (resbuf->tail[0].iov_base == NULL) { if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) goto out_err; resbuf->tail[0].iov_base = resbuf->head[0].iov_base -- cgit v1.2.3 From 54f9247b3f6e51b24a4b7e873b3ab34d8e59dc45 Mon Sep 17 00:00:00 2001 From: Frank Filz Date: Wed, 9 May 2007 02:34:53 -0700 Subject: knfsd: fix resource leak resulting in module refcount leak for rpcsec_gss_krb5.ko I have been investigating a module reference count leak on the server for rpcsec_gss_krb5.ko. It turns out the problem is a reference count leak for the security context in net/sunrpc/auth_gss/svcauth_gss.c. The problem is that gss_write_init_verf() calls gss_svc_searchbyctx() which does a rsc_lookup() but never releases the reference to the context. There is another issue that rpc.svcgssd sets an "end of time" expiration for the context By adding a cache_put() call in gss_svc_searchbyctx(), and setting an expiration timeout in the downcall, cache_clean() does clean up the context and the module reference count now goes to zero after unmount. I also verified that if the context expires and then the client makes a new request, a new context is established. Here is the patch to fix the kernel, I will start a separate thread to discuss what expiration time should be set by rpc.svcgssd. Acked-by: "J. Bruce Fields" Signed-off-by: Frank Filz Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/sunrpc/auth_gss/svcauth_gss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index c678f5f461c..9c0508e5493 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -924,6 +924,7 @@ static inline int gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip) { struct rsc *rsci; + int rc; if (rsip->major_status != GSS_S_COMPLETE) return gss_write_null_verf(rqstp); @@ -932,7 +933,9 @@ gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip) rsip->major_status = GSS_S_NO_CONTEXT; return gss_write_null_verf(rqstp); } - return gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); + rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); + cache_put(&rsci->h, &rsc_cache); + return rc; } /* -- cgit v1.2.3 From c5e434c98b49f4877ea1614a629499e082b1a818 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 9 May 2007 02:34:54 -0700 Subject: knfsd: rpcgss: RPC_GSS_PROC_ DESTROY request will get a bad rpc If I send a RPC_GSS_PROC_DESTROY message to NFSv4 server, it will reply with a bad rpc reply which lacks an authentication verifier. Maybe this patch is needed. Send/recv packets as following: send: RemoteProcedureCall xid rpcvers = 2 prog = 100003 vers = 4 proc = 0 cred = AUTH_GSS version = 1 gss_proc = 3 (RPCSEC_GSS_DESTROY) service = 1 (RPC_GSS_SVC_NONE) verf = AUTH_GSS checksum reply: RemoteProcedureReply xid msg_type reply_stat accepted_reply Signed-off-by: Wei Yongjun Signed-off-by: "J. Bruce Fields" Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 9c0508e5493..099a983797d 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1092,6 +1092,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) } goto complete; case RPC_GSS_PROC_DESTROY: + if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) + goto auth_err; set_bit(CACHE_NEGATIVE, &rsci->h.flags); if (resv->iov_len + 4 > PAGE_SIZE) goto drop; -- cgit v1.2.3 From 05ed690efbb28b54af79f97af8c9705e82a6fbd7 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:55 -0700 Subject: knfsd: simplify a 'while' condition in svcsock.c This while loop has an overly complex condition, which performs a couple of assignments. This hurts readability. We don't really need a loop at all. We can just return -EAGAIN and (providing we set SK_DATA), the function will be called again. So discard the loop, make the complex conditional become a few clear function calls, and hopefully improve readability. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/sunrpc/svcsock.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index fdb1386f5dc..5baf48de255 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -788,15 +788,20 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) } clear_bit(SK_DATA, &svsk->sk_flags); - while ((err = kernel_recvmsg(svsk->sk_sock, &msg, NULL, - 0, 0, MSG_PEEK | MSG_DONTWAIT)) < 0 || - (skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { - if (err == -EAGAIN) { - svc_sock_received(svsk); - return err; + skb = NULL; + err = kernel_recvmsg(svsk->sk_sock, &msg, NULL, + 0, 0, MSG_PEEK | MSG_DONTWAIT); + if (err >= 0) + skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err); + + if (skb == NULL) { + if (err != -EAGAIN) { + /* possibly an icmp error */ + dprintk("svc: recvfrom returned error %d\n", -err); + set_bit(SK_DATA, &svsk->sk_flags); } - /* possibly an icmp error */ - dprintk("svc: recvfrom returned error %d\n", -err); + svc_sock_received(svsk); + return -EAGAIN; } rqstp->rq_addrlen = sizeof(rqstp->rq_addr); if (skb->tstamp.tv64 == 0) { -- cgit v1.2.3 From f725b217b16e2cb1777c5a6e13c99f7913f1514a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 9 May 2007 02:34:56 -0700 Subject: knfsd: trivial makefile cleanup kbuild directly interprets -y as objects to build into a module, no need to assign it to the old foo-objs variable. Signed-off-by: Christoph Hellwig Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index ce341dc76d5..9b118ee2019 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile @@ -11,4 +11,3 @@ nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ nfs4acl.o nfs4callback.o nfs4recover.o -nfsd-objs := $(nfsd-y) -- cgit v1.2.3 From 072f62ed85a71bbb3429a52678500ec9f9441e0d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:57 -0700 Subject: knfsd: various nfsd xdr cleanups 1/ decode_sattr and decode_sattr3 never return NULL, so remove several checks for that. ditto for xdr_decode_hyper. 2/ replace some open coded XDR_QUADLEN calls with calls to XDR_QUADLEN 3/ in decode_writeargs, simply an 'if' to use a single calculation. .page_len is the length of that part of the packet that did not fit in the first page (the head). So the length of the data part is the remainder of the head, plus page_len. 3/ other minor cleanups. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfs3xdr.c | 55 ++++++++++++++++++++----------------------------------- fs/nfsd/nfsxdr.c | 39 ++++++++++++++------------------------- 2 files changed, 34 insertions(+), 60 deletions(-) diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 43fb360784b..10f6e7dcf63 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -239,7 +239,7 @@ static __be32 * encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp) { struct dentry *dentry = fhp->fh_dentry; - if (dentry && dentry->d_inode != NULL) { + if (dentry && dentry->d_inode) { int err; struct kstat stat; @@ -300,9 +300,9 @@ int nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_sattrargs *args) { - if (!(p = decode_fh(p, &args->fh)) - || !(p = decode_sattr3(p, &args->attrs))) + if (!(p = decode_fh(p, &args->fh))) return 0; + p = decode_sattr3(p, &args->attrs); if ((args->check_guard = ntohl(*p++)) != 0) { struct timespec time; @@ -343,9 +343,9 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, int v,pn; u32 max_blocksize = svc_max_payload(rqstp); - if (!(p = decode_fh(p, &args->fh)) - || !(p = xdr_decode_hyper(p, &args->offset))) + if (!(p = decode_fh(p, &args->fh))) return 0; + p = xdr_decode_hyper(p, &args->offset); len = args->count = ntohl(*p++); @@ -372,9 +372,9 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, unsigned int len, v, hdr, dlen; u32 max_blocksize = svc_max_payload(rqstp); - if (!(p = decode_fh(p, &args->fh)) - || !(p = xdr_decode_hyper(p, &args->offset))) + if (!(p = decode_fh(p, &args->fh))) return 0; + p = xdr_decode_hyper(p, &args->offset); args->count = ntohl(*p++); args->stable = ntohl(*p++); @@ -388,29 +388,16 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, /* * Check to make sure that we got the right number of * bytes. - * - * If more than one page was used, then compute the length - * of the data in the request as the total size of the - * request minus the transport protocol headers minus the - * RPC protocol headers minus the NFS protocol fields - * already consumed. If the request fits into a single - * page, then compete the length of the data as the size - * of the NFS portion of the request minus the NFS - * protocol fields already consumed. */ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; - if (rqstp->rq_respages != rqstp->rq_pages + 1) { - dlen = rqstp->rq_arg.len - - (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr; - } else { - dlen = rqstp->rq_arg.head[0].iov_len - hdr; - } + dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len + - hdr; /* * Round the length of the data which was specified up to * the next multiple of XDR units and then compare that * against the length which was actually received. */ - if (dlen != ((len + 3) & ~0x3)) + if (dlen != XDR_QUADLEN(len)*4) return 0; if (args->count > max_blocksize) { @@ -442,8 +429,7 @@ nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p, switch (args->createmode = ntohl(*p++)) { case NFS3_CREATE_UNCHECKED: case NFS3_CREATE_GUARDED: - if (!(p = decode_sattr3(p, &args->attrs))) - return 0; + p = decode_sattr3(p, &args->attrs); break; case NFS3_CREATE_EXCLUSIVE: args->verf = p; @@ -459,10 +445,10 @@ int nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_createargs *args) { - if (!(p = decode_fh(p, &args->fh)) - || !(p = decode_filename(p, &args->name, &args->len)) - || !(p = decode_sattr3(p, &args->attrs))) + if (!(p = decode_fh(p, &args->fh)) || + !(p = decode_filename(p, &args->name, &args->len))) return 0; + p = decode_sattr3(p, &args->attrs); return xdr_argsize_check(rqstp, p); } @@ -476,11 +462,12 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p, char *old, *new; struct kvec *vec; - if (!(p = decode_fh(p, &args->ffh)) - || !(p = decode_filename(p, &args->fname, &args->flen)) - || !(p = decode_sattr3(p, &args->attrs)) + if (!(p = decode_fh(p, &args->ffh)) || + !(p = decode_filename(p, &args->fname, &args->flen)) ) return 0; + p = decode_sattr3(p, &args->attrs); + /* now decode the pathname, which might be larger than the first page. * As we have to check for nul's anyway, we copy it into a new page * This page appears in the rq_res.pages list, but as pages_len is always @@ -530,10 +517,8 @@ nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p, args->ftype = ntohl(*p++); if (args->ftype == NF3BLK || args->ftype == NF3CHR - || args->ftype == NF3SOCK || args->ftype == NF3FIFO) { - if (!(p = decode_sattr3(p, &args->attrs))) - return 0; - } + || args->ftype == NF3SOCK || args->ftype == NF3FIFO) + p = decode_sattr3(p, &args->attrs); if (args->ftype == NF3BLK || args->ftype == NF3CHR) { args->major = ntohl(*p++); diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 6035e03655c..cb3e7fadb77 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -231,9 +231,10 @@ int nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_sattrargs *args) { - if (!(p = decode_fh(p, &args->fh)) - || !(p = decode_sattr(p, &args->attrs))) + p = decode_fh(p, &args->fh); + if (!p) return 0; + p = decode_sattr(p, &args->attrs); return xdr_argsize_check(rqstp, p); } @@ -303,29 +304,17 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, /* * Check to make sure that we got the right number of * bytes. - * - * If more than one page was used, then compute the length - * of the data in the request as the total size of the - * request minus the transport protocol headers minus the - * RPC protocol headers minus the NFS protocol fields - * already consumed. If the request fits into a single - * page, then compete the length of the data as the size - * of the NFS portion of the request minus the NFS - * protocol fields already consumed. */ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; - if (rqstp->rq_respages != rqstp->rq_pages + 1) { - dlen = rqstp->rq_arg.len - - (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr; - } else { - dlen = rqstp->rq_arg.head[0].iov_len - hdr; - } + dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len + - hdr; + /* * Round the length of the data which was specified up to * the next multiple of XDR units and then compare that * against the length which was actually received. */ - if (dlen != ((len + 3) & ~0x3)) + if (dlen != XDR_QUADLEN(len)*4) return 0; rqstp->rq_vec[0].iov_base = (void*)p; @@ -346,10 +335,10 @@ int nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_createargs *args) { - if (!(p = decode_fh(p, &args->fh)) - || !(p = decode_filename(p, &args->name, &args->len)) - || !(p = decode_sattr(p, &args->attrs))) + if ( !(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len))) return 0; + p = decode_sattr(p, &args->attrs); return xdr_argsize_check(rqstp, p); } @@ -393,11 +382,11 @@ int nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_symlinkargs *args) { - if (!(p = decode_fh(p, &args->ffh)) - || !(p = decode_filename(p, &args->fname, &args->flen)) - || !(p = decode_pathname(p, &args->tname, &args->tlen)) - || !(p = decode_sattr(p, &args->attrs))) + if ( !(p = decode_fh(p, &args->ffh)) + || !(p = decode_filename(p, &args->fname, &args->flen)) + || !(p = decode_pathname(p, &args->tname, &args->tlen))) return 0; + p = decode_sattr(p, &args->attrs); return xdr_argsize_check(rqstp, p); } -- cgit v1.2.3 From b41eeef14d7c73af6d16c7d02b7a939082a137ff Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2007 02:34:57 -0700 Subject: knfsd: avoid Oops if buggy userspace performs confusing filehandle->dentry mapping When a lookup request arrives, nfsd uses information provided by userspace (mountd) to find the right filesystem. It then assumes that the same filehandle type as the incoming filehandle can be used to create an outgoing filehandle. However if mountd is buggy, or maybe just being creative, the filesystem may not support that filesystem type, and the kernel could oops, particularly if 'ex_uuid' is NULL but a FSID_UUID* filehandle type is used. So add some proper checking that the fsid version/type from the incoming filehandle is actually supportable, and ignore that information if it isn't supportable. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/nfsfh.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 739dd3c5c3b..6ca2d24fc21 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -323,7 +323,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, * */ - u8 version = 1; + u8 version; u8 fsid_type = 0; struct inode * inode = dentry->d_inode; struct dentry *parent = dentry->d_parent; @@ -341,15 +341,59 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, * the reference filehandle (if it is in the same export) * or the export options. */ + retry: + version = 1; if (ref_fh && ref_fh->fh_export == exp) { version = ref_fh->fh_handle.fh_version; - if (version == 0xca) + fsid_type = ref_fh->fh_handle.fh_fsid_type; + + if (ref_fh == fhp) + fh_put(ref_fh); + ref_fh = NULL; + + switch (version) { + case 0xca: fsid_type = FSID_DEV; - else - fsid_type = ref_fh->fh_handle.fh_fsid_type; - /* We know this version/type works for this export - * so there is no need for further checks. + break; + case 1: + break; + default: + goto retry; + } + + /* Need to check that this type works for this + * export point. As the fsid -> filesystem mapping + * was guided by user-space, there is no guarantee + * that the filesystem actually supports that fsid + * type. If it doesn't we loop around again without + * ref_fh set. */ + switch(fsid_type) { + case FSID_DEV: + if (!old_valid_dev(ex_dev)) + goto retry; + /* FALL THROUGH */ + case FSID_MAJOR_MINOR: + case FSID_ENCODE_DEV: + if (!(exp->ex_dentry->d_inode->i_sb->s_type->fs_flags + & FS_REQUIRES_DEV)) + goto retry; + break; + case FSID_NUM: + if (! (exp->ex_flags & NFSEXP_FSID)) + goto retry; + break; + case FSID_UUID8: + case FSID_UUID16: + if (!root_export) + goto retry; + /* fall through */ + case FSID_UUID4_INUM: + case FSID_UUID16_INUM: + if (exp->ex_uuid == NULL) + goto retry; + break; + } } else if (exp->ex_uuid) { if (fhp->fh_maxsize >= 64) { if (root_export) -- cgit v1.2.3 From b8522ead3534c6cd06752b47a3bc380956191a2a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:34:58 -0700 Subject: aio is unlikely Stick an unlikely() around is_aio(): I assert that most IO is synchronous. Cc: Suparna Bhattacharya Cc: Ingo Molnar Cc: Benjamin LaHaise Cc: Zach Brown Cc: Ulrich Drepper Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/aio.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/aio.h b/include/linux/aio.h index a30ef13c9e6..43dc2ebfaa0 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -226,7 +226,8 @@ int FASTCALL(io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, __put_ioctx(kioctx); \ } while (0) -#define in_aio() !is_sync_wait(current->io_wait) +#define in_aio() (unlikely(!is_sync_wait(current->io_wait))) + /* may be used for debugging */ #define warn_if_async() \ do { \ -- cgit v1.2.3 From f34c506b0385b43abd25c490335036ecbb173aed Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 9 May 2007 02:34:59 -0700 Subject: declare struct ktime Some smarty went and inflicted ktime_t as a typedef upon us, so we cannot forward declare it. Create a new `union ktime', map ktime_t onto that. Now we need to kill off this ktime_t thing. Cc: Ingo Molnar Cc: Thomas Gleixner Cc: john stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ktime.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 81bb9c7a4eb..c762954bda1 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -43,7 +43,7 @@ * plain scalar nanosecond based representation can be selected by the * config switch CONFIG_KTIME_SCALAR. */ -typedef union { +union ktime { s64 tv64; #if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) struct { @@ -54,7 +54,9 @@ typedef union { # endif } tv; #endif -} ktime_t; +}; + +typedef union ktime ktime_t; /* Kill this */ #define KTIME_MAX ((s64)~((u64)1 << 63)) #if (BITS_PER_LONG == 64) -- cgit v1.2.3 From ec92d08292d3e9b0823eba138a4564d2d39f25c7 Mon Sep 17 00:00:00 2001 From: Pierre Peiffer Date: Wed, 9 May 2007 02:35:00 -0700 Subject: futex priority based wakeup Today, all threads waiting for a given futex are woken in FIFO order (first waiter woken first) instead of priority order. This patch makes use of plist (pirotity ordered lists) instead of simple list in futex_hash_bucket. All non-RT threads are stored with priority MAX_RT_PRIO, causing them to be woken last, in FIFO order (RT-threads are woken first, in priority order). Signed-off-by: Sebastien Dugue Signed-off-by: Pierre Peiffer Cc: Ingo Molnar Cc: Ulrich Drepper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/futex.c | 78 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 600bc9d801f..685ee2362a5 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -81,12 +81,12 @@ struct futex_pi_state { * we can wake only the relevant ones (hashed queues may be shared). * * A futex_q has a woken state, just like tasks have TASK_RUNNING. - * It is considered woken when list_empty(&q->list) || q->lock_ptr == 0. + * It is considered woken when plist_node_empty(&q->list) || q->lock_ptr == 0. * The order of wakup is always to make the first condition true, then * wake up q->waiters, then make the second condition true. */ struct futex_q { - struct list_head list; + struct plist_node list; wait_queue_head_t waiters; /* Which hash list lock to use: */ @@ -108,8 +108,8 @@ struct futex_q { * Split the global futex_lock into every hash list lock. */ struct futex_hash_bucket { - spinlock_t lock; - struct list_head chain; + spinlock_t lock; + struct plist_head chain; }; static struct futex_hash_bucket futex_queues[1<chain; - list_for_each_entry_safe(this, next, head, list) { + plist_for_each_entry_safe(this, next, head, list) { if (match_futex(&this->key, &me->key)) { /* * Another waiter already exists - bump up @@ -513,12 +513,12 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) */ static void wake_futex(struct futex_q *q) { - list_del_init(&q->list); + plist_del(&q->list, &q->list.plist); if (q->filp) send_sigio(&q->filp->f_owner, q->fd, POLL_IN); /* * The lock in wake_up_all() is a crucial memory barrier after the - * list_del_init() and also before assigning to q->lock_ptr. + * plist_del() and also before assigning to q->lock_ptr. */ wake_up_all(&q->waiters); /* @@ -633,7 +633,7 @@ static int futex_wake(u32 __user *uaddr, int nr_wake) { struct futex_hash_bucket *hb; struct futex_q *this, *next; - struct list_head *head; + struct plist_head *head; union futex_key key; int ret; @@ -647,7 +647,7 @@ static int futex_wake(u32 __user *uaddr, int nr_wake) spin_lock(&hb->lock); head = &hb->chain; - list_for_each_entry_safe(this, next, head, list) { + plist_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key)) { if (this->pi_state) { ret = -EINVAL; @@ -675,7 +675,7 @@ futex_wake_op(u32 __user *uaddr1, u32 __user *uaddr2, { union futex_key key1, key2; struct futex_hash_bucket *hb1, *hb2; - struct list_head *head; + struct plist_head *head; struct futex_q *this, *next; int ret, op_ret, attempt = 0; @@ -748,7 +748,7 @@ retry: head = &hb1->chain; - list_for_each_entry_safe(this, next, head, list) { + plist_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key1)) { wake_futex(this); if (++ret >= nr_wake) @@ -760,7 +760,7 @@ retry: head = &hb2->chain; op_ret = 0; - list_for_each_entry_safe(this, next, head, list) { + plist_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key2)) { wake_futex(this); if (++op_ret >= nr_wake2) @@ -787,7 +787,7 @@ static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, { union futex_key key1, key2; struct futex_hash_bucket *hb1, *hb2; - struct list_head *head1; + struct plist_head *head1; struct futex_q *this, *next; int ret, drop_count = 0; @@ -836,7 +836,7 @@ static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, } head1 = &hb1->chain; - list_for_each_entry_safe(this, next, head1, list) { + plist_for_each_entry_safe(this, next, head1, list) { if (!match_futex (&this->key, &key1)) continue; if (++ret <= nr_wake) { @@ -847,9 +847,13 @@ static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, * requeue. */ if (likely(head1 != &hb2->chain)) { - list_move_tail(&this->list, &hb2->chain); + plist_del(&this->list, &hb1->chain); + plist_add(&this->list, &hb2->chain); this->lock_ptr = &hb2->lock; - } +#ifdef CONFIG_DEBUG_PI_LIST + this->list.plist.lock = &hb2->lock; +#endif + } this->key = key2; get_futex_key_refs(&key2); drop_count++; @@ -894,7 +898,23 @@ queue_lock(struct futex_q *q, int fd, struct file *filp) static inline void __queue_me(struct futex_q *q, struct futex_hash_bucket *hb) { - list_add_tail(&q->list, &hb->chain); + int prio; + + /* + * The priority used to register this element is + * - either the real thread-priority for the real-time threads + * (i.e. threads with a priority lower than MAX_RT_PRIO) + * - or MAX_RT_PRIO for non-RT threads. + * Thus, all RT-threads are woken first in priority order, and + * the others are woken last, in FIFO order. + */ + prio = min(current->normal_prio, MAX_RT_PRIO); + + plist_node_init(&q->list, prio); +#ifdef CONFIG_DEBUG_PI_LIST + q->list.plist.lock = &hb->lock; +#endif + plist_add(&q->list, &hb->chain); q->task = current; spin_unlock(&hb->lock); } @@ -949,8 +969,8 @@ static int unqueue_me(struct futex_q *q) spin_unlock(lock_ptr); goto retry; } - WARN_ON(list_empty(&q->list)); - list_del(&q->list); + WARN_ON(plist_node_empty(&q->list)); + plist_del(&q->list, &q->list.plist); BUG_ON(q->pi_state); @@ -968,8 +988,8 @@ static int unqueue_me(struct futex_q *q) */ static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) { - WARN_ON(list_empty(&q->list)); - list_del(&q->list); + WARN_ON(plist_node_empty(&q->list)); + plist_del(&q->list, &q->list.plist); BUG_ON(!q->pi_state); free_pi_state(q->pi_state); @@ -1065,11 +1085,11 @@ static int futex_wait_abstime(u32 __user *uaddr, u32 val, __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&q.waiters, &wait); /* - * !list_empty() is safe here without any lock. + * !plist_node_empty() is safe here without any lock. * q.lock_ptr != 0 is not safe, because of ordering against wakeup. */ time_left = 0; - if (likely(!list_empty(&q.list))) { + if (likely(!plist_node_empty(&q.list))) { unsigned long rel_time; if (timed) { @@ -1384,7 +1404,7 @@ static int futex_unlock_pi(u32 __user *uaddr) struct futex_hash_bucket *hb; struct futex_q *this, *next; u32 uval; - struct list_head *head; + struct plist_head *head; union futex_key key; int ret, attempt = 0; @@ -1435,7 +1455,7 @@ retry_locked: */ head = &hb->chain; - list_for_each_entry_safe(this, next, head, list) { + plist_for_each_entry_safe(this, next, head, list) { if (!match_futex (&this->key, &key)) continue; ret = wake_futex_pi(uaddr, uval, this); @@ -1509,10 +1529,10 @@ static unsigned int futex_poll(struct file *filp, poll_wait(filp, &q->waiters, wait); /* - * list_empty() is safe here without any lock. + * plist_node_empty() is safe here without any lock. * q->lock_ptr != 0 is not safe, because of ordering against wakeup. */ - if (list_empty(&q->list)) + if (plist_node_empty(&q->list)) ret = POLLIN | POLLRDNORM; return ret; @@ -1895,7 +1915,7 @@ static int __init init(void) } for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { - INIT_LIST_HEAD(&futex_queues[i].chain); + plist_head_init(&futex_queues[i].chain, &futex_queues[i].lock); spin_lock_init(&futex_queues[i].lock); } return 0; -- cgit v1.2.3 From c19384b5b296905d4988c7c684ff540a0f9d65be Mon Sep 17 00:00:00 2001 From: Pierre Peiffer Date: Wed, 9 May 2007 02:35:02 -0700 Subject: Make futex_wait() use an hrtimer for timeout This patch modifies futex_wait() to use an hrtimer + schedule() in place of schedule_timeout(). schedule_timeout() is tick based, therefore the timeout granularity is the tick (1 ms, 4 ms or 10 ms depending on HZ). By using a high resolution timer for timeout wakeup, we can attain a much finer timeout granularity (in the microsecond range). This parallels what is already done for futex_lock_pi(). The timeout passed to the syscall is no longer converted to jiffies and is therefore passed to do_futex() and futex_wait() as an absolute ktime_t therefore keeping nanosecond resolution. Also this removes the need to pass the nanoseconds timeout part to futex_lock_pi() in val2. In futex_wait(), if there is no timeout then a regular schedule() is performed. Otherwise, an hrtimer is fired before schedule() is called. [akpm@linux-foundation.org: fix `make headers_check'] Signed-off-by: Sebastien Dugue Signed-off-by: Pierre Peiffer Cc: Ingo Molnar Cc: Ulrich Drepper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/futex.h | 4 ++- kernel/futex.c | 89 ++++++++++++++++++++++++++------------------------- kernel/futex_compat.c | 19 ++++++----- 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/include/linux/futex.h b/include/linux/futex.h index 820125c628c..34e54f2b899 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -3,6 +3,8 @@ #include +union ktime; + /* Second argument to futex syscall */ @@ -94,7 +96,7 @@ struct robust_list_head { #define ROBUST_LIST_LIMIT 2048 #ifdef __KERNEL__ -long do_futex(u32 __user *uaddr, int op, u32 val, unsigned long timeout, +long do_futex(u32 __user *uaddr, int op, u32 val, union ktime *timeout, u32 __user *uaddr2, u32 val2, u32 val3); extern int diff --git a/kernel/futex.c b/kernel/futex.c index 685ee2362a5..e1246ccbf89 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1001,16 +1001,16 @@ static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) } static long futex_wait_restart(struct restart_block *restart); -static int futex_wait_abstime(u32 __user *uaddr, u32 val, - int timed, unsigned long abs_time) +static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) { struct task_struct *curr = current; DECLARE_WAITQUEUE(wait, curr); struct futex_hash_bucket *hb; struct futex_q q; - unsigned long time_left = 0; u32 uval; int ret; + struct hrtimer_sleeper t; + int rem = 0; q.pi_state = NULL; retry: @@ -1088,20 +1088,29 @@ static int futex_wait_abstime(u32 __user *uaddr, u32 val, * !plist_node_empty() is safe here without any lock. * q.lock_ptr != 0 is not safe, because of ordering against wakeup. */ - time_left = 0; if (likely(!plist_node_empty(&q.list))) { - unsigned long rel_time; - - if (timed) { - unsigned long now = jiffies; - if (time_after(now, abs_time)) - rel_time = 0; - else - rel_time = abs_time - now; - } else - rel_time = MAX_SCHEDULE_TIMEOUT; + if (!abs_time) + schedule(); + else { + hrtimer_init(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + hrtimer_init_sleeper(&t, current); + t.timer.expires = *abs_time; + + hrtimer_start(&t.timer, t.timer.expires, HRTIMER_MODE_ABS); + + /* + * the timer could have already expired, in which + * case current would be flagged for rescheduling. + * Don't bother calling schedule. + */ + if (likely(t.task)) + schedule(); + + hrtimer_cancel(&t.timer); - time_left = schedule_timeout(rel_time); + /* Flag if a timeout occured */ + rem = (t.task == NULL); + } } __set_current_state(TASK_RUNNING); @@ -1113,14 +1122,14 @@ static int futex_wait_abstime(u32 __user *uaddr, u32 val, /* If we were woken (and unqueued), we succeeded, whatever. */ if (!unqueue_me(&q)) return 0; - if (time_left == 0) + if (rem) return -ETIMEDOUT; /* * We expect signal_pending(current), but another thread may * have handled it for us already. */ - if (time_left == MAX_SCHEDULE_TIMEOUT) + if (!abs_time) return -ERESTARTSYS; else { struct restart_block *restart; @@ -1128,8 +1137,7 @@ static int futex_wait_abstime(u32 __user *uaddr, u32 val, restart->fn = futex_wait_restart; restart->arg0 = (unsigned long)uaddr; restart->arg1 = (unsigned long)val; - restart->arg2 = (unsigned long)timed; - restart->arg3 = abs_time; + restart->arg2 = (unsigned long)abs_time; return -ERESTART_RESTARTBLOCK; } @@ -1141,21 +1149,15 @@ static int futex_wait_abstime(u32 __user *uaddr, u32 val, return ret; } -static int futex_wait(u32 __user *uaddr, u32 val, unsigned long rel_time) -{ - int timed = (rel_time != MAX_SCHEDULE_TIMEOUT); - return futex_wait_abstime(uaddr, val, timed, jiffies+rel_time); -} static long futex_wait_restart(struct restart_block *restart) { u32 __user *uaddr = (u32 __user *)restart->arg0; u32 val = (u32)restart->arg1; - int timed = (int)restart->arg2; - unsigned long abs_time = restart->arg3; + ktime_t *abs_time = (ktime_t *)restart->arg2; restart->fn = do_no_restart_syscall; - return (long)futex_wait_abstime(uaddr, val, timed, abs_time); + return (long)futex_wait(uaddr, val, abs_time); } @@ -1165,8 +1167,8 @@ static long futex_wait_restart(struct restart_block *restart) * if there are waiters then it will block, it does PI, etc. (Due to * races the kernel might see a 0 value of the futex too.) */ -static int futex_lock_pi(u32 __user *uaddr, int detect, unsigned long sec, - long nsec, int trylock) +static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, + int trylock) { struct hrtimer_sleeper timeout, *to = NULL; struct task_struct *curr = current; @@ -1178,11 +1180,11 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, unsigned long sec, if (refill_pi_state_cache()) return -ENOMEM; - if (sec != MAX_SCHEDULE_TIMEOUT) { + if (time) { to = &timeout; hrtimer_init(&to->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); hrtimer_init_sleeper(to, current); - to->timer.expires = ktime_set(sec, nsec); + to->timer.expires = *time; } q.pi_state = NULL; @@ -1818,7 +1820,7 @@ void exit_robust_list(struct task_struct *curr) } } -long do_futex(u32 __user *uaddr, int op, u32 val, unsigned long timeout, +long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, u32 __user *uaddr2, u32 val2, u32 val3) { int ret; @@ -1844,13 +1846,13 @@ long do_futex(u32 __user *uaddr, int op, u32 val, unsigned long timeout, ret = futex_wake_op(uaddr, uaddr2, val, val2, val3); break; case FUTEX_LOCK_PI: - ret = futex_lock_pi(uaddr, val, timeout, val2, 0); + ret = futex_lock_pi(uaddr, val, timeout, 0); break; case FUTEX_UNLOCK_PI: ret = futex_unlock_pi(uaddr); break; case FUTEX_TRYLOCK_PI: - ret = futex_lock_pi(uaddr, 0, timeout, val2, 1); + ret = futex_lock_pi(uaddr, 0, timeout, 1); break; default: ret = -ENOSYS; @@ -1863,21 +1865,20 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, struct timespec __user *utime, u32 __user *uaddr2, u32 val3) { - struct timespec t; - unsigned long timeout = MAX_SCHEDULE_TIMEOUT; + struct timespec ts; + ktime_t t, *tp = NULL; u32 val2 = 0; if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) { - if (copy_from_user(&t, utime, sizeof(t)) != 0) + if (copy_from_user(&ts, utime, sizeof(ts)) != 0) return -EFAULT; - if (!timespec_valid(&t)) + if (!timespec_valid(&ts)) return -EINVAL; + + t = timespec_to_ktime(ts); if (op == FUTEX_WAIT) - timeout = timespec_to_jiffies(&t) + 1; - else { - timeout = t.tv_sec; - val2 = t.tv_nsec; - } + t = ktime_add(ktime_get(), t); + tp = &t; } /* * requeue parameter in 'utime' if op == FUTEX_REQUEUE. @@ -1885,7 +1886,7 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) val2 = (u32) (unsigned long) utime; - return do_futex(uaddr, op, val, timeout, uaddr2, val2, val3); + return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); } static int futexfs_get_sb(struct file_system_type *fs_type, diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 50f24eea6cd..dff27c471ea 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -141,24 +141,23 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, struct compat_timespec __user *utime, u32 __user *uaddr2, u32 val3) { - struct timespec t; - unsigned long timeout = MAX_SCHEDULE_TIMEOUT; + struct timespec ts; + ktime_t t, *tp = NULL; int val2 = 0; if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) { - if (get_compat_timespec(&t, utime)) + if (get_compat_timespec(&ts, utime)) return -EFAULT; - if (!timespec_valid(&t)) + if (!timespec_valid(&ts)) return -EINVAL; + + t = timespec_to_ktime(ts); if (op == FUTEX_WAIT) - timeout = timespec_to_jiffies(&t) + 1; - else { - timeout = t.tv_sec; - val2 = t.tv_nsec; - } + t = ktime_add(ktime_get(), t); + tp = &t; } if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) val2 = (int) (unsigned long) utime; - return do_futex(uaddr, op, val, timeout, uaddr2, val2, val3); + return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); } -- cgit v1.2.3 From d0aa7a70bf03b9de9e995ab272293be1f7937822 Mon Sep 17 00:00:00 2001 From: Pierre Peiffer Date: Wed, 9 May 2007 02:35:02 -0700 Subject: futex_requeue_pi optimization This patch provides the futex_requeue_pi functionality, which allows some threads waiting on a normal futex to be requeued on the wait-queue of a PI-futex. This provides an optimization, already used for (normal) futexes, to be used with the PI-futexes. This optimization is currently used by the glibc in pthread_broadcast, when using "normal" mutexes. With futex_requeue_pi, it can be used with PRIO_INHERIT mutexes too. Signed-off-by: Pierre Peiffer Cc: Ingo Molnar Cc: Ulrich Drepper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/futex.h | 9 +- kernel/futex.c | 541 +++++++++++++++++++++++++++++++++++++++++++----- kernel/futex_compat.c | 3 +- kernel/rtmutex.c | 41 +--- kernel/rtmutex_common.h | 34 +++ 5 files changed, 540 insertions(+), 88 deletions(-) diff --git a/include/linux/futex.h b/include/linux/futex.h index 34e54f2b899..1bd8dfcb037 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -17,6 +17,7 @@ union ktime; #define FUTEX_LOCK_PI 6 #define FUTEX_UNLOCK_PI 7 #define FUTEX_TRYLOCK_PI 8 +#define FUTEX_CMP_REQUEUE_PI 9 /* * Support for robust futexes: the kernel cleans up held futexes at @@ -84,10 +85,15 @@ struct robust_list_head { */ #define FUTEX_OWNER_DIED 0x40000000 +/* + * Some processes have been requeued on this PI-futex + */ +#define FUTEX_WAITER_REQUEUED 0x20000000 + /* * The rest of the robust-futex field is for the TID: */ -#define FUTEX_TID_MASK 0x3fffffff +#define FUTEX_TID_MASK 0x0fffffff /* * This limit protects against a deliberately circular list. @@ -111,6 +117,7 @@ handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi); * We set bit 0 to indicate if it's an inode-based key. */ union futex_key { + u32 __user *uaddr; struct { unsigned long pgoff; struct inode *inode; diff --git a/kernel/futex.c b/kernel/futex.c index e1246ccbf89..4a60ef55dab 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -53,6 +53,12 @@ #include "rtmutex_common.h" +#ifdef CONFIG_DEBUG_RT_MUTEXES +# include "rtmutex-debug.h" +#else +# include "rtmutex.h" +#endif + #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8) /* @@ -102,6 +108,12 @@ struct futex_q { /* Optional priority inheritance state: */ struct futex_pi_state *pi_state; struct task_struct *task; + + /* + * This waiter is used in case of requeue from a + * normal futex to a PI-futex + */ + struct rt_mutex_waiter waiter; }; /* @@ -180,6 +192,9 @@ int get_futex_key(u32 __user *uaddr, union futex_key *key) if (unlikely((vma->vm_flags & (VM_IO|VM_READ)) != VM_READ)) return (vma->vm_flags & VM_IO) ? -EPERM : -EACCES; + /* Save the user address in the ley */ + key->uaddr = uaddr; + /* * Private mappings are handled in a simple way. * @@ -439,7 +454,8 @@ void exit_pi_state_list(struct task_struct *curr) } static int -lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) +lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, + union futex_key *key, struct futex_pi_state **ps) { struct futex_pi_state *pi_state = NULL; struct futex_q *this, *next; @@ -450,7 +466,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) head = &hb->chain; plist_for_each_entry_safe(this, next, head, list) { - if (match_futex(&this->key, &me->key)) { + if (match_futex(&this->key, key)) { /* * Another waiter already exists - bump up * the refcount and return its pi_state: @@ -465,7 +481,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) WARN_ON(!atomic_read(&pi_state->refcount)); atomic_inc(&pi_state->refcount); - me->pi_state = pi_state; + *ps = pi_state; return 0; } @@ -492,7 +508,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) rt_mutex_init_proxy_locked(&pi_state->pi_mutex, p); /* Store the key for possible exit cleanups: */ - pi_state->key = me->key; + pi_state->key = *key; spin_lock_irq(&p->pi_lock); WARN_ON(!list_empty(&pi_state->list)); @@ -502,7 +518,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) put_task_struct(p); - me->pi_state = pi_state; + *ps = pi_state; return 0; } @@ -562,6 +578,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) */ if (!(uval & FUTEX_OWNER_DIED)) { newval = FUTEX_WAITERS | new_owner->pid; + /* Keep the FUTEX_WAITER_REQUEUED flag if it was set */ + newval |= (uval & FUTEX_WAITER_REQUEUED); pagefault_disable(); curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); @@ -665,6 +683,254 @@ out: return ret; } +/* + * Called from futex_requeue_pi. + * Set FUTEX_WAITERS and FUTEX_WAITER_REQUEUED flags on the + * PI-futex value; search its associated pi_state if an owner exist + * or create a new one without owner. + */ +static inline int +lookup_pi_state_for_requeue(u32 __user *uaddr, struct futex_hash_bucket *hb, + union futex_key *key, + struct futex_pi_state **pi_state) +{ + u32 curval, uval, newval; + +retry: + /* + * We can't handle a fault cleanly because we can't + * release the locks here. Simply return the fault. + */ + if (get_futex_value_locked(&curval, uaddr)) + return -EFAULT; + + /* set the flags FUTEX_WAITERS and FUTEX_WAITER_REQUEUED */ + if ((curval & (FUTEX_WAITERS | FUTEX_WAITER_REQUEUED)) + != (FUTEX_WAITERS | FUTEX_WAITER_REQUEUED)) { + /* + * No waiters yet, we prepare the futex to have some waiters. + */ + + uval = curval; + newval = uval | FUTEX_WAITERS | FUTEX_WAITER_REQUEUED; + + pagefault_disable(); + curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); + pagefault_enable(); + + if (unlikely(curval == -EFAULT)) + return -EFAULT; + if (unlikely(curval != uval)) + goto retry; + } + + if (!(curval & FUTEX_TID_MASK) + || lookup_pi_state(curval, hb, key, pi_state)) { + /* the futex has no owner (yet) or the lookup failed: + allocate one pi_state without owner */ + + *pi_state = alloc_pi_state(); + + /* Already stores the key: */ + (*pi_state)->key = *key; + + /* init the mutex without owner */ + __rt_mutex_init(&(*pi_state)->pi_mutex, NULL); + } + + return 0; +} + +/* + * Keep the first nr_wake waiter from futex1, wake up one, + * and requeue the next nr_requeue waiters following hashed on + * one physical page to another physical page (PI-futex uaddr2) + */ +static int futex_requeue_pi(u32 __user *uaddr1, u32 __user *uaddr2, + int nr_wake, int nr_requeue, u32 *cmpval) +{ + union futex_key key1, key2; + struct futex_hash_bucket *hb1, *hb2; + struct plist_head *head1; + struct futex_q *this, *next; + struct futex_pi_state *pi_state2 = NULL; + struct rt_mutex_waiter *waiter, *top_waiter = NULL; + struct rt_mutex *lock2 = NULL; + int ret, drop_count = 0; + + if (refill_pi_state_cache()) + return -ENOMEM; + +retry: + /* + * First take all the futex related locks: + */ + down_read(¤t->mm->mmap_sem); + + ret = get_futex_key(uaddr1, &key1); + if (unlikely(ret != 0)) + goto out; + ret = get_futex_key(uaddr2, &key2); + if (unlikely(ret != 0)) + goto out; + + hb1 = hash_futex(&key1); + hb2 = hash_futex(&key2); + + double_lock_hb(hb1, hb2); + + if (likely(cmpval != NULL)) { + u32 curval; + + ret = get_futex_value_locked(&curval, uaddr1); + + if (unlikely(ret)) { + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); + + /* + * If we would have faulted, release mmap_sem, fault + * it in and start all over again. + */ + up_read(¤t->mm->mmap_sem); + + ret = get_user(curval, uaddr1); + + if (!ret) + goto retry; + + return ret; + } + if (curval != *cmpval) { + ret = -EAGAIN; + goto out_unlock; + } + } + + head1 = &hb1->chain; + plist_for_each_entry_safe(this, next, head1, list) { + if (!match_futex (&this->key, &key1)) + continue; + if (++ret <= nr_wake) { + wake_futex(this); + } else { + /* + * FIRST: get and set the pi_state + */ + if (!pi_state2) { + int s; + /* do this only the first time we requeue someone */ + s = lookup_pi_state_for_requeue(uaddr2, hb2, + &key2, &pi_state2); + if (s) { + ret = s; + goto out_unlock; + } + + lock2 = &pi_state2->pi_mutex; + spin_lock(&lock2->wait_lock); + + /* Save the top waiter of the wait_list */ + if (rt_mutex_has_waiters(lock2)) + top_waiter = rt_mutex_top_waiter(lock2); + } else + atomic_inc(&pi_state2->refcount); + + + this->pi_state = pi_state2; + + /* + * SECOND: requeue futex_q to the correct hashbucket + */ + + /* + * If key1 and key2 hash to the same bucket, no need to + * requeue. + */ + if (likely(head1 != &hb2->chain)) { + plist_del(&this->list, &hb1->chain); + plist_add(&this->list, &hb2->chain); + this->lock_ptr = &hb2->lock; +#ifdef CONFIG_DEBUG_PI_LIST + this->list.plist.lock = &hb2->lock; +#endif + } + this->key = key2; + get_futex_key_refs(&key2); + drop_count++; + + + /* + * THIRD: queue it to lock2 + */ + spin_lock_irq(&this->task->pi_lock); + waiter = &this->waiter; + waiter->task = this->task; + waiter->lock = lock2; + plist_node_init(&waiter->list_entry, this->task->prio); + plist_node_init(&waiter->pi_list_entry, this->task->prio); + plist_add(&waiter->list_entry, &lock2->wait_list); + this->task->pi_blocked_on = waiter; + spin_unlock_irq(&this->task->pi_lock); + + if (ret - nr_wake >= nr_requeue) + break; + } + } + + /* If we've requeued some tasks and the top_waiter of the rt_mutex + has changed, we must adjust the priority of the owner, if any */ + if (drop_count) { + struct task_struct *owner = rt_mutex_owner(lock2); + if (owner && + (top_waiter != (waiter = rt_mutex_top_waiter(lock2)))) { + int chain_walk = 0; + + spin_lock_irq(&owner->pi_lock); + if (top_waiter) + plist_del(&top_waiter->pi_list_entry, &owner->pi_waiters); + else + /* + * There was no waiters before the requeue, + * the flag must be updated + */ + mark_rt_mutex_waiters(lock2); + + plist_add(&waiter->pi_list_entry, &owner->pi_waiters); + __rt_mutex_adjust_prio(owner); + if (owner->pi_blocked_on) { + chain_walk = 1; + get_task_struct(owner); + } + + spin_unlock_irq(&owner->pi_lock); + spin_unlock(&lock2->wait_lock); + + if (chain_walk) + rt_mutex_adjust_prio_chain(owner, 0, lock2, NULL, + current); + } else { + /* No owner or the top_waiter does not change */ + mark_rt_mutex_waiters(lock2); + spin_unlock(&lock2->wait_lock); + } + } + +out_unlock: + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); + + /* drop_futex_key_refs() must be called outside the spinlocks. */ + while (--drop_count >= 0) + drop_futex_key_refs(&key1); + +out: + up_read(¤t->mm->mmap_sem); + return ret; +} + /* * Wake up all waiters hashed on the physical page that is mapped * to this virtual address: @@ -984,9 +1250,10 @@ static int unqueue_me(struct futex_q *q) /* * PI futexes can not be requeued and must remove themself from the - * hash bucket. The hash bucket lock is held on entry and dropped here. + * hash bucket. The hash bucket lock (i.e. lock_ptr) is held on entry + * and dropped here. */ -static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) +static void unqueue_me_pi(struct futex_q *q) { WARN_ON(plist_node_empty(&q->list)); plist_del(&q->list, &q->list.plist); @@ -995,11 +1262,65 @@ static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) free_pi_state(q->pi_state); q->pi_state = NULL; - spin_unlock(&hb->lock); + spin_unlock(q->lock_ptr); drop_futex_key_refs(&q->key); } +/* + * Fixup the pi_state owner with current. + * + * The cur->mm semaphore must be held, it is released at return of this + * function. + */ +static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, + struct futex_hash_bucket *hb, + struct task_struct *curr) +{ + u32 newtid = curr->pid | FUTEX_WAITERS; + struct futex_pi_state *pi_state = q->pi_state; + u32 uval, curval, newval; + int ret; + + /* Owner died? */ + if (pi_state->owner != NULL) { + spin_lock_irq(&pi_state->owner->pi_lock); + WARN_ON(list_empty(&pi_state->list)); + list_del_init(&pi_state->list); + spin_unlock_irq(&pi_state->owner->pi_lock); + } else + newtid |= FUTEX_OWNER_DIED; + + pi_state->owner = curr; + + spin_lock_irq(&curr->pi_lock); + WARN_ON(!list_empty(&pi_state->list)); + list_add(&pi_state->list, &curr->pi_state_list); + spin_unlock_irq(&curr->pi_lock); + + /* Unqueue and drop the lock */ + unqueue_me_pi(q); + up_read(&curr->mm->mmap_sem); + /* + * We own it, so we have to replace the pending owner + * TID. This must be atomic as we have preserve the + * owner died bit here. + */ + ret = get_user(uval, uaddr); + while (!ret) { + newval = (uval & FUTEX_OWNER_DIED) | newtid; + newval |= (uval & FUTEX_WAITER_REQUEUED); + curval = futex_atomic_cmpxchg_inatomic(uaddr, + uval, newval); + if (curval == -EFAULT) + ret = -EFAULT; + if (curval == uval) + break; + uval = curval; + } + return ret; +} + static long futex_wait_restart(struct restart_block *restart); static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) { @@ -1009,7 +1330,7 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) struct futex_q q; u32 uval; int ret; - struct hrtimer_sleeper t; + struct hrtimer_sleeper t, *to = NULL; int rem = 0; q.pi_state = NULL; @@ -1063,6 +1384,14 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) if (uval != val) goto out_unlock_release_sem; + /* + * This rt_mutex_waiter structure is prepared here and will + * be used only if this task is requeued from a normal futex to + * a PI-futex with futex_requeue_pi. + */ + debug_rt_mutex_init_waiter(&q.waiter); + q.waiter.task = NULL; + /* Only actually queue if *uaddr contained val. */ __queue_me(&q, hb); @@ -1092,6 +1421,7 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) if (!abs_time) schedule(); else { + to = &t; hrtimer_init(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); hrtimer_init_sleeper(&t, current); t.timer.expires = *abs_time; @@ -1119,6 +1449,66 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) * we are the only user of it. */ + if (q.pi_state) { + /* + * We were woken but have been requeued on a PI-futex. + * We have to complete the lock acquisition by taking + * the rtmutex. + */ + + struct rt_mutex *lock = &q.pi_state->pi_mutex; + + spin_lock(&lock->wait_lock); + if (unlikely(q.waiter.task)) { + remove_waiter(lock, &q.waiter); + } + spin_unlock(&lock->wait_lock); + + if (rem) + ret = -ETIMEDOUT; + else + ret = rt_mutex_timed_lock(lock, to, 1); + + down_read(&curr->mm->mmap_sem); + spin_lock(q.lock_ptr); + + /* + * Got the lock. We might not be the anticipated owner if we + * did a lock-steal - fix up the PI-state in that case. + */ + if (!ret && q.pi_state->owner != curr) { + /* + * We MUST play with the futex we were requeued on, + * NOT the current futex. + * We can retrieve it from the key of the pi_state + */ + uaddr = q.pi_state->key.uaddr; + + /* mmap_sem and hash_bucket lock are unlocked at + return of this function */ + ret = fixup_pi_state_owner(uaddr, &q, hb, curr); + } else { + /* + * Catch the rare case, where the lock was released + * when we were on the way back before we locked + * the hash bucket. + */ + if (ret && q.pi_state->owner == curr) { + if (rt_mutex_trylock(&q.pi_state->pi_mutex)) + ret = 0; + } + /* Unqueue and drop the lock */ + unqueue_me_pi(&q); + up_read(&curr->mm->mmap_sem); + } + + debug_rt_mutex_free_waiter(&q.waiter); + + return ret; + } + + debug_rt_mutex_free_waiter(&q.waiter); + /* If we were woken (and unqueued), we succeeded, whatever. */ if (!unqueue_me(&q)) return 0; @@ -1161,6 +1551,51 @@ static long futex_wait_restart(struct restart_block *restart) } +static void set_pi_futex_owner(struct futex_hash_bucket *hb, + union futex_key *key, struct task_struct *p) +{ + struct plist_head *head; + struct futex_q *this, *next; + struct futex_pi_state *pi_state = NULL; + struct rt_mutex *lock; + + /* Search a waiter that should already exists */ + + head = &hb->chain; + + plist_for_each_entry_safe(this, next, head, list) { + if (match_futex (&this->key, key)) { + pi_state = this->pi_state; + break; + } + } + + BUG_ON(!pi_state); + + /* set p as pi_state's owner */ + lock = &pi_state->pi_mutex; + + spin_lock(&lock->wait_lock); + spin_lock_irq(&p->pi_lock); + + list_add(&pi_state->list, &p->pi_state_list); + pi_state->owner = p; + + + /* set p as pi_mutex's owner */ + debug_rt_mutex_proxy_lock(lock, p); + WARN_ON(rt_mutex_owner(lock)); + rt_mutex_set_owner(lock, p, 0); + rt_mutex_deadlock_account_lock(lock, p); + + plist_add(&rt_mutex_top_waiter(lock)->pi_list_entry, + &p->pi_waiters); + __rt_mutex_adjust_prio(p); + + spin_unlock_irq(&p->pi_lock); + spin_unlock(&lock->wait_lock); +} + /* * Userspace tried a 0 -> TID atomic transition of the futex value * and failed. The kernel side here does the whole locking operation: @@ -1175,7 +1610,7 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, struct futex_hash_bucket *hb; u32 uval, newval, curval; struct futex_q q; - int ret, attempt = 0; + int ret, lock_held, attempt = 0; if (refill_pi_state_cache()) return -ENOMEM; @@ -1198,6 +1633,8 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, hb = queue_lock(&q, -1, NULL); retry_locked: + lock_held = 0; + /* * To avoid races, we attempt to take the lock here again * (by doing a 0 -> TID atomic cmpxchg), while holding all @@ -1216,7 +1653,16 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, if (unlikely((curval & FUTEX_TID_MASK) == current->pid)) { if (!detect && 0) force_sig(SIGKILL, current); - ret = -EDEADLK; + /* + * Normally, this check is done in user space. + * In case of requeue, the owner may attempt to lock this futex, + * even if the ownership has already been given by the previous + * waker. + * In the usual case, this is a case of deadlock, but not in case + * of REQUEUE_PI. + */ + if (!(curval & FUTEX_WAITER_REQUEUED)) + ret = -EDEADLK; goto out_unlock_release_sem; } @@ -1228,7 +1674,18 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, goto out_unlock_release_sem; uval = curval; - newval = uval | FUTEX_WAITERS; + /* + * In case of a requeue, check if there already is an owner + * If not, just take the futex. + */ + if ((curval & FUTEX_WAITER_REQUEUED) && !(curval & FUTEX_TID_MASK)) { + /* set current as futex owner */ + newval = curval | current->pid; + lock_held = 1; + } else + /* Set the WAITERS flag, so the owner will know it has someone + to wake at next unlock */ + newval = curval | FUTEX_WAITERS; pagefault_disable(); curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); @@ -1239,11 +1696,16 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, if (unlikely(curval != uval)) goto retry_locked; + if (lock_held) { + set_pi_futex_owner(hb, &q.key, curr); + goto out_unlock_release_sem; + } + /* * We dont have the lock. Look up the PI state (or create it if * we are the first waiter): */ - ret = lookup_pi_state(uval, hb, &q); + ret = lookup_pi_state(uval, hb, &q.key, &q.pi_state); if (unlikely(ret)) { /* @@ -1306,45 +1768,10 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, * Got the lock. We might not be the anticipated owner if we * did a lock-steal - fix up the PI-state in that case. */ - if (!ret && q.pi_state->owner != curr) { - u32 newtid = current->pid | FUTEX_WAITERS; - - /* Owner died? */ - if (q.pi_state->owner != NULL) { - spin_lock_irq(&q.pi_state->owner->pi_lock); - WARN_ON(list_empty(&q.pi_state->list)); - list_del_init(&q.pi_state->list); - spin_unlock_irq(&q.pi_state->owner->pi_lock); - } else - newtid |= FUTEX_OWNER_DIED; - - q.pi_state->owner = current; - - spin_lock_irq(¤t->pi_lock); - WARN_ON(!list_empty(&q.pi_state->list)); - list_add(&q.pi_state->list, ¤t->pi_state_list); - spin_unlock_irq(¤t->pi_lock); - - /* Unqueue and drop the lock */ - unqueue_me_pi(&q, hb); - up_read(&curr->mm->mmap_sem); - /* - * We own it, so we have to replace the pending owner - * TID. This must be atomic as we have preserve the - * owner died bit here. - */ - ret = get_user(uval, uaddr); - while (!ret) { - newval = (uval & FUTEX_OWNER_DIED) | newtid; - curval = futex_atomic_cmpxchg_inatomic(uaddr, - uval, newval); - if (curval == -EFAULT) - ret = -EFAULT; - if (curval == uval) - break; - uval = curval; - } - } else { + if (!ret && q.pi_state->owner != curr) + /* mmap_sem is unlocked at return of this function */ + ret = fixup_pi_state_owner(uaddr, &q, hb, curr); + else { /* * Catch the rare case, where the lock was released * when we were on the way back before we locked @@ -1355,7 +1782,7 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, ret = 0; } /* Unqueue and drop the lock */ - unqueue_me_pi(&q, hb); + unqueue_me_pi(&q); up_read(&curr->mm->mmap_sem); } @@ -1724,6 +2151,8 @@ retry: * userspace. */ mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + /* Also keep the FUTEX_WAITER_REQUEUED flag if set */ + mval |= (uval & FUTEX_WAITER_REQUEUED); nval = futex_atomic_cmpxchg_inatomic(uaddr, uval, mval); if (nval == -EFAULT) @@ -1854,6 +2283,9 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, case FUTEX_TRYLOCK_PI: ret = futex_lock_pi(uaddr, 0, timeout, 1); break; + case FUTEX_CMP_REQUEUE_PI: + ret = futex_requeue_pi(uaddr, uaddr2, val, val2, &val3); + break; default: ret = -ENOSYS; } @@ -1883,7 +2315,8 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, /* * requeue parameter in 'utime' if op == FUTEX_REQUEUE. */ - if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) + if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE + || op == FUTEX_CMP_REQUEUE_PI) val2 = (u32) (unsigned long) utime; return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index dff27c471ea..338a9b489fb 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -156,7 +156,8 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, t = ktime_add(ktime_get(), t); tp = &t; } - if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) + if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE + || op == FUTEX_CMP_REQUEUE_PI) val2 = (int) (unsigned long) utime; return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); diff --git a/kernel/rtmutex.c b/kernel/rtmutex.c index 180978cb2f7..12879f6c1ec 100644 --- a/kernel/rtmutex.c +++ b/kernel/rtmutex.c @@ -56,7 +56,7 @@ * state. */ -static void +void rt_mutex_set_owner(struct rt_mutex *lock, struct task_struct *owner, unsigned long mask) { @@ -80,29 +80,6 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock) clear_rt_mutex_waiters(lock); } -/* - * We can speed up the acquire/release, if the architecture - * supports cmpxchg and if there's no debugging state to be set up - */ -#if defined(__HAVE_ARCH_CMPXCHG) && !defined(CONFIG_DEBUG_RT_MUTEXES) -# define rt_mutex_cmpxchg(l,c,n) (cmpxchg(&l->owner, c, n) == c) -static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) -{ - unsigned long owner, *p = (unsigned long *) &lock->owner; - - do { - owner = *p; - } while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner); -} -#else -# define rt_mutex_cmpxchg(l,c,n) (0) -static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) -{ - lock->owner = (struct task_struct *) - ((unsigned long)lock->owner | RT_MUTEX_HAS_WAITERS); -} -#endif - /* * Calculate task priority from the waiter list priority * @@ -123,7 +100,7 @@ int rt_mutex_getprio(struct task_struct *task) * * This can be both boosting and unboosting. task->pi_lock must be held. */ -static void __rt_mutex_adjust_prio(struct task_struct *task) +void __rt_mutex_adjust_prio(struct task_struct *task) { int prio = rt_mutex_getprio(task); @@ -159,11 +136,11 @@ int max_lock_depth = 1024; * Decreases task's usage by one - may thus free the task. * Returns 0 or -EDEADLK. */ -static int rt_mutex_adjust_prio_chain(struct task_struct *task, - int deadlock_detect, - struct rt_mutex *orig_lock, - struct rt_mutex_waiter *orig_waiter, - struct task_struct *top_task) +int rt_mutex_adjust_prio_chain(struct task_struct *task, + int deadlock_detect, + struct rt_mutex *orig_lock, + struct rt_mutex_waiter *orig_waiter, + struct task_struct *top_task) { struct rt_mutex *lock; struct rt_mutex_waiter *waiter, *top_waiter = orig_waiter; @@ -524,8 +501,8 @@ static void wakeup_next_waiter(struct rt_mutex *lock) * * Must be called with lock->wait_lock held */ -static void remove_waiter(struct rt_mutex *lock, - struct rt_mutex_waiter *waiter) +void remove_waiter(struct rt_mutex *lock, + struct rt_mutex_waiter *waiter) { int first = (waiter == rt_mutex_top_waiter(lock)); struct task_struct *owner = rt_mutex_owner(lock); diff --git a/kernel/rtmutex_common.h b/kernel/rtmutex_common.h index 9c75856e791..242ec7ee740 100644 --- a/kernel/rtmutex_common.h +++ b/kernel/rtmutex_common.h @@ -112,6 +112,29 @@ static inline unsigned long rt_mutex_owner_pending(struct rt_mutex *lock) return (unsigned long)lock->owner & RT_MUTEX_OWNER_PENDING; } +/* + * We can speed up the acquire/release, if the architecture + * supports cmpxchg and if there's no debugging state to be set up + */ +#if defined(__HAVE_ARCH_CMPXCHG) && !defined(CONFIG_DEBUG_RT_MUTEXES) +# define rt_mutex_cmpxchg(l,c,n) (cmpxchg(&l->owner, c, n) == c) +static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) +{ + unsigned long owner, *p = (unsigned long *) &lock->owner; + + do { + owner = *p; + } while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner); +} +#else +# define rt_mutex_cmpxchg(l,c,n) (0) +static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) +{ + lock->owner = (struct task_struct *) + ((unsigned long)lock->owner | RT_MUTEX_HAS_WAITERS); +} +#endif + /* * PI-futex support (proxy locking functions, etc.): */ @@ -120,4 +143,15 @@ extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock, struct task_struct *proxy_owner); extern void rt_mutex_proxy_unlock(struct rt_mutex *lock, struct task_struct *proxy_owner); + +extern void rt_mutex_set_owner(struct rt_mutex *lock, struct task_struct *owner, + unsigned long mask); +extern void __rt_mutex_adjust_prio(struct task_struct *task); +extern int rt_mutex_adjust_prio_chain(struct task_struct *task, + int deadlock_detect, + struct rt_mutex *orig_lock, + struct rt_mutex_waiter *orig_waiter, + struct task_struct *top_task); +extern void remove_waiter(struct rt_mutex *lock, + struct rt_mutex_waiter *waiter); #endif -- cgit v1.2.3 From 34f01cc1f512fa783302982776895c73714ebbc2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 May 2007 02:35:04 -0700 Subject: FUTEX: new PRIVATE futexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Analysis of current linux futex code : -------------------------------------- A central hash table futex_queues[] holds all contexts (futex_q) of waiting threads. Each futex_wait()/futex_wait() has to obtain a spinlock on a hash slot to perform lookups or insert/deletion of a futex_q. When a futex_wait() is done, calling thread has to : 1) - Obtain a read lock on mmap_sem to be able to validate the user pointer (calling find_vma()). This validation tells us if the futex uses an inode based store (mapped file), or mm based store (anonymous mem) 2) - compute a hash key 3) - Atomic increment of reference counter on an inode or a mm_struct 4) - lock part of futex_queues[] hash table 5) - perform the test on value of futex. (rollback is value != expected_value, returns EWOULDBLOCK) (various loops if test triggers mm faults) 6) queue the context into hash table, release the lock got in 4) 7) - release the read_lock on mmap_sem 8) Eventually unqueue the context (but rarely, as this part  may be done by the futex_wake()) Futexes were designed to improve scalability but current implementation has various problems : - Central hashtable : This means scalability problems if many processes/threads want to use futexes at the same time. This means NUMA unbalance because this hashtable is located on one node. - Using mmap_sem on every futex() syscall : Even if mmap_sem is a rw_semaphore, up_read()/down_read() are doing atomic ops on mmap_sem, dirtying cache line : - lot of cache line ping pongs on SMP configurations. mmap_sem is also extensively used by mm code (page faults, mmap()/munmap()) Highly threaded processes might suffer from mmap_sem contention. mmap_sem is also used by oprofile code. Enabling oprofile hurts threaded programs because of contention on the mmap_sem cache line. - Using an atomic_inc()/atomic_dec() on inode ref counter or mm ref counter: It's also a cache line ping pong on SMP. It also increases mmap_sem hold time because of cache misses. Most of these scalability problems come from the fact that futexes are in one global namespace. As we use a central hash table, we must make sure they are all using the same reference (given by the mm subsystem). We chose to force all futexes be 'shared'. This has a cost. But fact is POSIX defined PRIVATE and SHARED, allowing clear separation, and optimal performance if carefuly implemented. Time has come for linux to have better threading performance. The goal is to permit new futex commands to avoid : - Taking the mmap_sem semaphore, conflicting with other subsystems. - Modifying a ref_count on mm or an inode, still conflicting with mm or fs. This is possible because, for one process using PTHREAD_PROCESS_PRIVATE futexes, we only need to distinguish futexes by their virtual address, no matter the underlying mm storage is. If glibc wants to exploit this new infrastructure, it should use new _PRIVATE futex subcommands for PTHREAD_PROCESS_PRIVATE futexes. And be prepared to fallback on old subcommands for old kernels. Using one global variable with the FUTEX_PRIVATE_FLAG or 0 value should be OK. PTHREAD_PROCESS_SHARED futexes should still use the old subcommands. Compatibility with old applications is preserved, they still hit the scalability problems, but new applications can fly :) Note : the same SHARED futex (mapped on a file) can be used by old binaries *and* new binaries, because both binaries will use the old subcommands. Note : Vast majority of futexes should be using PROCESS_PRIVATE semantic, as this is the default semantic. Almost all applications should benefit of this changes (new kernel and updated libc) Some bench results on a Pentium M 1.6 GHz (SMP kernel on a UP machine) /* calling futex_wait(addr, value) with value != *addr */ 433 cycles per futex(FUTEX_WAIT) call (mixing 2 futexes) 424 cycles per futex(FUTEX_WAIT) call (using one futex) 334 cycles per futex(FUTEX_WAIT_PRIVATE) call (mixing 2 futexes) 334 cycles per futex(FUTEX_WAIT_PRIVATE) call (using one futex) For reference : 187 cycles per getppid() call 188 cycles per umask() call 181 cycles per ni_syscall() call Signed-off-by: Eric Dumazet Pierre Peiffer Cc: "Ulrich Drepper" Cc: "Nick Piggin" Cc: "Ingo Molnar" Cc: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/futex.h | 29 ++++- kernel/futex.c | 324 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 236 insertions(+), 117 deletions(-) diff --git a/include/linux/futex.h b/include/linux/futex.h index 1bd8dfcb037..899fc7f20ed 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -19,6 +19,18 @@ union ktime; #define FUTEX_TRYLOCK_PI 8 #define FUTEX_CMP_REQUEUE_PI 9 +#define FUTEX_PRIVATE_FLAG 128 +#define FUTEX_CMD_MASK ~FUTEX_PRIVATE_FLAG + +#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) +#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) +#define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG) +#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG) +#define FUTEX_WAKE_OP_PRIVATE (FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG) +#define FUTEX_LOCK_PI_PRIVATE (FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG) +#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG) +#define FUTEX_TRYLOCK_PI_PRIVATE (FUTEX_TRYLOCK_PI | FUTEX_PRIVATE_FLAG) + /* * Support for robust futexes: the kernel cleans up held futexes at * thread exit time. @@ -114,8 +126,18 @@ handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi); * Don't rearrange members without looking at hash_futex(). * * offset is aligned to a multiple of sizeof(u32) (== 4) by definition. - * We set bit 0 to indicate if it's an inode-based key. - */ + * We use the two low order bits of offset to tell what is the kind of key : + * 00 : Private process futex (PTHREAD_PROCESS_PRIVATE) + * (no reference on an inode or mm) + * 01 : Shared futex (PTHREAD_PROCESS_SHARED) + * mapped on a file (reference on the underlying inode) + * 10 : Shared futex (PTHREAD_PROCESS_SHARED) + * (but private mapping on an mm, and reference taken on it) +*/ + +#define FUT_OFF_INODE 1 /* We set bit 0 if key has a reference on inode */ +#define FUT_OFF_MMSHARED 2 /* We set bit 1 if key has a reference on mm */ + union futex_key { u32 __user *uaddr; struct { @@ -134,7 +156,8 @@ union futex_key { int offset; } both; }; -int get_futex_key(u32 __user *uaddr, union futex_key *key); +int get_futex_key(u32 __user *uaddr, struct rw_semaphore *shared, + union futex_key *key); void get_futex_key_refs(union futex_key *key); void drop_futex_key_refs(union futex_key *key); diff --git a/kernel/futex.c b/kernel/futex.c index 4a60ef55dab..b7ce15c67e3 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -16,6 +16,9 @@ * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar * Copyright (C) 2006 Timesys Corp., Thomas Gleixner * + * PRIVATE futexes by Eric Dumazet + * Copyright (C) 2007 Eric Dumazet + * * Thanks to Ben LaHaise for yelling "hashed waitqueues" loudly * enough at me, Linus for the original (flawed) idea, Matthew * Kirkwood for proof-of-concept implementation. @@ -150,19 +153,26 @@ static inline int match_futex(union futex_key *key1, union futex_key *key2) && key1->both.offset == key2->both.offset); } -/* - * Get parameters which are the keys for a futex. +/** + * get_futex_key - Get parameters which are the keys for a futex. + * @uaddr: virtual address of the futex + * @shared: NULL for a PROCESS_PRIVATE futex, + * ¤t->mm->mmap_sem for a PROCESS_SHARED futex + * @key: address where result is stored. + * + * Returns a negative error code or 0 + * The key words are stored in *key on success. * * For shared mappings, it's (page->index, vma->vm_file->f_path.dentry->d_inode, * offset_within_page). For private mappings, it's (uaddr, current->mm). * We can usually work out the index without swapping in the page. * - * Returns: 0, or negative error code. - * The key words are stored in *key on success. - * - * Should be called with ¤t->mm->mmap_sem but NOT any spinlocks. + * fshared is NULL for PROCESS_PRIVATE futexes + * For other futexes, it points to ¤t->mm->mmap_sem and + * caller must have taken the reader lock. but NOT any spinlocks. */ -int get_futex_key(u32 __user *uaddr, union futex_key *key) +int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared, + union futex_key *key) { unsigned long address = (unsigned long)uaddr; struct mm_struct *mm = current->mm; @@ -174,10 +184,24 @@ int get_futex_key(u32 __user *uaddr, union futex_key *key) * The futex address must be "naturally" aligned. */ key->both.offset = address % PAGE_SIZE; - if (unlikely((key->both.offset % sizeof(u32)) != 0)) + if (unlikely((address % sizeof(u32)) != 0)) return -EINVAL; address -= key->both.offset; + /* + * PROCESS_PRIVATE futexes are fast. + * As the mm cannot disappear under us and the 'key' only needs + * virtual address, we dont even have to find the underlying vma. + * Note : We do have to check 'uaddr' is a valid user address, + * but access_ok() should be faster than find_vma() + */ + if (!fshared) { + if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))) + return -EFAULT; + key->private.mm = mm; + key->private.address = address; + return 0; + } /* * The futex is hashed differently depending on whether * it's in a shared or private mapping. So check vma first. @@ -205,6 +229,7 @@ int get_futex_key(u32 __user *uaddr, union futex_key *key) * mappings of _writable_ handles. */ if (likely(!(vma->vm_flags & VM_MAYSHARE))) { + key->both.offset |= FUT_OFF_MMSHARED; /* reference taken on mm */ key->private.mm = mm; key->private.address = address; return 0; @@ -214,7 +239,7 @@ int get_futex_key(u32 __user *uaddr, union futex_key *key) * Linear file mappings are also simple. */ key->shared.inode = vma->vm_file->f_path.dentry->d_inode; - key->both.offset++; /* Bit 0 of offset indicates inode-based key. */ + key->both.offset |= FUT_OFF_INODE; /* inode-based key. */ if (likely(!(vma->vm_flags & VM_NONLINEAR))) { key->shared.pgoff = (((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff); @@ -242,16 +267,18 @@ EXPORT_SYMBOL_GPL(get_futex_key); * Take a reference to the resource addressed by a key. * Can be called while holding spinlocks. * - * NOTE: mmap_sem MUST be held between get_futex_key() and calling this - * function, if it is called at all. mmap_sem keeps key->shared.inode valid. */ inline void get_futex_key_refs(union futex_key *key) { - if (key->both.ptr != 0) { - if (key->both.offset & 1) + if (key->both.ptr == 0) + return; + switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { + case FUT_OFF_INODE: atomic_inc(&key->shared.inode->i_count); - else + break; + case FUT_OFF_MMSHARED: atomic_inc(&key->private.mm->mm_count); + break; } } EXPORT_SYMBOL_GPL(get_futex_key_refs); @@ -262,11 +289,15 @@ EXPORT_SYMBOL_GPL(get_futex_key_refs); */ void drop_futex_key_refs(union futex_key *key) { - if (key->both.ptr != 0) { - if (key->both.offset & 1) + if (key->both.ptr == 0) + return; + switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { + case FUT_OFF_INODE: iput(key->shared.inode); - else + break; + case FUT_OFF_MMSHARED: mmdrop(key->private.mm); + break; } } EXPORT_SYMBOL_GPL(drop_futex_key_refs); @@ -283,28 +314,38 @@ static inline int get_futex_value_locked(u32 *dest, u32 __user *from) } /* - * Fault handling. Called with current->mm->mmap_sem held. + * Fault handling. + * if fshared is non NULL, current->mm->mmap_sem is already held */ -static int futex_handle_fault(unsigned long address, int attempt) +static int futex_handle_fault(unsigned long address, + struct rw_semaphore *fshared, int attempt) { struct vm_area_struct * vma; struct mm_struct *mm = current->mm; + int ret = -EFAULT; - if (attempt > 2 || !(vma = find_vma(mm, address)) || - vma->vm_start > address || !(vma->vm_flags & VM_WRITE)) - return -EFAULT; + if (attempt > 2) + return ret; - switch (handle_mm_fault(mm, vma, address, 1)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - default: - return -EFAULT; + if (!fshared) + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + if (vma && address >= vma->vm_start && + (vma->vm_flags & VM_WRITE)) { + switch (handle_mm_fault(mm, vma, address, 1)) { + case VM_FAULT_MINOR: + ret = 0; + current->min_flt++; + break; + case VM_FAULT_MAJOR: + ret = 0; + current->maj_flt++; + break; + } } - return 0; + if (!fshared) + up_read(&mm->mmap_sem); + return ret; } /* @@ -647,7 +688,8 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2) * Wake up all waiters hashed on the physical page that is mapped * to this virtual address: */ -static int futex_wake(u32 __user *uaddr, int nr_wake) +static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared, + int nr_wake) { struct futex_hash_bucket *hb; struct futex_q *this, *next; @@ -655,9 +697,10 @@ static int futex_wake(u32 __user *uaddr, int nr_wake) union futex_key key; int ret; - down_read(¤t->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr, &key); + ret = get_futex_key(uaddr, fshared, &key); if (unlikely(ret != 0)) goto out; @@ -679,7 +722,8 @@ static int futex_wake(u32 __user *uaddr, int nr_wake) spin_unlock(&hb->lock); out: - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; } @@ -746,7 +790,9 @@ retry: * and requeue the next nr_requeue waiters following hashed on * one physical page to another physical page (PI-futex uaddr2) */ -static int futex_requeue_pi(u32 __user *uaddr1, u32 __user *uaddr2, +static int futex_requeue_pi(u32 __user *uaddr1, + struct rw_semaphore *fshared, + u32 __user *uaddr2, int nr_wake, int nr_requeue, u32 *cmpval) { union futex_key key1, key2; @@ -765,12 +811,13 @@ retry: /* * First take all the futex related locks: */ - down_read(¤t->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr1, &key1); + ret = get_futex_key(uaddr1, fshared, &key1); if (unlikely(ret != 0)) goto out; - ret = get_futex_key(uaddr2, &key2); + ret = get_futex_key(uaddr2, fshared, &key2); if (unlikely(ret != 0)) goto out; @@ -793,7 +840,8 @@ retry: * If we would have faulted, release mmap_sem, fault * it in and start all over again. */ - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(curval, uaddr1); @@ -927,7 +975,8 @@ out_unlock: drop_futex_key_refs(&key1); out: - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; } @@ -936,7 +985,8 @@ out: * to this virtual address: */ static int -futex_wake_op(u32 __user *uaddr1, u32 __user *uaddr2, +futex_wake_op(u32 __user *uaddr1, struct rw_semaphore *fshared, + u32 __user *uaddr2, int nr_wake, int nr_wake2, int op) { union futex_key key1, key2; @@ -946,12 +996,13 @@ futex_wake_op(u32 __user *uaddr1, u32 __user *uaddr2, int ret, op_ret, attempt = 0; retryfull: - down_read(¤t->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr1, &key1); + ret = get_futex_key(uaddr1, fshared, &key1); if (unlikely(ret != 0)) goto out; - ret = get_futex_key(uaddr2, &key2); + ret = get_futex_key(uaddr2, fshared, &key2); if (unlikely(ret != 0)) goto out; @@ -991,11 +1042,10 @@ retry: * still holding the mmap_sem. */ if (attempt++) { - if (futex_handle_fault((unsigned long)uaddr2, - attempt)) { - ret = -EFAULT; + ret = futex_handle_fault((unsigned long)uaddr2, + fshared, attempt); + if (ret) goto out; - } goto retry; } @@ -1003,7 +1053,8 @@ retry: * If we would have faulted, release mmap_sem, * fault it in and start all over again. */ - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(dummy, uaddr2); if (ret) @@ -1040,7 +1091,8 @@ retry: if (hb1 != hb2) spin_unlock(&hb2->lock); out: - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; } @@ -1048,7 +1100,8 @@ out: * Requeue all waiters hashed on one physical page to another * physical page. */ -static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, +static int futex_requeue(u32 __user *uaddr1, struct rw_semaphore *fshared, + u32 __user *uaddr2, int nr_wake, int nr_requeue, u32 *cmpval) { union futex_key key1, key2; @@ -1058,12 +1111,13 @@ static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, int ret, drop_count = 0; retry: - down_read(¤t->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr1, &key1); + ret = get_futex_key(uaddr1, fshared, &key1); if (unlikely(ret != 0)) goto out; - ret = get_futex_key(uaddr2, &key2); + ret = get_futex_key(uaddr2, fshared, &key2); if (unlikely(ret != 0)) goto out; @@ -1086,7 +1140,8 @@ static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, * If we would have faulted, release mmap_sem, fault * it in and start all over again. */ - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(curval, uaddr1); @@ -1139,7 +1194,8 @@ out_unlock: drop_futex_key_refs(&key1); out: - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; } @@ -1273,7 +1329,8 @@ static void unqueue_me_pi(struct futex_q *q) * The cur->mm semaphore must be held, it is released at return of this * function. */ -static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, +static int fixup_pi_state_owner(u32 __user *uaddr, struct rw_semaphore *fshared, + struct futex_q *q, struct futex_hash_bucket *hb, struct task_struct *curr) { @@ -1300,7 +1357,8 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, /* Unqueue and drop the lock */ unqueue_me_pi(q); - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); /* * We own it, so we have to replace the pending owner * TID. This must be atomic as we have preserve the @@ -1321,8 +1379,15 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, return ret; } +/* + * In case we must use restart_block to restart a futex_wait, + * we encode in the 'arg3' shared capability + */ +#define ARG3_SHARED 1 + static long futex_wait_restart(struct restart_block *restart); -static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) +static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared, + u32 val, ktime_t *abs_time) { struct task_struct *curr = current; DECLARE_WAITQUEUE(wait, curr); @@ -1335,9 +1400,10 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) q.pi_state = NULL; retry: - down_read(&curr->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr, &q.key); + ret = get_futex_key(uaddr, fshared, &q.key); if (unlikely(ret != 0)) goto out_release_sem; @@ -1360,8 +1426,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) * a wakeup when *uaddr != val on entry to the syscall. This is * rare, but normal. * - * We hold the mmap semaphore, so the mapping cannot have changed - * since we looked it up in get_futex_key. + * for shared futexes, we hold the mmap semaphore, so the mapping + * cannot have changed since we looked it up in get_futex_key. */ ret = get_futex_value_locked(&uval, uaddr); @@ -1372,7 +1438,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) * If we would have faulted, release mmap_sem, fault it in and * start all over again. */ - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(uval, uaddr); @@ -1399,7 +1466,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) * Now the futex is queued and we have checked the data, we * don't want to hold mmap_sem while we sleep. */ - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); /* * There might have been scheduling since the queue_me(), as we @@ -1469,7 +1537,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) else ret = rt_mutex_timed_lock(lock, to, 1); - down_read(&curr->mm->mmap_sem); + if (fshared) + down_read(fshared); spin_lock(q.lock_ptr); /* @@ -1486,7 +1555,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) /* mmap_sem and hash_bucket lock are unlocked at return of this function */ - ret = fixup_pi_state_owner(uaddr, &q, hb, curr); + ret = fixup_pi_state_owner(uaddr, fshared, + &q, hb, curr); } else { /* * Catch the rare case, where the lock was released @@ -1499,7 +1569,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) } /* Unqueue and drop the lock */ unqueue_me_pi(&q); - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); } debug_rt_mutex_free_waiter(&q.waiter); @@ -1528,6 +1599,9 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) restart->arg0 = (unsigned long)uaddr; restart->arg1 = (unsigned long)val; restart->arg2 = (unsigned long)abs_time; + restart->arg3 = 0; + if (fshared) + restart->arg3 |= ARG3_SHARED; return -ERESTART_RESTARTBLOCK; } @@ -1535,7 +1609,8 @@ static int futex_wait(u32 __user *uaddr, u32 val, ktime_t *abs_time) queue_unlock(&q, hb); out_release_sem: - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; } @@ -1545,9 +1620,12 @@ static long futex_wait_restart(struct restart_block *restart) u32 __user *uaddr = (u32 __user *)restart->arg0; u32 val = (u32)restart->arg1; ktime_t *abs_time = (ktime_t *)restart->arg2; + struct rw_semaphore *fshared = NULL; restart->fn = do_no_restart_syscall; - return (long)futex_wait(uaddr, val, abs_time); + if (restart->arg3 & ARG3_SHARED) + fshared = ¤t->mm->mmap_sem; + return (long)futex_wait(uaddr, fshared, val, abs_time); } @@ -1602,8 +1680,8 @@ static void set_pi_futex_owner(struct futex_hash_bucket *hb, * if there are waiters then it will block, it does PI, etc. (Due to * races the kernel might see a 0 value of the futex too.) */ -static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, - int trylock) +static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared, + int detect, ktime_t *time, int trylock) { struct hrtimer_sleeper timeout, *to = NULL; struct task_struct *curr = current; @@ -1624,9 +1702,10 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, q.pi_state = NULL; retry: - down_read(&curr->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr, &q.key); + ret = get_futex_key(uaddr, fshared, &q.key); if (unlikely(ret != 0)) goto out_release_sem; @@ -1747,7 +1826,8 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, * Now the futex is queued and we have checked the data, we * don't want to hold mmap_sem while we sleep. */ - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); WARN_ON(!q.pi_state); /* @@ -1761,7 +1841,8 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, ret = ret ? 0 : -EWOULDBLOCK; } - down_read(&curr->mm->mmap_sem); + if (fshared) + down_read(fshared); spin_lock(q.lock_ptr); /* @@ -1770,7 +1851,7 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, */ if (!ret && q.pi_state->owner != curr) /* mmap_sem is unlocked at return of this function */ - ret = fixup_pi_state_owner(uaddr, &q, hb, curr); + ret = fixup_pi_state_owner(uaddr, fshared, &q, hb, curr); else { /* * Catch the rare case, where the lock was released @@ -1783,7 +1864,8 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, } /* Unqueue and drop the lock */ unqueue_me_pi(&q); - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); } if (!detect && ret == -EDEADLK && 0) @@ -1795,7 +1877,8 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, queue_unlock(&q, hb); out_release_sem: - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; uaddr_faulted: @@ -1806,15 +1889,16 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, * still holding the mmap_sem. */ if (attempt++) { - if (futex_handle_fault((unsigned long)uaddr, attempt)) { - ret = -EFAULT; + ret = futex_handle_fault((unsigned long)uaddr, fshared, + attempt); + if (ret) goto out_unlock_release_sem; - } goto retry_locked; } queue_unlock(&q, hb); - up_read(&curr->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(uval, uaddr); if (!ret && (uval != -EFAULT)) @@ -1828,7 +1912,7 @@ static int futex_lock_pi(u32 __user *uaddr, int detect, ktime_t *time, * This is the in-kernel slowpath: we look up the PI state (if any), * and do the rt-mutex unlock. */ -static int futex_unlock_pi(u32 __user *uaddr) +static int futex_unlock_pi(u32 __user *uaddr, struct rw_semaphore *fshared) { struct futex_hash_bucket *hb; struct futex_q *this, *next; @@ -1848,9 +1932,10 @@ retry: /* * First take all the futex related locks: */ - down_read(¤t->mm->mmap_sem); + if (fshared) + down_read(fshared); - ret = get_futex_key(uaddr, &key); + ret = get_futex_key(uaddr, fshared, &key); if (unlikely(ret != 0)) goto out; @@ -1909,7 +1994,8 @@ retry_locked: out_unlock: spin_unlock(&hb->lock); out: - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); return ret; @@ -1921,15 +2007,16 @@ pi_faulted: * still holding the mmap_sem. */ if (attempt++) { - if (futex_handle_fault((unsigned long)uaddr, attempt)) { - ret = -EFAULT; + ret = futex_handle_fault((unsigned long)uaddr, fshared, + attempt); + if (ret) goto out_unlock; - } goto retry_locked; } spin_unlock(&hb->lock); - up_read(¤t->mm->mmap_sem); + if (fshared) + up_read(fshared); ret = get_user(uval, uaddr); if (!ret && (uval != -EFAULT)) @@ -1981,6 +2068,7 @@ static int futex_fd(u32 __user *uaddr, int signal) struct futex_q *q; struct file *filp; int ret, err; + struct rw_semaphore *fshared; static unsigned long printk_interval; if (printk_timed_ratelimit(&printk_interval, 60 * 60 * 1000)) { @@ -2022,11 +2110,12 @@ static int futex_fd(u32 __user *uaddr, int signal) } q->pi_state = NULL; - down_read(¤t->mm->mmap_sem); - err = get_futex_key(uaddr, &q->key); + fshared = ¤t->mm->mmap_sem; + down_read(fshared); + err = get_futex_key(uaddr, fshared, &q->key); if (unlikely(err != 0)) { - up_read(¤t->mm->mmap_sem); + up_read(fshared); kfree(q); goto error; } @@ -2038,7 +2127,7 @@ static int futex_fd(u32 __user *uaddr, int signal) filp->private_data = q; queue_me(q, ret, filp); - up_read(¤t->mm->mmap_sem); + up_read(fshared); /* Now we map fd to filp, so userspace can access it */ fd_install(ret, filp); @@ -2167,7 +2256,7 @@ retry: */ if (!pi) { if (uval & FUTEX_WAITERS) - futex_wake(uaddr, 1); + futex_wake(uaddr, &curr->mm->mmap_sem, 1); } } return 0; @@ -2223,7 +2312,8 @@ void exit_robust_list(struct task_struct *curr) return; if (pending) - handle_futex_death((void __user *)pending + futex_offset, curr, pip); + handle_futex_death((void __user *)pending + futex_offset, + curr, pip); while (entry != &head->list) { /* @@ -2253,38 +2343,43 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, u32 __user *uaddr2, u32 val2, u32 val3) { int ret; + int cmd = op & FUTEX_CMD_MASK; + struct rw_semaphore *fshared = NULL; + + if (!(op & FUTEX_PRIVATE_FLAG)) + fshared = ¤t->mm->mmap_sem; - switch (op) { + switch (cmd) { case FUTEX_WAIT: - ret = futex_wait(uaddr, val, timeout); + ret = futex_wait(uaddr, fshared, val, timeout); break; case FUTEX_WAKE: - ret = futex_wake(uaddr, val); + ret = futex_wake(uaddr, fshared, val); break; case FUTEX_FD: /* non-zero val means F_SETOWN(getpid()) & F_SETSIG(val) */ ret = futex_fd(uaddr, val); break; case FUTEX_REQUEUE: - ret = futex_requeue(uaddr, uaddr2, val, val2, NULL); + ret = futex_requeue(uaddr, fshared, uaddr2, val, val2, NULL); break; case FUTEX_CMP_REQUEUE: - ret = futex_requeue(uaddr, uaddr2, val, val2, &val3); + ret = futex_requeue(uaddr, fshared, uaddr2, val, val2, &val3); break; case FUTEX_WAKE_OP: - ret = futex_wake_op(uaddr, uaddr2, val, val2, val3); + ret = futex_wake_op(uaddr, fshared, uaddr2, val, val2, val3); break; case FUTEX_LOCK_PI: - ret = futex_lock_pi(uaddr, val, timeout, 0); + ret = futex_lock_pi(uaddr, fshared, val, timeout, 0); break; case FUTEX_UNLOCK_PI: - ret = futex_unlock_pi(uaddr); + ret = futex_unlock_pi(uaddr, fshared); break; case FUTEX_TRYLOCK_PI: - ret = futex_lock_pi(uaddr, 0, timeout, 1); + ret = futex_lock_pi(uaddr, fshared, 0, timeout, 1); break; case FUTEX_CMP_REQUEUE_PI: - ret = futex_requeue_pi(uaddr, uaddr2, val, val2, &val3); + ret = futex_requeue_pi(uaddr, fshared, uaddr2, val, val2, &val3); break; default: ret = -ENOSYS; @@ -2300,23 +2395,24 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, struct timespec ts; ktime_t t, *tp = NULL; u32 val2 = 0; + int cmd = op & FUTEX_CMD_MASK; - if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) { + if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI)) { if (copy_from_user(&ts, utime, sizeof(ts)) != 0) return -EFAULT; if (!timespec_valid(&ts)) return -EINVAL; t = timespec_to_ktime(ts); - if (op == FUTEX_WAIT) + if (cmd == FUTEX_WAIT) t = ktime_add(ktime_get(), t); tp = &t; } /* - * requeue parameter in 'utime' if op == FUTEX_REQUEUE. + * requeue parameter in 'utime' if cmd == FUTEX_REQUEUE. */ - if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE - || op == FUTEX_CMP_REQUEUE_PI) + if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE + || cmd == FUTEX_CMP_REQUEUE_PI) val2 = (u32) (unsigned long) utime; return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); -- cgit v1.2.3 From 38a23e311b6cd389b9d8af2ea6c28c8cffbe581c Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Wed, 9 May 2007 02:35:05 -0700 Subject: timer: parenthesis fix in tbase_get_deferrable() etc Signed-off-by: Jarek Poplawski Cc: Thomas Gleixner Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/timer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/timer.c b/kernel/timer.c index 7a6448340f9..58f6dd07c80 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -92,24 +92,24 @@ static DEFINE_PER_CPU(tvec_base_t *, tvec_bases) = &boot_tvec_bases; /* Functions below help us manage 'deferrable' flag */ static inline unsigned int tbase_get_deferrable(tvec_base_t *base) { - return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG); + return (unsigned int)((unsigned long)base & TBASE_DEFERRABLE_FLAG); } static inline tvec_base_t *tbase_get_base(tvec_base_t *base) { - return ((tvec_base_t *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG)); + return (tvec_base_t *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG); } static inline void timer_set_deferrable(struct timer_list *timer) { - timer->base = ((tvec_base_t *)((unsigned long)(timer->base) | - TBASE_DEFERRABLE_FLAG)); + timer->base = (tvec_base_t *)((unsigned long)timer->base | + TBASE_DEFERRABLE_FLAG); } static inline void timer_set_base(struct timer_list *timer, tvec_base_t *new_base) { - timer->base = (tvec_base_t *)((unsigned long)(new_base) | + timer->base = (tvec_base_t *)((unsigned long)new_base | tbase_get_deferrable(timer->base)); } -- cgit v1.2.3 From 01f2705daf5a36208e69d7cf95db9c330f843af6 Mon Sep 17 00:00:00 2001 From: Nate Diller Date: Wed, 9 May 2007 02:35:07 -0700 Subject: fs: convert core functions to zero_user_page It's very common for file systems to need to zero part or all of a page, the simplist way is just to use kmap_atomic() and memset(). There's actually a library function in include/linux/highmem.h that does exactly that, but it's confusingly named memclear_highpage_flush(), which is descriptive of *how* it does the work rather than what the *purpose* is. So this patchset renames the function to zero_user_page(), and calls it from the various places that currently open code it. This first patch introduces the new function call, and converts all the core kernel callsites, both the open-coded ones and the old memclear_highpage_flush() ones. Following this patch is a series of conversions for each file system individually, per AKPM, and finally a patch deprecating the old call. The diffstat below shows the entire patchset. [akpm@linux-foundation.org: fix a few things] Signed-off-by: Nate Diller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/loop.c | 6 +----- fs/buffer.c | 56 +++++++++++-------------------------------------- fs/direct-io.c | 8 ++----- fs/mpage.c | 15 +++++-------- include/linux/highmem.h | 28 +++++++++++++++++-------- mm/filemap_xip.c | 7 +------ mm/truncate.c | 3 ++- 7 files changed, 42 insertions(+), 81 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index af6d7274a7c..18cdd8c7762 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -243,17 +243,13 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec, transfer_result = lo_do_transfer(lo, WRITE, page, offset, bvec->bv_page, bv_offs, size, IV); if (unlikely(transfer_result)) { - char *kaddr; - /* * The transfer failed, but we still write the data to * keep prepare/commit calls balanced. */ printk(KERN_ERR "loop: transfer error block %llu\n", (unsigned long long)index); - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, size); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, size, KM_USER0); } flush_dcache_page(page); ret = aops->commit_write(file, page, offset, diff --git a/fs/buffer.c b/fs/buffer.c index eb820b82a63..fc2d763a8d7 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1846,13 +1846,8 @@ static int __block_prepare_write(struct inode *inode, struct page *page, if (block_start >= to) break; if (buffer_new(bh)) { - void *kaddr; - clear_buffer_new(bh); - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr+block_start, 0, bh->b_size); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, block_start, bh->b_size, KM_USER0); set_buffer_uptodate(bh); mark_buffer_dirty(bh); } @@ -1940,10 +1935,8 @@ int block_read_full_page(struct page *page, get_block_t *get_block) SetPageError(page); } if (!buffer_mapped(bh)) { - void *kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + i * blocksize, 0, blocksize); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, i * blocksize, blocksize, + KM_USER0); if (!err) set_buffer_uptodate(bh); continue; @@ -2086,7 +2079,6 @@ int cont_prepare_write(struct page *page, unsigned offset, long status; unsigned zerofrom; unsigned blocksize = 1 << inode->i_blkbits; - void *kaddr; while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) { status = -ENOMEM; @@ -2108,10 +2100,8 @@ int cont_prepare_write(struct page *page, unsigned offset, PAGE_CACHE_SIZE, get_block); if (status) goto out_unmap; - kaddr = kmap_atomic(new_page, KM_USER0); - memset(kaddr+zerofrom, 0, PAGE_CACHE_SIZE-zerofrom); - flush_dcache_page(new_page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, zerofrom, PAGE_CACHE_SIZE - zerofrom, + KM_USER0); generic_commit_write(NULL, new_page, zerofrom, PAGE_CACHE_SIZE); unlock_page(new_page); page_cache_release(new_page); @@ -2138,10 +2128,7 @@ int cont_prepare_write(struct page *page, unsigned offset, if (status) goto out1; if (zerofrom < offset) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr+zerofrom, 0, offset-zerofrom); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, zerofrom, offset - zerofrom, KM_USER0); __block_commit_write(inode, page, zerofrom, offset); } return 0; @@ -2340,10 +2327,7 @@ failed: * Error recovery is pretty slack. Clear the page and mark it dirty * so we'll later zero out any blocks which _were_ allocated. */ - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr, 0, PAGE_CACHE_SIZE); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); SetPageUptodate(page); set_page_dirty(page); return ret; @@ -2382,7 +2366,6 @@ int nobh_writepage(struct page *page, get_block_t *get_block, loff_t i_size = i_size_read(inode); const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; unsigned offset; - void *kaddr; int ret; /* Is the page fully inside i_size? */ @@ -2413,10 +2396,7 @@ int nobh_writepage(struct page *page, get_block_t *get_block, * the page size, the remaining memory is zeroed when mapped, and * writes to that region are not written out to the file." */ - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0); out: ret = mpage_writepage(page, get_block, wbc); if (ret == -EAGAIN) @@ -2437,7 +2417,6 @@ int nobh_truncate_page(struct address_space *mapping, loff_t from) unsigned to; struct page *page; const struct address_space_operations *a_ops = mapping->a_ops; - char *kaddr; int ret = 0; if ((offset & (blocksize - 1)) == 0) @@ -2451,10 +2430,8 @@ int nobh_truncate_page(struct address_space *mapping, loff_t from) to = (offset + blocksize) & ~(blocksize - 1); ret = a_ops->prepare_write(NULL, page, offset, to); if (ret == 0) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, + KM_USER0); /* * It would be more correct to call aops->commit_write() * here, but this is more efficient. @@ -2480,7 +2457,6 @@ int block_truncate_page(struct address_space *mapping, struct inode *inode = mapping->host; struct page *page; struct buffer_head *bh; - void *kaddr; int err; blocksize = 1 << inode->i_blkbits; @@ -2534,11 +2510,7 @@ int block_truncate_page(struct address_space *mapping, goto unlock; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - + zero_user_page(page, offset, length, KM_USER0); mark_buffer_dirty(bh); err = 0; @@ -2559,7 +2531,6 @@ int block_write_full_page(struct page *page, get_block_t *get_block, loff_t i_size = i_size_read(inode); const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; unsigned offset; - void *kaddr; /* Is the page fully inside i_size? */ if (page->index < end_index) @@ -2585,10 +2556,7 @@ int block_write_full_page(struct page *page, get_block_t *get_block, * the page size, the remaining memory is zeroed when mapped, and * writes to that region are not written out to the file." */ - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0); return __block_write_full_page(inode, page, get_block, wbc); } diff --git a/fs/direct-io.c b/fs/direct-io.c index d9d0833444f..8aa2d8b04ef 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -867,7 +867,6 @@ static int do_direct_IO(struct dio *dio) do_holes: /* Handle holes */ if (!buffer_mapped(map_bh)) { - char *kaddr; loff_t i_size_aligned; /* AKPM: eargh, -ENOTBLK is a hack */ @@ -888,11 +887,8 @@ do_holes: page_cache_release(page); goto out; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + (block_in_page << blkbits), - 0, 1 << blkbits); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, block_in_page << blkbits, + 1 << blkbits, KM_USER0); dio->block_in_file++; block_in_page++; goto next_block; diff --git a/fs/mpage.c b/fs/mpage.c index fa2441f57b4..0fb914fc2ee 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -284,11 +284,9 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, } if (first_hole != blocks_per_page) { - char *kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + (first_hole << blkbits), 0, - PAGE_CACHE_SIZE - (first_hole << blkbits)); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, first_hole << blkbits, + PAGE_CACHE_SIZE - (first_hole << blkbits), + KM_USER0); if (first_hole == 0) { SetPageUptodate(page); unlock_page(page); @@ -576,14 +574,11 @@ page_is_mapped: * written out to the file." */ unsigned offset = i_size & (PAGE_CACHE_SIZE - 1); - char *kaddr; if (page->index > end_index || !offset) goto confused; - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, + KM_USER0); } /* diff --git a/include/linux/highmem.h b/include/linux/highmem.h index a515eb0afdf..b5f2ab42d98 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -94,17 +94,27 @@ static inline void clear_highpage(struct page *page) /* * Same but also flushes aliased cache contents to RAM. + * + * This must be a macro because KM_USER0 and friends aren't defined if + * !CONFIG_HIGHMEM */ -static inline void memclear_highpage_flush(struct page *page, unsigned int offset, unsigned int size) +#define zero_user_page(page, offset, size, km_type) \ + do { \ + void *kaddr; \ + \ + BUG_ON((offset) + (size) > PAGE_SIZE); \ + \ + kaddr = kmap_atomic(page, km_type); \ + memset((char *)kaddr + (offset), 0, (size)); \ + flush_dcache_page(page); \ + kunmap_atomic(kaddr, (km_type)); \ + } while (0) + + +static inline void memclear_highpage_flush(struct page *page, + unsigned int offset, unsigned int size) { - void *kaddr; - - BUG_ON(offset + size > PAGE_SIZE); - - kaddr = kmap_atomic(page, KM_USER0); - memset((char *)kaddr + offset, 0, size); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, size, KM_USER0); } #ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index cbb335813ec..1b49dab9b25 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -434,7 +434,6 @@ xip_truncate_page(struct address_space *mapping, loff_t from) unsigned blocksize; unsigned length; struct page *page; - void *kaddr; BUG_ON(!mapping->a_ops->get_xip_page); @@ -458,11 +457,7 @@ xip_truncate_page(struct address_space *mapping, loff_t from) else return PTR_ERR(page); } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - kunmap_atomic(kaddr, KM_USER0); - - flush_dcache_page(page); + zero_user_page(page, offset, length, KM_USER0); return 0; } EXPORT_SYMBOL_GPL(xip_truncate_page); diff --git a/mm/truncate.c b/mm/truncate.c index 0f4b6d18ab0..4fbe1a2da5f 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include /* grr. try_to_release_page, @@ -46,7 +47,7 @@ void do_invalidatepage(struct page *page, unsigned long offset) static inline void truncate_partial_page(struct page *page, unsigned partial) { - memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial); + zero_user_page(page, partial, PAGE_CACHE_SIZE - partial, KM_USER0); if (PagePrivate(page)) do_invalidatepage(page, partial); } -- cgit v1.2.3 From f36dca90e674a1a62cad810f630629c0008b2128 Mon Sep 17 00:00:00 2001 From: Nate Diller Date: Wed, 9 May 2007 02:35:07 -0700 Subject: affs: use zero_user_page Use zero_user_page() instead of open-coding it. Signed-off-by: Nate Diller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/affs/file.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/affs/file.c b/fs/affs/file.c index 4aa8079e71b..c8796906f58 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -628,11 +628,7 @@ static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned return err; } if (to < PAGE_CACHE_SIZE) { - char *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, to, PAGE_CACHE_SIZE - to, KM_USER0); if (size > offset + to) { if (size < offset + PAGE_CACHE_SIZE) tmp = size & ~PAGE_CACHE_MASK; -- cgit v1.2.3 From 0c11d7a9e9e9793219baf715048c190a84bead57 Mon Sep 17 00:00:00 2001 From: Nate Diller Date: Wed, 9 May 2007 02:35:08 -0700 Subject: ext3: use zero_user_page Use zero_user_page() instead of open-coding it. Signed-off-by: Nate Diller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/inode.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index e1bb0317198..a6cb6171c3a 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1767,7 +1767,6 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page, struct inode *inode = mapping->host; struct buffer_head *bh; int err = 0; - void *kaddr; blocksize = inode->i_sb->s_blocksize; length = blocksize - (offset & (blocksize - 1)); @@ -1779,10 +1778,7 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page, */ if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode) && PageUptodate(page)) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); set_page_dirty(page); goto unlock; } @@ -1835,11 +1831,7 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page, goto unlock; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - + zero_user_page(page, offset, length, KM_USER0); BUFFER_TRACE(bh, "zeroed end of block"); err = 0; -- cgit v1.2.3 From f2fff596955867d407cc7e8e426097bd9ad2be9b Mon Sep 17 00:00:00 2001 From: Nate Diller Date: Wed, 9 May 2007 02:35:09 -0700 Subject: reiserfs: use zero_user_page Use zero_user_page() instead of open-coding it. Signed-off-by: Nate Diller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/file.c | 39 +++++++++++---------------------------- fs/reiserfs/inode.c | 13 ++----------- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index ab45db529c8..9e451a68580 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1059,20 +1059,12 @@ static int reiserfs_prepare_file_region_for_write(struct inode *inode maping blocks, since there is none, so we just zero out remaining parts of first and last pages in write area (if needed) */ if ((pos & ~((loff_t) PAGE_CACHE_SIZE - 1)) > inode->i_size) { - if (from != 0) { /* First page needs to be partially zeroed */ - char *kaddr = kmap_atomic(prepared_pages[0], KM_USER0); - memset(kaddr, 0, from); - kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(prepared_pages[0]); - } - if (to != PAGE_CACHE_SIZE) { /* Last page needs to be partially zeroed */ - char *kaddr = - kmap_atomic(prepared_pages[num_pages - 1], - KM_USER0); - memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); - kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(prepared_pages[num_pages - 1]); - } + if (from != 0) /* First page needs to be partially zeroed */ + zero_user_page(prepared_pages[0], 0, from, KM_USER0); + + if (to != PAGE_CACHE_SIZE) /* Last page needs to be partially zeroed */ + zero_user_page(prepared_pages[num_pages-1], to, + PAGE_CACHE_SIZE - to, KM_USER0); /* Since all blocks are new - use already calculated value */ return blocks; @@ -1199,13 +1191,9 @@ static int reiserfs_prepare_file_region_for_write(struct inode *inode ll_rw_block(READ, 1, &bh); *wait_bh++ = bh; } else { /* Not mapped, zero it */ - char *kaddr = - kmap_atomic(prepared_pages[0], - KM_USER0); - memset(kaddr + block_start, 0, - from - block_start); - kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(prepared_pages[0]); + zero_user_page(prepared_pages[0], + block_start, + from - block_start, KM_USER0); set_buffer_uptodate(bh); } } @@ -1237,13 +1225,8 @@ static int reiserfs_prepare_file_region_for_write(struct inode *inode ll_rw_block(READ, 1, &bh); *wait_bh++ = bh; } else { /* Not mapped, zero it */ - char *kaddr = - kmap_atomic(prepared_pages - [num_pages - 1], - KM_USER0); - memset(kaddr + to, 0, block_end - to); - kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(prepared_pages[num_pages - 1]); + zero_user_page(prepared_pages[num_pages-1], + to, block_end - to, KM_USER0); set_buffer_uptodate(bh); } } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 9fcbfe31697..1272d11399f 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2148,13 +2148,8 @@ int reiserfs_truncate_file(struct inode *p_s_inode, int update_timestamps) length = offset & (blocksize - 1); /* if we are not on a block boundary */ if (length) { - char *kaddr; - length = blocksize - length; - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); if (buffer_mapped(bh) && bh->b_blocknr != 0) { mark_buffer_dirty(bh); } @@ -2370,7 +2365,6 @@ static int reiserfs_write_full_page(struct page *page, ** last byte in the file */ if (page->index >= end_index) { - char *kaddr; unsigned last_offset; last_offset = inode->i_size & (PAGE_CACHE_SIZE - 1); @@ -2379,10 +2373,7 @@ static int reiserfs_write_full_page(struct page *page, unlock_page(page); return 0; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + last_offset, 0, PAGE_CACHE_SIZE - last_offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, last_offset, PAGE_CACHE_SIZE - last_offset, KM_USER0); } bh = head; block = page->index << (PAGE_CACHE_SHIFT - s->s_blocksize_bits); -- cgit v1.2.3 From f37bc2712b54ec641e0c0c8634f1a4b61d9956c0 Mon Sep 17 00:00:00 2001 From: Nate Diller Date: Wed, 9 May 2007 02:35:09 -0700 Subject: fs: deprecate memclear_highpage_flush Now that all the in-tree users are converted over to zero_user_page(), deprecate the old memclear_highpage_flush() call. Signed-off-by: Nate Diller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/highmem.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/highmem.h b/include/linux/highmem.h index b5f2ab42d98..98e2cce996a 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -110,8 +110,7 @@ static inline void clear_highpage(struct page *page) kunmap_atomic(kaddr, (km_type)); \ } while (0) - -static inline void memclear_highpage_flush(struct page *page, +static inline void __deprecated memclear_highpage_flush(struct page *page, unsigned int offset, unsigned int size) { zero_user_page(page, offset, size, KM_USER0); -- cgit v1.2.3 From 8bb7844286fb8c9fce6f65d8288aeb09d03a5e0d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 May 2007 02:35:10 -0700 Subject: Add suspend-related notifications for CPU hotplug Since nonboot CPUs are now disabled after tasks and devices have been frozen and the CPU hotplug infrastructure is used for this purpose, we need special CPU hotplug notifications that will help the CPU-hotplug-aware subsystems distinguish normal CPU hotplug events from CPU hotplug events related to a system-wide suspend or resume operation in progress. This patch introduces such notifications and causes them to be used during suspend and resume transitions. It also changes all of the CPU-hotplug-aware subsystems to take these notifications into consideration (for now they are handled in the same way as the corresponding "normal" ones). [oleg@tv-sign.ru: cleanups] Signed-off-by: Rafael J. Wysocki Cc: Gautham R Shenoy Cc: Pavel Machek Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cpu-hotplug.txt | 9 ++++++-- arch/i386/kernel/cpu/intel_cacheinfo.c | 2 ++ arch/i386/kernel/cpu/mcheck/therm_throt.c | 2 ++ arch/i386/kernel/cpuid.c | 2 ++ arch/i386/kernel/microcode.c | 3 +++ arch/i386/kernel/msr.c | 2 ++ arch/ia64/kernel/err_inject.c | 2 ++ arch/ia64/kernel/palinfo.c | 2 ++ arch/ia64/kernel/salinfo.c | 2 ++ arch/ia64/kernel/topology.c | 2 ++ arch/powerpc/kernel/sysfs.c | 2 ++ arch/powerpc/mm/numa.c | 3 +++ arch/s390/appldata/appldata_base.c | 2 ++ arch/s390/kernel/smp.c | 2 ++ arch/x86_64/kernel/mce.c | 2 ++ arch/x86_64/kernel/mce_amd.c | 2 ++ arch/x86_64/kernel/vsyscall.c | 2 +- block/ll_rw_blk.c | 2 +- drivers/base/topology.c | 3 +++ drivers/cpufreq/cpufreq.c | 3 +++ drivers/cpufreq/cpufreq_stats.c | 2 ++ drivers/hwmon/coretemp.c | 2 ++ drivers/infiniband/hw/ehca/ehca_irq.c | 6 ++++++ drivers/kvm/kvm_main.c | 3 +++ fs/buffer.c | 2 +- fs/xfs/xfs_mount.c | 3 +++ include/linux/notifier.h | 12 +++++++++++ kernel/cpu.c | 34 ++++++++++++++++--------------- kernel/hrtimer.c | 2 ++ kernel/profile.c | 4 ++++ kernel/rcupdate.c | 2 ++ kernel/relay.c | 2 ++ kernel/sched.c | 10 +++++++++ kernel/softirq.c | 4 ++++ kernel/softlockup.c | 4 ++++ kernel/timer.c | 2 ++ kernel/workqueue.c | 2 ++ lib/radix-tree.c | 2 +- mm/page_alloc.c | 5 ++++- mm/slab.c | 6 ++++++ mm/slub.c | 2 ++ mm/swap.c | 2 +- mm/vmscan.c | 2 +- mm/vmstat.c | 3 +++ net/core/dev.c | 2 +- net/core/flow.c | 2 +- net/iucv/iucv.c | 6 ++++++ 47 files changed, 152 insertions(+), 27 deletions(-) diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt index cc60d29b954..b6d24c22274 100644 --- a/Documentation/cpu-hotplug.txt +++ b/Documentation/cpu-hotplug.txt @@ -217,14 +217,17 @@ Q: What happens when a CPU is being logically offlined? A: The following happen, listed in no particular order :-) - A notification is sent to in-kernel registered modules by sending an event - CPU_DOWN_PREPARE + CPU_DOWN_PREPARE or CPU_DOWN_PREPARE_FROZEN, depending on whether or not the + CPU is being offlined while tasks are frozen due to a suspend operation in + progress - All process is migrated away from this outgoing CPU to a new CPU - All interrupts targeted to this CPU is migrated to a new CPU - timers/bottom half/task lets are also migrated to a new CPU - Once all services are migrated, kernel calls an arch specific routine __cpu_disable() to perform arch specific cleanup. - Once this is successful, an event for successful cleanup is sent by an event - CPU_DEAD. + CPU_DEAD (or CPU_DEAD_FROZEN if tasks are frozen due to a suspend while the + CPU is being offlined). "It is expected that each service cleans up when the CPU_DOWN_PREPARE notifier is called, when CPU_DEAD is called its expected there is nothing @@ -242,9 +245,11 @@ A: This is what you would need in your kernel code to receive notifications. switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: foobar_online_action(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: foobar_dead_action(cpu); break; } diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index 80b4c5d421b..e5be819492e 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c @@ -733,9 +733,11 @@ static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb, sys_dev = get_cpu_sysdev(cpu); switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cache_add_dev(sys_dev); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: cache_remove_dev(sys_dev); break; } diff --git a/arch/i386/kernel/cpu/mcheck/therm_throt.c b/arch/i386/kernel/cpu/mcheck/therm_throt.c index 065005c3f16..5b0a040213c 100644 --- a/arch/i386/kernel/cpu/mcheck/therm_throt.c +++ b/arch/i386/kernel/cpu/mcheck/therm_throt.c @@ -137,10 +137,12 @@ static __cpuinit int thermal_throttle_cpu_callback(struct notifier_block *nfb, mutex_lock(&therm_cpu_lock); switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: err = thermal_throttle_add_dev(sys_dev); WARN_ON(err); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: thermal_throttle_remove_dev(sys_dev); break; } diff --git a/arch/i386/kernel/cpuid.c b/arch/i386/kernel/cpuid.c index eeae0d99233..5c2faa10e9f 100644 --- a/arch/i386/kernel/cpuid.c +++ b/arch/i386/kernel/cpuid.c @@ -169,9 +169,11 @@ static int cpuid_class_cpu_callback(struct notifier_block *nfb, unsigned long ac switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpuid_device_create(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu)); break; } diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index cbe7ec8dbb9..7d934e493e7 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -775,10 +775,13 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) sys_dev = get_cpu_sysdev(cpu); switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: mc_sysdev_add(sys_dev); break; case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: mc_sysdev_remove(sys_dev); break; } diff --git a/arch/i386/kernel/msr.c b/arch/i386/kernel/msr.c index 8cd0a91ce10..0c1069b8d63 100644 --- a/arch/i386/kernel/msr.c +++ b/arch/i386/kernel/msr.c @@ -153,9 +153,11 @@ static int msr_class_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: msr_device_create(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu)); break; } diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c index d3e9f33e8bd..6a49600cf33 100644 --- a/arch/ia64/kernel/err_inject.c +++ b/arch/ia64/kernel/err_inject.c @@ -236,9 +236,11 @@ static int __cpuinit err_inject_cpu_callback(struct notifier_block *nfb, sys_dev = get_cpu_sysdev(cpu); switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: err_inject_add_dev(sys_dev); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: err_inject_remove_dev(sys_dev); break; } diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index a71df9ae039..85829e27785 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -975,9 +975,11 @@ static int palinfo_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: create_palinfo_proc_entries(hotcpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: remove_palinfo_proc_entries(hotcpu); break; } diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c index a51f1d0bfb7..89f6b138a62 100644 --- a/arch/ia64/kernel/salinfo.c +++ b/arch/ia64/kernel/salinfo.c @@ -582,6 +582,7 @@ salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu struct salinfo_data *data; switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: spin_lock_irqsave(&data_saved_lock, flags); for (i = 0, data = salinfo_data; i < ARRAY_SIZE(salinfo_data); @@ -592,6 +593,7 @@ salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu spin_unlock_irqrestore(&data_saved_lock, flags); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: spin_lock_irqsave(&data_saved_lock, flags); for (i = 0, data = salinfo_data; i < ARRAY_SIZE(salinfo_data); diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index 687500ddb4b..94ae3c87d82 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c @@ -412,9 +412,11 @@ static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, sys_dev = get_cpu_sysdev(cpu); switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cache_add_dev(sys_dev); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: cache_remove_dev(sys_dev); break; } diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index cae39d9dfe4..68991c2d4a1 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -342,10 +342,12 @@ static int __cpuinit sysfs_cpu_notify(struct notifier_block *self, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: register_cpu_online(cpu); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: + case CPU_DEAD_FROZEN: unregister_cpu_online(cpu); break; #endif diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index b3a592b25ab..de45aa82d97 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -252,12 +252,15 @@ static int __cpuinit cpu_numa_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: numa_setup_cpu(lcpu); ret = NOTIFY_OK; break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: + case CPU_DEAD_FROZEN: case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: unmap_cpu_from_node(lcpu); break; ret = NOTIFY_OK; diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index ee89b33145d..81a2b92ab0c 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -567,9 +567,11 @@ appldata_cpu_notify(struct notifier_block *self, { switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: appldata_online_cpu((long) hcpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: appldata_offline_cpu((long) hcpu); break; default: diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index b7977027a28..09f028a3266 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -789,10 +789,12 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: if (sysdev_create_file(s, &attr_capability)) return NOTIFY_BAD; break; case CPU_DEAD: + case CPU_DEAD_FROZEN: sysdev_remove_file(s, &attr_capability); break; } diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 442169640e4..a14375dd542 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c @@ -720,9 +720,11 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: mce_create_device(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: mce_remove_device(cpu); break; } diff --git a/arch/x86_64/kernel/mce_amd.c b/arch/x86_64/kernel/mce_amd.c index d0bd5d66e10..03356e64f9c 100644 --- a/arch/x86_64/kernel/mce_amd.c +++ b/arch/x86_64/kernel/mce_amd.c @@ -654,9 +654,11 @@ static int threshold_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: threshold_create_device(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: threshold_remove_device(cpu); break; default: diff --git a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c index dc32cef9619..51d4c6fa88c 100644 --- a/arch/x86_64/kernel/vsyscall.c +++ b/arch/x86_64/kernel/vsyscall.c @@ -327,7 +327,7 @@ static int __cpuinit cpu_vsyscall_notifier(struct notifier_block *n, unsigned long action, void *arg) { long cpu = (long)arg; - if (action == CPU_ONLINE) + if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 0, 1); return NOTIFY_DONE; } diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index df506571ed6..cd54672da99 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -3507,7 +3507,7 @@ static int blk_cpu_notify(struct notifier_block *self, unsigned long action, * If a CPU goes away, splice its entries to the current CPU * and trigger a run of the softirq */ - if (action == CPU_DEAD) { + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) { int cpu = (unsigned long) hcpu; local_irq_disable(); diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 067a9e8bc37..8d8cdfec652 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -126,10 +126,13 @@ static int __cpuinit topology_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: rc = topology_add_dev(cpu); break; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: topology_remove_dev(cpu); break; } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 893dbaf386f..eb37fba9b7e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1685,9 +1685,11 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, if (sys_dev) { switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpufreq_add_dev(sys_dev); break; case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: if (unlikely(lock_policy_rwsem_write(cpu))) BUG(); @@ -1699,6 +1701,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, __cpufreq_remove_dev(sys_dev); break; case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: cpufreq_add_dev(sys_dev); break; } diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index d1c7cac9316..d2f0cbd8b8f 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -313,9 +313,11 @@ static int cpufreq_stat_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpufreq_update_policy(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; } diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 03b1f650d1c..75e3911810a 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -309,9 +309,11 @@ static int coretemp_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: coretemp_device_add(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: coretemp_device_remove(cpu); break; } diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index f284be1c916..82dda2faf4d 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -745,6 +745,7 @@ static int comp_pool_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu); if(!create_comp_task(pool, cpu)) { ehca_gen_err("Can't create comp_task for cpu: %x", cpu); @@ -752,24 +753,29 @@ static int comp_pool_callback(struct notifier_block *nfb, } break; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: ehca_gen_dbg("CPU: %x (CPU_CANCELED)", cpu); cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); kthread_bind(cct->task, any_online_cpu(cpu_online_map)); destroy_comp_task(pool, cpu); break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: ehca_gen_dbg("CPU: %x (CPU_ONLINE)", cpu); cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); kthread_bind(cct->task, cpu); wake_up_process(cct->task); break; case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: ehca_gen_dbg("CPU: %x (CPU_DOWN_PREPARE)", cpu); break; case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: ehca_gen_dbg("CPU: %x (CPU_DOWN_FAILED)", cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: ehca_gen_dbg("CPU: %x (CPU_DEAD)", cpu); destroy_comp_task(pool, cpu); take_over_work(pool, cpu); diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index c8b8cfa332b..0d892600ff0 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c @@ -2889,7 +2889,9 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val, switch (val) { case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n", cpu); decache_vcpus_on_cpu(cpu); @@ -2897,6 +2899,7 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val, NULL, 0, 1); break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n", cpu); smp_call_function_single(cpu, kvm_arch_ops->hardware_enable, diff --git a/fs/buffer.c b/fs/buffer.c index fc2d763a8d7..aecd057cd0e 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2946,7 +2946,7 @@ static void buffer_exit_cpu(int cpu) static int buffer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { - if (action == CPU_DEAD) + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) buffer_exit_cpu((unsigned long)hcpu); return NOTIFY_OK; } diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index f5aa3ef855f..a96bde6df96 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1734,11 +1734,13 @@ xfs_icsb_cpu_notify( per_cpu_ptr(mp->m_sb_cnts, (unsigned long)hcpu); switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: /* Easy Case - initialize the area and locks, and * then rebalance when online does everything else for us. */ memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: xfs_icsb_lock(mp); xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, 0, 0); xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, 0, 0); @@ -1746,6 +1748,7 @@ xfs_icsb_cpu_notify( xfs_icsb_unlock(mp); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: /* Disable all the counters, then fold the dead cpu's * count into the total on the global superblock and * re-enable the counters. */ diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 1903e5490c0..9431101bf87 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -197,5 +197,17 @@ extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, #define CPU_LOCK_ACQUIRE 0x0008 /* Acquire all hotcpu locks */ #define CPU_LOCK_RELEASE 0x0009 /* Release all hotcpu locks */ +/* Used for CPU hotplug events occuring while tasks are frozen due to a suspend + * operation in progress + */ +#define CPU_TASKS_FROZEN 0x0010 + +#define CPU_ONLINE_FROZEN (CPU_ONLINE | CPU_TASKS_FROZEN) +#define CPU_UP_PREPARE_FROZEN (CPU_UP_PREPARE | CPU_TASKS_FROZEN) +#define CPU_UP_CANCELED_FROZEN (CPU_UP_CANCELED | CPU_TASKS_FROZEN) +#define CPU_DOWN_PREPARE_FROZEN (CPU_DOWN_PREPARE | CPU_TASKS_FROZEN) +#define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) +#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) + #endif /* __KERNEL__ */ #endif /* _LINUX_NOTIFIER_H */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 28cb6c71a47..369d2892687 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -120,12 +120,13 @@ static int take_cpu_down(void *unused) } /* Requires cpu_add_remove_lock to be held */ -static int _cpu_down(unsigned int cpu) +static int _cpu_down(unsigned int cpu, int tasks_frozen) { int err, nr_calls = 0; struct task_struct *p; cpumask_t old_allowed, tmp; void *hcpu = (void *)(long)cpu; + unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; if (num_online_cpus() == 1) return -EBUSY; @@ -134,11 +135,11 @@ static int _cpu_down(unsigned int cpu) return -EINVAL; raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, hcpu); - err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, + err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls); if (err == NOTIFY_BAD) { - __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, hcpu, - nr_calls, NULL); + __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, + hcpu, nr_calls, NULL); printk("%s: attempt to take down CPU %u failed\n", __FUNCTION__, cpu); err = -EINVAL; @@ -157,7 +158,7 @@ static int _cpu_down(unsigned int cpu) if (IS_ERR(p) || cpu_online(cpu)) { /* CPU didn't die: tell everyone. Can't complain. */ - if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, + if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, hcpu) == NOTIFY_BAD) BUG(); @@ -176,7 +177,8 @@ static int _cpu_down(unsigned int cpu) __cpu_die(cpu); /* CPU is completely dead: tell everyone. Too late to complain. */ - if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD, hcpu) == NOTIFY_BAD) + if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD | mod, + hcpu) == NOTIFY_BAD) BUG(); check_for_tasks(cpu); @@ -186,8 +188,7 @@ out_thread: out_allowed: set_cpus_allowed(current, old_allowed); out_release: - raw_notifier_call_chain(&cpu_chain, CPU_LOCK_RELEASE, - (void *)(long)cpu); + raw_notifier_call_chain(&cpu_chain, CPU_LOCK_RELEASE, hcpu); return err; } @@ -199,7 +200,7 @@ int cpu_down(unsigned int cpu) if (cpu_hotplug_disabled) err = -EBUSY; else - err = _cpu_down(cpu); + err = _cpu_down(cpu, 0); mutex_unlock(&cpu_add_remove_lock); return err; @@ -207,16 +208,17 @@ int cpu_down(unsigned int cpu) #endif /*CONFIG_HOTPLUG_CPU*/ /* Requires cpu_add_remove_lock to be held */ -static int __cpuinit _cpu_up(unsigned int cpu) +static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) { int ret, nr_calls = 0; void *hcpu = (void *)(long)cpu; + unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; if (cpu_online(cpu) || !cpu_present(cpu)) return -EINVAL; raw_notifier_call_chain(&cpu_chain, CPU_LOCK_ACQUIRE, hcpu); - ret = __raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu, + ret = __raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls); if (ret == NOTIFY_BAD) { printk("%s: attempt to bring up CPU %u failed\n", @@ -234,12 +236,12 @@ static int __cpuinit _cpu_up(unsigned int cpu) BUG_ON(!cpu_online(cpu)); /* Now call notifier in preparation. */ - raw_notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu); + raw_notifier_call_chain(&cpu_chain, CPU_ONLINE | mod, hcpu); out_notify: if (ret != 0) __raw_notifier_call_chain(&cpu_chain, - CPU_UP_CANCELED, hcpu, nr_calls, NULL); + CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL); raw_notifier_call_chain(&cpu_chain, CPU_LOCK_RELEASE, hcpu); return ret; @@ -253,7 +255,7 @@ int __cpuinit cpu_up(unsigned int cpu) if (cpu_hotplug_disabled) err = -EBUSY; else - err = _cpu_up(cpu); + err = _cpu_up(cpu, 0); mutex_unlock(&cpu_add_remove_lock); return err; @@ -283,7 +285,7 @@ int disable_nonboot_cpus(void) for_each_online_cpu(cpu) { if (cpu == first_cpu) continue; - error = _cpu_down(cpu); + error = _cpu_down(cpu, 1); if (!error) { cpu_set(cpu, frozen_cpus); printk("CPU%d is down\n", cpu); @@ -318,7 +320,7 @@ void enable_nonboot_cpus(void) suspend_cpu_hotplug = 1; printk("Enabling non-boot CPUs ...\n"); for_each_cpu_mask(cpu, frozen_cpus) { - error = _cpu_up(cpu); + error = _cpu_up(cpu, 1); if (!error) { printk("CPU%d is up\n", cpu); continue; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index c9f4f044a8a..23c03f43e19 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1411,11 +1411,13 @@ static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: init_hrtimers_cpu(cpu); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: + case CPU_DEAD_FROZEN: clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &cpu); migrate_hrtimers(cpu); break; diff --git a/kernel/profile.c b/kernel/profile.c index 9bfadb248dd..cc91b9bf759 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -340,6 +340,7 @@ static int __devinit profile_cpu_callback(struct notifier_block *info, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: node = cpu_to_node(cpu); per_cpu(cpu_profile_flip, cpu) = 0; if (!per_cpu(cpu_profile_hits, cpu)[1]) { @@ -365,10 +366,13 @@ static int __devinit profile_cpu_callback(struct notifier_block *info, __free_page(page); return NOTIFY_BAD; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpu_set(cpu, prof_cpu_mask); break; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: cpu_clear(cpu, prof_cpu_mask); if (per_cpu(cpu_profile_hits, cpu)[0]) { page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 3554b76da84..2c2dd8410dc 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -558,9 +558,11 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self, long cpu = (long)hcpu; switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: rcu_online_cpu(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: rcu_offline_cpu(cpu); break; default: diff --git a/kernel/relay.c b/kernel/relay.c index e804589c863..61a504900ea 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -484,6 +484,7 @@ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, switch(action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: mutex_lock(&relay_channels_mutex); list_for_each_entry(chan, &relay_channels, list) { if (chan->buf[hotcpu]) @@ -500,6 +501,7 @@ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, mutex_unlock(&relay_channels_mutex); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: /* No need to flush the cpu : will be flushed upon * final relay_flush() call. */ break; diff --git a/kernel/sched.c b/kernel/sched.c index fe1a9c2b855..799d23b4e35 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5394,6 +5394,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) break; case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: p = kthread_create(migration_thread, hcpu, "migration/%d",cpu); if (IS_ERR(p)) return NOTIFY_BAD; @@ -5407,12 +5408,14 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: /* Strictly unneccessary, as first user will wake it. */ wake_up_process(cpu_rq(cpu)->migration_thread); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: if (!cpu_rq(cpu)->migration_thread) break; /* Unbind it from offline cpu so it can run. Fall thru. */ @@ -5423,6 +5426,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) break; case CPU_DEAD: + case CPU_DEAD_FROZEN: migrate_live_tasks(cpu); rq = cpu_rq(cpu); kthread_stop(rq->migration_thread); @@ -6912,14 +6916,20 @@ static int update_sched_domains(struct notifier_block *nfb, { switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: detach_destroy_domains(&cpu_online_map); return NOTIFY_OK; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: case CPU_ONLINE: + case CPU_ONLINE_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: /* * Fall through and re-initialise the domains. */ diff --git a/kernel/softirq.c b/kernel/softirq.c index 8b75008e2bd..0b9886a00e7 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -593,6 +593,7 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu); if (IS_ERR(p)) { printk("ksoftirqd for %i failed\n", hotcpu); @@ -602,16 +603,19 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb, per_cpu(ksoftirqd, hotcpu) = p; break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: wake_up_process(per_cpu(ksoftirqd, hotcpu)); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: if (!per_cpu(ksoftirqd, hotcpu)) break; /* Unbind so it can run. Fall thru. */ kthread_bind(per_cpu(ksoftirqd, hotcpu), any_online_cpu(cpu_online_map)); case CPU_DEAD: + case CPU_DEAD_FROZEN: p = per_cpu(ksoftirqd, hotcpu); per_cpu(ksoftirqd, hotcpu) = NULL; kthread_stop(p); diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 8fa7040247a..0131e296ffb 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -146,6 +146,7 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: BUG_ON(per_cpu(watchdog_task, hotcpu)); p = kthread_create(watchdog, hcpu, "watchdog/%d", hotcpu); if (IS_ERR(p)) { @@ -157,16 +158,19 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) kthread_bind(p, hotcpu); break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: wake_up_process(per_cpu(watchdog_task, hotcpu)); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: if (!per_cpu(watchdog_task, hotcpu)) break; /* Unbind so it can run. Fall thru. */ kthread_bind(per_cpu(watchdog_task, hotcpu), any_online_cpu(cpu_online_map)); case CPU_DEAD: + case CPU_DEAD_FROZEN: p = per_cpu(watchdog_task, hotcpu); per_cpu(watchdog_task, hotcpu) = NULL; kthread_stop(p); diff --git a/kernel/timer.c b/kernel/timer.c index 58f6dd07c80..de85f8491c1 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1293,11 +1293,13 @@ static int __cpuinit timer_cpu_notify(struct notifier_block *self, long cpu = (long)hcpu; switch(action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: if (init_timers_cpu(cpu) < 0) return NOTIFY_BAD; break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: + case CPU_DEAD_FROZEN: migrate_timers(cpu); break; #endif diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b976ed87dd3..fb56fedd5c0 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -799,6 +799,8 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, struct cpu_workqueue_struct *cwq; struct workqueue_struct *wq; + action &= ~CPU_TASKS_FROZEN; + switch (action) { case CPU_LOCK_ACQUIRE: mutex_lock(&workqueue_mutex); diff --git a/lib/radix-tree.c b/lib/radix-tree.c index d69ddbe4386..402eb4eb6b2 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1004,7 +1004,7 @@ static int radix_tree_callback(struct notifier_block *nfb, struct radix_tree_preload *rtp; /* Free per-cpu pool of perloaded nodes */ - if (action == CPU_DEAD) { + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) { rtp = &per_cpu(radix_tree_preloads, cpu); while (rtp->nr) { kmem_cache_free(radix_tree_node_cachep, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6fd0b7455b0..d53cbf8acb8 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2148,11 +2148,14 @@ static int __cpuinit pageset_cpuup_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: if (process_zones(cpu)) ret = NOTIFY_BAD; break; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: free_zone_pagesets(cpu); break; default: @@ -3012,7 +3015,7 @@ static int page_alloc_cpu_notify(struct notifier_block *self, { int cpu = (unsigned long)hcpu; - if (action == CPU_DEAD) { + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) { local_irq_disable(); __drain_pages(cpu); vm_events_fold_cpu(cpu); diff --git a/mm/slab.c b/mm/slab.c index 1a7a10de2a4..6f3d6e240c6 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1190,6 +1190,7 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, mutex_lock(&cache_chain_mutex); break; case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: /* * We need to do this right in the beginning since * alloc_arraycache's are going to use this list. @@ -1276,10 +1277,12 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, } break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: start_cpu_timer(cpu); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: /* * Shutdown cache reaper. Note that the cache_chain_mutex is * held so that if cache_reap() is invoked it cannot do @@ -1291,9 +1294,11 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, per_cpu(reap_work, cpu).work.func = NULL; break; case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: start_cpu_timer(cpu); break; case CPU_DEAD: + case CPU_DEAD_FROZEN: /* * Even if all the cpus of a node are down, we don't free the * kmem_list3 of any cache. This to avoid a race between @@ -1305,6 +1310,7 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb, /* fall thru */ #endif case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: list_for_each_entry(cachep, &cache_chain, next) { struct array_cache *nc; struct array_cache *shared; diff --git a/mm/slub.c b/mm/slub.c index f7c120b93c4..a581fa8ae11 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2514,7 +2514,9 @@ static int __cpuinit slab_cpuup_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: for_all_slabs(__flush_cpu_slab, cpu); break; default: diff --git a/mm/swap.c b/mm/swap.c index 218c52a24a2..d3cb966fe99 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -488,7 +488,7 @@ static int cpu_swap_callback(struct notifier_block *nfb, long *committed; committed = &per_cpu(committed_space, (long)hcpu); - if (action == CPU_DEAD) { + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) { atomic_add(*committed, &vm_committed_space); *committed = 0; __lru_add_drain((long)hcpu); diff --git a/mm/vmscan.c b/mm/vmscan.c index 1c8e75a1cfc..1be5a6376ef 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1528,7 +1528,7 @@ static int __devinit cpu_callback(struct notifier_block *nfb, pg_data_t *pgdat; cpumask_t mask; - if (action == CPU_ONLINE) { + if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) { for_each_online_pgdat(pgdat) { mask = node_to_cpumask(pgdat->node_id); if (any_online_cpu(mask) != NR_CPUS) diff --git a/mm/vmstat.c b/mm/vmstat.c index 6c488d6ac42..9a66dc4aed4 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -650,8 +650,11 @@ static int __cpuinit vmstat_cpuup_callback(struct notifier_block *nfb, { switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: refresh_zone_stat_thresholds(); break; default: diff --git a/net/core/dev.c b/net/core/dev.c index 4317c1be4d3..8301e2ac747 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3450,7 +3450,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; - if (action != CPU_DEAD) + if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) return NOTIFY_OK; local_irq_disable(); diff --git a/net/core/flow.c b/net/core/flow.c index 5d25697920b..051430545a0 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -338,7 +338,7 @@ static int flow_cache_cpu(struct notifier_block *nfb, unsigned long action, void *hcpu) { - if (action == CPU_DEAD) + if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) __flow_cache_shrink((unsigned long)hcpu, 0); return NOTIFY_OK; } diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index fb3faf72e85..b7333061016 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -556,6 +556,7 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, switch (action) { case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: if (!percpu_populate(iucv_irq_data, sizeof(struct iucv_irq_data), GFP_KERNEL|GFP_DMA, cpu)) @@ -567,15 +568,20 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, } break; case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: + case CPU_DEAD_FROZEN: percpu_depopulate(iucv_param, cpu); percpu_depopulate(iucv_irq_data, cpu); break; case CPU_ONLINE: + case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: smp_call_function_on(iucv_declare_cpu, NULL, 0, 1, cpu); break; case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: cpumask = iucv_buffer_cpumask; cpu_clear(cpu, cpumask); if (cpus_empty(cpumask)) -- cgit v1.2.3 From 455c017ae3934797653549704c286e7bcc3a9397 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 May 2007 02:35:11 -0700 Subject: microcode: use suspend-related CPU hotplug notifications Make the microcode driver use the suspend-related CPU hotplug notifications to handle the CPU hotplug events occuring during system-wide suspend and resume transitions. Remove the global variable suspend_cpu_hotplug previously used for this purpose. Signed-off-by: Rafael J. Wysocki Cc: Gautham R Shenoy Cc: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/microcode.c | 62 +++++++++++++++++++++++++------------------- kernel/cpu.c | 10 ------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 7d934e493e7..83f825f2e2d 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -567,7 +567,7 @@ static int cpu_request_microcode(int cpu) return error; } -static int apply_microcode_on_cpu(int cpu) +static int apply_microcode_check_cpu(int cpu) { struct cpuinfo_x86 *c = cpu_data + cpu; struct ucode_cpu_info *uci = ucode_cpu_info + cpu; @@ -575,8 +575,9 @@ static int apply_microcode_on_cpu(int cpu) unsigned int val[2]; int err = 0; + /* Check if the microcode is available */ if (!uci->mc) - return -EINVAL; + return 0; old = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(cpu)); @@ -614,7 +615,7 @@ static int apply_microcode_on_cpu(int cpu) return err; } -static void microcode_init_cpu(int cpu) +static void microcode_init_cpu(int cpu, int resume) { cpumask_t old; struct ucode_cpu_info *uci = ucode_cpu_info + cpu; @@ -624,8 +625,7 @@ static void microcode_init_cpu(int cpu) set_cpus_allowed(current, cpumask_of_cpu(cpu)); mutex_lock(µcode_mutex); collect_cpu_info(cpu); - if (uci->valid && system_state == SYSTEM_RUNNING && - !suspend_cpu_hotplug) + if (uci->valid && system_state == SYSTEM_RUNNING && !resume) cpu_request_microcode(cpu); mutex_unlock(µcode_mutex); set_cpus_allowed(current, old); @@ -702,7 +702,7 @@ static struct attribute_group mc_attr_group = { .name = "microcode", }; -static int mc_sysdev_add(struct sys_device *sys_dev) +static int __mc_sysdev_add(struct sys_device *sys_dev, int resume) { int err, cpu = sys_dev->id; struct ucode_cpu_info *uci = ucode_cpu_info + cpu; @@ -711,39 +711,31 @@ static int mc_sysdev_add(struct sys_device *sys_dev) return 0; pr_debug("Microcode:CPU %d added\n", cpu); - /* If suspend_cpu_hotplug is set, the system is resuming and we should - * use the data from before the suspend. - */ - if (suspend_cpu_hotplug) { - err = apply_microcode_on_cpu(cpu); - if (err) - microcode_fini_cpu(cpu); - } - if (!uci->valid) - memset(uci, 0, sizeof(*uci)); + memset(uci, 0, sizeof(*uci)); err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); if (err) return err; - if (!uci->valid) - microcode_init_cpu(cpu); + microcode_init_cpu(cpu, resume); return 0; } +static int mc_sysdev_add(struct sys_device *sys_dev) +{ + return __mc_sysdev_add(sys_dev, 0); +} + static int mc_sysdev_remove(struct sys_device *sys_dev) { int cpu = sys_dev->id; if (!cpu_online(cpu)) return 0; + pr_debug("Microcode:CPU %d removed\n", cpu); - /* If suspend_cpu_hotplug is set, the system is suspending and we should - * keep the microcode in memory for the resume. - */ - if (!suspend_cpu_hotplug) - microcode_fini_cpu(cpu); + microcode_fini_cpu(cpu); sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); return 0; } @@ -774,16 +766,34 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) sys_dev = get_cpu_sysdev(cpu); switch (action) { + case CPU_UP_CANCELED_FROZEN: + /* The CPU refused to come up during a system resume */ + microcode_fini_cpu(cpu); + break; case CPU_ONLINE: - case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: - case CPU_DOWN_FAILED_FROZEN: mc_sysdev_add(sys_dev); break; + case CPU_ONLINE_FROZEN: + /* System-wide resume is in progress, try to apply microcode */ + if (apply_microcode_check_cpu(cpu)) { + /* The application of microcode failed */ + microcode_fini_cpu(cpu); + __mc_sysdev_add(sys_dev, 1); + break; + } + case CPU_DOWN_FAILED_FROZEN: + if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) + printk(KERN_ERR "Microcode: Failed to create the sysfs " + "group for CPU%d\n", cpu); + break; case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: mc_sysdev_remove(sys_dev); break; + case CPU_DOWN_PREPARE_FROZEN: + /* Suspend is in progress, only remove the interface */ + sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); + break; } return NOTIFY_OK; } diff --git a/kernel/cpu.c b/kernel/cpu.c index 369d2892687..208cf3497c1 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -262,12 +262,6 @@ int __cpuinit cpu_up(unsigned int cpu) } #ifdef CONFIG_SUSPEND_SMP -/* Needed to prevent the microcode driver from requesting firmware in its CPU - * hotplug notifier during the suspend/resume. - */ -int suspend_cpu_hotplug; -EXPORT_SYMBOL(suspend_cpu_hotplug); - static cpumask_t frozen_cpus; int disable_nonboot_cpus(void) @@ -275,7 +269,6 @@ int disable_nonboot_cpus(void) int cpu, first_cpu, error = 0; mutex_lock(&cpu_add_remove_lock); - suspend_cpu_hotplug = 1; first_cpu = first_cpu(cpu_online_map); /* We take down all of the non-boot CPUs in one shot to avoid races * with the userspace trying to use the CPU hotplug at the same time @@ -302,7 +295,6 @@ int disable_nonboot_cpus(void) } else { printk(KERN_ERR "Non-boot CPUs are not disabled\n"); } - suspend_cpu_hotplug = 0; mutex_unlock(&cpu_add_remove_lock); return error; } @@ -317,7 +309,6 @@ void enable_nonboot_cpus(void) if (cpus_empty(frozen_cpus)) goto out; - suspend_cpu_hotplug = 1; printk("Enabling non-boot CPUs ...\n"); for_each_cpu_mask(cpu, frozen_cpus) { error = _cpu_up(cpu, 1); @@ -328,7 +319,6 @@ void enable_nonboot_cpus(void) printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error); } cpus_clear(frozen_cpus); - suspend_cpu_hotplug = 0; out: mutex_unlock(&cpu_add_remove_lock); } -- cgit v1.2.3 From d1187ed21026fd512b87851d0ca26d9ae16f9059 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:35:12 -0700 Subject: vmstat: use our own timer events vmstat is currently using the cache reaper to periodically bring the statistics up to date. The cache reaper does only exists in SLUB as a way to provide compatibility with SLAB. This patch removes the vmstat calls from the slab allocators and provides its own handling. The advantage is also that we can use a different frequency for the updates. Refreshing vm stats is a pretty fast job so we can run this every second and stagger this by only one tick. This will lead to some overlap in large systems. F.e a system running at 250 HZ with 1024 processors will have 4 vm updates occurring at once. However, the vm stats update only accesses per node information. It is only necessary to stagger the vm statistics updates per processor in each node. Vm counter updates occurring on distant nodes will not cause cacheline contention. We could implement an alternate approach that runs the first processor on each node at the second and then each of the other processor on a node on a subsequent tick. That may be useful to keep a large amount of the second free of timer activity. Maybe the timer folks will have some feedback on this one? [jirislaby@gmail.com: add missing break] Cc: Arjan van de Ven Signed-off-by: Christoph Lameter Signed-off-by: Jiri Slaby Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmstat.h | 3 --- mm/slab.c | 1 - mm/slub.c | 1 - mm/vmstat.c | 40 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index acb1f105870..d9325cf8a13 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -212,8 +212,6 @@ extern void dec_zone_state(struct zone *, enum zone_stat_item); extern void __dec_zone_state(struct zone *, enum zone_stat_item); void refresh_cpu_vm_stats(int); -void refresh_vm_stats(void); - #else /* CONFIG_SMP */ /* @@ -260,7 +258,6 @@ static inline void __dec_zone_page_state(struct page *page, #define mod_zone_page_state __mod_zone_page_state static inline void refresh_cpu_vm_stats(int cpu) { } -static inline void refresh_vm_stats(void) { } #endif #endif /* _LINUX_VMSTAT_H */ diff --git a/mm/slab.c b/mm/slab.c index 6f3d6e240c6..e50908b2bfa 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -4156,7 +4156,6 @@ next: check_irq_on(); mutex_unlock(&cache_chain_mutex); next_reap_node(); - refresh_cpu_vm_stats(smp_processor_id()); out: /* Set up the next iteration */ schedule_delayed_work(work, round_jiffies_relative(REAPTIMEOUT_CPUC)); diff --git a/mm/slub.c b/mm/slub.c index a581fa8ae11..dbb206503a8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2580,7 +2580,6 @@ static DEFINE_PER_CPU(struct delayed_work, reap_work); static void cache_reap(struct work_struct *unused) { next_reap_node(); - refresh_cpu_vm_stats(smp_processor_id()); schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC); } diff --git a/mm/vmstat.c b/mm/vmstat.c index 9a66dc4aed4..9d824643a22 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -640,6 +640,22 @@ const struct seq_operations vmstat_op = { #endif /* CONFIG_PROC_FS */ #ifdef CONFIG_SMP +static DEFINE_PER_CPU(struct delayed_work, vmstat_work); + +static void vmstat_update(struct work_struct *w) +{ + refresh_cpu_vm_stats(smp_processor_id()); + schedule_delayed_work(&__get_cpu_var(vmstat_work), HZ); +} + +static void __devinit start_cpu_timer(int cpu) +{ + struct delayed_work *vmstat_work = &per_cpu(vmstat_work, cpu); + + INIT_DELAYED_WORK(vmstat_work, vmstat_update); + schedule_delayed_work_on(cpu, vmstat_work, HZ + cpu); +} + /* * Use the cpu notifier to insure that the thresholds are recalculated * when necessary. @@ -648,11 +664,22 @@ static int __cpuinit vmstat_cpuup_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { + long cpu = (long)hcpu; + switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + start_cpu_timer(cpu); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + cancel_rearming_delayed_work(&per_cpu(vmstat_work, cpu)); + per_cpu(vmstat_work, cpu).work.func = NULL; + break; + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + start_cpu_timer(cpu); + break; case CPU_DEAD: case CPU_DEAD_FROZEN: refresh_zone_stat_thresholds(); @@ -668,8 +695,13 @@ static struct notifier_block __cpuinitdata vmstat_notifier = int __init setup_vmstat(void) { + int cpu; + refresh_zone_stat_thresholds(); register_cpu_notifier(&vmstat_notifier); + + for_each_online_cpu(cpu) + start_cpu_timer(cpu); return 0; } module_init(setup_vmstat) -- cgit v1.2.3 From 77461ab33229d48614402decfb1b2eaa6d446861 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:35:13 -0700 Subject: Make vm statistics update interval configurable Make it configurable. Code in mm makes the vm statistics intervals independent from the cache reaper use that opportunity to make it configurable. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sysctl.c | 12 ++++++++++++ mm/vmstat.c | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index f0664bd5011..4073353abd4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -77,6 +77,7 @@ extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; extern int compat_log; extern int maps_protect; +extern int sysctl_stat_interval; /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ static int maxolduid = 65535; @@ -857,6 +858,17 @@ static ctl_table vm_table[] = { .extra2 = &one_hundred, }, #endif +#ifdef CONFIG_SMP + { + .ctl_name = CTL_UNNUMBERED, + .procname = "stat_interval", + .data = &sysctl_stat_interval, + .maxlen = sizeof(sysctl_stat_interval), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, + }, +#endif #if defined(CONFIG_X86_32) || \ (defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL)) { diff --git a/mm/vmstat.c b/mm/vmstat.c index 9d824643a22..006eb762186 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -641,11 +641,13 @@ const struct seq_operations vmstat_op = { #ifdef CONFIG_SMP static DEFINE_PER_CPU(struct delayed_work, vmstat_work); +int sysctl_stat_interval __read_mostly = HZ; static void vmstat_update(struct work_struct *w) { refresh_cpu_vm_stats(smp_processor_id()); - schedule_delayed_work(&__get_cpu_var(vmstat_work), HZ); + schedule_delayed_work(&__get_cpu_var(vmstat_work), + sysctl_stat_interval); } static void __devinit start_cpu_timer(int cpu) -- cgit v1.2.3 From 4037d452202e34214e8a939fa5621b2b3bbb45b7 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2007 02:35:14 -0700 Subject: Move remote node draining out of slab allocators Currently the slab allocators contain callbacks into the page allocator to perform the draining of pagesets on remote nodes. This requires SLUB to have a whole subsystem in order to be compatible with SLAB. Moving node draining out of the slab allocators avoids a section of code in SLUB. Move the node draining so that is is done when the vm statistics are updated. At that point we are already touching all the cachelines with the pagesets of a processor. Add a expire counter there. If we have to update per zone or global vm statistics then assume that the pageset will require subsequent draining. The expire counter will be decremented on each vm stats update pass until it reaches zero. Then we will drain one batch from the pageset. The draining will cause vm counter updates which will then cause another expiration until the pcp is empty. So we will drain a batch every 3 seconds. Note that remote node draining is a somewhat esoteric feature that is required on large NUMA systems because otherwise significant portions of system memory can become trapped in pcp queues. The number of pcp is determined by the number of processors and nodes in a system. A system with 4 processors and 2 nodes has 8 pcps which is okay. But a system with 1024 processors and 512 nodes has 512k pcps with a high potential for large amount of memory being caught in them. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 6 +--- include/linux/mmzone.h | 3 ++ mm/page_alloc.c | 45 +++++++++------------------ mm/slab.c | 6 ---- mm/slub.c | 84 -------------------------------------------------- mm/vmstat.c | 54 +++++++++++++++++++++++++++++--- 6 files changed, 67 insertions(+), 131 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 97a36c3d96e..0d2ef0b082a 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -176,10 +176,6 @@ extern void FASTCALL(free_cold_page(struct page *page)); #define free_page(addr) free_pages((addr),0) void page_alloc_init(void); -#ifdef CONFIG_NUMA -void drain_node_pages(int node); -#else -static inline void drain_node_pages(int node) { }; -#endif +void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp); #endif /* __LINUX_GFP_H */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2f1544e8304..d09b1345a3a 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -83,6 +83,9 @@ struct per_cpu_pages { struct per_cpu_pageset { struct per_cpu_pages pcp[2]; /* 0: hot. 1: cold */ +#ifdef CONFIG_NUMA + s8 expire; +#endif #ifdef CONFIG_SMP s8 stat_threshold; s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS]; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d53cbf8acb8..f9b5d6d5f4d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -691,43 +691,26 @@ static void __init setup_nr_node_ids(void) {} #ifdef CONFIG_NUMA /* - * Called from the slab reaper to drain pagesets on a particular node that - * belongs to the currently executing processor. + * Called from the vmstat counter updater to drain pagesets of this + * currently executing processor on remote nodes after they have + * expired. + * * Note that this function must be called with the thread pinned to * a single processor. */ -void drain_node_pages(int nodeid) +void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp) { - int i; - enum zone_type z; unsigned long flags; + int to_drain; - for (z = 0; z < MAX_NR_ZONES; z++) { - struct zone *zone = NODE_DATA(nodeid)->node_zones + z; - struct per_cpu_pageset *pset; - - if (!populated_zone(zone)) - continue; - - pset = zone_pcp(zone, smp_processor_id()); - for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) { - struct per_cpu_pages *pcp; - - pcp = &pset->pcp[i]; - if (pcp->count) { - int to_drain; - - local_irq_save(flags); - if (pcp->count >= pcp->batch) - to_drain = pcp->batch; - else - to_drain = pcp->count; - free_pages_bulk(zone, to_drain, &pcp->list, 0); - pcp->count -= to_drain; - local_irq_restore(flags); - } - } - } + local_irq_save(flags); + if (pcp->count >= pcp->batch) + to_drain = pcp->batch; + else + to_drain = pcp->count; + free_pages_bulk(zone, to_drain, &pcp->list, 0); + pcp->count -= to_drain; + local_irq_restore(flags); } #endif diff --git a/mm/slab.c b/mm/slab.c index e50908b2bfa..944b20581f8 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -928,12 +928,6 @@ static void next_reap_node(void) { int node = __get_cpu_var(reap_node); - /* - * Also drain per cpu pages on remote zones - */ - if (node != numa_node_id()) - drain_node_pages(node); - node = next_node(node, node_online_map); if (unlikely(node >= MAX_NUMNODES)) node = first_node(node_online_map); diff --git a/mm/slub.c b/mm/slub.c index dbb206503a8..bd2efae02bc 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2530,90 +2530,6 @@ static struct notifier_block __cpuinitdata slab_notifier = #endif -#ifdef CONFIG_NUMA - -/***************************************************************** - * Generic reaper used to support the page allocator - * (the cpu slabs are reaped by a per slab workqueue). - * - * Maybe move this to the page allocator? - ****************************************************************/ - -static DEFINE_PER_CPU(unsigned long, reap_node); - -static void init_reap_node(int cpu) -{ - int node; - - node = next_node(cpu_to_node(cpu), node_online_map); - if (node == MAX_NUMNODES) - node = first_node(node_online_map); - - __get_cpu_var(reap_node) = node; -} - -static void next_reap_node(void) -{ - int node = __get_cpu_var(reap_node); - - /* - * Also drain per cpu pages on remote zones - */ - if (node != numa_node_id()) - drain_node_pages(node); - - node = next_node(node, node_online_map); - if (unlikely(node >= MAX_NUMNODES)) - node = first_node(node_online_map); - __get_cpu_var(reap_node) = node; -} -#else -#define init_reap_node(cpu) do { } while (0) -#define next_reap_node(void) do { } while (0) -#endif - -#define REAPTIMEOUT_CPUC (2*HZ) - -#ifdef CONFIG_SMP -static DEFINE_PER_CPU(struct delayed_work, reap_work); - -static void cache_reap(struct work_struct *unused) -{ - next_reap_node(); - schedule_delayed_work(&__get_cpu_var(reap_work), - REAPTIMEOUT_CPUC); -} - -static void __devinit start_cpu_timer(int cpu) -{ - struct delayed_work *reap_work = &per_cpu(reap_work, cpu); - - /* - * When this gets called from do_initcalls via cpucache_init(), - * init_workqueues() has already run, so keventd will be setup - * at that time. - */ - if (keventd_up() && reap_work->work.func == NULL) { - init_reap_node(cpu); - INIT_DELAYED_WORK(reap_work, cache_reap); - schedule_delayed_work_on(cpu, reap_work, HZ + 3 * cpu); - } -} - -static int __init cpucache_init(void) -{ - int cpu; - - /* - * Register the timers that drain pcp pages and update vm statistics - */ - for_each_online_cpu(cpu) - start_cpu_timer(cpu); - return 0; -} -__initcall(cpucache_init); -#endif - void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, void *caller) { struct kmem_cache *s = get_slab(size, gfpflags); diff --git a/mm/vmstat.c b/mm/vmstat.c index 006eb762186..9832d9a41d8 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -281,6 +281,17 @@ EXPORT_SYMBOL(dec_zone_page_state); /* * Update the zone counters for one cpu. + * + * Note that refresh_cpu_vm_stats strives to only access + * node local memory. The per cpu pagesets on remote zones are placed + * in the memory local to the processor using that pageset. So the + * loop over all zones will access a series of cachelines local to + * the processor. + * + * The call to zone_page_state_add updates the cachelines with the + * statistics in the remote zone struct as well as the global cachelines + * with the global counters. These could cause remote node cache line + * bouncing and will have to be only done when necessary. */ void refresh_cpu_vm_stats(int cpu) { @@ -289,21 +300,54 @@ void refresh_cpu_vm_stats(int cpu) unsigned long flags; for_each_zone(zone) { - struct per_cpu_pageset *pcp; + struct per_cpu_pageset *p; if (!populated_zone(zone)) continue; - pcp = zone_pcp(zone, cpu); + p = zone_pcp(zone, cpu); for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) - if (pcp->vm_stat_diff[i]) { + if (p->vm_stat_diff[i]) { local_irq_save(flags); - zone_page_state_add(pcp->vm_stat_diff[i], + zone_page_state_add(p->vm_stat_diff[i], zone, i); - pcp->vm_stat_diff[i] = 0; + p->vm_stat_diff[i] = 0; +#ifdef CONFIG_NUMA + /* 3 seconds idle till flush */ + p->expire = 3; +#endif local_irq_restore(flags); } +#ifdef CONFIG_NUMA + /* + * Deal with draining the remote pageset of this + * processor + * + * Check if there are pages remaining in this pageset + * if not then there is nothing to expire. + */ + if (!p->expire || (!p->pcp[0].count && !p->pcp[1].count)) + continue; + + /* + * We never drain zones local to this processor. + */ + if (zone_to_nid(zone) == numa_node_id()) { + p->expire = 0; + continue; + } + + p->expire--; + if (p->expire) + continue; + + if (p->pcp[0].count) + drain_zone_pages(zone, p->pcp + 0); + + if (p->pcp[1].count) + drain_zone_pages(zone, p->pcp + 1); +#endif } } -- cgit v1.2.3 From b52f52a093bb1e841e014c2087b5bee7162da413 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 9 May 2007 02:35:15 -0700 Subject: clocksource: fix resume logic We need to make sure that the clocksources are resumed, when timekeeping is resumed. The current resume logic does not guarantee this. Add a resume function pointer to the clocksource struct, so clocksource drivers which need to reinitialize the clocksource can provide a resume function. Add a resume function, which calls the maybe available clocksource resume functions and resets the watchdog function, so a stable TSC can be used accross suspend/resume. Signed-off-by: Thomas Gleixner Cc: john stultz Cc: Andi Kleen Cc: Ingo Molnar Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/clocksource.h | 3 +++ kernel/time/clocksource.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ kernel/timer.c | 2 ++ 3 files changed, 50 insertions(+) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 2665ca04cf8..bf297b03a4e 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -49,6 +49,7 @@ struct clocksource; * @shift: cycle to nanosecond divisor (power of two) * @flags: flags describing special properties * @vread: vsyscall based read + * @resume: resume function for the clocksource, if necessary * @cycle_interval: Used internally by timekeeping core, please ignore. * @xtime_interval: Used internally by timekeeping core, please ignore. */ @@ -65,6 +66,7 @@ struct clocksource { u32 shift; unsigned long flags; cycle_t (*vread)(void); + void (*resume)(void); /* timekeeping specific data, ignore */ cycle_t cycle_interval; @@ -209,6 +211,7 @@ static inline void clocksource_calculate_interval(struct clocksource *c, extern int clocksource_register(struct clocksource*); extern struct clocksource* clocksource_get_next(void); extern void clocksource_change_rating(struct clocksource *cs, int rating); +extern void clocksource_resume(void); #ifdef CONFIG_GENERIC_TIME_VSYSCALL extern void update_vsyscall(struct timespec *ts, struct clocksource *c); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index db0c725de5e..3db5c3c460d 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -74,6 +74,8 @@ static struct clocksource *watchdog; static struct timer_list watchdog_timer; static DEFINE_SPINLOCK(watchdog_lock); static cycle_t watchdog_last; +static int watchdog_resumed; + /* * Interval: 0.5sec Threshold: 0.0625s */ @@ -98,15 +100,26 @@ static void clocksource_watchdog(unsigned long data) struct clocksource *cs, *tmp; cycle_t csnow, wdnow; int64_t wd_nsec, cs_nsec; + int resumed; spin_lock(&watchdog_lock); + resumed = watchdog_resumed; + if (unlikely(resumed)) + watchdog_resumed = 0; + wdnow = watchdog->read(); wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask); watchdog_last = wdnow; list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { csnow = cs->read(); + + if (unlikely(resumed)) { + cs->wd_last = csnow; + continue; + } + /* Initialized ? */ if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { if ((cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && @@ -136,6 +149,13 @@ static void clocksource_watchdog(unsigned long data) } spin_unlock(&watchdog_lock); } +static void clocksource_resume_watchdog(void) +{ + spin_lock(&watchdog_lock); + watchdog_resumed = 1; + spin_unlock(&watchdog_lock); +} + static void clocksource_check_watchdog(struct clocksource *cs) { struct clocksource *cse; @@ -182,8 +202,33 @@ static void clocksource_check_watchdog(struct clocksource *cs) if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; } + +static inline void clocksource_resume_watchdog(void) { } #endif +/** + * clocksource_resume - resume the clocksource(s) + */ +void clocksource_resume(void) +{ + struct list_head *tmp; + unsigned long flags; + + spin_lock_irqsave(&clocksource_lock, flags); + + list_for_each(tmp, &clocksource_list) { + struct clocksource *cs; + + cs = list_entry(tmp, struct clocksource, list); + if (cs->resume) + cs->resume(); + } + + clocksource_resume_watchdog(); + + spin_unlock_irqrestore(&clocksource_lock, flags); +} + /** * clocksource_get_next - Returns the selected clocksource * diff --git a/kernel/timer.c b/kernel/timer.c index de85f8491c1..59a28b1752f 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1499,6 +1499,8 @@ unregister_time_interpolator(struct time_interpolator *ti) prev = &curr->next; } + clocksource_resume(); + write_seqlock_irqsave(&xtime_lock, flags); if (ti == time_interpolator) { /* we lost the best time-interpolator: */ -- cgit v1.2.3 From e61a1c1c4f240cec61300c8f27518c3e47570fd4 Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Wed, 9 May 2007 02:35:15 -0700 Subject: Allow arch to initialize arch field of the module structure This will later allow an arch to add module specific information via linker generated tables instead of poking directly in the module object structure. Signed-off-by: Roman Zippel Signed-off-by: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/module.h | 3 +++ scripts/mod/modpost.c | 1 + 2 files changed, 4 insertions(+) diff --git a/include/linux/module.h b/include/linux/module.h index 6d3dc9c4ff9..792d483c9af 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -356,6 +356,9 @@ struct module keeping pointers to this stuff */ char *args; }; +#ifndef MODULE_ARCH_INIT +#define MODULE_ARCH_INIT {} +#endif /* FIXME: It'd be nice to isolate modules during init, too, so they aren't used before they (may) fail. But presently too much code diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 480e18b00aa..113dc77b9f6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1343,6 +1343,7 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" " .exit = cleanup_module,\n" "#endif\n"); + buf_printf(b, " .arch = MODULE_ARCH_INIT,\n"); buf_printf(b, "};\n"); } -- cgit v1.2.3 From c9f4f06d3191bd91c1a081b54a6c8e913e7b8a83 Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Wed, 9 May 2007 02:35:16 -0700 Subject: wrap access to thread_info Recently a few direct accesses to the thread_info in the task structure snuck back, so this wraps them with the appropriate wrapper. Signed-off-by: Roman Zippel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/avr32/kernel/process.c | 6 +++--- arch/avr32/kernel/ptrace.c | 2 +- arch/blackfin/kernel/asm-offsets.c | 2 +- arch/i386/kernel/traps.c | 2 +- arch/m68knommu/kernel/asm-offsets.c | 2 +- arch/mips/kernel/smtc.c | 2 +- arch/x86_64/kernel/irq.c | 2 +- include/asm-i386/thread_info.h | 2 +- include/asm-ia64/thread_info.h | 2 +- include/asm-mips/system.h | 2 +- include/asm-parisc/compat.h | 2 +- include/asm-x86_64/thread_info.h | 2 +- kernel/mutex.c | 8 ++++---- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 4e4181ed1c6..13f98840261 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -330,13 +330,13 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, { struct pt_regs *childregs; - childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)p->thread_info)) - 1; + childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)task_stack_page(p))) - 1; *childregs = *regs; if (user_mode(regs)) childregs->sp = usp; else - childregs->sp = (unsigned long)p->thread_info + THREAD_SIZE; + childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; childregs->r12 = 0; /* Set return value for child */ @@ -403,7 +403,7 @@ unsigned long get_wchan(struct task_struct *p) if (!p || p == current || p->state == TASK_RUNNING) return 0; - stack_page = (unsigned long)p->thread_info; + stack_page = (unsigned long)task_stack_page(p); BUG_ON(!stack_page); /* diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c index 8ac74dddbbd..3c36c2d1614 100644 --- a/arch/avr32/kernel/ptrace.c +++ b/arch/avr32/kernel/ptrace.c @@ -24,7 +24,7 @@ static struct pt_regs *get_user_regs(struct task_struct *tsk) { - return (struct pt_regs *)((unsigned long) tsk->thread_info + + return (struct pt_regs *)((unsigned long)task_stack_page(tsk) + THREAD_SIZE - sizeof(struct pt_regs)); } diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c index 41d9a9f8970..e455f450450 100644 --- a/arch/blackfin/kernel/asm-offsets.c +++ b/arch/blackfin/kernel/asm-offsets.c @@ -46,7 +46,7 @@ int main(void) DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); - DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, pending)); diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 4bec0cbf407..c05e7e861b2 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -305,7 +305,7 @@ void show_registers(struct pt_regs *regs) regs->xds & 0xffff, regs->xes & 0xffff, regs->xfs & 0xffff, gs, ss); printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)", TASK_COMM_LEN, current->comm, current->pid, - current_thread_info(), current, current->thread_info); + current_thread_info(), current, task_thread_info(current)); /* * When in-kernel, we also print out the stack and code at the * time of the fault.. diff --git a/arch/m68knommu/kernel/asm-offsets.c b/arch/m68knommu/kernel/asm-offsets.c index b988c7bdc6e..7cd183d346e 100644 --- a/arch/m68knommu/kernel/asm-offsets.c +++ b/arch/m68knommu/kernel/asm-offsets.c @@ -31,7 +31,7 @@ int main(void) DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); - DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index 5dcfab6b288..b361edb83dc 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -560,7 +560,7 @@ void smtc_boot_secondary(int cpu, struct task_struct *idle) write_tc_gpr_sp(__KSTK_TOS(idle)); /* global pointer */ - write_tc_gpr_gp((unsigned long)idle->thread_info); + write_tc_gpr_gp((unsigned long)task_thread_info(idle)); smtc_status |= SMTC_MTC_ACTIVE; write_tc_c0_tchalt(0); diff --git a/arch/x86_64/kernel/irq.c b/arch/x86_64/kernel/irq.c index 3bc30d2c13d..3eaceac3248 100644 --- a/arch/x86_64/kernel/irq.c +++ b/arch/x86_64/kernel/irq.c @@ -32,7 +32,7 @@ atomic_t irq_err_count; */ static inline void stack_overflow_check(struct pt_regs *regs) { - u64 curbase = (u64) current->thread_info; + u64 curbase = (u64)task_stack_page(current); static unsigned long warned = -60*HZ; if (regs->rsp >= curbase && regs->rsp <= curbase + THREAD_SIZE && diff --git a/include/asm-i386/thread_info.h b/include/asm-i386/thread_info.h index bf01d4b342b..4cb0f91ae64 100644 --- a/include/asm-i386/thread_info.h +++ b/include/asm-i386/thread_info.h @@ -172,7 +172,7 @@ static inline struct thread_info *current_thread_info(void) #define TS_USEDFPU 0x0001 /* FPU was used by this task this quantum (SMP) */ #define TS_POLLING 0x0002 /* True if in idle loop and not sleeping */ -#define tsk_is_polling(t) ((t)->thread_info->status & TS_POLLING) +#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING) #endif /* __KERNEL__ */ diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h index 91698599f91..d2814750658 100644 --- a/include/asm-ia64/thread_info.h +++ b/include/asm-ia64/thread_info.h @@ -110,6 +110,6 @@ struct thread_info { #define TS_POLLING 1 /* true if in idle loop and not sleeping */ -#define tsk_is_polling(t) ((t)->thread_info->status & TS_POLLING) +#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING) #endif /* _ASM_IA64_THREAD_INFO_H */ diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 30f23a2b46c..3713d256d36 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -55,7 +55,7 @@ do { \ if (cpu_has_dsp) \ __save_dsp(prev); \ next->thread.emulated_fp = 0; \ - (last) = resume(prev, next, next->thread_info); \ + (last) = resume(prev, next, task_thread_info(next)); \ if (cpu_has_dsp) \ __restore_dsp(current); \ } while(0) diff --git a/include/asm-parisc/compat.h b/include/asm-parisc/compat.h index fe857902353..11f4222597a 100644 --- a/include/asm-parisc/compat.h +++ b/include/asm-parisc/compat.h @@ -152,7 +152,7 @@ static __inline__ void __user *compat_alloc_user_space(long len) static inline int __is_compat_task(struct task_struct *t) { - return test_ti_thread_flag(t->thread_info, TIF_32BIT); + return test_ti_thread_flag(task_thread_info(t), TIF_32BIT); } static inline int is_compat_task(void) diff --git a/include/asm-x86_64/thread_info.h b/include/asm-x86_64/thread_info.h index 74a6c74397f..10bb5a8ed68 100644 --- a/include/asm-x86_64/thread_info.h +++ b/include/asm-x86_64/thread_info.h @@ -162,7 +162,7 @@ static inline struct thread_info *stack_thread_info(void) #define TS_COMPAT 0x0002 /* 32bit syscall active */ #define TS_POLLING 0x0004 /* true if in idle loop and not sleeping */ -#define tsk_is_polling(t) ((t)->thread_info->status & TS_POLLING) +#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING) #endif /* __KERNEL__ */ diff --git a/kernel/mutex.c b/kernel/mutex.c index e7cbbb82765..303eab18484 100644 --- a/kernel/mutex.c +++ b/kernel/mutex.c @@ -133,7 +133,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass) debug_mutex_lock_common(lock, &waiter); mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_); - debug_mutex_add_waiter(lock, &waiter, task->thread_info); + debug_mutex_add_waiter(lock, &waiter, task_thread_info(task)); /* add waiting tasks to the end of the waitqueue (FIFO): */ list_add_tail(&waiter.list, &lock->wait_list); @@ -159,7 +159,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass) */ if (unlikely(state == TASK_INTERRUPTIBLE && signal_pending(task))) { - mutex_remove_waiter(lock, &waiter, task->thread_info); + mutex_remove_waiter(lock, &waiter, task_thread_info(task)); mutex_release(&lock->dep_map, 1, _RET_IP_); spin_unlock_mutex(&lock->wait_lock, flags); @@ -175,8 +175,8 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass) } /* got the lock - rejoice! */ - mutex_remove_waiter(lock, &waiter, task->thread_info); - debug_mutex_set_owner(lock, task->thread_info); + mutex_remove_waiter(lock, &waiter, task_thread_info(task)); + debug_mutex_set_owner(lock, task_thread_info(task)); /* set it to 0 if there are no waiters left: */ if (likely(list_empty(&lock->wait_list))) -- cgit v1.2.3 From f7e4217b007d1f73e7e3cf10ba4fea4a608c603f Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Wed, 9 May 2007 02:35:17 -0700 Subject: rename thread_info to stack This finally renames the thread_info field in task structure to stack, so that the assumptions about this field are gone and archs have more freedom about placing the thread_info structure. Nonbroken archs which have a proper thread pointer can do the access to both current thread and task structure via a single pointer. It'll allow for a few more cleanups of the fork code, from which e.g. ia64 could benefit. Signed-off-by: Roman Zippel [akpm@linux-foundation.org: build fix] Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Russell King Cc: Ian Molton Cc: Haavard Skinnemoen Cc: Mikael Starvik Cc: David Howells Cc: Yoshinori Sato Cc: "Luck, Tony" Cc: Hirokazu Takata Cc: Geert Uytterhoeven Cc: Roman Zippel Cc: Greg Ungerer Cc: Ralf Baechle Cc: Ralf Baechle Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Paul Mundt Cc: Kazumoto Kojima Cc: Richard Curnow Cc: William Lee Irwin III Cc: "David S. Miller" Cc: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Cc: Miles Bader Cc: Andi Kleen Cc: Chris Zankel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/blackfin/kernel/ptrace.c | 6 +++--- arch/h8300/kernel/asm-offsets.c | 2 +- arch/ia64/kernel/mca.c | 2 +- arch/mips/kernel/asm-offsets.c | 2 +- arch/parisc/kernel/asm-offsets.c | 2 +- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/ppc/kernel/asm-offsets.c | 2 +- arch/s390/kernel/asm-offsets.c | 2 +- arch/sparc/kernel/asm-offsets.c | 2 +- arch/v850/kernel/asm-offsets.c | 2 +- arch/xtensa/kernel/asm-offsets.c | 2 +- include/asm-alpha/thread_info.h | 8 ++++---- include/asm-blackfin/processor.h | 6 +++--- include/asm-blackfin/system.h | 4 ++-- include/asm-m68k/thread_info.h | 6 +++--- include/asm-x86_64/system.h | 2 +- include/linux/init_task.h | 2 +- include/linux/sched.h | 8 ++++---- kernel/fork.c | 4 ++-- 19 files changed, 33 insertions(+), 33 deletions(-) diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index d7c8e514cb9..e718bb4a1ef 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c @@ -73,7 +73,7 @@ static inline struct pt_regs *get_user_regs(struct task_struct *task) { return (struct pt_regs *) - ((unsigned long)task->thread_info + + ((unsigned long)task_stack_page(task) + (THREAD_SIZE - sizeof(struct pt_regs))); } @@ -99,7 +99,7 @@ static inline long get_reg(struct task_struct *task, int regno) unsigned char *reg_ptr; struct pt_regs *regs = - (struct pt_regs *)((unsigned long)task->thread_info + + (struct pt_regs *)((unsigned long)task_stack_page(task) + (THREAD_SIZE - sizeof(struct pt_regs))); reg_ptr = (char *)regs; @@ -125,7 +125,7 @@ put_reg(struct task_struct *task, int regno, unsigned long data) char * reg_ptr; struct pt_regs *regs = - (struct pt_regs *)((unsigned long)task->thread_info + + (struct pt_regs *)((unsigned long)task_stack_page(task) + (THREAD_SIZE - sizeof(struct pt_regs))); reg_ptr = (char *)regs; diff --git a/arch/h8300/kernel/asm-offsets.c b/arch/h8300/kernel/asm-offsets.c index b78b82ad28a..fc30b4fd091 100644 --- a/arch/h8300/kernel/asm-offsets.c +++ b/arch/h8300/kernel/asm-offsets.c @@ -30,7 +30,7 @@ int main(void) DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); - DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 1d7cc7e2ce3..f8ae709de0b 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -1689,7 +1689,7 @@ format_mca_init_stack(void *mca_data, unsigned long offset, ti->preempt_count = 1; ti->task = p; ti->cpu = cpu; - p->thread_info = ti; + p->stack = ti; p->state = TASK_UNINTERRUPTIBLE; cpu_set(cpu, p->cpus_allowed); INIT_LIST_HEAD(&p->tasks); diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index 761a779d5c4..3b27309d54b 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -82,7 +82,7 @@ void output_task_defines(void) { text("/* MIPS task_struct offsets. */"); offset("#define TASK_STATE ", struct task_struct, state); - offset("#define TASK_THREAD_INFO ", struct task_struct, thread_info); + offset("#define TASK_THREAD_INFO ", struct task_struct, stack); offset("#define TASK_FLAGS ", struct task_struct, flags); offset("#define TASK_MM ", struct task_struct, mm); offset("#define TASK_PID ", struct task_struct, pid); diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c index 54fdb959149..d3b7917a87c 100644 --- a/arch/parisc/kernel/asm-offsets.c +++ b/arch/parisc/kernel/asm-offsets.c @@ -54,7 +54,7 @@ int main(void) { - DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(TASK_STATE, offsetof(struct task_struct, state)); DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, pending)); diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 8f48560b7ee..37bc35e69db 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -58,7 +58,7 @@ int main(void) #ifdef CONFIG_PPC64 DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context)); #else - DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(PTRACE, offsetof(struct task_struct, ptrace)); #endif /* CONFIG_PPC64 */ diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c index c5850a27265..e8e94321b59 100644 --- a/arch/ppc/kernel/asm-offsets.c +++ b/arch/ppc/kernel/asm-offsets.c @@ -35,7 +35,7 @@ int main(void) { DEFINE(THREAD, offsetof(struct task_struct, thread)); - DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(MM, offsetof(struct task_struct, mm)); DEFINE(PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index ec514fe5ccd..1375f8a4469 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -15,7 +15,7 @@ int main(void) { - DEFINE(__THREAD_info, offsetof(struct task_struct, thread_info),); + DEFINE(__THREAD_info, offsetof(struct task_struct, stack),); DEFINE(__THREAD_ksp, offsetof(struct task_struct, thread.ksp),); DEFINE(__THREAD_per, offsetof(struct task_struct, thread.per_info),); DEFINE(__THREAD_mm_segment, diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c index 29d7cfd1c97..6773ed76e41 100644 --- a/arch/sparc/kernel/asm-offsets.c +++ b/arch/sparc/kernel/asm-offsets.c @@ -28,7 +28,7 @@ int foo(void) DEFINE(AOFF_task_gid, offsetof(struct task_struct, gid)); DEFINE(AOFF_task_euid, offsetof(struct task_struct, euid)); DEFINE(AOFF_task_egid, offsetof(struct task_struct, egid)); - /* DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); */ + /* DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); */ DEFINE(ASIZ_task_uid, sizeof(current->uid)); DEFINE(ASIZ_task_gid, sizeof(current->gid)); DEFINE(ASIZ_task_euid, sizeof(current->euid)); diff --git a/arch/v850/kernel/asm-offsets.c b/arch/v850/kernel/asm-offsets.c index 24f29136907..cee5c3142d4 100644 --- a/arch/v850/kernel/asm-offsets.c +++ b/arch/v850/kernel/asm-offsets.c @@ -29,7 +29,7 @@ int main (void) DEFINE (TASK_PTRACE, offsetof (struct task_struct, ptrace)); DEFINE (TASK_BLOCKED, offsetof (struct task_struct, blocked)); DEFINE (TASK_THREAD, offsetof (struct task_struct, thread)); - DEFINE (TASK_THREAD_INFO, offsetof (struct task_struct, thread_info)); + DEFINE (TASK_THREAD_INFO, offsetof (struct task_struct, stack)); DEFINE (TASK_MM, offsetof (struct task_struct, mm)); DEFINE (TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm)); DEFINE (TASK_PID, offsetof (struct task_struct, pid)); diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index b256cfbef34..698079b3a33 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -70,7 +70,7 @@ int main(void) DEFINE(TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm)); DEFINE(TASK_PID, offsetof (struct task_struct, pid)); DEFINE(TASK_THREAD, offsetof (struct task_struct, thread)); - DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, thread_info)); + DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, stack)); DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct)); BLANK(); diff --git a/include/asm-alpha/thread_info.h b/include/asm-alpha/thread_info.h index eeb3bef91e1..f4defc2bd3f 100644 --- a/include/asm-alpha/thread_info.h +++ b/include/asm-alpha/thread_info.h @@ -97,7 +97,7 @@ register struct thread_info *__current_thread_info __asm__("$8"); 1 << TIF_UAC_SIGBUS) #define SET_UNALIGN_CTL(task,value) ({ \ - (task)->thread_info->flags = (((task)->thread_info->flags & \ + task_thread_info(task)->flags = ((task_thread_info(task)->flags & \ ~ALPHA_UAC_MASK) \ | (((value) << ALPHA_UAC_SHIFT) & (1<thread_info->flags & (1 << TIF_UAC_NOPRINT)) \ + put_user((task_thread_info(task)->flags & (1 << TIF_UAC_NOPRINT))\ >> ALPHA_UAC_SHIFT \ - | ((task)->thread_info->flags & (1 << TIF_UAC_SIGBUS)) \ + | (task_thread_info(task)->flags & (1 << TIF_UAC_SIGBUS))\ >> (ALPHA_UAC_SHIFT + 1) \ - | ((task)->thread_info->flags & (1 << TIF_UAC_NOFIX)) \ + | (task_thread_info(task)->flags & (1 << TIF_UAC_NOFIX))\ >> (ALPHA_UAC_SHIFT - 1), \ (int __user *)(value)); \ }) diff --git a/include/asm-blackfin/processor.h b/include/asm-blackfin/processor.h index 997465c93e8..0336ff132c1 100644 --- a/include/asm-blackfin/processor.h +++ b/include/asm-blackfin/processor.h @@ -58,10 +58,10 @@ do { \ (_regs)->pc = (_pc); \ if (current->mm) \ (_regs)->p5 = current->mm->start_data; \ - current->thread_info->l1_task_info.stack_start \ + task_thread_info(current)->l1_task_info.stack_start \ = (void *)current->mm->context.stack_start; \ - current->thread_info->l1_task_info.lowest_sp = (void *)(_usp); \ - memcpy(L1_SCRATCH_TASK_INFO, ¤t->thread_info->l1_task_info, \ + task_thread_info(current)->l1_task_info.lowest_sp = (void *)(_usp); \ + memcpy(L1_SCRATCH_TASK_INFO, &task_thread_info(current)->l1_task_info, \ sizeof(*L1_SCRATCH_TASK_INFO)); \ wrusp(_usp); \ } while(0) diff --git a/include/asm-blackfin/system.h b/include/asm-blackfin/system.h index b5bf6e7cb5e..5e5f1a0566c 100644 --- a/include/asm-blackfin/system.h +++ b/include/asm-blackfin/system.h @@ -239,9 +239,9 @@ asmlinkage struct task_struct *resume(struct task_struct *prev, struct task_stru #define switch_to(prev,next,last) \ do { \ - memcpy (&prev->thread_info->l1_task_info, L1_SCRATCH_TASK_INFO, \ + memcpy (&task_thread_info(prev)->l1_task_info, L1_SCRATCH_TASK_INFO, \ sizeof *L1_SCRATCH_TASK_INFO); \ - memcpy (L1_SCRATCH_TASK_INFO, &next->thread_info->l1_task_info, \ + memcpy (L1_SCRATCH_TASK_INFO, &task_thread_info(next)->l1_task_info, \ sizeof *L1_SCRATCH_TASK_INFO); \ (last) = resume (prev, next); \ } while (0) diff --git a/include/asm-m68k/thread_info.h b/include/asm-m68k/thread_info.h index c4d622a57df..d635a375248 100644 --- a/include/asm-m68k/thread_info.h +++ b/include/asm-m68k/thread_info.h @@ -37,17 +37,17 @@ struct thread_info { #define init_stack (init_thread_union.stack) #define task_thread_info(tsk) (&(tsk)->thread.info) -#define task_stack_page(tsk) ((void *)(tsk)->thread_info) +#define task_stack_page(tsk) ((tsk)->stack) #define current_thread_info() task_thread_info(current) #define __HAVE_THREAD_FUNCTIONS #define setup_thread_stack(p, org) ({ \ - *(struct task_struct **)(p)->thread_info = (p); \ + *(struct task_struct **)(p)->stack = (p); \ task_thread_info(p)->task = (p); \ }) -#define end_of_stack(p) ((unsigned long *)(p)->thread_info + 1) +#define end_of_stack(p) ((unsigned long *)(p)->stack + 1) /* entry.S relies on these definitions! * bits 0-7 are tested at every exception exit diff --git a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h index b7b8021e8c4..ead9f9a5623 100644 --- a/include/asm-x86_64/system.h +++ b/include/asm-x86_64/system.h @@ -39,7 +39,7 @@ [threadrsp] "i" (offsetof(struct task_struct, thread.rsp)), \ [ti_flags] "i" (offsetof(struct thread_info, flags)),\ [tif_fork] "i" (TIF_FORK), \ - [thread_info] "i" (offsetof(struct task_struct, thread_info)), \ + [thread_info] "i" (offsetof(struct task_struct, stack)), \ [pda_pcurrent] "i" (offsetof(struct x8664_pda, pcurrent)) \ : "memory", "cc" __EXTRA_CLOBBER) diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 795102309bf..45170b2fa25 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -95,7 +95,7 @@ extern struct group_info init_groups; #define INIT_TASK(tsk) \ { \ .state = 0, \ - .thread_info = &init_thread_info, \ + .stack = &init_thread_info, \ .usage = ATOMIC_INIT(2), \ .flags = 0, \ .lock_depth = -1, \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 28000b1658f..17b72d88c4c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -817,7 +817,7 @@ struct prio_array; struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ - struct thread_info *thread_info; + void *stack; atomic_t usage; unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; @@ -1513,8 +1513,8 @@ static inline void unlock_task_sighand(struct task_struct *tsk, #ifndef __HAVE_THREAD_FUNCTIONS -#define task_thread_info(task) (task)->thread_info -#define task_stack_page(task) ((void*)((task)->thread_info)) +#define task_thread_info(task) ((struct thread_info *)(task)->stack) +#define task_stack_page(task) ((task)->stack) static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org) { @@ -1524,7 +1524,7 @@ static inline void setup_thread_stack(struct task_struct *p, struct task_struct static inline unsigned long *end_of_stack(struct task_struct *p) { - return (unsigned long *)(p->thread_info + 1); + return (unsigned long *)(task_thread_info(p) + 1); } #endif diff --git a/kernel/fork.c b/kernel/fork.c index a8dd75d4992..5dd3979747f 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -105,7 +105,7 @@ static struct kmem_cache *mm_cachep; void free_task(struct task_struct *tsk) { - free_thread_info(tsk->thread_info); + free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); free_task_struct(tsk); } @@ -175,7 +175,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) } *tsk = *orig; - tsk->thread_info = ti; + tsk->stack = ti; setup_thread_stack(tsk, orig); #ifdef CONFIG_CC_STACKPROTECTOR -- cgit v1.2.3 From 0d7ebbbc6eaa5539f78ab20ed6ff1725a4e332ef Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 9 May 2007 02:35:27 -0700 Subject: compiler: introduce __used and __maybe_unused __used is defined to be __attribute__((unused)) for all pre-3.3 gcc compilers to suppress warnings for unused functions because perhaps they are referenced only in inline assembly. It is defined to be __attribute__((used)) for gcc 3.3 and later so that the code is still emitted for such functions. __maybe_unused is defined to be __attribute__((unused)) for both function and variable use if it could possibly be unreferenced due to the evaluation of preprocessor macros. Function prototypes shall be marked with __maybe_unused if the actual definition of the function is dependant on preprocessor macros. No update to compiler-intel.h is necessary because ICC supports both __attribute__((used)) and __attribute__((unused)) as specified by the gcc manual. __attribute_used__ is deprecated and will be removed once all current code is converted to using __used. Cc: Rusty Russell Cc: Adrian Bunk Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compiler-gcc.h | 1 + include/linux/compiler-gcc3.h | 6 ++++-- include/linux/compiler-gcc4.h | 3 ++- include/linux/compiler.h | 21 ++++++++++++++++++--- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index a9f794716a8..03ec2311fb2 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -40,3 +40,4 @@ #define noinline __attribute__((noinline)) #define __attribute_pure__ __attribute__((pure)) #define __attribute_const__ __attribute__((__const__)) +#define __maybe_unused __attribute__((unused)) diff --git a/include/linux/compiler-gcc3.h b/include/linux/compiler-gcc3.h index ecd621fd27d..a9e2863c2db 100644 --- a/include/linux/compiler-gcc3.h +++ b/include/linux/compiler-gcc3.h @@ -4,9 +4,11 @@ #include #if __GNUC_MINOR__ >= 3 -# define __attribute_used__ __attribute__((__used__)) +# define __used __attribute__((__used__)) +# define __attribute_used__ __used /* deprecated */ #else -# define __attribute_used__ __attribute__((__unused__)) +# define __used __attribute__((__unused__)) +# define __attribute_used__ __used /* deprecated */ #endif #if __GNUC_MINOR__ >= 4 diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index fd0cc7c4a63..a03e9398a6c 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h @@ -12,7 +12,8 @@ # define __inline __inline __attribute__((always_inline)) #endif -#define __attribute_used__ __attribute__((__used__)) +#define __used __attribute__((__used__)) +#define __attribute_used__ __used /* deprecated */ #define __must_check __attribute__((warn_unused_result)) #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) #define __always_inline inline __attribute__((always_inline)) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 3b6949b4174..498c3592076 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -108,15 +108,30 @@ extern void __chk_io_ptr(const void __iomem *); * Allow us to avoid 'defined but not used' warnings on functions and data, * as well as force them to be emitted to the assembly file. * - * As of gcc 3.3, static functions that are not marked with attribute((used)) - * may be elided from the assembly file. As of gcc 3.3, static data not so + * As of gcc 3.4, static functions that are not marked with attribute((used)) + * may be elided from the assembly file. As of gcc 3.4, static data not so * marked will not be elided, but this may change in a future gcc version. * + * NOTE: Because distributions shipped with a backported unit-at-a-time + * compiler in gcc 3.3, we must define __used to be __attribute__((used)) + * for gcc >=3.3 instead of 3.4. + * * In prior versions of gcc, such functions and data would be emitted, but * would be warned about except with attribute((unused)). + * + * Mark functions that are referenced only in inline assembly as __used so + * the code is emitted even though it appears to be unreferenced. */ #ifndef __attribute_used__ -# define __attribute_used__ /* unimplemented */ +# define __attribute_used__ /* deprecated */ +#endif + +#ifndef __used +# define __used /* unimplemented */ +#endif + +#ifndef __maybe_unused +# define __maybe_unused /* unimplemented */ #endif /* -- cgit v1.2.3 From f6744c02bc50797c1a83f8303ebcc96673ca0524 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 9 May 2007 02:35:28 -0700 Subject: i386 pci: use __maybe_unused Use the new macro here Cc: Andi Kleen Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/pci/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/i386/pci/init.c b/arch/i386/pci/init.c index 1cf11af96de..3de9f9ba2da 100644 --- a/arch/i386/pci/init.c +++ b/arch/i386/pci/init.c @@ -6,7 +6,7 @@ in the right sequence from here. */ static __init int pci_access_init(void) { - int type __attribute__((unused)) = 0; + int type __maybe_unused = 0; #ifdef CONFIG_PCI_DIRECT type = pci_direct_probe(); -- cgit v1.2.3 From d16aaffa752c7fd50df951e631dd0c11f80d9cbf Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 9 May 2007 02:35:28 -0700 Subject: sh: dma: use __maybe_unused There is no such thing as labeling a variable as __attribute__((used)). Since ts_shift is not referenced in inline assembly, we assume that we're simply suppressing a warning here if the variable is declared but unreferenced. Cc: Paul Mundt Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-sh/cpu-sh3/dma.h | 2 +- include/asm-sh/cpu-sh4/dma-sh7780.h | 2 +- include/asm-sh/cpu-sh4/dma.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/asm-sh/cpu-sh3/dma.h b/include/asm-sh/cpu-sh3/dma.h index 954801b4602..3a66dc45802 100644 --- a/include/asm-sh/cpu-sh3/dma.h +++ b/include/asm-sh/cpu-sh3/dma.h @@ -26,7 +26,7 @@ enum { XMIT_SZ_128BIT, }; -static unsigned int ts_shift[] __attribute__ ((used)) = { +static unsigned int ts_shift[] __maybe_unused = { [XMIT_SZ_8BIT] = 0, [XMIT_SZ_16BIT] = 1, [XMIT_SZ_32BIT] = 2, diff --git a/include/asm-sh/cpu-sh4/dma-sh7780.h b/include/asm-sh/cpu-sh4/dma-sh7780.h index 6c90d28331b..71b426a6e48 100644 --- a/include/asm-sh/cpu-sh4/dma-sh7780.h +++ b/include/asm-sh/cpu-sh4/dma-sh7780.h @@ -28,7 +28,7 @@ enum { /* * The DMA count is defined as the number of bytes to transfer. */ -static unsigned int __attribute__ ((used)) ts_shift[] = { +static unsigned int ts_shift[] __maybe_unused = { [XMIT_SZ_8BIT] = 0, [XMIT_SZ_16BIT] = 1, [XMIT_SZ_32BIT] = 2, diff --git a/include/asm-sh/cpu-sh4/dma.h b/include/asm-sh/cpu-sh4/dma.h index c135e9cebd9..36e26a96476 100644 --- a/include/asm-sh/cpu-sh4/dma.h +++ b/include/asm-sh/cpu-sh4/dma.h @@ -53,7 +53,7 @@ enum { /* * The DMA count is defined as the number of bytes to transfer. */ -static unsigned int ts_shift[] __attribute__ ((used)) = { +static unsigned int ts_shift[] __maybe_unused = { [XMIT_SZ_64BIT] = 3, [XMIT_SZ_8BIT] = 0, [XMIT_SZ_16BIT] = 1, -- cgit v1.2.3 From affd872ebbe4f045d70a3f4b33b20f5f6c894bcc Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 9 May 2007 02:35:29 -0700 Subject: i386: voyager: use __maybe_unused Replace automatic variable instances of __attribute__((unused)) with __maybe_unused in mca_nmi_hook(). Cc: James Bottomley Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/mach-voyager/voyager_basic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/i386/mach-voyager/voyager_basic.c b/arch/i386/mach-voyager/voyager_basic.c index 8fe7e4593d5..9b77b39b71a 100644 --- a/arch/i386/mach-voyager/voyager_basic.c +++ b/arch/i386/mach-voyager/voyager_basic.c @@ -292,8 +292,8 @@ machine_emergency_restart(void) void mca_nmi_hook(void) { - __u8 dumpval __attribute__((unused)) = inb(0xf823); - __u8 swnmi __attribute__((unused)) = inb(0xf813); + __u8 dumpval __maybe_unused = inb(0xf823); + __u8 swnmi __maybe_unused = inb(0xf813); /* FIXME: assume dump switch pressed */ /* check to see if the dump switch was pressed */ -- cgit v1.2.3 From c3c117f06e95ea993ba4ed3b246fd76f87a4683f Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 9 May 2007 02:35:30 -0700 Subject: i386 mmzone: use __maybe_unused Replace automatic variable instances of __attribute__ ((unused)) with __maybe_unused. Cc: Andy Whitcroft Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-i386/mmzone.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/asm-i386/mmzone.h b/include/asm-i386/mmzone.h index 3503ad66945..118e9812778 100644 --- a/include/asm-i386/mmzone.h +++ b/include/asm-i386/mmzone.h @@ -122,21 +122,21 @@ static inline int pfn_valid(int pfn) __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0) #define alloc_bootmem_node(pgdat, x) \ ({ \ - struct pglist_data __attribute__ ((unused)) \ + struct pglist_data __maybe_unused \ *__alloc_bootmem_node__pgdat = (pgdat); \ __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, \ __pa(MAX_DMA_ADDRESS)); \ }) #define alloc_bootmem_pages_node(pgdat, x) \ ({ \ - struct pglist_data __attribute__ ((unused)) \ + struct pglist_data __maybe_unused \ *__alloc_bootmem_node__pgdat = (pgdat); \ __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, \ __pa(MAX_DMA_ADDRESS)) \ }) #define alloc_bootmem_low_pages_node(pgdat, x) \ ({ \ - struct pglist_data __attribute__ ((unused)) \ + struct pglist_data __maybe_unused \ *__alloc_bootmem_node__pgdat = (pgdat); \ __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0); \ }) -- cgit v1.2.3 From 558b7bd86c32978648cda5deb5c758d77ef0c165 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 9 May 2007 02:35:31 -0700 Subject: vt8623fb: new framebuffer driver for VIA VT8623 This patch adds fbdev driver for graphics core in VIA VT8623 [adaplas@gmail.com: build fixes] Signed-off-by: Ondrej Zajicek Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/vt8623fb.txt | 64 +++ drivers/video/Kconfig | 14 + drivers/video/Makefile | 1 + drivers/video/vt8623fb.c | 926 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1005 insertions(+) create mode 100644 Documentation/fb/vt8623fb.txt create mode 100644 drivers/video/vt8623fb.c diff --git a/Documentation/fb/vt8623fb.txt b/Documentation/fb/vt8623fb.txt new file mode 100644 index 00000000000..f654576c56b --- /dev/null +++ b/Documentation/fb/vt8623fb.txt @@ -0,0 +1,64 @@ + + vt8623fb - fbdev driver for graphics core in VIA VT8623 chipset + =============================================================== + + +Supported Hardware +================== + + VIA VT8623 [CLE266] chipset and its graphics core + (known as CastleRock or Unichrome) + +I tested vt8623fb on VIA EPIA ML-6000 + + +Supported Features +================== + + * 4 bpp pseudocolor modes (with 18bit palette, two variants) + * 8 bpp pseudocolor mode (with 18bit palette) + * 16 bpp truecolor mode (RGB 565) + * 32 bpp truecolor mode (RGB 888) + * text mode (activated by bpp = 0) + * doublescan mode variant (not available in text mode) + * panning in both directions + * suspend/resume support + * DPMS support + +Text mode is supported even in higher resolutions, but there is limitation to +lower pixclocks (maximum about 100 MHz). This limitation is not enforced by +driver. Text mode supports 8bit wide fonts only (hardware limitation) and +16bit tall fonts (driver limitation). + +There are two 4 bpp modes. First mode (selected if nonstd == 0) is mode with +packed pixels, high nibble first. Second mode (selected if nonstd == 1) is mode +with interleaved planes (1 byte interleave), MSB first. Both modes support +8bit wide fonts only (driver limitation). + +Suspend/resume works on systems that initialize video card during resume and +if device is active (for example used by fbcon). + + +Missing Features +================ +(alias TODO list) + + * secondary (not initialized by BIOS) device support + * MMIO support + * interlaced mode variant + * support for fontwidths != 8 in 4 bpp modes + * support for fontheight != 16 in text mode + * hardware cursor + * video overlay support + * vsync synchronization + * acceleration support (8514-like 2D, busmaster transfers) + + +Known bugs +========== + + * cursor disable in text mode doesn't work + + +-- +Ondrej Zajicek diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1132ba5ff39..7e5010dcaff 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1348,6 +1348,20 @@ config FB_VOODOO1 Please read the for supported options and other important info support. +config FB_VT8623 + tristate "VIA VT8623 support" + depends on FB && PCI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_TILEBLITTING + select FB_SVGALIB + select VGASTATE + select FONT_8x16 if FRAMEBUFFER_CONSOLE + ---help--- + Driver for CastleRock integrated graphics core in the + VIA VT8623 [Apollo CLE266] chipset. + config FB_CYBLA tristate "Cyberblade/i1 support" depends on FB && PCI && X86_32 && !64BIT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a916c204274..c0c61468094 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_FB_VALKYRIE) += valkyriefb.o obj-$(CONFIG_FB_CT65550) += chipsfb.o obj-$(CONFIG_FB_IMSTT) += imsttfb.o obj-$(CONFIG_FB_FM2) += fm2fb.o +obj-$(CONFIG_FB_VT8623) += vt8623fb.o obj-$(CONFIG_FB_CYBLA) += cyblafb.o obj-$(CONFIG_FB_TRIDENT) += tridentfb.o obj-$(CONFIG_FB_LE80578) += vermilion/ diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c new file mode 100644 index 00000000000..8883b6f36a7 --- /dev/null +++ b/drivers/video/vt8623fb.c @@ -0,0 +1,926 @@ +/* + * linux/drivers/video/vt8623fb.c - fbdev driver for + * integrated graphic core in VIA VT8623 [CLE266] chipset + * + * Copyright (c) 2006-2007 Ondrej Zajicek + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * Code is based on s3fb, some parts are from David Boucher's viafb + * (http://davesdomain.org.uk/viafb/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Why should fb driver call console functions? because acquire_console_sem() */ +#include