From e6cf5df1838c28bb060ac45b5585e48e71bbc740 Mon Sep 17 00:00:00 2001 From: frans Date: Fri, 15 Aug 2008 23:14:31 +0200 Subject: [MTD] [NAND] nand_ecc.c: rewrite for improved performance This patch improves the performance of the ecc generation code by a factor of 18 on an INTEL D920 CPU, a factor of 7 on MIPS and a factor of 5 on ARM (NSLU2) Signed-off-by: Frans Meulenbroeks Signed-off-by: David Woodhouse --- Documentation/mtd/nand_ecc.txt | 714 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 714 insertions(+) create mode 100644 Documentation/mtd/nand_ecc.txt (limited to 'Documentation') diff --git a/Documentation/mtd/nand_ecc.txt b/Documentation/mtd/nand_ecc.txt new file mode 100644 index 00000000000..bdf93b7f0f2 --- /dev/null +++ b/Documentation/mtd/nand_ecc.txt @@ -0,0 +1,714 @@ +Introduction +============ + +Having looked at the linux mtd/nand driver and more specific at nand_ecc.c +I felt there was room for optimisation. I bashed the code for a few hours +performing tricks like table lookup removing superfluous code etc. +After that the speed was increased by 35-40%. +Still I was not too happy as I felt there was additional room for improvement. + +Bad! I was hooked. +I decided to annotate my steps in this file. Perhaps it is useful to someone +or someone learns something from it. + + +The problem +=========== + +NAND flash (at least SLC one) typically has sectors of 256 bytes. +However NAND flash is not extremely reliable so some error detection +(and sometimes correction) is needed. + +This is done by means of a Hamming code. I'll try to explain it in +laymans terms (and apologies to all the pro's in the field in case I do +not use the right terminology, my coding theory class was almost 30 +years ago, and I must admit it was not one of my favourites). + +As I said before the ecc calculation is performed on sectors of 256 +bytes. This is done by calculating several parity bits over the rows and +columns. The parity used is even parity which means that the parity bit = 1 +if the data over which the parity is calculated is 1 and the parity bit = 0 +if the data over which the parity is calculated is 0. So the total +number of bits over the data over which the parity is calculated + the +parity bit is even. (see wikipedia if you can't follow this). +Parity is often calculated by means of an exclusive or operation, +sometimes also referred to as xor. In C the operator for xor is ^ + +Back to ecc. +Let's give a small figure: + +byte 0: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp0 rp2 rp4 ... rp14 +byte 1: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp1 rp2 rp4 ... rp14 +byte 2: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp0 rp3 rp4 ... rp14 +byte 3: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp1 rp3 rp4 ... rp14 +byte 4: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp0 rp2 rp5 ... rp14 +.... +byte 254: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp0 rp3 rp5 ... rp15 +byte 255: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 rp1 rp3 rp5 ... rp15 + cp1 cp0 cp1 cp0 cp1 cp0 cp1 cp0 + cp3 cp3 cp2 cp2 cp3 cp3 cp2 cp2 + cp5 cp5 cp5 cp5 cp4 cp4 cp4 cp4 + +This figure represents a sector of 256 bytes. +cp is my abbreviaton for column parity, rp for row parity. + +Let's start to explain column parity. +cp0 is the parity that belongs to all bit0, bit2, bit4, bit6. +so the sum of all bit0, bit2, bit4 and bit6 values + cp0 itself is even. +Similarly cp1 is the sum of all bit1, bit3, bit5 and bit7. +cp2 is the parity over bit0, bit1, bit4 and bit5 +cp3 is the parity over bit2, bit3, bit6 and bit7. +cp4 is the parity over bit0, bit1, bit2 and bit3. +cp5 is the parity over bit4, bit5, bit6 and bit7. +Note that each of cp0 .. cp5 is exactly one bit. + +Row parity actually works almost the same. +rp0 is the parity of all even bytes (0, 2, 4, 6, ... 252, 254) +rp1 is the parity of all odd bytes (1, 3, 5, 7, ..., 253, 255) +rp2 is the parity of all bytes 0, 1, 4, 5, 8, 9, ... +(so handle two bytes, then skip 2 bytes). +rp3 is covers the half rp2 does not cover (bytes 2, 3, 6, 7, 10, 11, ...) +for rp4 the rule is cover 4 bytes, skip 4 bytes, cover 4 bytes, skip 4 etc. +so rp4 calculates parity over bytes 0, 1, 2, 3, 8, 9, 10, 11, 16, ...) +and rp5 covers the other half, so bytes 4, 5, 6, 7, 12, 13, 14, 15, 20, .. +The story now becomes quite boring. I guess you get the idea. +rp6 covers 8 bytes then skips 8 etc +rp7 skips 8 bytes then covers 8 etc +rp8 covers 16 bytes then skips 16 etc +rp9 skips 16 bytes then covers 16 etc +rp10 covers 32 bytes then skips 32 etc +rp11 skips 32 bytes then covers 32 etc +rp12 covers 64 bytes then skips 64 etc +rp13 skips 64 bytes then covers 64 etc +rp14 covers 128 bytes then skips 128 +rp15 skips 128 bytes then covers 128 + +In the end the parity bits are grouped together in three bytes as +follows: +ECC Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 +ECC 0 rp07 rp06 rp05 rp04 rp03 rp02 rp01 rp00 +ECC 1 rp15 rp14 rp13 rp12 rp11 rp10 rp09 rp08 +ECC 2 cp5 cp4 cp3 cp2 cp1 cp0 1 1 + +I detected after writing this that ST application note AN1823 +(http://www.st.com/stonline/books/pdf/docs/10123.pdf) gives a much +nicer picture.(but they use line parity as term where I use row parity) +Oh well, I'm graphically challenged, so suffer with me for a moment :-) +And I could not reuse the ST picture anyway for copyright reasons. + + +Attempt 0 +========= + +Implementing the parity calculation is pretty simple. +In C pseudocode: +for (i = 0; i < 256; i++) +{ + if (i & 0x01) + rp1 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp1; + else + rp0 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp1; + if (i & 0x02) + rp3 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp3; + else + rp2 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp2; + if (i & 0x04) + rp5 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp5; + else + rp4 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp4; + if (i & 0x08) + rp7 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp7; + else + rp6 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp6; + if (i & 0x10) + rp9 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp9; + else + rp8 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp8; + if (i & 0x20) + rp11 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp11; + else + rp10 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp10; + if (i & 0x40) + rp13 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp13; + else + rp12 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp12; + if (i & 0x80) + rp15 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp15; + else + rp14 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp14; + cp0 = bit6 ^ bit4 ^ bit2 ^ bit0 ^ cp0; + cp1 = bit7 ^ bit5 ^ bit3 ^ bit1 ^ cp1; + cp2 = bit5 ^ bit4 ^ bit1 ^ bit0 ^ cp2; + cp3 = bit7 ^ bit6 ^ bit3 ^ bit2 ^ cp3 + cp4 = bit3 ^ bit2 ^ bit1 ^ bit0 ^ cp4 + cp5 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ cp5 +} + + +Analysis 0 +========== + +C does have bitwise operators but not really operators to do the above +efficiently (and most hardware has no such instructions either). +Therefore without implementing this it was clear that the code above was +not going to bring me a Nobel prize :-) + +Fortunately the exclusive or operation is commutative, so we can combine +the values in any order. So instead of calculating all the bits +individually, let us try to rearrange things. +For the column parity this is easy. We can just xor the bytes and in the +end filter out the relevant bits. This is pretty nice as it will bring +all cp calculation out of the if loop. + +Similarly we can first xor the bytes for the various rows. +This leads to: + + +Attempt 1 +========= + +const char parity[256] = { + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 +}; + +void ecc1(const unsigned char *buf, unsigned char *code) +{ + int i; + const unsigned char *bp = buf; + unsigned char cur; + unsigned char rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; + unsigned char rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15; + unsigned char par; + + par = 0; + rp0 = 0; rp1 = 0; rp2 = 0; rp3 = 0; + rp4 = 0; rp5 = 0; rp6 = 0; rp7 = 0; + rp8 = 0; rp9 = 0; rp10 = 0; rp11 = 0; + rp12 = 0; rp13 = 0; rp14 = 0; rp15 = 0; + + for (i = 0; i < 256; i++) + { + cur = *bp++; + par ^= cur; + if (i & 0x01) rp1 ^= cur; else rp0 ^= cur; + if (i & 0x02) rp3 ^= cur; else rp2 ^= cur; + if (i & 0x04) rp5 ^= cur; else rp4 ^= cur; + if (i & 0x08) rp7 ^= cur; else rp6 ^= cur; + if (i & 0x10) rp9 ^= cur; else rp8 ^= cur; + if (i & 0x20) rp11 ^= cur; else rp10 ^= cur; + if (i & 0x40) rp13 ^= cur; else rp12 ^= cur; + if (i & 0x80) rp15 ^= cur; else rp14 ^= cur; + } + code[0] = + (parity[rp7] << 7) | + (parity[rp6] << 6) | + (parity[rp5] << 5) | + (parity[rp4] << 4) | + (parity[rp3] << 3) | + (parity[rp2] << 2) | + (parity[rp1] << 1) | + (parity[rp0]); + code[1] = + (parity[rp15] << 7) | + (parity[rp14] << 6) | + (parity[rp13] << 5) | + (parity[rp12] << 4) | + (parity[rp11] << 3) | + (parity[rp10] << 2) | + (parity[rp9] << 1) | + (parity[rp8]); + code[2] = + (parity[par & 0xf0] << 7) | + (parity[par & 0x0f] << 6) | + (parity[par & 0xcc] << 5) | + (parity[par & 0x33] << 4) | + (parity[par & 0xaa] << 3) | + (parity[par & 0x55] << 2); + code[0] = ~code[0]; + code[1] = ~code[1]; + code[2] = ~code[2]; +} + +Still pretty straightforward. The last three invert statements are there to +give a checksum of 0xff 0xff 0xff for an empty flash. In an empty flash +all data is 0xff, so the checksum then matches. + +I also introduced the parity lookup. I expected this to be the fastest +way to calculate the parity, but I will investigate alternatives later +on. + + +Analysis 1 +========== + +The code works, but is not terribly efficient. On my system it took +almost 4 times as much time as the linux driver code. But hey, if it was +*that* easy this would have been done long before. +No pain. no gain. + +Fortunately there is plenty of room for improvement. + +In step 1 we moved from bit-wise calculation to byte-wise calculation. +However in C we can also use the unsigned long data type and virtually +every modern microprocessor supports 32 bit operations, so why not try +to write our code in such a way that we process data in 32 bit chunks. + +Of course this means some modification as the row parity is byte by +byte. A quick analysis: +for the column parity we use the par variable. When extending to 32 bits +we can in the end easily calculate p0 and p1 from it. +(because par now consists of 4 bytes, contributing to rp1, rp0, rp1, rp0 +respectively) +also rp2 and rp3 can be easily retrieved from par as rp3 covers the +first two bytes and rp2 the last two bytes. + +Note that of course now the loop is executed only 64 times (256/4). +And note that care must taken wrt byte ordering. The way bytes are +ordered in a long is machine dependent, and might affect us. +Anyway, if there is an issue: this code is developed on x86 (to be +precise: a DELL PC with a D920 Intel CPU) + +And of course the performance might depend on alignment, but I expect +that the I/O buffers in the nand driver are aligned properly (and +otherwise that should be fixed to get maximum performance). + +Let's give it a try... + + +Attempt 2 +========= + +extern const char parity[256]; + +void ecc2(const unsigned char *buf, unsigned char *code) +{ + int i; + const unsigned long *bp = (unsigned long *)buf; + unsigned long cur; + unsigned long rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; + unsigned long rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15; + unsigned long par; + + par = 0; + rp0 = 0; rp1 = 0; rp2 = 0; rp3 = 0; + rp4 = 0; rp5 = 0; rp6 = 0; rp7 = 0; + rp8 = 0; rp9 = 0; rp10 = 0; rp11 = 0; + rp12 = 0; rp13 = 0; rp14 = 0; rp15 = 0; + + for (i = 0; i < 64; i++) + { + cur = *bp++; + par ^= cur; + if (i & 0x01) rp5 ^= cur; else rp4 ^= cur; + if (i & 0x02) rp7 ^= cur; else rp6 ^= cur; + if (i & 0x04) rp9 ^= cur; else rp8 ^= cur; + if (i & 0x08) rp11 ^= cur; else rp10 ^= cur; + if (i & 0x10) rp13 ^= cur; else rp12 ^= cur; + if (i & 0x20) rp15 ^= cur; else rp14 ^= cur; + } + /* + we need to adapt the code generation for the fact that rp vars are now + long; also the column parity calculation needs to be changed. + we'll bring rp4 to 15 back to single byte entities by shifting and + xoring + */ + rp4 ^= (rp4 >> 16); rp4 ^= (rp4 >> 8); rp4 &= 0xff; + rp5 ^= (rp5 >> 16); rp5 ^= (rp5 >> 8); rp5 &= 0xff; + rp6 ^= (rp6 >> 16); rp6 ^= (rp6 >> 8); rp6 &= 0xff; + rp7 ^= (rp7 >> 16); rp7 ^= (rp7 >> 8); rp7 &= 0xff; + rp8 ^= (rp8 >> 16); rp8 ^= (rp8 >> 8); rp8 &= 0xff; + rp9 ^= (rp9 >> 16); rp9 ^= (rp9 >> 8); rp9 &= 0xff; + rp10 ^= (rp10 >> 16); rp10 ^= (rp10 >> 8); rp10 &= 0xff; + rp11 ^= (rp11 >> 16); rp11 ^= (rp11 >> 8); rp11 &= 0xff; + rp12 ^= (rp12 >> 16); rp12 ^= (rp12 >> 8); rp12 &= 0xff; + rp13 ^= (rp13 >> 16); rp13 ^= (rp13 >> 8); rp13 &= 0xff; + rp14 ^= (rp14 >> 16); rp14 ^= (rp14 >> 8); rp14 &= 0xff; + rp15 ^= (rp15 >> 16); rp15 ^= (rp15 >> 8); rp15 &= 0xff; + rp3 = (par >> 16); rp3 ^= (rp3 >> 8); rp3 &= 0xff; + rp2 = par & 0xffff; rp2 ^= (rp2 >> 8); rp2 &= 0xff; + par ^= (par >> 16); + rp1 = (par >> 8); rp1 &= 0xff; + rp0 = (par & 0xff); + par ^= (par >> 8); par &= 0xff; + + code[0] = + (parity[rp7] << 7) | + (parity[rp6] << 6) | + (parity[rp5] << 5) | + (parity[rp4] << 4) | + (parity[rp3] << 3) | + (parity[rp2] << 2) | + (parity[rp1] << 1) | + (parity[rp0]); + code[1] = + (parity[rp15] << 7) | + (parity[rp14] << 6) | + (parity[rp13] << 5) | + (parity[rp12] << 4) | + (parity[rp11] << 3) | + (parity[rp10] << 2) | + (parity[rp9] << 1) | + (parity[rp8]); + code[2] = + (parity[par & 0xf0] << 7) | + (parity[par & 0x0f] << 6) | + (parity[par & 0xcc] << 5) | + (parity[par & 0x33] << 4) | + (parity[par & 0xaa] << 3) | + (parity[par & 0x55] << 2); + code[0] = ~code[0]; + code[1] = ~code[1]; + code[2] = ~code[2]; +} + +The parity array is not shown any more. Note also that for these +examples I kinda deviated from my regular programming style by allowing +multiple statements on a line, not using { } in then and else blocks +with only a single statement and by using operators like ^= + + +Analysis 2 +========== + +The code (of course) works, and hurray: we are a little bit faster than +the linux driver code (about 15%). But wait, don't cheer too quickly. +THere is more to be gained. +If we look at e.g. rp14 and rp15 we see that we either xor our data with +rp14 or with rp15. However we also have par which goes over all data. +This means there is no need to calculate rp14 as it can be calculated from +rp15 through rp14 = par ^ rp15; +(or if desired we can avoid calculating rp15 and calculate it from +rp14). That is why some places refer to inverse parity. +Of course the same thing holds for rp4/5, rp6/7, rp8/9, rp10/11 and rp12/13. +Effectively this means we can eliminate the else clause from the if +statements. Also we can optimise the calculation in the end a little bit +by going from long to byte first. Actually we can even avoid the table +lookups + +Attempt 3 +========= + +Odd replaced: + if (i & 0x01) rp5 ^= cur; else rp4 ^= cur; + if (i & 0x02) rp7 ^= cur; else rp6 ^= cur; + if (i & 0x04) rp9 ^= cur; else rp8 ^= cur; + if (i & 0x08) rp11 ^= cur; else rp10 ^= cur; + if (i & 0x10) rp13 ^= cur; else rp12 ^= cur; + if (i & 0x20) rp15 ^= cur; else rp14 ^= cur; +with + if (i & 0x01) rp5 ^= cur; + if (i & 0x02) rp7 ^= cur; + if (i & 0x04) rp9 ^= cur; + if (i & 0x08) rp11 ^= cur; + if (i & 0x10) rp13 ^= cur; + if (i & 0x20) rp15 ^= cur; + + and outside the loop added: + rp4 = par ^ rp5; + rp6 = par ^ rp7; + rp8 = par ^ rp9; + rp10 = par ^ rp11; + rp12 = par ^ rp13; + rp14 = par ^ rp15; + +And after that the code takes about 30% more time, although the number of +statements is reduced. This is also reflected in the assembly code. + + +Analysis 3 +========== + +Very weird. Guess it has to do with caching or instruction parallellism +or so. I also tried on an eeePC (Celeron, clocked at 900 Mhz). Interesting +observation was that this one is only 30% slower (according to time) +executing the code as my 3Ghz D920 processor. + +Well, it was expected not to be easy so maybe instead move to a +different track: let's move back to the code from attempt2 and do some +loop unrolling. This will eliminate a few if statements. I'll try +different amounts of unrolling to see what works best. + + +Attempt 4 +========= + +Unrolled the loop 1, 2, 3 and 4 times. +For 4 the code starts with: + + for (i = 0; i < 4; i++) + { + cur = *bp++; + par ^= cur; + rp4 ^= cur; + rp6 ^= cur; + rp8 ^= cur; + rp10 ^= cur; + if (i & 0x1) rp13 ^= cur; else rp12 ^= cur; + if (i & 0x2) rp15 ^= cur; else rp14 ^= cur; + cur = *bp++; + par ^= cur; + rp5 ^= cur; + rp6 ^= cur; + ... + + +Analysis 4 +========== + +Unrolling once gains about 15% +Unrolling twice keeps the gain at about 15% +Unrolling three times gives a gain of 30% compared to attempt 2. +Unrolling four times gives a marginal improvement compared to unrolling +three times. + +I decided to proceed with a four time unrolled loop anyway. It was my gut +feeling that in the next steps I would obtain additional gain from it. + +The next step was triggered by the fact that par contains the xor of all +bytes and rp4 and rp5 each contain the xor of half of the bytes. +So in effect par = rp4 ^ rp5. But as xor is commutative we can also say +that rp5 = par ^ rp4. So no need to keep both rp4 and rp5 around. We can +eliminate rp5 (or rp4, but I already foresaw another optimisation). +The same holds for rp6/7, rp8/9, rp10/11 rp12/13 and rp14/15. + + +Attempt 5 +========= + +Effectively so all odd digit rp assignments in the loop were removed. +This included the else clause of the if statements. +Of course after the loop we need to correct things by adding code like: + rp5 = par ^ rp4; +Also the initial assignments (rp5 = 0; etc) could be removed. +Along the line I also removed the initialisation of rp0/1/2/3. + + +Analysis 5 +========== + +Measurements showed this was a good move. The run-time roughly halved +compared with attempt 4 with 4 times unrolled, and we only require 1/3rd +of the processor time compared to the current code in the linux kernel. + +However, still I thought there was more. I didn't like all the if +statements. Why not keep a running parity and only keep the last if +statement. Time for yet another version! + + +Attempt 6 +========= + +THe code within the for loop was changed to: + + for (i = 0; i < 4; i++) + { + cur = *bp++; tmppar = cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= tmppar; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp8 ^= tmppar; + + cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp10 ^= tmppar; + + cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur; rp8 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; rp8 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp8 ^= cur; + cur = *bp++; tmppar ^= cur; rp8 ^= cur; + + cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) rp12 ^= tmppar; + if ((i & 0x2) == 0) rp14 ^= tmppar; + } + +As you can see tmppar is used to accumulate the parity within a for +iteration. In the last 3 statements is is added to par and, if needed, +to rp12 and rp14. + +While making the changes I also found that I could exploit that tmppar +contains the running parity for this iteration. So instead of having: +rp4 ^= cur; rp6 = cur; +I removed the rp6 = cur; statement and did rp6 ^= tmppar; on next +statement. A similar change was done for rp8 and rp10 + + +Analysis 6 +========== + +Measuring this code again showed big gain. When executing the original +linux code 1 million times, this took about 1 second on my system. +(using time to measure the performance). After this iteration I was back +to 0.075 sec. Actually I had to decide to start measuring over 10 +million interations in order not to loose too much accuracy. This one +definitely seemed to be the jackpot! + +There is a little bit more room for improvement though. There are three +places with statements: +rp4 ^= cur; rp6 ^= cur; +It seems more efficient to also maintain a variable rp4_6 in the while +loop; This eliminates 3 statements per loop. Of course after the loop we +need to correct by adding: + rp4 ^= rp4_6; + rp6 ^= rp4_6 +Furthermore there are 4 sequential assingments to rp8. This can be +encoded slightly more efficient by saving tmppar before those 4 lines +and later do rp8 = rp8 ^ tmppar ^ notrp8; +(where notrp8 is the value of rp8 before those 4 lines). +Again a use of the commutative property of xor. +Time for a new test! + + +Attempt 7 +========= + +The new code now looks like: + + for (i = 0; i < 4; i++) + { + cur = *bp++; tmppar = cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= tmppar; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp8 ^= tmppar; + + cur = *bp++; tmppar ^= cur; rp4_6 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; rp10 ^= tmppar; + + notrp8 = tmppar; + cur = *bp++; tmppar ^= cur; rp4_6 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; + rp8 = rp8 ^ tmppar ^ notrp8; + + cur = *bp++; tmppar ^= cur; rp4_6 ^= cur; + cur = *bp++; tmppar ^= cur; rp6 ^= cur; + cur = *bp++; tmppar ^= cur; rp4 ^= cur; + cur = *bp++; tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) rp12 ^= tmppar; + if ((i & 0x2) == 0) rp14 ^= tmppar; + } + rp4 ^= rp4_6; + rp6 ^= rp4_6; + + +Not a big change, but every penny counts :-) + + +Analysis 7 +========== + +Acutally this made things worse. Not very much, but I don't want to move +into the wrong direction. Maybe something to investigate later. Could +have to do with caching again. + +Guess that is what there is to win within the loop. Maybe unrolling one +more time will help. I'll keep the optimisations from 7 for now. + + +Attempt 8 +========= + +Unrolled the loop one more time. + + +Analysis 8 +========== + +This makes things worse. Let's stick with attempt 6 and continue from there. +Although it seems that the code within the loop cannot be optimised +further there is still room to optimize the generation of the ecc codes. +We can simply calcualate the total parity. If this is 0 then rp4 = rp5 +etc. If the parity is 1, then rp4 = !rp5; +But if rp4 = rp5 we do not need rp5 etc. We can just write the even bits +in the result byte and then do something like + code[0] |= (code[0] << 1); +Lets test this. + + +Attempt 9 +========= + +Changed the code but again this slightly degrades performance. Tried all +kind of other things, like having dedicated parity arrays to avoid the +shift after parity[rp7] << 7; No gain. +Change the lookup using the parity array by using shift operators (e.g. +replace parity[rp7] << 7 with: +rp7 ^= (rp7 << 4); +rp7 ^= (rp7 << 2); +rp7 ^= (rp7 << 1); +rp7 &= 0x80; +No gain. + +The only marginal change was inverting the parity bits, so we can remove +the last three invert statements. + +Ah well, pity this does not deliver more. Then again 10 million +iterations using the linux driver code takes between 13 and 13.5 +seconds, whereas my code now takes about 0.73 seconds for those 10 +million iterations. So basically I've improved the performance by a +factor 18 on my system. Not that bad. Of course on different hardware +you will get different results. No warranties! + +But of course there is no such thing as a free lunch. The codesize almost +tripled (from 562 bytes to 1434 bytes). Then again, it is not that much. + + +Correcting errors +================= + +For correcting errors I again used the ST application note as a starter, +but I also peeked at the existing code. +The algorithm itself is pretty straightforward. Just xor the given and +the calculated ecc. If all bytes are 0 there is no problem. If 11 bits +are 1 we have one correctable bit error. If there is 1 bit 1, we have an +error in the given ecc code. +It proved to be fastest to do some table lookups. Performance gain +introduced by this is about a factor 2 on my system when a repair had to +be done, and 1% or so if no repair had to be done. +Code size increased from 330 bytes to 686 bytes for this function. +(gcc 4.2, -O3) + + +Conclusion +========== + +The gain when calculating the ecc is tremendous. Om my development hardware +a speedup of a factor of 18 for ecc calculation was achieved. On a test on an +embedded system with a MIPS core a factor 7 was obtained. +On a test with a Linksys NSLU2 (ARMv5TE processor) the speedup was a factor +5 (big endian mode, gcc 4.1.2, -O3) +For correction not much gain could be obtained (as bitflips are rare). Then +again there are also much less cycles spent there. + +It seems there is not much more gain possible in this, at least when +programmed in C. Of course it might be possible to squeeze something more +out of it with an assembler program, but due to pipeline behaviour etc +this is very tricky (at least for intel hw). + +Author: Frans Meulenbroeks +Copyright (C) 2008 Koninklijke Philips Electronics NV. -- cgit v1.2.3-70-g09d2 From 6902aa84f565153ce05f3438ecb8e445d4f468d8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 17:14:42 +0900 Subject: doc: Add remaining SH parameters to kernel-parameters.txt. Signed-off-by: Paul Mundt --- Documentation/kernel-parameters.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1150444a21a..abeff96e9d4 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -796,6 +796,8 @@ and is between 256 and 4096 characters. It is defined in the file Defaults to the default architecture's huge page size if not specified. + hlt [BUGS=ARM,SH] + i8042.direct [HW] Put keyboard port into non-translated mode i8042.dumbkbd [HW] Pretend that controller can only read data from keyboard and cannot control its state @@ -1206,6 +1208,10 @@ and is between 256 and 4096 characters. It is defined in the file mem=nopentium [BUGS=X86-32] Disable usage of 4MB pages for kernel memory. + memchunk=nn[KMG] + [KNL,SH] Allow user to override the default size for + per-device physically contiguous DMA buffers. + memmap=exactmap [KNL,X86-32,X86_64] Enable setting of an exact E820 memory map, as specified by the user. Such memmap=exactmap lines can be constructed based on @@ -1365,6 +1371,8 @@ and is between 256 and 4096 characters. It is defined in the file nodisconnect [HW,SCSI,M68K] Disables SCSI disconnects. + nodsp [SH] Disable hardware DSP at boot time. + noefi [X86-32,X86-64] Disable EFI runtime services support. noexec [IA-64] @@ -1381,13 +1389,15 @@ and is between 256 and 4096 characters. It is defined in the file noexec32=off: disable non-executable mappings read implies executable mappings + nofpu [SH] Disable hardware FPU at boot time. + nofxsr [BUGS=X86-32] Disables x86 floating point extended register save and restore. The kernel will only save legacy floating-point registers on task switch. noclflush [BUGS=X86] Don't use the CLFLUSH instruction - nohlt [BUGS=ARM] + nohlt [BUGS=ARM,SH] no-hlt [BUGS=X86-32] Tells the kernel that the hlt instruction doesn't work correctly and not to -- cgit v1.2.3-70-g09d2 From 4793e7c5e1c88382ead18db5ca072bac54467318 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 2 Sep 2008 16:29:46 +0300 Subject: UBIFS: add bulk-read facility Some flash media are capable of reading sequentially at faster rates. UBIFS bulk-read facility is designed to take advantage of that, by reading in one go consecutive data nodes that are also located consecutively in the same LEB. Read speed on Arm platform with OneNAND goes from 17 MiB/s to 19 MiB/s. Signed-off-by: Adrian Hunter --- Documentation/filesystems/ubifs.txt | 3 + fs/ubifs/file.c | 248 +++++++++++++++++++++++++++++++ fs/ubifs/key.h | 22 ++- fs/ubifs/super.c | 31 ++++ fs/ubifs/tnc.c | 283 ++++++++++++++++++++++++++++++++++++ fs/ubifs/ubifs.h | 45 +++++- 6 files changed, 629 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ubifs.txt b/Documentation/filesystems/ubifs.txt index 6a0d70a22f0..340512c3250 100644 --- a/Documentation/filesystems/ubifs.txt +++ b/Documentation/filesystems/ubifs.txt @@ -86,6 +86,9 @@ norm_unmount (*) commit on unmount; the journal is committed fast_unmount do not commit on unmount; this option makes unmount faster, but the next mount slower because of the need to replay the journal. +bulk_read read more in one go to take advantage of flash + media that read faster sequentially +no_bulk_read (*) do not bulk-read Quick usage instructions diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 3d698e2022b..cdcfe95cbfb 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -577,8 +577,256 @@ out: return copied; } +/** + * populate_page - copy data nodes into a page for bulk-read. + * @c: UBIFS file-system description object + * @page: page + * @bu: bulk-read information + * @n: next zbranch slot + * + * This function returns %0 on success and a negative error code on failure. + */ +static int populate_page(struct ubifs_info *c, struct page *page, + struct bu_info *bu, int *n) +{ + int i = 0, nn = *n, offs = bu->zbranch[0].offs, hole = 1, read = 0; + struct inode *inode = page->mapping->host; + loff_t i_size = i_size_read(inode); + unsigned int page_block; + void *addr, *zaddr; + pgoff_t end_index; + + dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx", + inode->i_ino, page->index, i_size, page->flags); + + addr = zaddr = kmap(page); + + end_index = (i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (!i_size || page->index > end_index) { + memset(addr, 0, PAGE_CACHE_SIZE); + goto out_hole; + } + + page_block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT; + while (1) { + int err, len, out_len, dlen; + + if (nn >= bu->cnt || + key_block(c, &bu->zbranch[nn].key) != page_block) + memset(addr, 0, UBIFS_BLOCK_SIZE); + else { + struct ubifs_data_node *dn; + + dn = bu->buf + (bu->zbranch[nn].offs - offs); + + ubifs_assert(dn->ch.sqnum > + ubifs_inode(inode)->creat_sqnum); + + len = le32_to_cpu(dn->size); + if (len <= 0 || len > UBIFS_BLOCK_SIZE) + goto out_err; + + dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; + out_len = UBIFS_BLOCK_SIZE; + err = ubifs_decompress(&dn->data, dlen, addr, &out_len, + le16_to_cpu(dn->compr_type)); + if (err || len != out_len) + goto out_err; + + if (len < UBIFS_BLOCK_SIZE) + memset(addr + len, 0, UBIFS_BLOCK_SIZE - len); + + nn += 1; + hole = 0; + read = (i << UBIFS_BLOCK_SHIFT) + len; + } + if (++i >= UBIFS_BLOCKS_PER_PAGE) + break; + addr += UBIFS_BLOCK_SIZE; + page_block += 1; + } + + if (end_index == page->index) { + int len = i_size & (PAGE_CACHE_SIZE - 1); + + if (len < read) + memset(zaddr + len, 0, read - len); + } + +out_hole: + if (hole) { + SetPageChecked(page); + dbg_gen("hole"); + } + + SetPageUptodate(page); + ClearPageError(page); + flush_dcache_page(page); + kunmap(page); + *n = nn; + return 0; + +out_err: + ClearPageUptodate(page); + SetPageError(page); + flush_dcache_page(page); + kunmap(page); + ubifs_err("bad data node (block %u, inode %lu)", + page_block, inode->i_ino); + return -EINVAL; +} + +/** + * ubifs_do_bulk_read - do bulk-read. + * @c: UBIFS file-system description object + * @page1: first page + * + * This function returns %1 if the bulk-read is done, otherwise %0 is returned. + */ +static int ubifs_do_bulk_read(struct ubifs_info *c, struct page *page1) +{ + pgoff_t offset = page1->index, end_index; + struct address_space *mapping = page1->mapping; + struct inode *inode = mapping->host; + struct ubifs_inode *ui = ubifs_inode(inode); + struct bu_info *bu; + int err, page_idx, page_cnt, ret = 0, n = 0; + loff_t isize; + + bu = kmalloc(sizeof(struct bu_info), GFP_NOFS); + if (!bu) + return 0; + + bu->buf_len = c->bulk_read_buf_size; + bu->buf = kmalloc(bu->buf_len, GFP_NOFS); + if (!bu->buf) + goto out_free; + + data_key_init(c, &bu->key, inode->i_ino, + offset << UBIFS_BLOCKS_PER_PAGE_SHIFT); + + err = ubifs_tnc_get_bu_keys(c, bu); + if (err) + goto out_warn; + + if (bu->eof) { + /* Turn off bulk-read at the end of the file */ + ui->read_in_a_row = 1; + ui->bulk_read = 0; + } + + page_cnt = bu->blk_cnt >> UBIFS_BLOCKS_PER_PAGE_SHIFT; + if (!page_cnt) { + /* + * This happens when there are multiple blocks per page and the + * blocks for the first page we are looking for, are not + * together. If all the pages were like this, bulk-read would + * reduce performance, so we turn it off for a while. + */ + ui->read_in_a_row = 0; + ui->bulk_read = 0; + goto out_free; + } + + if (bu->cnt) { + err = ubifs_tnc_bulk_read(c, bu); + if (err) + goto out_warn; + } + + err = populate_page(c, page1, bu, &n); + if (err) + goto out_warn; + + unlock_page(page1); + ret = 1; + + isize = i_size_read(inode); + if (isize == 0) + goto out_free; + end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); + + for (page_idx = 1; page_idx < page_cnt; page_idx++) { + pgoff_t page_offset = offset + page_idx; + struct page *page; + + if (page_offset > end_index) + break; + page = find_or_create_page(mapping, page_offset, + GFP_NOFS | __GFP_COLD); + if (!page) + break; + if (!PageUptodate(page)) + err = populate_page(c, page, bu, &n); + unlock_page(page); + page_cache_release(page); + if (err) + break; + } + + ui->last_page_read = offset + page_idx - 1; + +out_free: + kfree(bu->buf); + kfree(bu); + return ret; + +out_warn: + ubifs_warn("ignoring error %d and skipping bulk-read", err); + goto out_free; +} + +/** + * ubifs_bulk_read - determine whether to bulk-read and, if so, do it. + * @page: page from which to start bulk-read. + * + * Some flash media are capable of reading sequentially at faster rates. UBIFS + * bulk-read facility is designed to take advantage of that, by reading in one + * go consecutive data nodes that are also located consecutively in the same + * LEB. This function returns %1 if a bulk-read is done and %0 otherwise. + */ +static int ubifs_bulk_read(struct page *page) +{ + struct inode *inode = page->mapping->host; + struct ubifs_info *c = inode->i_sb->s_fs_info; + struct ubifs_inode *ui = ubifs_inode(inode); + pgoff_t index = page->index, last_page_read = ui->last_page_read; + int ret = 0; + + ui->last_page_read = index; + + if (!c->bulk_read) + return 0; + /* + * Bulk-read is protected by ui_mutex, but it is an optimization, so + * don't bother if we cannot lock the mutex. + */ + if (!mutex_trylock(&ui->ui_mutex)) + return 0; + if (index != last_page_read + 1) { + /* Turn off bulk-read if we stop reading sequentially */ + ui->read_in_a_row = 1; + if (ui->bulk_read) + ui->bulk_read = 0; + goto out_unlock; + } + if (!ui->bulk_read) { + ui->read_in_a_row += 1; + if (ui->read_in_a_row < 3) + goto out_unlock; + /* Three reads in a row, so switch on bulk-read */ + ui->bulk_read = 1; + } + ret = ubifs_do_bulk_read(c, page); +out_unlock: + mutex_unlock(&ui->ui_mutex); + return ret; +} + static int ubifs_readpage(struct file *file, struct page *page) { + if (ubifs_bulk_read(page)) + return 0; do_readpage(page); unlock_page(page); return 0; diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index 8f747600754..9ee65086f62 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h @@ -484,7 +484,7 @@ static inline void key_copy(const struct ubifs_info *c, * @key2: the second key to compare * * This function compares 2 keys and returns %-1 if @key1 is less than - * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. + * @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2. */ static inline int keys_cmp(const struct ubifs_info *c, const union ubifs_key *key1, @@ -502,6 +502,26 @@ static inline int keys_cmp(const struct ubifs_info *c, return 0; } +/** + * keys_eq - determine if keys are equivalent. + * @c: UBIFS file-system description object + * @key1: the first key to compare + * @key2: the second key to compare + * + * This function compares 2 keys and returns %1 if @key1 is equal to @key2 and + * %0 if not. + */ +static inline int keys_eq(const struct ubifs_info *c, + const union ubifs_key *key1, + const union ubifs_key *key2) +{ + if (key1->u32[0] != key2->u32[0]) + return 0; + if (key1->u32[1] != key2->u32[1]) + return 0; + return 1; +} + /** * is_hash_key - is a key vulnerable to hash collisions. * @c: UBIFS file-system description object diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index d87b0cf5f66..b1c57e8ee85 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -401,6 +401,11 @@ static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt) else if (c->mount_opts.unmount_mode == 1) seq_printf(s, ",norm_unmount"); + if (c->mount_opts.bulk_read == 2) + seq_printf(s, ",bulk_read"); + else if (c->mount_opts.bulk_read == 1) + seq_printf(s, ",no_bulk_read"); + return 0; } @@ -538,6 +543,18 @@ static int init_constants_early(struct ubifs_info *c) * calculations when reporting free space. */ c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ; + /* Buffer size for bulk-reads */ + c->bulk_read_buf_size = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ; + if (c->bulk_read_buf_size > c->leb_size) + c->bulk_read_buf_size = c->leb_size; + if (c->bulk_read_buf_size > 128 * 1024) { + /* Check if we can kmalloc more than 128KiB */ + void *try = kmalloc(c->bulk_read_buf_size, GFP_KERNEL); + + kfree(try); + if (!try) + c->bulk_read_buf_size = 128 * 1024; + } return 0; } @@ -840,17 +857,23 @@ static int check_volume_empty(struct ubifs_info *c) * * Opt_fast_unmount: do not run a journal commit before un-mounting * Opt_norm_unmount: run a journal commit before un-mounting + * Opt_bulk_read: enable bulk-reads + * Opt_no_bulk_read: disable bulk-reads * Opt_err: just end of array marker */ enum { Opt_fast_unmount, Opt_norm_unmount, + Opt_bulk_read, + Opt_no_bulk_read, Opt_err, }; static match_table_t tokens = { {Opt_fast_unmount, "fast_unmount"}, {Opt_norm_unmount, "norm_unmount"}, + {Opt_bulk_read, "bulk_read"}, + {Opt_no_bulk_read, "no_bulk_read"}, {Opt_err, NULL}, }; @@ -888,6 +911,14 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, c->mount_opts.unmount_mode = 1; c->fast_unmount = 0; break; + case Opt_bulk_read: + c->mount_opts.bulk_read = 2; + c->bulk_read = 1; + break; + case Opt_no_bulk_read: + c->mount_opts.bulk_read = 1; + c->bulk_read = 0; + break; default: ubifs_err("unrecognized mount option \"%s\" " "or missing value", p); diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index ba13c92fdf6..d279012d8dd 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -1491,6 +1491,289 @@ out: return err; } +/** + * ubifs_tnc_get_bu_keys - lookup keys for bulk-read. + * @c: UBIFS file-system description object + * @bu: bulk-read parameters and results + * + * Lookup consecutive data node keys for the same inode that reside + * consecutively in the same LEB. + */ +int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu) +{ + int n, err = 0, lnum = -1, uninitialized_var(offs); + int uninitialized_var(len); + unsigned int block = key_block(c, &bu->key); + struct ubifs_znode *znode; + + bu->cnt = 0; + bu->blk_cnt = 0; + bu->eof = 0; + + mutex_lock(&c->tnc_mutex); + /* Find first key */ + err = ubifs_lookup_level0(c, &bu->key, &znode, &n); + if (err < 0) + goto out; + if (err) { + /* Key found */ + len = znode->zbranch[n].len; + /* The buffer must be big enough for at least 1 node */ + if (len > bu->buf_len) { + err = -EINVAL; + goto out; + } + /* Add this key */ + bu->zbranch[bu->cnt++] = znode->zbranch[n]; + bu->blk_cnt += 1; + lnum = znode->zbranch[n].lnum; + offs = ALIGN(znode->zbranch[n].offs + len, 8); + } + while (1) { + struct ubifs_zbranch *zbr; + union ubifs_key *key; + unsigned int next_block; + + /* Find next key */ + err = tnc_next(c, &znode, &n); + if (err) + goto out; + zbr = &znode->zbranch[n]; + key = &zbr->key; + /* See if there is another data key for this file */ + if (key_inum(c, key) != key_inum(c, &bu->key) || + key_type(c, key) != UBIFS_DATA_KEY) { + err = -ENOENT; + goto out; + } + if (lnum < 0) { + /* First key found */ + lnum = zbr->lnum; + offs = ALIGN(zbr->offs + zbr->len, 8); + len = zbr->len; + if (len > bu->buf_len) { + err = -EINVAL; + goto out; + } + } else { + /* + * The data nodes must be in consecutive positions in + * the same LEB. + */ + if (zbr->lnum != lnum || zbr->offs != offs) + goto out; + offs += ALIGN(zbr->len, 8); + len = ALIGN(len, 8) + zbr->len; + /* Must not exceed buffer length */ + if (len > bu->buf_len) + goto out; + } + /* Allow for holes */ + next_block = key_block(c, key); + bu->blk_cnt += (next_block - block - 1); + if (bu->blk_cnt >= UBIFS_MAX_BULK_READ) + goto out; + block = next_block; + /* Add this key */ + bu->zbranch[bu->cnt++] = *zbr; + bu->blk_cnt += 1; + /* See if we have room for more */ + if (bu->cnt >= UBIFS_MAX_BULK_READ) + goto out; + if (bu->blk_cnt >= UBIFS_MAX_BULK_READ) + goto out; + } +out: + if (err == -ENOENT) { + bu->eof = 1; + err = 0; + } + bu->gc_seq = c->gc_seq; + mutex_unlock(&c->tnc_mutex); + if (err) + return err; + /* + * An enormous hole could cause bulk-read to encompass too many + * page cache pages, so limit the number here. + */ + if (bu->blk_cnt >= UBIFS_MAX_BULK_READ) + bu->blk_cnt = UBIFS_MAX_BULK_READ; + /* + * Ensure that bulk-read covers a whole number of page cache + * pages. + */ + if (UBIFS_BLOCKS_PER_PAGE == 1 || + !(bu->blk_cnt & (UBIFS_BLOCKS_PER_PAGE - 1))) + return 0; + if (bu->eof) { + /* At the end of file we can round up */ + bu->blk_cnt += UBIFS_BLOCKS_PER_PAGE - 1; + return 0; + } + /* Exclude data nodes that do not make up a whole page cache page */ + block = key_block(c, &bu->key) + bu->blk_cnt; + block &= ~(UBIFS_BLOCKS_PER_PAGE - 1); + while (bu->cnt) { + if (key_block(c, &bu->zbranch[bu->cnt - 1].key) < block) + break; + bu->cnt -= 1; + } + return 0; +} + +/** + * read_wbuf - bulk-read from a LEB with a wbuf. + * @wbuf: wbuf that may overlap the read + * @buf: buffer into which to read + * @len: read length + * @lnum: LEB number from which to read + * @offs: offset from which to read + * + * This functions returns %0 on success or a negative error code on failure. + */ +static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum, + int offs) +{ + const struct ubifs_info *c = wbuf->c; + int rlen, overlap; + + dbg_io("LEB %d:%d, length %d", lnum, offs, len); + ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); + ubifs_assert(!(offs & 7) && offs < c->leb_size); + ubifs_assert(offs + len <= c->leb_size); + + spin_lock(&wbuf->lock); + overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs); + if (!overlap) { + /* We may safely unlock the write-buffer and read the data */ + spin_unlock(&wbuf->lock); + return ubi_read(c->ubi, lnum, buf, offs, len); + } + + /* Don't read under wbuf */ + rlen = wbuf->offs - offs; + if (rlen < 0) + rlen = 0; + + /* Copy the rest from the write-buffer */ + memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen); + spin_unlock(&wbuf->lock); + + if (rlen > 0) + /* Read everything that goes before write-buffer */ + return ubi_read(c->ubi, lnum, buf, offs, rlen); + + return 0; +} + +/** + * validate_data_node - validate data nodes for bulk-read. + * @c: UBIFS file-system description object + * @buf: buffer containing data node to validate + * @zbr: zbranch of data node to validate + * + * This functions returns %0 on success or a negative error code on failure. + */ +static int validate_data_node(struct ubifs_info *c, void *buf, + struct ubifs_zbranch *zbr) +{ + union ubifs_key key1; + struct ubifs_ch *ch = buf; + int err, len; + + if (ch->node_type != UBIFS_DATA_NODE) { + ubifs_err("bad node type (%d but expected %d)", + ch->node_type, UBIFS_DATA_NODE); + goto out_err; + } + + err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0); + if (err) { + ubifs_err("expected node type %d", UBIFS_DATA_NODE); + goto out; + } + + len = le32_to_cpu(ch->len); + if (len != zbr->len) { + ubifs_err("bad node length %d, expected %d", len, zbr->len); + goto out_err; + } + + /* Make sure the key of the read node is correct */ + key_read(c, buf + UBIFS_KEY_OFFSET, &key1); + if (!keys_eq(c, &zbr->key, &key1)) { + ubifs_err("bad key in node at LEB %d:%d", + zbr->lnum, zbr->offs); + dbg_tnc("looked for key %s found node's key %s", + DBGKEY(&zbr->key), DBGKEY1(&key1)); + goto out_err; + } + + return 0; + +out_err: + err = -EINVAL; +out: + ubifs_err("bad node at LEB %d:%d", zbr->lnum, zbr->offs); + dbg_dump_node(c, buf); + dbg_dump_stack(); + return err; +} + +/** + * ubifs_tnc_bulk_read - read a number of data nodes in one go. + * @c: UBIFS file-system description object + * @bu: bulk-read parameters and results + * + * This functions reads and validates the data nodes that were identified by the + * 'ubifs_tnc_get_bu_keys()' function. This functions returns %0 on success, + * -EAGAIN to indicate a race with GC, or another negative error code on + * failure. + */ +int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) +{ + int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i; + struct ubifs_wbuf *wbuf; + void *buf; + + len = bu->zbranch[bu->cnt - 1].offs; + len += bu->zbranch[bu->cnt - 1].len - offs; + if (len > bu->buf_len) { + ubifs_err("buffer too small %d vs %d", bu->buf_len, len); + return -EINVAL; + } + + /* Do the read */ + wbuf = ubifs_get_wbuf(c, lnum); + if (wbuf) + err = read_wbuf(wbuf, bu->buf, len, lnum, offs); + else + err = ubi_read(c->ubi, lnum, bu->buf, offs, len); + + /* Check for a race with GC */ + if (maybe_leb_gced(c, lnum, bu->gc_seq)) + return -EAGAIN; + + if (err && err != -EBADMSG) { + ubifs_err("failed to read from LEB %d:%d, error %d", + lnum, offs, err); + dbg_dump_stack(); + dbg_tnc("key %s", DBGKEY(&bu->key)); + return err; + } + + /* Validate the nodes read */ + buf = bu->buf; + for (i = 0; i < bu->cnt; i++) { + err = validate_data_node(c, buf, &bu->zbranch[i]); + if (err) + return err; + buf = buf + ALIGN(bu->zbranch[i].len, 8); + } + + return 0; +} + /** * do_lookup_nm- look up a "hashed" node. * @c: UBIFS file-system description object diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index ce8654928aa..8513239ea8a 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -142,6 +142,9 @@ /* Maximum expected tree height for use by bottom_up_buf */ #define BOTTOM_UP_HEIGHT 64 +/* Maximum number of data nodes to bulk-read */ +#define UBIFS_MAX_BULK_READ 32 + /* * Lockdep classes for UBIFS inode @ui_mutex. */ @@ -329,8 +332,8 @@ struct ubifs_gced_idx_leb { * @dirty: non-zero if the inode is dirty * @xattr: non-zero if this is an extended attribute inode * @ui_mutex: serializes inode write-back with the rest of VFS operations, - * serializes "clean <-> dirty" state changes, protects @dirty, - * @ui_size, and @xattr_size + * serializes "clean <-> dirty" state changes, serializes bulk-read, + * protects @dirty, @ui_size, and @xattr_size * @ui_lock: protects @synced_i_size * @synced_i_size: synchronized size of inode, i.e. the value of inode size * currently stored on the flash; used only for regular file @@ -338,6 +341,9 @@ struct ubifs_gced_idx_leb { * @ui_size: inode size used by UBIFS when writing to flash * @flags: inode flags (@UBIFS_COMPR_FL, etc) * @compr_type: default compression type used for this inode + * @last_page_read: page number of last page read (for bulk read) + * @read_in_a_row: number of consecutive pages read in a row (for bulk read) + * @bulk_read: indicates whether bulk-read should be used * @data_len: length of the data attached to the inode * @data: inode's data * @@ -385,6 +391,9 @@ struct ubifs_inode { loff_t ui_size; int flags; int compr_type; + pgoff_t last_page_read; + pgoff_t read_in_a_row; + int bulk_read; int data_len; void *data; }; @@ -743,6 +752,28 @@ struct ubifs_znode { struct ubifs_zbranch zbranch[]; }; +/** + * struct bu_info - bulk-read information + * @key: first data node key + * @zbranch: zbranches of data nodes to bulk read + * @buf: buffer to read into + * @buf_len: buffer length + * @gc_seq: GC sequence number to detect races with GC + * @cnt: number of data nodes for bulk read + * @blk_cnt: number of data blocks including holes + * @oef: end of file reached + */ +struct bu_info { + union ubifs_key key; + struct ubifs_zbranch zbranch[UBIFS_MAX_BULK_READ]; + void *buf; + int buf_len; + int gc_seq; + int cnt; + int blk_cnt; + int eof; +}; + /** * struct ubifs_node_range - node length range description data structure. * @len: fixed node length @@ -862,9 +893,11 @@ struct ubifs_orphan { /** * struct ubifs_mount_opts - UBIFS-specific mount options information. * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast) + * @bulk_read: enable bulk-reads */ struct ubifs_mount_opts { unsigned int unmount_mode:2; + unsigned int bulk_read:2; }; /** @@ -965,6 +998,9 @@ struct ubifs_mount_opts { * @old_leb_cnt: count of logical eraseblocks before re-size * @ro_media: the underlying UBI volume is read-only * + * @bulk_read: enable bulk-reads + * @bulk_read_buf_size: buffer size for bulk-reads + * * @dirty_pg_cnt: number of dirty pages (not used) * @dirty_zn_cnt: number of dirty znodes * @clean_zn_cnt: number of clean znodes @@ -1205,6 +1241,9 @@ struct ubifs_info { int old_leb_cnt; int ro_media; + int bulk_read; + int bulk_read_buf_size; + atomic_long_t dirty_pg_cnt; atomic_long_t dirty_zn_cnt; atomic_long_t clean_zn_cnt; @@ -1490,6 +1529,8 @@ void destroy_old_idx(struct ubifs_info *c); int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level, int lnum, int offs); int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode); +int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu); +int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu); /* tnc_misc.c */ struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr, -- cgit v1.2.3-70-g09d2 From 2953e73f1ce4b3284b409aefb9d46bbde6515c37 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 4 Sep 2008 16:26:00 +0300 Subject: UBIFS: add no_chk_data_crc mount option UBIFS read performance can be improved by skipping the CRC check when data nodes are read. This option can be used if the underlying media is considered to be highly reliable. Note that CRCs are always checked for metadata. Read speed on Arm platform with OneNAND goes from 19 MiB/s to 27 MiB/s with data CRC checking disabled. Signed-off-by: Adrian Hunter --- Documentation/filesystems/ubifs.txt | 6 ++++++ fs/ubifs/io.c | 11 ++++++++--- fs/ubifs/scan.c | 2 +- fs/ubifs/super.c | 34 +++++++++++++++++++++++++++++++--- fs/ubifs/tnc.c | 6 +++++- fs/ubifs/ubifs.h | 11 ++++++++++- 6 files changed, 61 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ubifs.txt b/Documentation/filesystems/ubifs.txt index 340512c3250..dd84ea3c10d 100644 --- a/Documentation/filesystems/ubifs.txt +++ b/Documentation/filesystems/ubifs.txt @@ -89,6 +89,12 @@ fast_unmount do not commit on unmount; this option makes bulk_read read more in one go to take advantage of flash media that read faster sequentially no_bulk_read (*) do not bulk-read +no_chk_data_crc skip checking of CRCs on data nodes in order to + improve read performance. Use this option only + if the flash media is highly reliable. The effect + of this option is that corruption of the contents + of a file can go unnoticed. +chk_data_crc (*) do not skip checking CRCs on data nodes Quick usage instructions diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index 054363f2b20..40e2790b62c 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -74,6 +74,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) * @lnum: logical eraseblock number * @offs: offset within the logical eraseblock * @quiet: print no messages + * @chk_crc: indicates whether to always check the CRC * * This function checks node magic number and CRC checksum. This function also * validates node length to prevent UBIFS from becoming crazy when an attacker @@ -85,7 +86,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) * or magic. */ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, - int offs, int quiet) + int offs, int quiet, int chk_crc) { int err = -EINVAL, type, node_len; uint32_t crc, node_crc, magic; @@ -121,6 +122,10 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, node_len > c->ranges[type].max_len) goto out_len; + if (!chk_crc && type == UBIFS_DATA_NODE && !c->always_chk_crc) + if (c->no_chk_data_crc) + return 0; + crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); node_crc = le32_to_cpu(ch->crc); if (crc != node_crc) { @@ -722,7 +727,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, goto out; } - err = ubifs_check_node(c, buf, lnum, offs, 0); + err = ubifs_check_node(c, buf, lnum, offs, 0, 0); if (err) { ubifs_err("expected node type %d", type); return err; @@ -781,7 +786,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, goto out; } - err = ubifs_check_node(c, buf, lnum, offs, 0); + err = ubifs_check_node(c, buf, lnum, offs, 0, 0); if (err) { ubifs_err("expected node type %d", type); return err; diff --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c index acf5c5fffc6..0ed82479b44 100644 --- a/fs/ubifs/scan.c +++ b/fs/ubifs/scan.c @@ -87,7 +87,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, dbg_scan("scanning %s", dbg_ntype(ch->node_type)); - if (ubifs_check_node(c, buf, lnum, offs, quiet)) + if (ubifs_check_node(c, buf, lnum, offs, quiet, 1)) return SCANNED_A_CORRUPT_NODE; if (ch->node_type == UBIFS_PAD_NODE) { diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index b1c57e8ee85..cf078b5cc88 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -406,6 +406,11 @@ static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt) else if (c->mount_opts.bulk_read == 1) seq_printf(s, ",no_bulk_read"); + if (c->mount_opts.chk_data_crc == 2) + seq_printf(s, ",chk_data_crc"); + else if (c->mount_opts.chk_data_crc == 1) + seq_printf(s, ",no_chk_data_crc"); + return 0; } @@ -859,6 +864,8 @@ static int check_volume_empty(struct ubifs_info *c) * Opt_norm_unmount: run a journal commit before un-mounting * Opt_bulk_read: enable bulk-reads * Opt_no_bulk_read: disable bulk-reads + * Opt_chk_data_crc: check CRCs when reading data nodes + * Opt_no_chk_data_crc: do not check CRCs when reading data nodes * Opt_err: just end of array marker */ enum { @@ -866,6 +873,8 @@ enum { Opt_norm_unmount, Opt_bulk_read, Opt_no_bulk_read, + Opt_chk_data_crc, + Opt_no_chk_data_crc, Opt_err, }; @@ -874,6 +883,8 @@ static match_table_t tokens = { {Opt_norm_unmount, "norm_unmount"}, {Opt_bulk_read, "bulk_read"}, {Opt_no_bulk_read, "no_bulk_read"}, + {Opt_chk_data_crc, "chk_data_crc"}, + {Opt_no_chk_data_crc, "no_chk_data_crc"}, {Opt_err, NULL}, }; @@ -919,6 +930,14 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, c->mount_opts.bulk_read = 1; c->bulk_read = 0; break; + case Opt_chk_data_crc: + c->mount_opts.chk_data_crc = 2; + c->no_chk_data_crc = 0; + break; + case Opt_no_chk_data_crc: + c->mount_opts.chk_data_crc = 1; + c->no_chk_data_crc = 1; + break; default: ubifs_err("unrecognized mount option \"%s\" " "or missing value", p); @@ -1027,6 +1046,8 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } + c->always_chk_crc = 1; + err = ubifs_read_superblock(c); if (err) goto out_free; @@ -1168,6 +1189,8 @@ static int mount_ubifs(struct ubifs_info *c) if (err) goto out_infos; + c->always_chk_crc = 0; + ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"", c->vi.ubi_num, c->vi.vol_id, c->vi.name); if (mounted_read_only) @@ -1313,6 +1336,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) mutex_lock(&c->umount_mutex); c->remounting_rw = 1; + c->always_chk_crc = 1; /* Check for enough free space */ if (ubifs_calc_available(c, c->min_idx_lebs) <= 0) { @@ -1381,13 +1405,15 @@ static int ubifs_remount_rw(struct ubifs_info *c) c->bgt = NULL; ubifs_err("cannot spawn \"%s\", error %d", c->bgt_name, err); - return err; + goto out; } wake_up_process(c->bgt); c->orph_buf = vmalloc(c->leb_size); - if (!c->orph_buf) - return -ENOMEM; + if (!c->orph_buf) { + err = -ENOMEM; + goto out; + } /* Check for enough log space */ lnum = c->lhead_lnum + 1; @@ -1414,6 +1440,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) dbg_gen("re-mounted read-write"); c->vfs_sb->s_flags &= ~MS_RDONLY; c->remounting_rw = 0; + c->always_chk_crc = 0; mutex_unlock(&c->umount_mutex); return 0; @@ -1429,6 +1456,7 @@ out: c->ileb_buf = NULL; ubifs_lpt_free(c, 1); c->remounting_rw = 0; + c->always_chk_crc = 0; mutex_unlock(&c->umount_mutex); return err; } diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index d279012d8dd..66dc57100bd 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -470,6 +470,10 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, if (node_len != len) return 0; + if (type == UBIFS_DATA_NODE && !c->always_chk_crc) + if (c->no_chk_data_crc) + return 0; + crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); node_crc = le32_to_cpu(ch->crc); if (crc != node_crc) @@ -1687,7 +1691,7 @@ static int validate_data_node(struct ubifs_info *c, void *buf, goto out_err; } - err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0); + err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0, 0); if (err) { ubifs_err("expected node type %d", UBIFS_DATA_NODE); goto out; diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 8513239ea8a..d6ae3f7b2b0 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -894,10 +894,12 @@ struct ubifs_orphan { * struct ubifs_mount_opts - UBIFS-specific mount options information. * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast) * @bulk_read: enable bulk-reads + * @chk_data_crc: check CRCs when reading data nodes */ struct ubifs_mount_opts { unsigned int unmount_mode:2; unsigned int bulk_read:2; + unsigned int chk_data_crc:2; }; /** @@ -1001,6 +1003,9 @@ struct ubifs_mount_opts { * @bulk_read: enable bulk-reads * @bulk_read_buf_size: buffer size for bulk-reads * + * @no_chk_data_crc: do not check CRCs when reading data nodes (except during + * recovery) + * * @dirty_pg_cnt: number of dirty pages (not used) * @dirty_zn_cnt: number of dirty znodes * @clean_zn_cnt: number of clean znodes @@ -1138,6 +1143,7 @@ struct ubifs_mount_opts { * @rcvrd_mst_node: recovered master node to write when mounting ro to rw * @size_tree: inode size information for recovery * @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY) + * @always_chk_crc: always check CRCs (while mounting and remounting rw) * @mount_opts: UBIFS-specific mount options * * @dbg_buf: a buffer of LEB size used for debugging purposes @@ -1244,6 +1250,8 @@ struct ubifs_info { int bulk_read; int bulk_read_buf_size; + int no_chk_data_crc; + atomic_long_t dirty_pg_cnt; atomic_long_t dirty_zn_cnt; atomic_long_t clean_zn_cnt; @@ -1374,6 +1382,7 @@ struct ubifs_info { struct ubifs_mst_node *rcvrd_mst_node; struct rb_root size_tree; int remounting_rw; + int always_chk_crc; struct ubifs_mount_opts mount_opts; #ifdef CONFIG_UBIFS_FS_DEBUG @@ -1416,7 +1425,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum, int offs, int dtype); int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, - int offs, int quiet); + int offs, int quiet, int chk_crc); void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad); void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last); int ubifs_io_init(struct ubifs_info *c); -- cgit v1.2.3-70-g09d2 From 36d9573928f9ab126d0089ead7ea5d2ab18fbfa9 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 6 Oct 2008 02:51:09 -0400 Subject: Input: document i8042.debug in kernel-parameters.txt i8042.debug parameter was missing in Documentation/kernel-parameters.txt. Add it. Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- Documentation/kernel-parameters.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e7bea3e8530..88600fe5fdc 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -794,6 +794,7 @@ and is between 256 and 4096 characters. It is defined in the file Defaults to the default architecture's huge page size if not specified. + i8042.debug [HW] Toggle i8042 debug mode i8042.direct [HW] Put keyboard port into non-translated mode i8042.dumbkbd [HW] Pretend that controller can only read data from keyboard and cannot control its state -- cgit v1.2.3-70-g09d2 From 061b1bd394ca8628b7c24eb4658ba3535da4249a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Sep 2008 14:46:44 -0700 Subject: Staging: add TAINT_CRAP for all drivers/staging code We need to add a flag for all code that is in the drivers/staging/ directory to prevent all other kernel developers from worrying about issues here, and to notify users that the drivers might not be as good as they are normally used to. Based on code from Andreas Gruenbacher and Jeff Mahoney to provide a TAINT flag for the support level of a kernel module in the Novell enterprise kernel release. This is the kernel portion of this feature, the ability for the flag to be set needs to be done in the build process and will happen in a follow-up patch. Cc: Andreas Gruenbacher Cc: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- Documentation/sysctl/kernel.txt | 1 + include/linux/kernel.h | 1 + kernel/module.c | 11 +++++++++++ kernel/panic.c | 6 ++++-- 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index e1ff0d920a5..bde799e0659 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -369,4 +369,5 @@ can be ORed together: 2 - A module was force loaded by insmod -f. Set by modutils >= 2.4.9 and module-init-tools. 4 - Unsafe SMP processors: SMP with CPUs not designed for SMP. + 64 - A module from drivers/staging was loaded. diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2651f805ba6..b36805cb95f 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -260,6 +260,7 @@ extern enum system_states { #define TAINT_DIE (1<<7) #define TAINT_OVERRIDDEN_ACPI_TABLE (1<<8) #define TAINT_WARN (1<<9) +#define TAINT_CRAP (1<<10) extern void dump_stack(void) __cold; diff --git a/kernel/module.c b/kernel/module.c index 9db11911e04..152b1655bba 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1806,6 +1806,7 @@ static noinline struct module *load_module(void __user *umod, Elf_Ehdr *hdr; Elf_Shdr *sechdrs; char *secstrings, *args, *modmagic, *strtab = NULL; + char *staging; unsigned int i; unsigned int symindex = 0; unsigned int strindex = 0; @@ -1960,6 +1961,14 @@ static noinline struct module *load_module(void __user *umod, goto free_hdr; } + staging = get_modinfo(sechdrs, infoindex, "staging"); + if (staging) { + add_taint_module(mod, TAINT_CRAP); + printk(KERN_WARNING "%s: module is from the staging directory," + " the quality is unknown, you have been warned.\n", + mod->name); + } + /* Now copy in args */ args = strndup_user(uargs, ~0UL >> 1); if (IS_ERR(args)) { @@ -2556,6 +2565,8 @@ static char *module_flags(struct module *mod, char *buf) buf[bx++] = 'P'; if (mod->taints & TAINT_FORCED_MODULE) buf[bx++] = 'F'; + if (mod->taints & TAINT_CRAP) + buf[bx++] = 'C'; /* * TAINT_FORCED_RMMOD: could be added. * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't diff --git a/kernel/panic.c b/kernel/panic.c index 12c5a0a6c89..98e2047f4db 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -155,6 +155,7 @@ EXPORT_SYMBOL(panic); * 'U' - Userspace-defined naughtiness. * 'A' - ACPI table overridden. * 'W' - Taint on warning. + * 'C' - modules from drivers/staging are loaded. * * The string is overwritten by the next call to print_taint(). */ @@ -163,7 +164,7 @@ const char *print_tainted(void) { static char buf[20]; if (tainted) { - snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c%c%c", + snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c%c%c%c", tainted & TAINT_PROPRIETARY_MODULE ? 'P' : 'G', tainted & TAINT_FORCED_MODULE ? 'F' : ' ', tainted & TAINT_UNSAFE_SMP ? 'S' : ' ', @@ -173,7 +174,8 @@ const char *print_tainted(void) tainted & TAINT_USER ? 'U' : ' ', tainted & TAINT_DIE ? 'D' : ' ', tainted & TAINT_OVERRIDDEN_ACPI_TABLE ? 'A' : ' ', - tainted & TAINT_WARN ? 'W' : ' '); + tainted & TAINT_WARN ? 'W' : ' ', + tainted & TAINT_CRAP ? 'C' : ' '); } else snprintf(buf, sizeof(buf), "Not tainted"); -- cgit v1.2.3-70-g09d2 From 24b8d831d56aac7907752d22d2aba5d8127db6f6 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Fri, 18 Jul 2008 12:16:16 -0400 Subject: tracing: tracepoints, documentation Documentation of tracepoint usage. Signed-off-by: Mathieu Desnoyers Acked-by: 'Peter Zijlstra' Signed-off-by: Ingo Molnar --- Documentation/tracepoints.txt | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Documentation/tracepoints.txt (limited to 'Documentation') diff --git a/Documentation/tracepoints.txt b/Documentation/tracepoints.txt new file mode 100644 index 00000000000..5d354e16749 --- /dev/null +++ b/Documentation/tracepoints.txt @@ -0,0 +1,101 @@ + Using the Linux Kernel Tracepoints + + Mathieu Desnoyers + + +This document introduces Linux Kernel Tracepoints and their use. It provides +examples of how to insert tracepoints in the kernel and connect probe functions +to them and provides some examples of probe functions. + + +* Purpose of tracepoints + +A tracepoint placed in code provides a hook to call a function (probe) that you +can provide at runtime. A tracepoint can be "on" (a probe is connected to it) or +"off" (no probe is attached). When a tracepoint is "off" it has no effect, +except for adding a tiny time penalty (checking a condition for a branch) and +space penalty (adding a few bytes for the function call at the end of the +instrumented function and adds a data structure in a separate section). When a +tracepoint is "on", the function you provide is called each time the tracepoint +is executed, in the execution context of the caller. When the function provided +ends its execution, it returns to the caller (continuing from the tracepoint +site). + +You can put tracepoints at important locations in the code. They are +lightweight hooks that can pass an arbitrary number of parameters, +which prototypes are described in a tracepoint declaration placed in a header +file. + +They can be used for tracing and performance accounting. + + +* Usage + +Two elements are required for tracepoints : + +- A tracepoint definition, placed in a header file. +- The tracepoint statement, in C code. + +In order to use tracepoints, you should include linux/tracepoint.h. + +In include/trace/subsys.h : + +#include + +DEFINE_TRACE(subsys_eventname, + TPPTOTO(int firstarg, struct task_struct *p), + TPARGS(firstarg, p)); + +In subsys/file.c (where the tracing statement must be added) : + +#include + +void somefct(void) +{ + ... + trace_subsys_eventname(arg, task); + ... +} + +Where : +- subsys_eventname is an identifier unique to your event + - subsys is the name of your subsystem. + - eventname is the name of the event to trace. +- TPPTOTO(int firstarg, struct task_struct *p) is the prototype of the function + called by this tracepoint. +- TPARGS(firstarg, p) are the parameters names, same as found in the prototype. + +Connecting a function (probe) to a tracepoint is done by providing a probe +(function to call) for the specific tracepoint through +register_trace_subsys_eventname(). Removing a probe is done through +unregister_trace_subsys_eventname(); it will remove the probe sure there is no +caller left using the probe when it returns. Probe removal is preempt-safe +because preemption is disabled around the probe call. See the "Probe example" +section below for a sample probe module. + +The tracepoint mechanism supports inserting multiple instances of the same +tracepoint, but a single definition must be made of a given tracepoint name over +all the kernel to make sure no type conflict will occur. Name mangling of the +tracepoints is done using the prototypes to make sure typing is correct. +Verification of probe type correctness is done at the registration site by the +compiler. Tracepoints can be put in inline functions, inlined static functions, +and unrolled loops as well as regular functions. + +The naming scheme "subsys_event" is suggested here as a convention intended +to limit collisions. Tracepoint names are global to the kernel: they are +considered as being the same whether they are in the core kernel image or in +modules. + + +* Probe / tracepoint example + +See the example provided in samples/tracepoints/src + +Compile them with your kernel. + +Run, as root : +modprobe tracepoint-example (insmod order is not important) +modprobe tracepoint-probe-example +cat /proc/tracepoint-example (returns an expected error) +rmmod tracepoint-example tracepoint-probe-example +dmesg -- cgit v1.2.3-70-g09d2 From 5bf9a1ee350a10feb94107de32a203d81fbbe706 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 16 Sep 2008 22:06:42 +0300 Subject: ftrace: inject markers via trace_marker file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow a user to inject a marker (TRACE_PRINT entry) into the trace ring buffer. The related file operations are derived from code by Frédéric Weisbecker . Signed-off-by: Pekka Paalanen Acked-by: Steven Rostedt Signed-off-by: Ingo Molnar --- Documentation/tracers/mmiotrace.txt | 5 +-- kernel/trace/trace.c | 76 ++++++++++++++++++++++++++++++++++--- kernel/trace/trace.h | 4 ++ 3 files changed, 77 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/tracers/mmiotrace.txt b/Documentation/tracers/mmiotrace.txt index a4afb560a45..5bbbe209622 100644 --- a/Documentation/tracers/mmiotrace.txt +++ b/Documentation/tracers/mmiotrace.txt @@ -36,7 +36,7 @@ $ mount -t debugfs debugfs /debug $ echo mmiotrace > /debug/tracing/current_tracer $ cat /debug/tracing/trace_pipe > mydump.txt & Start X or whatever. -$ echo "X is up" > /debug/tracing/marker +$ echo "X is up" > /debug/tracing/trace_marker $ echo none > /debug/tracing/current_tracer Check for lost events. @@ -59,9 +59,8 @@ The 'cat' process should stay running (sleeping) in the background. Load the driver you want to trace and use it. Mmiotrace will only catch MMIO accesses to areas that are ioremapped while mmiotrace is active. -[Unimplemented feature:] During tracing you can place comments (markers) into the trace by -$ echo "X is up" > /debug/tracing/marker +$ echo "X is up" > /debug/tracing/trace_marker This makes it easier to see which part of the (huge) trace corresponds to which action. It is recommended to place descriptive markers about what you do. diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7e7154f7700..eee1fd96489 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2879,6 +2879,66 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, return cnt; } +static int tracing_open_mark(struct inode *inode, struct file *filp) +{ + int ret; + + ret = tracing_open_generic(inode, filp); + if (ret) + return ret; + + if (current_trace == &no_tracer) + return -ENODEV; + + return 0; +} + +static int mark_printk(const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = trace_vprintk(0, fmt, args); + va_end(args); + return ret; +} + +static ssize_t +tracing_mark_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *fpos) +{ + char *buf; + char *end; + struct trace_array *tr = &global_trace; + + if (current_trace == &no_tracer || !tr->ctrl || tracing_disabled) + return -EINVAL; + + if (cnt > TRACE_BUF_SIZE) + cnt = TRACE_BUF_SIZE; + + buf = kmalloc(cnt + 1, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, cnt)) { + kfree(buf); + return -EFAULT; + } + + /* Cut from the first nil or newline. */ + buf[cnt] = '\0'; + end = strchr(buf, '\n'); + if (end) + *end = '\0'; + + cnt = mark_printk("%s\n", buf); + kfree(buf); + *fpos += cnt; + + return cnt; +} + static struct file_operations tracing_max_lat_fops = { .open = tracing_open_generic, .read = tracing_max_lat_read, @@ -2910,6 +2970,11 @@ static struct file_operations tracing_entries_fops = { .write = tracing_entries_write, }; +static struct file_operations tracing_mark_fops = { + .open = tracing_open_mark, + .write = tracing_mark_write, +}; + #ifdef CONFIG_DYNAMIC_FTRACE static ssize_t @@ -3027,6 +3092,12 @@ static __init void tracer_init_debugfs(void) pr_warning("Could not create debugfs " "'trace_entries' entry\n"); + entry = debugfs_create_file("trace_marker", 0220, d_tracer, + NULL, &tracing_mark_fops); + if (!entry) + pr_warning("Could not create debugfs " + "'trace_marker' entry\n"); + #ifdef CONFIG_DYNAMIC_FTRACE entry = debugfs_create_file("dyn_ftrace_total_info", 0444, d_tracer, &ftrace_update_tot_cnt, @@ -3040,11 +3111,6 @@ static __init void tracer_init_debugfs(void) #endif } -#define TRACE_BUF_SIZE 1024 -#define TRACE_PRINT_BUF_SIZE \ - (sizeof(struct trace_field) - offsetof(struct trace_field, print.buf)) -#define TRACE_CONT_BUF_SIZE sizeof(struct trace_field) - int trace_vprintk(unsigned long ip, const char *fmt, va_list args) { static DEFINE_SPINLOCK(trace_buf_lock); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 648433d18cc..42f65d0097f 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -124,6 +124,10 @@ struct trace_entry { }; #define TRACE_ENTRY_SIZE sizeof(struct trace_entry) +#define TRACE_BUF_SIZE 1024 +#define TRACE_PRINT_BUF_SIZE \ + (sizeof(struct trace_field) - offsetof(struct trace_field, print.buf)) +#define TRACE_CONT_BUF_SIZE sizeof(struct trace_field) /* * The CPU trace array - it consists of thousands of trace entries -- cgit v1.2.3-70-g09d2 From 91a8d46c47e7eb1c53c181e4328a3cfa45ae4ad3 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 29 Sep 2008 11:10:34 -0400 Subject: markers: documentation fix for teardown Document the need for a marker_synchronize_unregister() before the end of exit() to make sure every probe callers have exited the non preemptible section and thus are not executing the probe code anymore. Signed-off-by: Mathieu Desnoyers Signed-off-by: Ingo Molnar --- Documentation/markers.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/markers.txt b/Documentation/markers.txt index d9f50a19fa0..089f6138fcd 100644 --- a/Documentation/markers.txt +++ b/Documentation/markers.txt @@ -50,10 +50,12 @@ Connecting a function (probe) to a marker is done by providing a probe (function to call) for the specific marker through marker_probe_register() and can be activated by calling marker_arm(). Marker deactivation can be done by calling marker_disarm() as many times as marker_arm() has been called. Removing a probe -is done through marker_probe_unregister(); it will disarm the probe and make -sure there is no caller left using the probe when it returns. Probe removal is -preempt-safe because preemption is disabled around the probe call. See the -"Probe example" section below for a sample probe module. +is done through marker_probe_unregister(); it will disarm the probe. +marker_synchronize_unregister() must be called before the end of the module exit +function to make sure there is no caller left using the probe. This, and the +fact that preemption is disabled around the probe call, make sure that probe +removal and module unload are safe. See the "Probe example" section below for a +sample probe module. The marker mechanism supports inserting multiple instances of the same marker. Markers can be put in inline functions, inlined static functions, and -- cgit v1.2.3-70-g09d2 From 3497b2f274b62292df67b6321d8947e24fce94a9 Mon Sep 17 00:00:00 2001 From: Randy Macleod Date: Tue, 14 Oct 2008 13:49:38 -0700 Subject: Phonet: Simple doc fix. From: "Randy Macleod" Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index 0e6e592f4f5..6a07e45d4a9 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -146,8 +146,8 @@ WARNING: When polling a connected pipe socket for writability, there is an intrinsic race condition whereby writability might be lost between the polling and the writing system calls. In this case, the socket will -block until write because possible again, unless non-blocking mode -becomes enabled. +block until write becomes possible again, unless non-blocking mode +is enabled. The pipe protocol provides two socket options at the SOL_PNPIPE level: -- cgit v1.2.3-70-g09d2 From 346e15beb5343c2eb8216d820f2ed8f150822b08 Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Tue, 12 Aug 2008 16:46:19 -0400 Subject: driver core: basic infrastructure for per-module dynamic debug messages Base infrastructure to enable per-module debug messages. I've introduced CONFIG_DYNAMIC_PRINTK_DEBUG, which when enabled centralizes control of debugging statements on a per-module basis in one /proc file, currently, /dynamic_printk/modules. When, CONFIG_DYNAMIC_PRINTK_DEBUG, is not set, debugging statements can still be enabled as before, often by defining 'DEBUG' for the proper compilation unit. Thus, this patch set has no affect when CONFIG_DYNAMIC_PRINTK_DEBUG is not set. The infrastructure currently ties into all pr_debug() and dev_dbg() calls. That is, if CONFIG_DYNAMIC_PRINTK_DEBUG is set, all pr_debug() and dev_dbg() calls can be dynamically enabled/disabled on a per-module basis. Future plans include extending this functionality to subsystems, that define their own debug levels and flags. Usage: Dynamic debugging is controlled by the debugfs file, /dynamic_printk/modules. This file contains a list of the modules that can be enabled. The format of the file is as follows: . . . : Name of the module in which the debug call resides : whether the messages are enabled or not For example: snd_hda_intel enabled=0 fixup enabled=1 driver enabled=0 Enable a module: $echo "set enabled=1 " > dynamic_printk/modules Disable a module: $echo "set enabled=0 " > dynamic_printk/modules Enable all modules: $echo "set enabled=1 all" > dynamic_printk/modules Disable all modules: $echo "set enabled=0 all" > dynamic_printk/modules Finally, passing "dynamic_printk" at the command line enables debugging for all modules. This mode can be turned off via the above disable command. [gkh: minor cleanups and tweaks to make the build work quietly] Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 5 + include/asm-generic/vmlinux.lds.h | 10 +- include/linux/device.h | 6 +- include/linux/dynamic_printk.h | 93 ++++++++ include/linux/kernel.h | 7 +- include/linux/module.h | 1 - kernel/module.c | 31 +++ lib/Kconfig.debug | 55 +++++ lib/Makefile | 2 + lib/dynamic_printk.c | 418 ++++++++++++++++++++++++++++++++++++ net/netfilter/nf_conntrack_pptp.c | 2 +- scripts/Makefile.lib | 11 +- scripts/basic/Makefile | 2 +- scripts/basic/hash.c | 64 ++++++ 14 files changed, 700 insertions(+), 7 deletions(-) create mode 100644 include/linux/dynamic_printk.h create mode 100644 lib/dynamic_printk.c create mode 100644 scripts/basic/hash.c (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2443f5bb436..b429c84ceef 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1713,6 +1713,11 @@ and is between 256 and 4096 characters. It is defined in the file autoconfiguration. Ranges are in pairs (memory base and size). + dynamic_printk + Enables pr_debug()/dev_dbg() calls if + CONFIG_DYNAMIC_PRINTK_DEBUG has been enabled. These can also + be switched on/off via /dynamic_printk/modules + print-fatal-signals= [KNL] debug: print fatal signals print-fatal-signals=1: print segfault info to diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 7440a0dcedd..74c5faf26c0 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -268,7 +268,15 @@ CPU_DISCARD(init.data) \ CPU_DISCARD(init.rodata) \ MEM_DISCARD(init.data) \ - MEM_DISCARD(init.rodata) + MEM_DISCARD(init.rodata) \ + /* implement dynamic printk debug */ \ + VMLINUX_SYMBOL(__start___verbose_strings) = .; \ + *(__verbose_strings) \ + VMLINUX_SYMBOL(__stop___verbose_strings) = .; \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__start___verbose) = .; \ + *(__verbose) \ + VMLINUX_SYMBOL(__stop___verbose) = .; #define INIT_TEXT \ *(.init.text) \ diff --git a/include/linux/device.h b/include/linux/device.h index 60f6456691a..fb034461b39 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -550,7 +550,11 @@ extern const char *dev_driver_string(const struct device *dev); #define dev_info(dev, format, arg...) \ dev_printk(KERN_INFO , dev , format , ## arg) -#ifdef DEBUG +#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#define dev_dbg(dev, format, ...) do { \ + dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ + } while (0) +#elif defined(DEBUG) #define dev_dbg(dev, format, arg...) \ dev_printk(KERN_DEBUG , dev , format , ## arg) #else diff --git a/include/linux/dynamic_printk.h b/include/linux/dynamic_printk.h new file mode 100644 index 00000000000..2d528d00907 --- /dev/null +++ b/include/linux/dynamic_printk.h @@ -0,0 +1,93 @@ +#ifndef _DYNAMIC_PRINTK_H +#define _DYNAMIC_PRINTK_H + +#define DYNAMIC_DEBUG_HASH_BITS 6 +#define DEBUG_HASH_TABLE_SIZE (1 << DYNAMIC_DEBUG_HASH_BITS) + +#define TYPE_BOOLEAN 1 + +#define DYNAMIC_ENABLED_ALL 0 +#define DYNAMIC_ENABLED_NONE 1 +#define DYNAMIC_ENABLED_SOME 2 + +extern int dynamic_enabled; + +/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which + * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They + * use independent hash functions, to reduce the chance of false positives. + */ +extern long long dynamic_printk_enabled; +extern long long dynamic_printk_enabled2; + +struct mod_debug { + char *modname; + char *logical_modname; + char *flag_names; + int type; + int hash; + int hash2; +} __attribute__((aligned(8))); + +int register_dynamic_debug_module(char *mod_name, int type, char *share_name, + char *flags, int hash, int hash2); + +#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +extern int unregister_dynamic_debug_module(char *mod_name); +extern int __dynamic_dbg_enabled_helper(char *modname, int type, + int value, int hash); + +#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ \ + int __ret = 0; \ + if (unlikely((dynamic_printk_enabled & (1LL << DEBUG_HASH)) && \ + (dynamic_printk_enabled2 & (1LL << DEBUG_HASH2)))) \ + __ret = __dynamic_dbg_enabled_helper(module, type, \ + value, hash);\ + __ret; }) + +#define dynamic_pr_debug(fmt, ...) do { \ + static char mod_name[] \ + __attribute__((section("__verbose_strings"))) \ + = KBUILD_MODNAME; \ + static struct mod_debug descriptor \ + __used \ + __attribute__((section("__verbose"), aligned(8))) = \ + { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\ + if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \ + 0, 0, DEBUG_HASH)) \ + printk(KERN_DEBUG KBUILD_MODNAME ":" fmt, \ + ##__VA_ARGS__); \ + } while (0) + +#define dynamic_dev_dbg(dev, format, ...) do { \ + static char mod_name[] \ + __attribute__((section("__verbose_strings"))) \ + = KBUILD_MODNAME; \ + static struct mod_debug descriptor \ + __used \ + __attribute__((section("__verbose"), aligned(8))) = \ + { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\ + if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \ + 0, 0, DEBUG_HASH)) \ + dev_printk(KERN_DEBUG, dev, \ + KBUILD_MODNAME ": " format, \ + ##__VA_ARGS__); \ + } while (0) + +#else + +static inline int unregister_dynamic_debug_module(const char *mod_name) +{ + return 0; +} +static inline int __dynamic_dbg_enabled_helper(char *modname, int type, + int value, int hash) +{ + return 0; +} + +#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ 0; }) +#define dynamic_pr_debug(fmt, ...) do { } while (0) +#define dynamic_dev_dbg(dev, format, ...) do { } while (0) +#endif + +#endif diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 75d81f157d2..ededb6e83b4 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -303,8 +304,12 @@ static inline char *pack_hex_byte(char *buf, u8 byte) #define pr_info(fmt, arg...) \ printk(KERN_INFO fmt, ##arg) -#ifdef DEBUG /* If you are writing a driver, please use dev_dbg instead */ +#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#define pr_debug(fmt, ...) do { \ + dynamic_pr_debug(fmt, ##__VA_ARGS__); \ + } while (0) +#elif defined(DEBUG) #define pr_debug(fmt, arg...) \ printk(KERN_DEBUG fmt, ##arg) #else diff --git a/include/linux/module.h b/include/linux/module.h index 68e09557c95..a41555cbe00 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -345,7 +345,6 @@ struct module /* Reference counts */ struct module_ref ref[NR_CPUS]; #endif - }; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} diff --git a/kernel/module.c b/kernel/module.c index d5fcd24e5af..c5270066729 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -784,6 +784,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags) mutex_lock(&module_mutex); /* Store the name of the last unloaded module for diagnostic purposes */ strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module)); + unregister_dynamic_debug_module(mod->name); free_module(mod); out: @@ -1783,6 +1784,33 @@ static inline void add_kallsyms(struct module *mod, } #endif /* CONFIG_KALLSYMS */ +#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG +static void dynamic_printk_setup(Elf_Shdr *sechdrs, unsigned int verboseindex) +{ + struct mod_debug *debug_info; + unsigned long pos, end; + unsigned int num_verbose; + + pos = sechdrs[verboseindex].sh_addr; + num_verbose = sechdrs[verboseindex].sh_size / + sizeof(struct mod_debug); + end = pos + (num_verbose * sizeof(struct mod_debug)); + + for (; pos < end; pos += sizeof(struct mod_debug)) { + debug_info = (struct mod_debug *)pos; + register_dynamic_debug_module(debug_info->modname, + debug_info->type, debug_info->logical_modname, + debug_info->flag_names, debug_info->hash, + debug_info->hash2); + } +} +#else +static inline void dynamic_printk_setup(Elf_Shdr *sechdrs, + unsigned int verboseindex) +{ +} +#endif /* CONFIG_DYNAMIC_PRINTK_DEBUG */ + static void *module_alloc_update_bounds(unsigned long size) { void *ret = module_alloc(size); @@ -1831,6 +1859,7 @@ static noinline struct module *load_module(void __user *umod, #endif unsigned int markersindex; unsigned int markersstringsindex; + unsigned int verboseindex; struct module *mod; long err = 0; void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ @@ -2117,6 +2146,7 @@ static noinline struct module *load_module(void __user *umod, markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); markersstringsindex = find_sec(hdr, sechdrs, secstrings, "__markers_strings"); + verboseindex = find_sec(hdr, sechdrs, secstrings, "__verbose"); /* Now do relocations. */ for (i = 1; i < hdr->e_shnum; i++) { @@ -2167,6 +2197,7 @@ static noinline struct module *load_module(void __user *umod, marker_update_probe_range(mod->markers, mod->markers + mod->num_markers); #endif + dynamic_printk_setup(sechdrs, verboseindex); err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index aa81d284844..31d784dd80d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -807,6 +807,61 @@ menuconfig BUILD_DOCSRC Say N if you are unsure. +config DYNAMIC_PRINTK_DEBUG + bool "Enable dynamic printk() call support" + default n + depends on PRINTK + select PRINTK_DEBUG + help + + Compiles debug level messages into the kernel, which would not + otherwise be available at runtime. These messages can then be + enabled/disabled on a per module basis. This mechanism implicitly + enables all pr_debug() and dev_dbg() calls. The impact of this + compile option is a larger kernel text size of about 2%. + + Usage: + + Dynamic debugging is controlled by the debugfs file, + dynamic_printk/modules. This file contains a list of the modules that + can be enabled. The format of the file is the module name, followed + by a set of flags that can be enabled. The first flag is always the + 'enabled' flag. For example: + + + . + . + . + + : Name of the module in which the debug call resides + : whether the messages are enabled or not + + From a live system: + + snd_hda_intel enabled=0 + fixup enabled=0 + driver enabled=0 + + Enable a module: + + $echo "set enabled=1 " > dynamic_printk/modules + + Disable a module: + + $echo "set enabled=0 " > dynamic_printk/modules + + Enable all modules: + + $echo "set enabled=1 all" > dynamic_printk/modules + + Disable all modules: + + $echo "set enabled=0 all" > dynamic_printk/modules + + Finally, passing "dynamic_printk" at the command line enables + debugging for all modules. This mode can be turned off via the above + disable command. + source "samples/Kconfig" source "lib/Kconfig.kgdb" diff --git a/lib/Makefile b/lib/Makefile index 44001af76a7..16feaab057b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -81,6 +81,8 @@ obj-$(CONFIG_HAVE_LMB) += lmb.o obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o +obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/dynamic_printk.c b/lib/dynamic_printk.c new file mode 100644 index 00000000000..d640f87bdc9 --- /dev/null +++ b/lib/dynamic_printk.c @@ -0,0 +1,418 @@ +/* + * lib/dynamic_printk.c + * + * make pr_debug()/dev_dbg() calls runtime configurable based upon their + * their source module. + * + * Copyright (C) 2008 Red Hat, Inc., Jason Baron + */ + +#include +#include +#include +#include +#include +#include + +extern struct mod_debug __start___verbose[]; +extern struct mod_debug __stop___verbose[]; + +struct debug_name { + struct hlist_node hlist; + struct hlist_node hlist2; + int hash1; + int hash2; + char *name; + int enable; + int type; +}; + +static int nr_entries; +static int num_enabled; +int dynamic_enabled = DYNAMIC_ENABLED_NONE; +static struct hlist_head module_table[DEBUG_HASH_TABLE_SIZE] = + { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; +static struct hlist_head module_table2[DEBUG_HASH_TABLE_SIZE] = + { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; +static DECLARE_MUTEX(debug_list_mutex); + +/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which + * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They + * use independent hash functions, to reduce the chance of false positives. + */ +long long dynamic_printk_enabled; +EXPORT_SYMBOL_GPL(dynamic_printk_enabled); +long long dynamic_printk_enabled2; +EXPORT_SYMBOL_GPL(dynamic_printk_enabled2); + +/* returns the debug module pointer. */ +static struct debug_name *find_debug_module(char *module_name) +{ + int i; + struct hlist_head *head; + struct hlist_node *node; + struct debug_name *element; + + element = NULL; + for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { + head = &module_table[i]; + hlist_for_each_entry_rcu(element, node, head, hlist) + if (!strcmp(element->name, module_name)) + return element; + } + return NULL; +} + +/* returns the debug module pointer. */ +static struct debug_name *find_debug_module_hash(char *module_name, int hash) +{ + struct hlist_head *head; + struct hlist_node *node; + struct debug_name *element; + + element = NULL; + head = &module_table[hash]; + hlist_for_each_entry_rcu(element, node, head, hlist) + if (!strcmp(element->name, module_name)) + return element; + return NULL; +} + +/* caller must hold mutex*/ +static int __add_debug_module(char *mod_name, int hash, int hash2) +{ + struct debug_name *new; + char *module_name; + int ret = 0; + + if (find_debug_module(mod_name)) { + ret = -EINVAL; + goto out; + } + module_name = kmalloc(strlen(mod_name) + 1, GFP_KERNEL); + if (!module_name) { + ret = -ENOMEM; + goto out; + } + module_name = strcpy(module_name, mod_name); + module_name[strlen(mod_name)] = '\0'; + new = kzalloc(sizeof(struct debug_name), GFP_KERNEL); + if (!new) { + kfree(module_name); + ret = -ENOMEM; + goto out; + } + INIT_HLIST_NODE(&new->hlist); + INIT_HLIST_NODE(&new->hlist2); + new->name = module_name; + new->hash1 = hash; + new->hash2 = hash2; + hlist_add_head_rcu(&new->hlist, &module_table[hash]); + hlist_add_head_rcu(&new->hlist2, &module_table2[hash2]); + nr_entries++; +out: + return ret; +} + +int unregister_dynamic_debug_module(char *mod_name) +{ + struct debug_name *element; + int ret = 0; + + down(&debug_list_mutex); + element = find_debug_module(mod_name); + if (!element) { + ret = -EINVAL; + goto out; + } + hlist_del_rcu(&element->hlist); + hlist_del_rcu(&element->hlist2); + synchronize_rcu(); + kfree(element->name); + if (element->enable) + num_enabled--; + kfree(element); + nr_entries--; +out: + up(&debug_list_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(unregister_dynamic_debug_module); + +int register_dynamic_debug_module(char *mod_name, int type, char *share_name, + char *flags, int hash, int hash2) +{ + struct debug_name *elem; + int ret = 0; + + down(&debug_list_mutex); + elem = find_debug_module(mod_name); + if (!elem) { + if (__add_debug_module(mod_name, hash, hash2)) + goto out; + elem = find_debug_module(mod_name); + if (dynamic_enabled == DYNAMIC_ENABLED_ALL && + !strcmp(mod_name, share_name)) { + elem->enable = true; + num_enabled++; + } + } + elem->type |= type; +out: + up(&debug_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(register_dynamic_debug_module); + +int __dynamic_dbg_enabled_helper(char *mod_name, int type, int value, int hash) +{ + struct debug_name *elem; + int ret = 0; + + if (dynamic_enabled == DYNAMIC_ENABLED_ALL) + return 1; + rcu_read_lock(); + elem = find_debug_module_hash(mod_name, hash); + if (elem && elem->enable) + ret = 1; + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(__dynamic_dbg_enabled_helper); + +static void set_all(bool enable) +{ + struct debug_name *e; + struct hlist_node *node; + int i; + long long enable_mask; + + for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { + if (module_table[i].first != NULL) { + hlist_for_each_entry(e, node, &module_table[i], hlist) { + e->enable = enable; + } + } + } + if (enable) + enable_mask = ULLONG_MAX; + else + enable_mask = 0; + dynamic_printk_enabled = enable_mask; + dynamic_printk_enabled2 = enable_mask; +} + +static int disabled_hash(int i, bool first_table) +{ + struct debug_name *e; + struct hlist_node *node; + + if (first_table) { + hlist_for_each_entry(e, node, &module_table[i], hlist) { + if (e->enable) + return 0; + } + } else { + hlist_for_each_entry(e, node, &module_table2[i], hlist2) { + if (e->enable) + return 0; + } + } + return 1; +} + +static ssize_t pr_debug_write(struct file *file, const char __user *buf, + size_t length, loff_t *ppos) +{ + char *buffer, *s, *value_str, *setting_str; + int err, value; + struct debug_name *elem = NULL; + int all = 0; + + if (length > PAGE_SIZE || length < 0) + return -EINVAL; + + buffer = (char *)__get_free_page(GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + err = -EFAULT; + if (copy_from_user(buffer, buf, length)) + goto out; + + err = -EINVAL; + if (length < PAGE_SIZE) + buffer[length] = '\0'; + else if (buffer[PAGE_SIZE-1]) + goto out; + + err = -EINVAL; + down(&debug_list_mutex); + + if (strncmp("set", buffer, 3)) + goto out_up; + s = buffer + 3; + setting_str = strsep(&s, "="); + if (s == NULL) + goto out_up; + setting_str = strstrip(setting_str); + value_str = strsep(&s, " "); + if (s == NULL) + goto out_up; + s = strstrip(s); + if (!strncmp(s, "all", 3)) + all = 1; + else + elem = find_debug_module(s); + if (!strncmp(setting_str, "enable", 6)) { + value = !!simple_strtol(value_str, NULL, 10); + if (all) { + if (value) { + set_all(true); + num_enabled = nr_entries; + dynamic_enabled = DYNAMIC_ENABLED_ALL; + } else { + set_all(false); + num_enabled = 0; + dynamic_enabled = DYNAMIC_ENABLED_NONE; + } + err = 0; + } else { + if (elem) { + if (value && (elem->enable == 0)) { + dynamic_printk_enabled |= + (1LL << elem->hash1); + dynamic_printk_enabled2 |= + (1LL << elem->hash2); + elem->enable = 1; + num_enabled++; + dynamic_enabled = DYNAMIC_ENABLED_SOME; + err = 0; + printk(KERN_DEBUG + "debugging enabled for module %s", + elem->name); + } else if (!value && (elem->enable == 1)) { + elem->enable = 0; + num_enabled--; + if (disabled_hash(elem->hash1, true)) + dynamic_printk_enabled &= + ~(1LL << elem->hash1); + if (disabled_hash(elem->hash2, false)) + dynamic_printk_enabled2 &= + ~(1LL << elem->hash2); + if (num_enabled) + dynamic_enabled = + DYNAMIC_ENABLED_SOME; + else + dynamic_enabled = + DYNAMIC_ENABLED_NONE; + err = 0; + printk(KERN_DEBUG + "debugging disabled for module " + "%s", elem->name); + } + } + } + } + if (!err) + err = length; +out_up: + up(&debug_list_mutex); +out: + free_page((unsigned long)buffer); + return err; +} + +static void *pr_debug_seq_start(struct seq_file *f, loff_t *pos) +{ + return (*pos < DEBUG_HASH_TABLE_SIZE) ? pos : NULL; +} + +static void *pr_debug_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + if (*pos >= DEBUG_HASH_TABLE_SIZE) + return NULL; + return pos; +} + +static void pr_debug_seq_stop(struct seq_file *s, void *v) +{ + /* Nothing to do */ +} + +static int pr_debug_seq_show(struct seq_file *s, void *v) +{ + struct hlist_head *head; + struct hlist_node *node; + struct debug_name *elem; + unsigned int i = *(loff_t *) v; + + rcu_read_lock(); + head = &module_table[i]; + hlist_for_each_entry_rcu(elem, node, head, hlist) { + seq_printf(s, "%s enabled=%d", elem->name, elem->enable); + seq_printf(s, "\n"); + } + rcu_read_unlock(); + return 0; +} + +static struct seq_operations pr_debug_seq_ops = { + .start = pr_debug_seq_start, + .next = pr_debug_seq_next, + .stop = pr_debug_seq_stop, + .show = pr_debug_seq_show +}; + +static int pr_debug_open(struct inode *inode, struct file *filp) +{ + return seq_open(filp, &pr_debug_seq_ops); +} + +static const struct file_operations pr_debug_operations = { + .open = pr_debug_open, + .read = seq_read, + .write = pr_debug_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init dynamic_printk_init(void) +{ + struct dentry *dir, *file; + struct mod_debug *iter; + unsigned long value; + + dir = debugfs_create_dir("dynamic_printk", NULL); + if (!dir) + return -ENOMEM; + file = debugfs_create_file("modules", 0644, dir, NULL, + &pr_debug_operations); + if (!file) { + debugfs_remove(dir); + return -ENOMEM; + } + for (value = (unsigned long)__start___verbose; + value < (unsigned long)__stop___verbose; + value += sizeof(struct mod_debug)) { + iter = (struct mod_debug *)value; + register_dynamic_debug_module(iter->modname, + iter->type, + iter->logical_modname, + iter->flag_names, iter->hash, iter->hash2); + } + return 0; +} +module_init(dynamic_printk_init); +/* may want to move this earlier so we can get traces as early as possible */ + +static int __init dynamic_printk_setup(char *str) +{ + if (str) + return -ENOENT; + set_all(true); + return 0; +} +/* Use early_param(), so we can get debug output as early as possible */ +early_param("dynamic_printk", dynamic_printk_setup); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 373e51e91ce..1bc3001d182 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -65,7 +65,7 @@ void struct nf_conntrack_expect *exp) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_PRINTK_DEBUG) /* PptpControlMessageType names */ const char *const pptp_msg_name[] = { "UNKNOWN_MESSAGE", diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index ea48b82a370..b4ca38a2115 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -96,6 +96,14 @@ basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))" modname_flags = $(if $(filter 1,$(words $(modname))),\ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") +#hash values +ifdef CONFIG_DYNAMIC_PRINTK_DEBUG +debug_flags = -D"DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname))"\ + -D"DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname))" +else +debug_flags = +endif + orig_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o) _c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags)) _a_flags = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o) @@ -121,7 +129,8 @@ endif c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ $(__c_flags) $(modkern_cflags) \ - -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) + -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) \ + $(debug_flags) a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ $(__a_flags) $(modkern_aflags) diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile index 4c324a1f1e0..09559951df1 100644 --- a/scripts/basic/Makefile +++ b/scripts/basic/Makefile @@ -9,7 +9,7 @@ # fixdep: Used to generate dependency information during build process # docproc: Used in Documentation/DocBook -hostprogs-y := fixdep docproc +hostprogs-y := fixdep docproc hash always := $(hostprogs-y) # fixdep is needed to compile other host programs diff --git a/scripts/basic/hash.c b/scripts/basic/hash.c new file mode 100644 index 00000000000..3299ad7fc8c --- /dev/null +++ b/scripts/basic/hash.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Red Hat, Inc., Jason Baron + * + */ + +#include +#include +#include + +#define DYNAMIC_DEBUG_HASH_BITS 6 + +static const char *program; + +static void usage(void) +{ + printf("Usage: %s \n", program); + exit(1); +} + +/* djb2 hashing algorithm by Dan Bernstein. From: + * http://www.cse.yorku.ca/~oz/hash.html + */ + +unsigned int djb2_hash(char *str) +{ + unsigned long hash = 5381; + int c; + + c = *str; + while (c) { + hash = ((hash << 5) + hash) + c; + c = *++str; + } + return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1)); +} + +unsigned int r5_hash(char *str) +{ + unsigned long hash = 0; + int c; + + c = *str; + while (c) { + hash = (hash + (c << 4) + (c >> 4)) * 11; + c = *++str; + } + return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1)); +} + +int main(int argc, char *argv[]) +{ + program = argv[0]; + + if (argc != 3) + usage(); + if (!strcmp(argv[1], "djb2")) + printf("%d\n", djb2_hash(argv[2])); + else if (!strcmp(argv[1], "r5")) + printf("%d\n", r5_hash(argv[2])); + else + usage(); + exit(0); +} + -- cgit v1.2.3-70-g09d2 From 030c1d2bfcc2187650fb975456ca0b61a5bb77f4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 8 May 2008 14:41:00 -0700 Subject: kobject: Fix kobject_rename and !CONFIG_SYSFS When looking at kobject_rename I found two bugs with that exist when sysfs support is disabled in the kernel. kobject_rename does not change the name on the kobject when sysfs support is not compiled in. kobject_rename without locking attempts to check the validity of a rename operation, which the kobject layer simply does not have the infrastructure to do. This patch documents the previously unstated requirement of kobject_rename that is the responsibility of the caller to provide mutual exclusion and to be certain that the new_name for the kobject is valid. This patch modifies sysfs_rename_dir in !CONFIG_SYSFS case to call kobject_set_name to actually change the kobject_name. This patch removes the bogus and misleading check in kobject_rename that attempts to see if a rename is valid. The check is bogus because we do not have the proper locking. The check is misleading because it looks like we can and do perform checking at the kobject level that we don't. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- Documentation/kobject.txt | 4 ++++ drivers/base/core.c | 5 +++++ include/linux/sysfs.h | 4 +++- lib/kobject.c | 18 +++++------------- 4 files changed, 17 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt index 51a8021ee53..f5d2aad65a6 100644 --- a/Documentation/kobject.txt +++ b/Documentation/kobject.txt @@ -118,6 +118,10 @@ the name of the kobject, call kobject_rename(): int kobject_rename(struct kobject *kobj, const char *new_name); +Note kobject_rename does perform any locking or have a solid notion of +what names are valid so the provide must provide their own sanity checking +and serialization. + There is a function called kobject_set_name() but that is legacy cruft and is being removed. If your code needs to call this function, it is incorrect and needs to be fixed. diff --git a/drivers/base/core.c b/drivers/base/core.c index 9649d1c422a..8c2cc2648f5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1327,6 +1327,11 @@ EXPORT_SYMBOL_GPL(device_destroy); * device_rename - renames a device * @dev: the pointer to the struct device to be renamed * @new_name: the new name of the device + * + * It is the responsibility of the caller to provide mutual + * exclusion between two different calls of device_rename + * on the same device to ensure that new_name is valid and + * won't conflict with other devices. */ int device_rename(struct device *dev, char *new_name) { diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index b330e289d71..39924a96220 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -20,6 +20,8 @@ struct kobject; struct module; +extern int kobject_set_name(struct kobject *kobj, const char *name, ...) + __attribute__((format(printf, 2, 3))); /* FIXME * The *owner field is no longer used, but leave around * until the tree gets cleaned up fully. @@ -147,7 +149,7 @@ static inline void sysfs_remove_dir(struct kobject *kobj) static inline int sysfs_rename_dir(struct kobject *kobj, const char *new_name) { - return 0; + return kobject_set_name(kobj, "%s", new_name); } static inline int sysfs_move_dir(struct kobject *kobj, diff --git a/lib/kobject.c b/lib/kobject.c index fbf0ae28237..ae6bb900bfb 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -387,6 +387,11 @@ EXPORT_SYMBOL_GPL(kobject_init_and_add); * kobject_rename - change the name of an object * @kobj: object in question. * @new_name: object's new name + * + * It is the responsibility of the caller to provide mutual + * exclusion between two different calls of kobject_rename + * on the same kobject and to ensure that new_name is valid and + * won't conflict with other kobjects. */ int kobject_rename(struct kobject *kobj, const char *new_name) { @@ -401,19 +406,6 @@ int kobject_rename(struct kobject *kobj, const char *new_name) if (!kobj->parent) return -EINVAL; - /* see if this name is already in use */ - if (kobj->kset) { - struct kobject *temp_kobj; - temp_kobj = kset_find_obj(kobj->kset, new_name); - if (temp_kobj) { - printk(KERN_WARNING "kobject '%s' cannot be renamed " - "to '%s' as '%s' is already in existence.\n", - kobject_name(kobj), new_name, new_name); - kobject_put(temp_kobj); - return -EINVAL; - } - } - devpath = kobject_get_path(kobj, GFP_KERNEL); if (!devpath) { error = -ENOMEM; -- cgit v1.2.3-70-g09d2 From d86f4bc4bc34c63c90e5fd46a60c506b234f5708 Mon Sep 17 00:00:00 2001 From: Alberto Bertogli Date: Fri, 26 Sep 2008 23:10:31 -0300 Subject: Documentation/block/data-integrity.txt: Fix section numbers Signed-off-by: Alberto Bertogli Signed-off-by: Jonathan Corbet --- Documentation/block/data-integrity.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/block/data-integrity.txt b/Documentation/block/data-integrity.txt index e9dc8d86adc..e8ca040ba2c 100644 --- a/Documentation/block/data-integrity.txt +++ b/Documentation/block/data-integrity.txt @@ -246,7 +246,7 @@ will require extra work due to the application tag. retrieve the tag buffer using bio_integrity_get_tag(). -6.3 PASSING EXISTING INTEGRITY METADATA +5.3 PASSING EXISTING INTEGRITY METADATA Filesystems that either generate their own integrity metadata or are capable of transferring IMD from user space can use the @@ -283,7 +283,7 @@ will require extra work due to the application tag. integrity upon completion. -6.4 REGISTERING A BLOCK DEVICE AS CAPABLE OF EXCHANGING INTEGRITY +5.4 REGISTERING A BLOCK DEVICE AS CAPABLE OF EXCHANGING INTEGRITY METADATA To enable integrity exchange on a block device the gendisk must be -- cgit v1.2.3-70-g09d2 From 75b021468368288ac8fec1a86a13f5cf2229139e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 30 Sep 2008 15:15:56 -0600 Subject: Add the development process document This is an extended document intended to help interested developers, their managers, and their employers work with the kernel development process. This work was supported by the Linux Foundation. Signed-off-by: Jonathan Corbet --- Documentation/00-INDEX | 3 + Documentation/development-process/1.Intro | 274 ++++++++++++ Documentation/development-process/2.Process | 459 +++++++++++++++++++++ Documentation/development-process/3.Early-stage | 195 +++++++++ Documentation/development-process/4.Coding | 384 +++++++++++++++++ Documentation/development-process/5.Posting | 278 +++++++++++++ Documentation/development-process/6.Followthrough | 202 +++++++++ Documentation/development-process/7.AdvancedTopics | 173 ++++++++ Documentation/development-process/8.Conclusion | 74 ++++ 9 files changed, 2042 insertions(+) create mode 100644 Documentation/development-process/1.Intro create mode 100644 Documentation/development-process/2.Process create mode 100644 Documentation/development-process/3.Early-stage create mode 100644 Documentation/development-process/4.Coding create mode 100644 Documentation/development-process/5.Posting create mode 100644 Documentation/development-process/6.Followthrough create mode 100644 Documentation/development-process/7.AdvancedTopics create mode 100644 Documentation/development-process/8.Conclusion (limited to 'Documentation') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 5b5aba404aa..1f3dbdfc9ae 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -21,6 +21,9 @@ Changes - list of changes that break older software packages. CodingStyle - how the boss likes the C code in the kernel to look. +development-process/ + - An extended tutorial on how to work with the kernel development + process. DMA-API.txt - DMA API, pci_ API & extensions for non-consistent memory machines. DMA-ISA-LPC.txt diff --git a/Documentation/development-process/1.Intro b/Documentation/development-process/1.Intro new file mode 100644 index 00000000000..8cc2cba2b10 --- /dev/null +++ b/Documentation/development-process/1.Intro @@ -0,0 +1,274 @@ +1: A GUIDE TO THE KERNEL DEVELOPMENT PROCESS + +The purpose of this document is to help developers (and their managers) +work with the development community with a minimum of frustration. It is +an attempt to document how this community works in a way which is +accessible to those who are not intimately familiar with Linux kernel +development (or, indeed, free software development in general). While +there is some technical material here, this is very much a process-oriented +discussion which does not require a deep knowledge of kernel programming to +understand. + + +1.1: EXECUTIVE SUMMARY + +The rest of this section covers the scope of the kernel development process +and the kinds of frustrations that developers and their employers can +encounter there. There are a great many reasons why kernel code should be +merged into the official ("mainline") kernel, including automatic +availability to users, community support in many forms, and the ability to +influence the direction of kernel development. Code contributed to the +Linux kernel must be made available under a GPL-compatible license. + +Section 2 introduces the development process, the kernel release cycle, and +the mechanics of the merge window. The various phases in the patch +development, review, and merging cycle are covered. There is some +discussion of tools and mailing lists. Developers wanting to get started +with kernel development are encouraged to track down and fix bugs as an +initial exercise. + +Section 3 covers early-stage project planning, with an emphasis on +involving the development community as soon as possible. + +Section 4 is about the coding process; several pitfalls which have been +encountered by other developers are discussed. Some requirements for +patches are covered, and there is an introduction to some of the tools +which can help to ensure that kernel patches are correct. + +Section 5 talks about the process of posting patches for review. To be +taken seriously by the development community, patches must be properly +formatted and described, and they must be sent to the right place. +Following the advice in this section should help to ensure the best +possible reception for your work. + +Section 6 covers what happens after posting patches; the job is far from +done at that point. Working with reviewers is a crucial part of the +development process; this section offers a number of tips on how to avoid +problems at this important stage. Developers are cautioned against +assuming that the job is done when a patch is merged into the mainline. + +Section 7 introduces a couple of "advanced" topics: managing patches with +git and reviewing patches posted by others. + +Section 8 concludes the document with pointers to sources for more +information on kernel development. + + +1.2: WHAT THIS DOCUMENT IS ABOUT + +The Linux kernel, at over 6 million lines of code and well over 1000 active +contributors, is one of the largest and most active free software projects +in existence. Since its humble beginning in 1991, this kernel has evolved +into a best-of-breed operating system component which runs on pocket-sized +digital music players, desktop PCs, the largest supercomputers in +existence, and all types of systems in between. It is a robust, efficient, +and scalable solution for almost any situation. + +With the growth of Linux has come an increase in the number of developers +(and companies) wishing to participate in its development. Hardware +vendors want to ensure that Linux supports their products well, making +those products attractive to Linux users. Embedded systems vendors, who +use Linux as a component in an integrated product, want Linux to be as +capable and well-suited to the task at hand as possible. Distributors and +other software vendors who base their products on Linux have a clear +interest in the capabilities, performance, and reliability of the Linux +kernel. And end users, too, will often wish to change Linux to make it +better suit their needs. + +One of the most compelling features of Linux is that it is accessible to +these developers; anybody with the requisite skills can improve Linux and +influence the direction of its development. Proprietary products cannot +offer this kind of openness, which is a characteristic of the free software +process. But, if anything, the kernel is even more open than most other +free software projects. A typical three-month kernel development cycle can +involve over 1000 developers working for more than 100 different companies +(or for no company at all). + +Working with the kernel development community is not especially hard. But, +that notwithstanding, many potential contributors have experienced +difficulties when trying to do kernel work. The kernel community has +evolved its own distinct ways of operating which allow it to function +smoothly (and produce a high-quality product) in an environment where +thousands of lines of code are being changed every day. So it is not +surprising that Linux kernel development process differs greatly from +proprietary development methods. + +The kernel's development process may come across as strange and +intimidating to new developers, but there are good reasons and solid +experience behind it. A developer who does not understand the kernel +community's ways (or, worse, who tries to flout or circumvent them) will +have a frustrating experience in store. The development community, while +being helpful to those who are trying to learn, has little time for those +who will not listen or who do not care about the development process. + +It is hoped that those who read this document will be able to avoid that +frustrating experience. There is a lot of material here, but the effort +involved in reading it will be repaid in short order. The development +community is always in need of developers who will help to make the kernel +better; the following text should help you - or those who work for you - +join our community. + + +1.3: CREDITS + +This document was written by Jonathan Corbet, corbet@lwn.net. It has been +improved by comments from Johannes Berg, James Berry, Alex Chiang, Roland +Dreier, Randy Dunlap, Jake Edge, Jiri Kosina, Matt Mackall, Arthur Marsh, +Amanda McPherson, Andrew Morton, Andrew Price, Tsugikazu Shibata, and +Jochen Voß. + +This work was supported by the Linux Foundation; thanks especially to +Amanda McPherson, who saw the value of this effort and made it all happen. + + +1.4: THE IMPORTANCE OF GETTING CODE INTO THE MAINLINE + +Some companies and developers occasionally wonder why they should bother +learning how to work with the kernel community and get their code into the +mainline kernel (the "mainline" being the kernel maintained by Linus +Torvalds and used as a base by Linux distributors). In the short term, +contributing code can look like an avoidable expense; it seems easier to +just keep the code separate and support users directly. The truth of the +matter is that keeping code separate ("out of tree") is a false economy. + +As a way of illustrating the costs of out-of-tree code, here are a few +relevant aspects of the kernel development process; most of these will be +discussed in greater detail later in this document. Consider: + +- Code which has been merged into the mainline kernel is available to all + Linux users. It will automatically be present on all distributions which + enable it. There is no need for driver disks, downloads, or the hassles + of supporting multiple versions of multiple distributions; it all just + works, for the developer and for the user. Incorporation into the + mainline solves a large number of distribution and support problems. + +- While kernel developers strive to maintain a stable interface to user + space, the internal kernel API is in constant flux. The lack of a stable + internal interface is a deliberate design decision; it allows fundamental + improvements to be made at any time and results in higher-quality code. + But one result of that policy is that any out-of-tree code requires + constant upkeep if it is to work with new kernels. Maintaining + out-of-tree code requires significant amounts of work just to keep that + code working. + + Code which is in the mainline, instead, does not require this work as the + result of a simple rule requiring any developer who makes an API change + to also fix any code that breaks as the result of that change. So code + which has been merged into the mainline has significantly lower + maintenance costs. + +- Beyond that, code which is in the kernel will often be improved by other + developers. Surprising results can come from empowering your user + community and customers to improve your product. + +- Kernel code is subjected to review, both before and after merging into + the mainline. No matter how strong the original developer's skills are, + this review process invariably finds ways in which the code can be + improved. Often review finds severe bugs and security problems. This is + especially true for code which has been developed in a closed + environment; such code benefits strongly from review by outside + developers. Out-of-tree code is lower-quality code. + +- Participation in the development process is your way to influence the + direction of kernel development. Users who complain from the sidelines + are heard, but active developers have a stronger voice - and the ability + to implement changes which make the kernel work better for their needs. + +- When code is maintained separately, the possibility that a third party + will contribute a different implementation of a similar feature always + exists. Should that happen, getting your code merged will become much + harder - to the point of impossibility. Then you will be faced with the + unpleasant alternatives of either (1) maintaining a nonstandard feature + out of tree indefinitely, or (2) abandoning your code and migrating your + users over to the in-tree version. + +- Contribution of code is the fundamental action which makes the whole + process work. By contributing your code you can add new functionality to + the kernel and provide capabilities and examples which are of use to + other kernel developers. If you have developed code for Linux (or are + thinking about doing so), you clearly have an interest in the continued + success of this platform; contributing code is one of the best ways to + help ensure that success. + +All of the reasoning above applies to any out-of-tree kernel code, +including code which is distributed in proprietary, binary-only form. +There are, however, additional factors which should be taken into account +before considering any sort of binary-only kernel code distribution. These +include: + +- The legal issues around the distribution of proprietary kernel modules + are cloudy at best; quite a few kernel copyright holders believe that + most binary-only modules are derived products of the kernel and that, as + a result, their distribution is a violation of the GNU General Public + license (about which more will be said below). Your author is not a + lawyer, and nothing in this document can possibly be considered to be + legal advice. The true legal status of closed-source modules can only be + determined by the courts. But the uncertainty which haunts those modules + is there regardless. + +- Binary modules greatly increase the difficulty of debugging kernel + problems, to the point that most kernel developers will not even try. So + the distribution of binary-only modules will make it harder for your + users to get support from the community. + +- Support is also harder for distributors of binary-only modules, who must + provide a version of the module for every distribution and every kernel + version they wish to support. Dozens of builds of a single module can + be required to provide reasonably comprehensive coverage, and your users + will have to upgrade your module separately every time they upgrade their + kernel. + +- Everything that was said above about code review applies doubly to + closed-source code. Since this code is not available at all, it cannot + have been reviewed by the community and will, beyond doubt, have serious + problems. + +Makers of embedded systems, in particular, may be tempted to disregard much +of what has been said in this section in the belief that they are shipping +a self-contained product which uses a frozen kernel version and requires no +more development after its release. This argument misses the value of +widespread code review and the value of allowing your users to add +capabilities to your product. But these products, too, have a limited +commercial life, after which a new version must be released. At that +point, vendors whose code is in the mainline and well maintained will be +much better positioned to get the new product ready for market quickly. + + +1.5: LICENSING + +Code is contributed to the Linux kernel under a number of licenses, but all +code must be compatible with version 2 of the GNU General Public License +(GPLv2), which is the license covering the kernel distribution as a whole. +In practice, that means that all code contributions are covered either by +GPLv2 (with, optionally, language allowing distribution under later +versions of the GPL) or the three-clause BSD license. Any contributions +which are not covered by a compatible license will not be accepted into the +kernel. + +Copyright assignments are not required (or requested) for code contributed +to the kernel. All code merged into the mainline kernel retains its +original ownership; as a result, the kernel now has thousands of owners. + +One implication of this ownership structure is that any attempt to change +the licensing of the kernel is doomed to almost certain failure. There are +few practical scenarios where the agreement of all copyright holders could +be obtained (or their code removed from the kernel). So, in particular, +there is no prospect of a migration to version 3 of the GPL in the +foreseeable future. + +It is imperative that all code contributed to the kernel be legitimately +free software. For that reason, code from anonymous (or pseudonymous) +contributors will not be accepted. All contributors are required to "sign +off" on their code, stating that the code can be distributed with the +kernel under the GPL. Code which has not been licensed as free software by +its owner, or which risks creating copyright-related problems for the +kernel (such as code which derives from reverse-engineering efforts lacking +proper safeguards) cannot be contributed. + +Questions about copyright-related issues are common on Linux development +mailing lists. Such questions will normally receive no shortage of +answers, but one should bear in mind that the people answering those +questions are not lawyers and cannot provide legal advice. If you have +legal questions relating to Linux source code, there is no substitute for +talking with a lawyer who understands this field. Relying on answers +obtained on technical mailing lists is a risky affair. diff --git a/Documentation/development-process/2.Process b/Documentation/development-process/2.Process new file mode 100644 index 00000000000..d750321acd5 --- /dev/null +++ b/Documentation/development-process/2.Process @@ -0,0 +1,459 @@ +2: HOW THE DEVELOPMENT PROCESS WORKS + +Linux kernel development in the early 1990's was a pretty loose affair, +with relatively small numbers of users and developers involved. With a +user base in the millions and with some 2,000 developers involved over the +course of one year, the kernel has since had to evolve a number of +processes to keep development happening smoothly. A solid understanding of +how the process works is required in order to be an effective part of it. + + +2.1: THE BIG PICTURE + +The kernel developers use a loosely time-based release process, with a new +major kernel release happening every two or three months. The recent +release history looks like this: + + 2.6.26 July 13, 2008 + 2.6.25 April 16, 2008 + 2.6.24 January 24, 2008 + 2.6.23 October 9, 2007 + 2.6.22 July 8, 2007 + 2.6.21 April 25, 2007 + 2.6.20 February 4, 2007 + +Every 2.6.x release is a major kernel release with new features, internal +API changes, and more. A typical 2.6 release can contain over 10,000 +changesets with changes to several hundred thousand lines of code. 2.6 is +thus the leading edge of Linux kernel development; the kernel uses a +rolling development model which is continually integrating major changes. + +A relatively straightforward discipline is followed with regard to the +merging of patches for each release. At the beginning of each development +cycle, the "merge window" is said to be open. At that time, code which is +deemed to be sufficiently stable (and which is accepted by the development +community) is merged into the mainline kernel. The bulk of changes for a +new development cycle (and all of the major changes) will be merged during +this time, at a rate approaching 1,000 changes ("patches," or "changesets") +per day. + +(As an aside, it is worth noting that the changes integrated during the +merge window do not come out of thin air; they have been collected, tested, +and staged ahead of time. How that process works will be described in +detail later on). + +The merge window lasts for two weeks. At the end of this time, Linus +Torvalds will declare that the window is closed and release the first of +the "rc" kernels. For the kernel which is destined to be 2.6.26, for +example, the release which happens at the end of the merge window will be +called 2.6.26-rc1. The -rc1 release is the signal that the time to merge +new features has passed, and that the time to stabilize the next kernel has +begun. + +Over the next six to ten weeks, only patches which fix problems should be +submitted to the mainline. On occasion a more significant change will be +allowed, but such occasions are rare; developers who try to merge new +features outside of the merge window tend to get an unfriendly reception. +As a general rule, if you miss the merge window for a given feature, the +best thing to do is to wait for the next development cycle. (An occasional +exception is made for drivers for previously-unsupported hardware; if they +touch no in-tree code, they cannot cause regressions and should be safe to +add at any time). + +As fixes make their way into the mainline, the patch rate will slow over +time. Linus releases new -rc kernels about once a week; a normal series +will get up to somewhere between -rc6 and -rc9 before the kernel is +considered to be sufficiently stable and the final 2.6.x release is made. +At that point the whole process starts over again. + +As an example, here is how the 2.6.25 development cycle went (all dates in +2008): + + January 24 2.6.24 stable release + February 10 2.6.25-rc1, merge window closes + February 15 2.6.25-rc2 + February 24 2.6.25-rc3 + March 4 2.6.25-rc4 + March 9 2.6.25-rc5 + March 16 2.6.25-rc6 + March 25 2.6.25-rc7 + April 1 2.6.25-rc8 + April 11 2.6.25-rc9 + April 16 2.6.25 stable release + +How do the developers decide when to close the development cycle and create +the stable release? The most significant metric used is the list of +regressions from previous releases. No bugs are welcome, but those which +break systems which worked in the past are considered to be especially +serious. For this reason, patches which cause regressions are looked upon +unfavorably and are quite likely to be reverted during the stabilization +period. + +The developers' goal is to fix all known regressions before the stable +release is made. In the real world, this kind of perfection is hard to +achieve; there are just too many variables in a project of this size. +There comes a point where delaying the final release just makes the problem +worse; the pile of changes waiting for the next merge window will grow +larger, creating even more regressions the next time around. So most 2.6.x +kernels go out with a handful of known regressions though, hopefully, none +of them are serious. + +Once a stable release is made, its ongoing maintenance is passed off to the +"stable team," currently comprised of Greg Kroah-Hartman and Chris Wright. +The stable team will release occasional updates to the stable release using +the 2.6.x.y numbering scheme. To be considered for an update release, a +patch must (1) fix a significant bug, and (2) already be merged into the +mainline for the next development kernel. Continuing our 2.6.25 example, +the history (as of this writing) is: + + May 1 2.6.25.1 + May 6 2.6.25.2 + May 9 2.6.25.3 + May 15 2.6.25.4 + June 7 2.6.25.5 + June 9 2.6.25.6 + June 16 2.6.25.7 + June 21 2.6.25.8 + June 24 2.6.25.9 + +Stable updates for a given kernel are made for approximately six months; +after that, the maintenance of stable releases is solely the responsibility +of the distributors which have shipped that particular kernel. + + +2.2: THE LIFECYCLE OF A PATCH + +Patches do not go directly from the developer's keyboard into the mainline +kernel. There is, instead, a somewhat involved (if somewhat informal) +process designed to ensure that each patch is reviewed for quality and that +each patch implements a change which is desirable to have in the mainline. +This process can happen quickly for minor fixes, or, in the case of large +and controversial changes, go on for years. Much developer frustration +comes from a lack of understanding of this process or from attempts to +circumvent it. + +In the hopes of reducing that frustration, this document will describe how +a patch gets into the kernel. What follows below is an introduction which +describes the process in a somewhat idealized way. A much more detailed +treatment will come in later sections. + +The stages that a patch goes through are, generally: + + - Design. This is where the real requirements for the patch - and the way + those requirements will be met - are laid out. Design work is often + done without involving the community, but it is better to do this work + in the open if at all possible; it can save a lot of time redesigning + things later. + + - Early review. Patches are posted to the relevant mailing list, and + developers on that list reply with any comments they may have. This + process should turn up any major problems with a patch if all goes + well. + + - Wider review. When the patch is getting close to ready for mainline + inclusion, it will be accepted by a relevant subsystem maintainer - + though this acceptance is not a guarantee that the patch will make it + all the way to the mainline. The patch will show up in the maintainer's + subsystem tree and into the staging trees (described below). When the + process works, this step leads to more extensive review of the patch and + the discovery of any problems resulting from the integration of this + patch with work being done by others. + + - Merging into the mainline. Eventually, a successful patch will be + merged into the mainline repository managed by Linus Torvalds. More + comments and/or problems may surface at this time; it is important that + the developer be responsive to these and fix any issues which arise. + + - Stable release. The number of users potentially affected by the patch + is now large, so, once again, new problems may arise. + + - Long-term maintenance. While it is certainly possible for a developer + to forget about code after merging it, that sort of behavior tends to + leave a poor impression in the development community. Merging code + eliminates some of the maintenance burden, in that others will fix + problems caused by API changes. But the original developer should + continue to take responsibility for the code if it is to remain useful + in the longer term. + +One of the largest mistakes made by kernel developers (or their employers) +is to try to cut the process down to a single "merging into the mainline" +step. This approach invariably leads to frustration for everybody +involved. + + +2.3: HOW PATCHES GET INTO THE KERNEL + +There is exactly one person who can merge patches into the mainline kernel +repository: Linus Torvalds. But, of the over 12,000 patches which went +into the 2.6.25 kernel, only 250 (around 2%) were directly chosen by Linus +himself. The kernel project has long since grown to a size where no single +developer could possibly inspect and select every patch unassisted. The +way the kernel developers have addressed this growth is through the use of +a lieutenant system built around a chain of trust. + +The kernel code base is logically broken down into a set of subsystems: +networking, specific architecture support, memory management, video +devices, etc. Most subsystems have a designated maintainer, a developer +who has overall responsibility for the code within that subsystem. These +subsystem maintainers are the gatekeepers (in a loose way) for the portion +of the kernel they manage; they are the ones who will (usually) accept a +patch for inclusion into the mainline kernel. + +Subsystem maintainers each manage their own version of the kernel source +tree, usually (but certainly not always) using the git source management +tool. Tools like git (and related tools like quilt or mercurial) allow +maintainers to track a list of patches, including authorship information +and other metadata. At any given time, the maintainer can identify which +patches in his or her repository are not found in the mainline. + +When the merge window opens, top-level maintainers will ask Linus to "pull" +the patches they have selected for merging from their repositories. If +Linus agrees, the stream of patches will flow up into his repository, +becoming part of the mainline kernel. The amount of attention that Linus +pays to specific patches received in a pull operation varies. It is clear +that, sometimes, he looks quite closely. But, as a general rule, Linus +trusts the subsystem maintainers to not send bad patches upstream. + +Subsystem maintainers, in turn, can pull patches from other maintainers. +For example, the networking tree is built from patches which accumulated +first in trees dedicated to network device drivers, wireless networking, +etc. This chain of repositories can be arbitrarily long, though it rarely +exceeds two or three links. Since each maintainer in the chain trusts +those managing lower-level trees, this process is known as the "chain of +trust." + +Clearly, in a system like this, getting patches into the kernel depends on +finding the right maintainer. Sending patches directly to Linus is not +normally the right way to go. + + +2.4: STAGING TREES + +The chain of subsystem trees guides the flow of patches into the kernel, +but it also raises an interesting question: what if somebody wants to look +at all of the patches which are being prepared for the next merge window? +Developers will be interested in what other changes are pending to see +whether there are any conflicts to worry about; a patch which changes a +core kernel function prototype, for example, will conflict with any other +patches which use the older form of that function. Reviewers and testers +want access to the changes in their integrated form before all of those +changes land in the mainline kernel. One could pull changes from all of +the interesting subsystem trees, but that would be a big and error-prone +job. + +The answer comes in the form of staging trees, where subsystem trees are +collected for testing and review. The older of these trees, maintained by +Andrew Morton, is called "-mm" (for memory management, which is how it got +started). The -mm tree integrates patches from a long list of subsystem +trees; it also has some patches aimed at helping with debugging. + +Beyond that, -mm contains a significant collection of patches which have +been selected by Andrew directly. These patches may have been posted on a +mailing list, or they may apply to a part of the kernel for which there is +no designated subsystem tree. As a result, -mm operates as a sort of +subsystem tree of last resort; if there is no other obvious path for a +patch into the mainline, it is likely to end up in -mm. Miscellaneous +patches which accumulate in -mm will eventually either be forwarded on to +an appropriate subsystem tree or be sent directly to Linus. In a typical +development cycle, approximately 10% of the patches going into the mainline +get there via -mm. + +The current -mm patch can always be found from the front page of + + http://kernel.org/ + +Those who want to see the current state of -mm can get the "-mm of the +moment" tree, found at: + + http://userweb.kernel.org/~akpm/mmotm/ + +Use of the MMOTM tree is likely to be a frustrating experience, though; +there is a definite chance that it will not even compile. + +The other staging tree, started more recently, is linux-next, maintained by +Stephen Rothwell. The linux-next tree is, by design, a snapshot of what +the mainline is expected to look like after the next merge window closes. +Linux-next trees are announced on the linux-kernel and linux-next mailing +lists when they are assembled; they can be downloaded from: + + http://www.kernel.org/pub/linux/kernel/people/sfr/linux-next/ + +Some information about linux-next has been gathered at: + + http://linux.f-seidel.de/linux-next/pmwiki/ + +How the linux-next tree will fit into the development process is still +changing. As of this writing, the first full development cycle involving +linux-next (2.6.26) is coming to an end; thus far, it has proved to be a +valuable resource for finding and fixing integration problems before the +beginning of the merge window. See http://lwn.net/Articles/287155/ for +more information on how linux-next has worked to set up the 2.6.27 merge +window. + +Some developers have begun to suggest that linux-next should be used as the +target for future development as well. The linux-next tree does tend to be +far ahead of the mainline and is more representative of the tree into which +any new work will be merged. The downside to this idea is that the +volatility of linux-next tends to make it a difficult development target. +See http://lwn.net/Articles/289013/ for more information on this topic, and +stay tuned; much is still in flux where linux-next is involved. + + +2.5: TOOLS + +As can be seen from the above text, the kernel development process depends +heavily on the ability to herd collections of patches in various +directions. The whole thing would not work anywhere near as well as it +does without suitably powerful tools. Tutorials on how to use these tools +are well beyond the scope of this document, but there is space for a few +pointers. + +By far the dominant source code management system used by the kernel +community is git. Git is one of a number of distributed version control +systems being developed in the free software community. It is well tuned +for kernel development, in that it performs quite well when dealing with +large repositories and large numbers of patches. It also has a reputation +for being difficult to learn and use, though it has gotten better over +time. Some sort of familiarity with git is almost a requirement for kernel +developers; even if they do not use it for their own work, they'll need git +to keep up with what other developers (and the mainline) are doing. + +Git is now packaged by almost all Linux distributions. There is a home +page at + + http://git.or.cz/ + +That page has pointers to documentation and tutorials. One should be +aware, in particular, of the Kernel Hacker's Guide to git, which has +information specific to kernel development: + + http://linux.yyz.us/git-howto.html + +Among the kernel developers who do not use git, the most popular choice is +almost certainly Mercurial: + + http://www.selenic.com/mercurial/ + +Mercurial shares many features with git, but it provides an interface which +many find easier to use. + +The other tool worth knowing about is Quilt: + + http://savannah.nongnu.org/projects/quilt/ + +Quilt is a patch management system, rather than a source code management +system. It does not track history over time; it is, instead, oriented +toward tracking a specific set of changes against an evolving code base. +Some major subsystem maintainers use quilt to manage patches intended to go +upstream. For the management of certain kinds of trees (-mm, for example), +quilt is the best tool for the job. + + +2.6: MAILING LISTS + +A great deal of Linux kernel development work is done by way of mailing +lists. It is hard to be a fully-functioning member of the community +without joining at least one list somewhere. But Linux mailing lists also +represent a potential hazard to developers, who risk getting buried under a +load of electronic mail, running afoul of the conventions used on the Linux +lists, or both. + +Most kernel mailing lists are run on vger.kernel.org; the master list can +be found at: + + http://vger.kernel.org/vger-lists.html + +There are lists hosted elsewhere, though; a number of them are at +lists.redhat.com. + +The core mailing list for kernel development is, of course, linux-kernel. +This list is an intimidating place to be; volume can reach 500 messages per +day, the amount of noise is high, the conversation can be severely +technical, and participants are not always concerned with showing a high +degree of politeness. But there is no other place where the kernel +development community comes together as a whole; developers who avoid this +list will miss important information. + +There are a few hints which can help with linux-kernel survival: + +- Have the list delivered to a separate folder, rather than your main + mailbox. One must be able to ignore the stream for sustained periods of + time. + +- Do not try to follow every conversation - nobody else does. It is + important to filter on both the topic of interest (though note that + long-running conversations can drift away from the original subject + without changing the email subject line) and the people who are + participating. + +- Do not feed the trolls. If somebody is trying to stir up an angry + response, ignore them. + +- When responding to linux-kernel email (or that on other lists) preserve + the Cc: header for all involved. In the absence of a strong reason (such + as an explicit request), you should never remove recipients. Always make + sure that the person you are responding to is in the Cc: list. This + convention also makes it unnecessary to explicitly ask to be copied on + replies to your postings. + +- Search the list archives (and the net as a whole) before asking + questions. Some developers can get impatient with people who clearly + have not done their homework. + +- Avoid top-posting (the practice of putting your answer above the quoted + text you are responding to). It makes your response harder to read and + makes a poor impression. + +- Ask on the correct mailing list. Linux-kernel may be the general meeting + point, but it is not the best place to find developers from all + subsystems. + +The last point - finding the correct mailing list - is a common place for +beginning developers to go wrong. Somebody who asks a networking-related +question on linux-kernel will almost certainly receive a polite suggestion +to ask on the netdev list instead, as that is the list frequented by most +networking developers. Other lists exist for the SCSI, video4linux, IDE, +filesystem, etc. subsystems. The best place to look for mailing lists is +in the MAINTAINERS file packaged with the kernel source. + + +2.7: GETTING STARTED WITH KERNEL DEVELOPMENT + +Questions about how to get started with the kernel development process are +common - from both individuals and companies. Equally common are missteps +which make the beginning of the relationship harder than it has to be. + +Companies often look to hire well-known developers to get a development +group started. This can, in fact, be an effective technique. But it also +tends to be expensive and does not do much to grow the pool of experienced +kernel developers. It is possible to bring in-house developers up to speed +on Linux kernel development, given the investment of a bit of time. Taking +this time can endow an employer with a group of developers who understand +the kernel and the company both, and who can help to train others as well. +Over the medium term, this is often the more profitable approach. + +Individual developers are often, understandably, at a loss for a place to +start. Beginning with a large project can be intimidating; one often wants +to test the waters with something smaller first. This is the point where +some developers jump into the creation of patches fixing spelling errors or +minor coding style issues. Unfortunately, such patches create a level of +noise which is distracting for the development community as a whole, so, +increasingly, they are looked down upon. New developers wishing to +introduce themselves to the community will not get the sort of reception +they wish for by these means. + +Andrew Morton gives this advice for aspiring kernel developers + + The #1 project for all kernel beginners should surely be "make sure + that the kernel runs perfectly at all times on all machines which + you can lay your hands on". Usually the way to do this is to work + with others on getting things fixed up (this can require + persistence!) but that's fine - it's a part of kernel development. + +(http://lwn.net/Articles/283982/). + +In the absence of obvious problems to fix, developers are advised to look +at the current lists of regressions and open bugs in general. There is +never any shortage of issues in need of fixing; by addressing these issues, +developers will gain experience with the process while, at the same time, +building respect with the rest of the development community. diff --git a/Documentation/development-process/3.Early-stage b/Documentation/development-process/3.Early-stage new file mode 100644 index 00000000000..307a159a70c --- /dev/null +++ b/Documentation/development-process/3.Early-stage @@ -0,0 +1,195 @@ +3: EARLY-STAGE PLANNING + +When contemplating a Linux kernel development project, it can be tempting +to jump right in and start coding. As with any significant project, +though, much of the groundwork for success is best laid before the first +line of code is written. Some time spent in early planning and +communication can save far more time later on. + + +3.1: SPECIFYING THE PROBLEM + +Like any engineering project, a successful kernel enhancement starts with a +clear description of the problem to be solved. In some cases, this step is +easy: when a driver is needed for a specific piece of hardware, for +example. In others, though, it is tempting to confuse the real problem +with the proposed solution, and that can lead to difficulties. + +Consider an example: some years ago, developers working with Linux audio +sought a way to run applications without dropouts or other artifacts caused +by excessive latency in the system. The solution they arrived at was a +kernel module intended to hook into the Linux Security Module (LSM) +framework; this module could be configured to give specific applications +access to the realtime scheduler. This module was implemented and sent to +the linux-kernel mailing list, where it immediately ran into problems. + +To the audio developers, this security module was sufficient to solve their +immediate problem. To the wider kernel community, though, it was seen as a +misuse of the LSM framework (which is not intended to confer privileges +onto processes which they would not otherwise have) and a risk to system +stability. Their preferred solutions involved realtime scheduling access +via the rlimit mechanism for the short term, and ongoing latency reduction +work in the long term. + +The audio community, however, could not see past the particular solution +they had implemented; they were unwilling to accept alternatives. The +resulting disagreement left those developers feeling disillusioned with the +entire kernel development process; one of them went back to an audio list +and posted this: + + There are a number of very good Linux kernel developers, but they + tend to get outshouted by a large crowd of arrogant fools. Trying + to communicate user requirements to these people is a waste of + time. They are much too "intelligent" to listen to lesser mortals. + +(http://lwn.net/Articles/131776/). + +The reality of the situation was different; the kernel developers were far +more concerned about system stability, long-term maintenance, and finding +the right solution to the problem than they were with a specific module. +The moral of the story is to focus on the problem - not a specific solution +- and to discuss it with the development community before investing in the +creation of a body of code. + +So, when contemplating a kernel development project, one should obtain +answers to a short set of questions: + + - What, exactly, is the problem which needs to be solved? + + - Who are the users affected by this problem? Which use cases should the + solution address? + + - How does the kernel fall short in addressing that problem now? + +Only then does it make sense to start considering possible solutions. + + +3.2: EARLY DISCUSSION + +When planning a kernel development project, it makes great sense to hold +discussions with the community before launching into implementation. Early +communication can save time and trouble in a number of ways: + + - It may well be that the problem is addressed by the kernel in ways which + you have not understood. The Linux kernel is large and has a number of + features and capabilities which are not immediately obvious. Not all + kernel capabilities are documented as well as one might like, and it is + easy to miss things. Your author has seen the posting of a complete + driver which duplicated an existing driver that the new author had been + unaware of. Code which reinvents existing wheels is not only wasteful; + it will also not be accepted into the mainline kernel. + + - There may be elements of the proposed solution which will not be + acceptable for mainline merging. It is better to find out about + problems like this before writing the code. + + - It's entirely possible that other developers have thought about the + problem; they may have ideas for a better solution, and may be willing + to help in the creation of that solution. + +Years of experience with the kernel development community have taught a +clear lesson: kernel code which is designed and developed behind closed +doors invariably has problems which are only revealed when the code is +released into the community. Sometimes these problems are severe, +requiring months or years of effort before the code can be brought up to +the kernel community's standards. Some examples include: + + - The Devicescape network stack was designed and implemented for + single-processor systems. It could not be merged into the mainline + until it was made suitable for multiprocessor systems. Retrofitting + locking and such into code is a difficult task; as a result, the merging + of this code (now called mac80211) was delayed for over a year. + + - The Reiser4 filesystem included a number of capabilities which, in the + core kernel developers' opinion, should have been implemented in the + virtual filesystem layer instead. It also included features which could + not easily be implemented without exposing the system to user-caused + deadlocks. The late revelation of these problems - and refusal to + address some of them - has caused Reiser4 to stay out of the mainline + kernel. + + - The AppArmor security module made use of internal virtual filesystem + data structures in ways which were considered to be unsafe and + unreliable. This code has since been significantly reworked, but + remains outside of the mainline. + +In each of these cases, a great deal of pain and extra work could have been +avoided with some early discussion with the kernel developers. + + +3.3: WHO DO YOU TALK TO? + +When developers decide to take their plans public, the next question will +be: where do we start? The answer is to find the right mailing list(s) and +the right maintainer. For mailing lists, the best approach is to look in +the MAINTAINERS file for a relevant place to post. If there is a suitable +subsystem list, posting there is often preferable to posting on +linux-kernel; you are more likely to reach developers with expertise in the +relevant subsystem and the environment may be more supportive. + +Finding maintainers can be a bit harder. Again, the MAINTAINERS file is +the place to start. That file tends to not always be up to date, though, +and not all subsystems are represented there. The person listed in the +MAINTAINERS file may, in fact, not be the person who is actually acting in +that role currently. So, when there is doubt about who to contact, a +useful trick is to use git (and "git log" in particular) to see who is +currently active within the subsystem of interest. Look at who is writing +patches, and who, if anybody, is attaching Signed-off-by lines to those +patches. Those are the people who will be best placed to help with a new +development project. + +If all else fails, talking to Andrew Morton can be an effective way to +track down a maintainer for a specific piece of code. + + +3.4: WHEN TO POST? + +If possible, posting your plans during the early stages can only be +helpful. Describe the problem being solved and any plans that have been +made on how the implementation will be done. Any information you can +provide can help the development community provide useful input on the +project. + +One discouraging thing which can happen at this stage is not a hostile +reaction, but, instead, little or no reaction at all. The sad truth of the +matter is (1) kernel developers tend to be busy, (2) there is no shortage +of people with grand plans and little code (or even prospect of code) to +back them up, and (3) nobody is obligated to review or comment on ideas +posted by others. If a request-for-comments posting yields little in the +way of comments, do not assume that it means there is no interest in the +project. Unfortunately, you also cannot assume that there are no problems +with your idea. The best thing to do in this situation is to proceed, +keeping the community informed as you go. + + +3.5: GETTING OFFICIAL BUY-IN + +If your work is being done in a corporate environment - as most Linux +kernel work is - you must, obviously, have permission from suitably +empowered managers before you can post your company's plans or code to a +public mailing list. The posting of code which has not been cleared for +release under a GPL-compatible license can be especially problematic; the +sooner that a company's management and legal staff can agree on the posting +of a kernel development project, the better off everybody involved will be. + +Some readers may be thinking at this point that their kernel work is +intended to support a product which does not yet have an officially +acknowledged existence. Revealing their employer's plans on a public +mailing list may not be a viable option. In cases like this, it is worth +considering whether the secrecy is really necessary; there is often no real +need to keep development plans behind closed doors. + +That said, there are also cases where a company legitimately cannot +disclose its plans early in the development process. Companies with +experienced kernel developers may choose to proceed in an open-loop manner +on the assumption that they will be able to avoid serious integration +problems later. For companies without that sort of in-house expertise, the +best option is often to hire an outside developer to review the plans under +a non-disclosure agreement. The Linux Foundation operates an NDA program +designed to help with this sort of situation; more information can be found +at: + + http://www.linuxfoundation.org/en/NDA_program + +This kind of review is often enough to avoid serious problems later on +without requiring public disclosure of the project. diff --git a/Documentation/development-process/4.Coding b/Documentation/development-process/4.Coding new file mode 100644 index 00000000000..014aca8f14e --- /dev/null +++ b/Documentation/development-process/4.Coding @@ -0,0 +1,384 @@ +4: GETTING THE CODE RIGHT + +While there is much to be said for a solid and community-oriented design +process, the proof of any kernel development project is in the resulting +code. It is the code which will be examined by other developers and merged +(or not) into the mainline tree. So it is the quality of this code which +will determine the ultimate success of the project. + +This section will examine the coding process. We'll start with a look at a +number of ways in which kernel developers can go wrong. Then the focus +will shift toward doing things right and the tools which can help in that +quest. + + +4.1: PITFALLS + +* Coding style + +The kernel has long had a standard coding style, described in +Documentation/CodingStyle. For much of that time, the policies described +in that file were taken as being, at most, advisory. As a result, there is +a substantial amount of code in the kernel which does not meet the coding +style guidelines. The presence of that code leads to two independent +hazards for kernel developers. + +The first of these is to believe that the kernel coding standards do not +matter and are not enforced. The truth of the matter is that adding new +code to the kernel is very difficult if that code is not coded according to +the standard; many developers will request that the code be reformatted +before they will even review it. A code base as large as the kernel +requires some uniformity of code to make it possible for developers to +quickly understand any part of it. So there is no longer room for +strangely-formatted code. + +Occasionally, the kernel's coding style will run into conflict with an +employer's mandated style. In such cases, the kernel's style will have to +win before the code can be merged. Putting code into the kernel means +giving up a degree of control in a number of ways - including control over +how the code is formatted. + +The other trap is to assume that code which is already in the kernel is +urgently in need of coding style fixes. Developers may start to generate +reformatting patches as a way of gaining familiarity with the process, or +as a way of getting their name into the kernel changelogs - or both. But +pure coding style fixes are seen as noise by the development community; +they tend to get a chilly reception. So this type of patch is best +avoided. It is natural to fix the style of a piece of code while working +on it for other reasons, but coding style changes should not be made for +their own sake. + +The coding style document also should not be read as an absolute law which +can never be transgressed. If there is a good reason to go against the +style (a line which becomes far less readable if split to fit within the +80-column limit, for example), just do it. + + +* Abstraction layers + +Computer Science professors teach students to make extensive use of +abstraction layers in the name of flexibility and information hiding. +Certainly the kernel makes extensive use of abstraction; no project +involving several million lines of code could do otherwise and survive. +But experience has shown that excessive or premature abstraction can be +just as harmful as premature optimization. Abstraction should be used to +the level required and no further. + +At a simple level, consider a function which has an argument which is +always passed as zero by all callers. One could retain that argument just +in case somebody eventually needs to use the extra flexibility that it +provides. By that time, though, chances are good that the code which +implements this extra argument has been broken in some subtle way which was +never noticed - because it has never been used. Or, when the need for +extra flexibility arises, it does not do so in a way which matches the +programmer's early expectation. Kernel developers will routinely submit +patches to remove unused arguments; they should, in general, not be added +in the first place. + +Abstraction layers which hide access to hardware - often to allow the bulk +of a driver to be used with multiple operating systems - are especially +frowned upon. Such layers obscure the code and may impose a performance +penalty; they do not belong in the Linux kernel. + +On the other hand, if you find yourself copying significant amounts of code +from another kernel subsystem, it is time to ask whether it would, in fact, +make sense to pull out some of that code into a separate library or to +implement that functionality at a higher level. There is no value in +replicating the same code throughout the kernel. + + +* #ifdef and preprocessor use in general + +The C preprocessor seems to present a powerful temptation to some C +programmers, who see it as a way to efficiently encode a great deal of +flexibility into a source file. But the preprocessor is not C, and heavy +use of it results in code which is much harder for others to read and +harder for the compiler to check for correctness. Heavy preprocessor use +is almost always a sign of code which needs some cleanup work. + +Conditional compilation with #ifdef is, indeed, a powerful feature, and it +is used within the kernel. But there is little desire to see code which is +sprinkled liberally with #ifdef blocks. As a general rule, #ifdef use +should be confined to header files whenever possible. +Conditionally-compiled code can be confined to functions which, if the code +is not to be present, simply become empty. The compiler will then quietly +optimize out the call to the empty function. The result is far cleaner +code which is easier to follow. + +C preprocessor macros present a number of hazards, including possible +multiple evaluation of expressions with side effects and no type safety. +If you are tempted to define a macro, consider creating an inline function +instead. The code which results will be the same, but inline functions are +easier to read, do not evaluate their arguments multiple times, and allow +the compiler to perform type checking on the arguments and return value. + + +* Inline functions + +Inline functions present a hazard of their own, though. Programmers can +become enamored of the perceived efficiency inherent in avoiding a function +call and fill a source file with inline functions. Those functions, +however, can actually reduce performance. Since their code is replicated +at each call site, they end up bloating the size of the compiled kernel. +That, in turn, creates pressure on the processor's memory caches, which can +slow execution dramatically. Inline functions, as a rule, should be quite +small and relatively rare. The cost of a function call, after all, is not +that high; the creation of large numbers of inline functions is a classic +example of premature optimization. + +In general, kernel programmers ignore cache effects at their peril. The +classic time/space tradeoff taught in beginning data structures classes +often does not apply to contemporary hardware. Space *is* time, in that a +larger program will run slower than one which is more compact. + + +* Locking + +In May, 2006, the "Devicescape" networking stack was, with great +fanfare, released under the GPL and made available for inclusion in the +mainline kernel. This donation was welcome news; support for wireless +networking in Linux was considered substandard at best, and the Devicescape +stack offered the promise of fixing that situation. Yet, this code did not +actually make it into the mainline until June, 2007 (2.6.22). What +happened? + +This code showed a number of signs of having been developed behind +corporate doors. But one large problem in particular was that it was not +designed to work on multiprocessor systems. Before this networking stack +(now called mac80211) could be merged, a locking scheme needed to be +retrofitted onto it. + +Once upon a time, Linux kernel code could be developed without thinking +about the concurrency issues presented by multiprocessor systems. Now, +however, this document is being written on a dual-core laptop. Even on +single-processor systems, work being done to improve responsiveness will +raise the level of concurrency within the kernel. The days when kernel +code could be written without thinking about locking are long past. + +Any resource (data structures, hardware registers, etc.) which could be +accessed concurrently by more than one thread must be protected by a lock. +New code should be written with this requirement in mind; retrofitting +locking after the fact is a rather more difficult task. Kernel developers +should take the time to understand the available locking primitives well +enough to pick the right tool for the job. Code which shows a lack of +attention to concurrency will have a difficult path into the mainline. + + +* Regressions + +One final hazard worth mentioning is this: it can be tempting to make a +change (which may bring big improvements) which causes something to break +for existing users. This kind of change is called a "regression," and +regressions have become most unwelcome in the mainline kernel. With few +exceptions, changes which cause regressions will be backed out if the +regression cannot be fixed in a timely manner. Far better to avoid the +regression in the first place. + +It is often argued that a regression can be justified if it causes things +to work for more people than it creates problems for. Why not make a +change if it brings new functionality to ten systems for each one it +breaks? The best answer to this question was expressed by Linus in July, +2007: + + So we don't fix bugs by introducing new problems. That way lies + madness, and nobody ever knows if you actually make any real + progress at all. Is it two steps forwards, one step back, or one + step forward and two steps back? + +(http://lwn.net/Articles/243460/). + +An especially unwelcome type of regression is any sort of change to the +user-space ABI. Once an interface has been exported to user space, it must +be supported indefinitely. This fact makes the creation of user-space +interfaces particularly challenging: since they cannot be changed in +incompatible ways, they must be done right the first time. For this +reason, a great deal of thought, clear documentation, and wide review for +user-space interfaces is always required. + + + +4.2: CODE CHECKING TOOLS + +For now, at least, the writing of error-free code remains an ideal that few +of us can reach. What we can hope to do, though, is to catch and fix as +many of those errors as possible before our code goes into the mainline +kernel. To that end, the kernel developers have put together an impressive +array of tools which can catch a wide variety of obscure problems in an +automated way. Any problem caught by the computer is a problem which will +not afflict a user later on, so it stands to reason that the automated +tools should be used whenever possible. + +The first step is simply to heed the warnings produced by the compiler. +Contemporary versions of gcc can detect (and warn about) a large number of +potential errors. Quite often, these warnings point to real problems. +Code submitted for review should, as a rule, not produce any compiler +warnings. When silencing warnings, take care to understand the real cause +and try to avoid "fixes" which make the warning go away without addressing +its cause. + +Note that not all compiler warnings are enabled by default. Build the +kernel with "make EXTRA_CFLAGS=-W" to get the full set. + +The kernel provides several configuration options which turn on debugging +features; most of these are found in the "kernel hacking" submenu. Several +of these options should be turned on for any kernel used for development or +testing purposes. In particular, you should turn on: + + - ENABLE_WARN_DEPRECATED, ENABLE_MUST_CHECK, and FRAME_WARN to get an + extra set of warnings for problems like the use of deprecated interfaces + or ignoring an important return value from a function. The output + generated by these warnings can be verbose, but one need not worry about + warnings from other parts of the kernel. + + - DEBUG_OBJECTS will add code to track the lifetime of various objects + created by the kernel and warn when things are done out of order. If + you are adding a subsystem which creates (and exports) complex objects + of its own, consider adding support for the object debugging + infrastructure. + + - DEBUG_SLAB can find a variety of memory allocation and use errors; it + should be used on most development kernels. + + - DEBUG_SPINLOCK, DEBUG_SPINLOCK_SLEEP, and DEBUG_MUTEXES will find a + number of common locking errors. + +There are quite a few other debugging options, some of which will be +discussed below. Some of them have a significant performance impact and +should not be used all of the time. But some time spent learning the +available options will likely be paid back many times over in short order. + +One of the heavier debugging tools is the locking checker, or "lockdep." +This tool will track the acquisition and release of every lock (spinlock or +mutex) in the system, the order in which locks are acquired relative to +each other, the current interrupt environment, and more. It can then +ensure that locks are always acquired in the same order, that the same +interrupt assumptions apply in all situations, and so on. In other words, +lockdep can find a number of scenarios in which the system could, on rare +occasion, deadlock. This kind of problem can be painful (for both +developers and users) in a deployed system; lockdep allows them to be found +in an automated manner ahead of time. Code with any sort of non-trivial +locking should be run with lockdep enabled before being submitted for +inclusion. + +As a diligent kernel programmer, you will, beyond doubt, check the return +status of any operation (such as a memory allocation) which can fail. The +fact of the matter, though, is that the resulting failure recovery paths +are, probably, completely untested. Untested code tends to be broken code; +you could be much more confident of your code if all those error-handling +paths had been exercised a few times. + +The kernel provides a fault injection framework which can do exactly that, +especially where memory allocations are involved. With fault injection +enabled, a configurable percentage of memory allocations will be made to +fail; these failures can be restricted to a specific range of code. +Running with fault injection enabled allows the programmer to see how the +code responds when things go badly. See +Documentation/fault-injection/fault-injection.text for more information on +how to use this facility. + +Other kinds of errors can be found with the "sparse" static analysis tool. +With sparse, the programmer can be warned about confusion between +user-space and kernel-space addresses, mixture of big-endian and +small-endian quantities, the passing of integer values where a set of bit +flags is expected, and so on. Sparse must be installed separately (it can +be found at http://www.kernel.org/pub/software/devel/sparse/ if your +distributor does not package it); it can then be run on the code by adding +"C=1" to your make command. + +Other kinds of portability errors are best found by compiling your code for +other architectures. If you do not happen to have an S/390 system or a +Blackfin development board handy, you can still perform the compilation +step. A large set of cross compilers for x86 systems can be found at + + http://www.kernel.org/pub/tools/crosstool/ + +Some time spent installing and using these compilers will help avoid +embarrassment later. + + +4.3: DOCUMENTATION + +Documentation has often been more the exception than the rule with kernel +development. Even so, adequate documentation will help to ease the merging +of new code into the kernel, make life easier for other developers, and +will be helpful for your users. In many cases, the addition of +documentation has become essentially mandatory. + +The first piece of documentation for any patch is its associated +changelog. Log entries should describe the problem being solved, the form +of the solution, the people who worked on the patch, any relevant +effects on performance, and anything else that might be needed to +understand the patch. + +Any code which adds a new user-space interface - including new sysfs or +/proc files - should include documentation of that interface which enables +user-space developers to know what they are working with. See +Documentation/ABI/README for a description of how this documentation should +be formatted and what information needs to be provided. + +The file Documentation/kernel-parameters.txt describes all of the kernel's +boot-time parameters. Any patch which adds new parameters should add the +appropriate entries to this file. + +Any new configuration options must be accompanied by help text which +clearly explains the options and when the user might want to select them. + +Internal API information for many subsystems is documented by way of +specially-formatted comments; these comments can be extracted and formatted +in a number of ways by the "kernel-doc" script. If you are working within +a subsystem which has kerneldoc comments, you should maintain them and add +them, as appropriate, for externally-available functions. Even in areas +which have not been so documented, there is no harm in adding kerneldoc +comments for the future; indeed, this can be a useful activity for +beginning kernel developers. The format of these comments, along with some +information on how to create kerneldoc templates can be found in the file +Documentation/kernel-doc-nano-HOWTO.txt. + +Anybody who reads through a significant amount of existing kernel code will +note that, often, comments are most notable by their absence. Once again, +the expectations for new code are higher than they were in the past; +merging uncommented code will be harder. That said, there is little desire +for verbosely-commented code. The code should, itself, be readable, with +comments explaining the more subtle aspects. + +Certain things should always be commented. Uses of memory barriers should +be accompanied by a line explaining why the barrier is necessary. The +locking rules for data structures generally need to be explained somewhere. +Major data structures need comprehensive documentation in general. +Non-obvious dependencies between separate bits of code should be pointed +out. Anything which might tempt a code janitor to make an incorrect +"cleanup" needs a comment saying why it is done the way it is. And so on. + + +4.4: INTERNAL API CHANGES + +The binary interface provided by the kernel to user space cannot be broken +except under the most severe circumstances. The kernel's internal +programming interfaces, instead, are highly fluid and can be changed when +the need arises. If you find yourself having to work around a kernel API, +or simply not using a specific functionality because it does not meet your +needs, that may be a sign that the API needs to change. As a kernel +developer, you are empowered to make such changes. + +There are, of course, some catches. API changes can be made, but they need +to be well justified. So any patch making an internal API change should be +accompanied by a description of what the change is and why it is +necessary. This kind of change should also be broken out into a separate +patch, rather than buried within a larger patch. + +The other catch is that a developer who changes an internal API is +generally charged with the task of fixing any code within the kernel tree +which is broken by the change. For a widely-used function, this duty can +lead to literally hundreds or thousands of changes - many of which are +likely to conflict with work being done by other developers. Needless to +say, this can be a large job, so it is best to be sure that the +justification is solid. + +When making an incompatible API change, one should, whenever possible, +ensure that code which has not been updated is caught by the compiler. +This will help you to be sure that you have found all in-tree uses of that +interface. It will also alert developers of out-of-tree code that there is +a change that they need to respond to. Supporting out-of-tree code is not +something that kernel developers need to be worried about, but we also do +not have to make life harder for out-of-tree developers than it it needs to +be. diff --git a/Documentation/development-process/5.Posting b/Documentation/development-process/5.Posting new file mode 100644 index 00000000000..dd48132a74d --- /dev/null +++ b/Documentation/development-process/5.Posting @@ -0,0 +1,278 @@ +5: POSTING PATCHES + +Sooner or later, the time comes when your work is ready to be presented to +the community for review and, eventually, inclusion into the mainline +kernel. Unsurprisingly, the kernel development community has evolved a set +of conventions and procedures which are used in the posting of patches; +following them will make life much easier for everybody involved. This +document will attempt to cover these expectations in reasonable detail; +more information can also be found in the files SubmittingPatches, +SubmittingDrivers, and SubmitChecklist in the kernel documentation +directory. + + +5.1: WHEN TO POST + +There is a constant temptation to avoid posting patches before they are +completely "ready." For simple patches, that is not a problem. If the +work being done is complex, though, there is a lot to be gained by getting +feedback from the community before the work is complete. So you should +consider posting in-progress work, or even making a git tree available so +that interested developers can catch up with your work at any time. + +When posting code which is not yet considered ready for inclusion, it is a +good idea to say so in the posting itself. Also mention any major work +which remains to be done and any known problems. Fewer people will look at +patches which are known to be half-baked, but those who do will come in +with the idea that they can help you drive the work in the right direction. + + +5.2: BEFORE CREATING PATCHES + +There are a number of things which should be done before you consider +sending patches to the development community. These include: + + - Test the code to the extent that you can. Make use of the kernel's + debugging tools, ensure that the kernel will build with all reasonable + combinations of configuration options, use cross-compilers to build for + different architectures, etc. + + - Make sure your code is compliant with the kernel coding style + guidelines. + + - Does your change have performance implications? If so, you should run + benchmarks showing what the impact (or benefit) of your change is; a + summary of the results should be included with the patch. + + - Be sure that you have the right to post the code. If this work was done + for an employer, the employer likely has a right to the work and must be + agreeable with its release under the GPL. + +As a general rule, putting in some extra thought before posting code almost +always pays back the effort in short order. + + +5.3: PATCH PREPARATION + +The preparation of patches for posting can be a surprising amount of work, +but, once again, attempting to save time here is not generally advisable +even in the short term. + +Patches must be prepared against a specific version of the kernel. As a +general rule, a patch should be based on the current mainline as found in +Linus's git tree. It may become necessary to make versions against -mm, +linux-next, or a subsystem tree, though, to facilitate wider testing and +review. Depending on the area of your patch and what is going on +elsewhere, basing a patch against these other trees can require a +significant amount of work resolving conflicts and dealing with API +changes. + +Only the most simple changes should be formatted as a single patch; +everything else should be made as a logical series of changes. Splitting +up patches is a bit of an art; some developers spend a long time figuring +out how to do it in the way that the community expects. There are a few +rules of thumb, however, which can help considerably: + + - The patch series you post will almost certainly not be the series of + changes found in your working revision control system. Instead, the + changes you have made need to be considered in their final form, then + split apart in ways which make sense. The developers are interested in + discrete, self-contained changes, not the path you took to get to those + changes. + + - Each logically independent change should be formatted as a separate + patch. These changes can be small ("add a field to this structure") or + large (adding a significant new driver, for example), but they should be + conceptually small and amenable to a one-line description. Each patch + should make a specific change which can be reviewed on its own and + verified to do what it says it does. + + - As a way of restating the guideline above: do not mix different types of + changes in the same patch. If a single patch fixes a critical security + bug, rearranges a few structures, and reformats the code, there is a + good chance that it will be passed over and the important fix will be + lost. + + - Each patch should yield a kernel which builds and runs properly; if your + patch series is interrupted in the middle, the result should still be a + working kernel. Partial application of a patch series is a common + scenario when the "git bisect" tool is used to find regressions; if the + result is a broken kernel, you will make life harder for developers and + users who are engaging in the noble work of tracking down problems. + + - Do not overdo it, though. One developer recently posted a set of edits + to a single file as 500 separate patches - an act which did not make him + the most popular person on the kernel mailing list. A single patch can + be reasonably large as long as it still contains a single *logical* + change. + + - It can be tempting to add a whole new infrastructure with a series of + patches, but to leave that infrastructure unused until the final patch + in the series enables the whole thing. This temptation should be + avoided if possible; if that series adds regressions, bisection will + finger the last patch as the one which caused the problem, even though + the real bug is elsewhere. Whenever possible, a patch which adds new + code should make that code active immediately. + +Working to create the perfect patch series can be a frustrating process +which takes quite a bit of time and thought after the "real work" has been +done. When done properly, though, it is time well spent. + + +5.4: PATCH FORMATTING + +So now you have a perfect series of patches for posting, but the work is +not done quite yet. Each patch needs to be formatted into a message which +quickly and clearly communicates its purpose to the rest of the world. To +that end, each patch will be composed of the following: + + - An optional "From" line naming the author of the patch. This line is + only necessary if you are passing on somebody else's patch via email, + but it never hurts to add it when in doubt. + + - A one-line description of what the patch does. This message should be + enough for a reader who sees it with no other context to figure out the + scope of the patch; it is the line that will show up in the "short form" + changelogs. This message is usually formatted with the relevant + subsystem name first, followed by the purpose of the patch. For + example: + + gpio: fix build on CONFIG_GPIO_SYSFS=n + + - A blank line followed by a detailed description of the contents of the + patch. This description can be as long as is required; it should say + what the patch does and why it should be applied to the kernel. + + - One or more tag lines, with, at a minimum, one Signed-off-by: line from + the author of the patch. Tags will be described in more detail below. + +The above three items should, normally, be the text used when committing +the change to a revision control system. They are followed by: + + - The patch itself, in the unified ("-u") patch format. Using the "-p" + option to diff will associate function names with changes, making the + resulting patch easier for others to read. + +You should avoid including changes to irrelevant files (those generated by +the build process, for example, or editor backup files) in the patch. The +file "dontdiff" in the Documentation directory can help in this regard; +pass it to diff with the "-X" option. + +The tags mentioned above are used to describe how various developers have +been associated with the development of this patch. They are described in +detail in the SubmittingPatches document; what follows here is a brief +summary. Each of these lines has the format: + + tag: Full Name optional-other-stuff + +The tags in common use are: + + - Signed-off-by: this is a developer's certification that he or she has + the right to submit the patch for inclusion into the kernel. It is an + agreement to the Developer's Certificate of Origin, the full text of + which can be found in Documentation/SubmittingPatches. Code without a + proper signoff cannot be merged into the mainline. + + - Acked-by: indicates an agreement by another developer (often a + maintainer of the relevant code) that the patch is appropriate for + inclusion into the kernel. + + - Tested-by: states that the named person has tested the patch and found + it to work. + + - Reviewed-by: the named developer has reviewed the patch for correctness; + see the reviewer's statement in Documentation/SubmittingPatches for more + detail. + + - Reported-by: names a user who reported a problem which is fixed by this + patch; this tag is used to give credit to the (often underappreciated) + people who test our code and let us know when things do not work + correctly. + + - Cc: the named person received a copy of the patch and had the + opportunity to comment on it. + +Be careful in the addition of tags to your patches: only Cc: is appropriate +for addition without the explicit permission of the person named. + + +5.5: SENDING THE PATCH + +Before you mail your patches, there are a couple of other things you should +take care of: + + - Are you sure that your mailer will not corrupt the patches? Patches + which have had gratuitous white-space changes or line wrapping performed + by the mail client will not apply at the other end, and often will not + be examined in any detail. If there is any doubt at all, mail the patch + to yourself and convince yourself that it shows up intact. + + Documentation/email-clients.txt has some helpful hints on making + specific mail clients work for sending patches. + + - Are you sure your patch is free of silly mistakes? You should always + run patches through scripts/checkpatch.pl and address the complaints it + comes up with. Please bear in mind that checkpatch.pl, while being the + embodiment of a fair amount of thought about what kernel patches should + look like, is not smarter than you. If fixing a checkpatch.pl complaint + would make the code worse, don't do it. + +Patches should always be sent as plain text. Please do not send them as +attachments; that makes it much harder for reviewers to quote sections of +the patch in their replies. Instead, just put the patch directly into your +message. + +When mailing patches, it is important to send copies to anybody who might +be interested in it. Unlike some other projects, the kernel encourages +people to err on the side of sending too many copies; don't assume that the +relevant people will see your posting on the mailing lists. In particular, +copies should go to: + + - The maintainer(s) of the affected subsystem(s). As described earlier, + the MAINTAINERS file is the first place to look for these people. + + - Other developers who have been working in the same area - especially + those who might be working there now. Using git to see who else has + modified the files you are working on can be helpful. + + - If you are responding to a bug report or a feature request, copy the + original poster as well. + + - Send a copy to the relevant mailing list, or, if nothing else applies, + the linux-kernel list. + + - If you are fixing a bug, think about whether the fix should go into the + next stable update. If so, stable@kernel.org should get a copy of the + patch. Also add a "Cc: stable@kernel.org" to the tags within the patch + itself; that will cause the stable team to get a notification when your + fix goes into the mainline. + +When selecting recipients for a patch, it is good to have an idea of who +you think will eventually accept the patch and get it merged. While it +is possible to send patches directly to Linus Torvalds and have him merge +them, things are not normally done that way. Linus is busy, and there are +subsystem maintainers who watch over specific parts of the kernel. Usually +you will be wanting that maintainer to merge your patches. If there is no +obvious maintainer, Andrew Morton is often the patch target of last resort. + +Patches need good subject lines. The canonical format for a patch line is +something like: + + [PATCH nn/mm] subsys: one-line description of the patch + +where "nn" is the ordinal number of the patch, "mm" is the total number of +patches in the series, and "subsys" is the name of the affected subsystem. +Clearly, nn/mm can be omitted for a single, standalone patch. + +If you have a significant series of patches, it is customary to send an +introductory description as part zero. This convention is not universally +followed though; if you use it, remember that information in the +introduction does not make it into the kernel changelogs. So please ensure +that the patches, themselves, have complete changelog information. + +In general, the second and following parts of a multi-part patch should be +sent as a reply to the first part so that they all thread together at the +receiving end. Tools like git and quilt have commands to mail out a set of +patches with the proper threading. If you have a long series, though, and +are using git, please provide the --no-chain-reply-to option to avoid +creating exceptionally deep nesting. diff --git a/Documentation/development-process/6.Followthrough b/Documentation/development-process/6.Followthrough new file mode 100644 index 00000000000..a8fba3d83a8 --- /dev/null +++ b/Documentation/development-process/6.Followthrough @@ -0,0 +1,202 @@ +6: FOLLOWTHROUGH + +At this point, you have followed the guidelines given so far and, with the +addition of your own engineering skills, have posted a perfect series of +patches. One of the biggest mistakes that even experienced kernel +developers can make is to conclude that their work is now done. In truth, +posting patches indicates a transition into the next stage of the process, +with, possibly, quite a bit of work yet to be done. + +It is a rare patch which is so good at its first posting that there is no +room for improvement. The kernel development process recognizes this fact, +and, as a result, is heavily oriented toward the improvement of posted +code. You, as the author of that code, will be expected to work with the +kernel community to ensure that your code is up to the kernel's quality +standards. A failure to participate in this process is quite likely to +prevent the inclusion of your patches into the mainline. + + +6.1: WORKING WITH REVIEWERS + +A patch of any significance will result in a number of comments from other +developers as they review the code. Working with reviewers can be, for +many developers, the most intimidating part of the kernel development +process. Life can be made much easier, though, if you keep a few things in +mind: + + - If you have explained your patch well, reviewers will understand its + value and why you went to the trouble of writing it. But that value + will not keep them from asking a fundamental question: what will it be + like to maintain a kernel with this code in it five or ten years later? + Many of the changes you may be asked to make - from coding style tweaks + to substantial rewrites - come from the understanding that Linux will + still be around and under development a decade from now. + + - Code review is hard work, and it is a relatively thankless occupation; + people remember who wrote kernel code, but there is little lasting fame + for those who reviewed it. So reviewers can get grumpy, especially when + they see the same mistakes being made over and over again. If you get a + review which seems angry, insulting, or outright offensive, resist the + impulse to respond in kind. Code review is about the code, not about + the people, and code reviewers are not attacking you personally. + + - Similarly, code reviewers are not trying to promote their employers' + agendas at the expense of your own. Kernel developers often expect to + be working on the kernel years from now, but they understand that their + employer could change. They truly are, almost without exception, + working toward the creation of the best kernel they can; they are not + trying to create discomfort for their employers' competitors. + +What all of this comes down to is that, when reviewers send you comments, +you need to pay attention to the technical observations that they are +making. Do not let their form of expression or your own pride keep that +from happening. When you get review comments on a patch, take the time to +understand what the reviewer is trying to say. If possible, fix the things +that the reviewer is asking you to fix. And respond back to the reviewer: +thank them, and describe how you will answer their questions. + +Note that you do not have to agree with every change suggested by +reviewers. If you believe that the reviewer has misunderstood your code, +explain what is really going on. If you have a technical objection to a +suggested change, describe it and justify your solution to the problem. If +your explanations make sense, the reviewer will accept them. Should your +explanation not prove persuasive, though, especially if others start to +agree with the reviewer, take some time to think things over again. It can +be easy to become blinded by your own solution to a problem to the point +that you don't realize that something is fundamentally wrong or, perhaps, +you're not even solving the right problem. + +One fatal mistake is to ignore review comments in the hope that they will +go away. They will not go away. If you repost code without having +responded to the comments you got the time before, you're likely to find +that your patches go nowhere. + +Speaking of reposting code: please bear in mind that reviewers are not +going to remember all the details of the code you posted the last time +around. So it is always a good idea to remind reviewers of previously +raised issues and how you dealt with them; the patch changelog is a good +place for this kind of information. Reviewers should not have to search +through list archives to familiarize themselves with what was said last +time; if you help them get a running start, they will be in a better mood +when they revisit your code. + +What if you've tried to do everything right and things still aren't going +anywhere? Most technical disagreements can be resolved through discussion, +but there are times when somebody simply has to make a decision. If you +honestly believe that this decision is going against you wrongly, you can +always try appealing to a higher power. As of this writing, that higher +power tends to be Andrew Morton. Andrew has a great deal of respect in the +kernel development community; he can often unjam a situation which seems to +be hopelessly blocked. Appealing to Andrew should not be done lightly, +though, and not before all other alternatives have been explored. And bear +in mind, of course, that he may not agree with you either. + + +6.2: WHAT HAPPENS NEXT + +If a patch is considered to be a good thing to add to the kernel, and once +most of the review issues have been resolved, the next step is usually +entry into a subsystem maintainer's tree. How that works varies from one +subsystem to the next; each maintainer has his or her own way of doing +things. In particular, there may be more than one tree - one, perhaps, +dedicated to patches planned for the next merge window, and another for +longer-term work. + +For patches applying to areas for which there is no obvious subsystem tree +(memory management patches, for example), the default tree often ends up +being -mm. Patches which affect multiple subsystems can also end up going +through the -mm tree. + +Inclusion into a subsystem tree can bring a higher level of visibility to a +patch. Now other developers working with that tree will get the patch by +default. Subsystem trees typically feed into -mm and linux-next as well, +making their contents visible to the development community as a whole. At +this point, there's a good chance that you will get more comments from a +new set of reviewers; these comments need to be answered as in the previous +round. + +What may also happen at this point, depending on the nature of your patch, +is that conflicts with work being done by others turn up. In the worst +case, heavy patch conflicts can result in some work being put on the back +burner so that the remaining patches can be worked into shape and merged. +Other times, conflict resolution will involve working with the other +developers and, possibly, moving some patches between trees to ensure that +everything applies cleanly. This work can be a pain, but count your +blessings: before the advent of the linux-next tree, these conflicts often +only turned up during the merge window and had to be addressed in a hurry. +Now they can be resolved at leisure, before the merge window opens. + +Some day, if all goes well, you'll log on and see that your patch has been +merged into the mainline kernel. Congratulations! Once the celebration is +complete (and you have added yourself to the MAINTAINERS file), though, it +is worth remembering an important little fact: the job still is not done. +Merging into the mainline brings its own challenges. + +To begin with, the visibility of your patch has increased yet again. There +may be a new round of comments from developers who had not been aware of +the patch before. It may be tempting to ignore them, since there is no +longer any question of your code being merged. Resist that temptation, +though; you still need to be responsive to developers who have questions or +suggestions. + +More importantly, though: inclusion into the mainline puts your code into +the hands of a much larger group of testers. Even if you have contributed +a driver for hardware which is not yet available, you will be surprised by +how many people will build your code into their kernels. And, of course, +where there are testers, there will be bug reports. + +The worst sort of bug reports are regressions. If your patch causes a +regression, you'll find an uncomfortable number of eyes upon you; +regressions need to be fixed as soon as possible. If you are unwilling or +unable to fix the regression (and nobody else does it for you), your patch +will almost certainly be removed during the stabilization period. Beyond +negating all of the work you have done to get your patch into the mainline, +having a patch pulled as the result of a failure to fix a regression could +well make it harder for you to get work merged in the future. + +After any regressions have been dealt with, there may be other, ordinary +bugs to deal with. The stabilization period is your best opportunity to +fix these bugs and ensure that your code's debut in a mainline kernel +release is as solid as possible. So, please, answer bug reports, and fix +the problems if at all possible. That's what the stabilization period is +for; you can start creating cool new patches once any problems with the old +ones have been taken care of. + +And don't forget that there are other milestones which may also create bug +reports: the next mainline stable release, when prominent distributors pick +up a version of the kernel containing your patch, etc. Continuing to +respond to these reports is a matter of basic pride in your work. If that +is insufficient motivation, though, it's also worth considering that the +development community remembers developers who lose interest in their code +after it's merged. The next time you post a patch, they will be evaluating +it with the assumption that you will not be around to maintain it +afterward. + + +6.3: OTHER THINGS THAT CAN HAPPEN + +One day, you may open your mail client and see that somebody has mailed you +a patch to your code. That is one of the advantages of having your code +out there in the open, after all. If you agree with the patch, you can +either forward it on to the subsystem maintainer (be sure to include a +proper From: line so that the attribution is correct, and add a signoff of +your own), or send an Acked-by: response back and let the original poster +send it upward. + +If you disagree with the patch, send a polite response explaining why. If +possible, tell the author what changes need to be made to make the patch +acceptable to you. There is a certain resistance to merging patches which +are opposed by the author and maintainer of the code, but it only goes so +far. If you are seen as needlessly blocking good work, those patches will +eventually flow around you and get into the mainline anyway. In the Linux +kernel, nobody has absolute veto power over any code. Except maybe Linus. + +On very rare occasion, you may see something completely different: another +developer posts a different solution to your problem. At that point, +chances are that one of the two patches will not be merged, and "mine was +here first" is not considered to be a compelling technical argument. If +somebody else's patch displaces yours and gets into the mainline, there is +really only one way to respond: be pleased that your problem got solved and +get on with your work. Having one's work shoved aside in this manner can +be hurtful and discouraging, but the community will remember your reaction +long after they have forgotten whose patch actually got merged. diff --git a/Documentation/development-process/7.AdvancedTopics b/Documentation/development-process/7.AdvancedTopics new file mode 100644 index 00000000000..a2cf74093aa --- /dev/null +++ b/Documentation/development-process/7.AdvancedTopics @@ -0,0 +1,173 @@ +7: ADVANCED TOPICS + +At this point, hopefully, you have a handle on how the development process +works. There is still more to learn, however! This section will cover a +number of topics which can be helpful for developers wanting to become a +regular part of the Linux kernel development process. + +7.1: MANAGING PATCHES WITH GIT + +The use of distributed version control for the kernel began in early 2002, +when Linus first started playing with the proprietary BitKeeper +application. While BitKeeper was controversial, the approach to software +version management it embodied most certainly was not. Distributed version +control enabled an immediate acceleration of the kernel development +project. In current times, there are several free alternatives to +BitKeeper. For better or for worse, the kernel project has settled on git +as its tool of choice. + +Managing patches with git can make life much easier for the developer, +especially as the volume of those patches grows. Git also has its rough +edges and poses certain hazards; it is a young and powerful tool which is +still being civilized by its developers. This document will not attempt to +teach the reader how to use git; that would be sufficient material for a +long document in its own right. Instead, the focus here will be on how git +fits into the kernel development process in particular. Developers who +wish to come up to speed with git will find more information at: + + http://git.or.cz/ + + http://www.kernel.org/pub/software/scm/git/docs/user-manual.html + +and on various tutorials found on the web. + +The first order of business is to read the above sites and get a solid +understanding of how git works before trying to use it to make patches +available to others. A git-using developer should be able to obtain a copy +of the mainline repository, explore the revision history, commit changes to +the tree, use branches, etc. An understanding of git's tools for the +rewriting of history (such as rebase) is also useful. Git comes with its +own terminology and concepts; a new user of git should know about refs, +remote branches, the index, fast-forward merges, pushes and pulls, detached +heads, etc. It can all be a little intimidating at the outset, but the +concepts are not that hard to grasp with a bit of study. + +Using git to generate patches for submission by email can be a good +exercise while coming up to speed. + +When you are ready to start putting up git trees for others to look at, you +will, of course, need a server that can be pulled from. Setting up such a +server with git-daemon is relatively straightforward if you have a system +which is accessible to the Internet. Otherwise, free, public hosting sites +(Github, for example) are starting to appear on the net. Established +developers can get an account on kernel.org, but those are not easy to come +by; see http://kernel.org/faq/ for more information. + +The normal git workflow involves the use of a lot of branches. Each line +of development can be separated into a separate "topic branch" and +maintained independently. Branches in git are cheap, there is no reason to +not make free use of them. And, in any case, you should not do your +development in any branch which you intend to ask others to pull from. +Publicly-available branches should be created with care; merge in patches +from development branches when they are in complete form and ready to go - +not before. + +Git provides some powerful tools which can allow you to rewrite your +development history. An inconvenient patch (one which breaks bisection, +say, or which has some other sort of obvious bug) can be fixed in place or +made to disappear from the history entirely. A patch series can be +rewritten as if it had been written on top of today's mainline, even though +you have been working on it for months. Changes can be transparently +shifted from one branch to another. And so on. Judicious use of git's +ability to revise history can help in the creation of clean patch sets with +fewer problems. + +Excessive use of this capability can lead to other problems, though, beyond +a simple obsession for the creation of the perfect project history. +Rewriting history will rewrite the changes contained in that history, +turning a tested (hopefully) kernel tree into an untested one. But, beyond +that, developers cannot easily collaborate if they do not have a shared +view of the project history; if you rewrite history which other developers +have pulled into their repositories, you will make life much more difficult +for those developers. So a simple rule of thumb applies here: history +which has been exported to others should generally be seen as immutable +thereafter. + +So, once you push a set of changes to your publicly-available server, those +changes should not be rewritten. Git will attempt to enforce this rule if +you try to push changes which do not result in a fast-forward merge +(i.e. changes which do not share the same history). It is possible to +override this check, and there may be times when it is necessary to rewrite +an exported tree. Moving changesets between trees to avoid conflicts in +linux-next is one example. But such actions should be rare. This is one +of the reasons why development should be done in private branches (which +can be rewritten if necessary) and only moved into public branches when +it's in a reasonably advanced state. + +As the mainline (or other tree upon which a set of changes is based) +advances, it is tempting to merge with that tree to stay on the leading +edge. For a private branch, rebasing can be an easy way to keep up with +another tree, but rebasing is not an option once a tree is exported to the +world. Once that happens, a full merge must be done. Merging occasionally +makes good sense, but overly frequent merges can clutter the history +needlessly. Suggested technique in this case is to merge infrequently, and +generally only at specific release points (such as a mainline -rc +release). If you are nervous about specific changes, you can always +perform test merges in a private branch. The git "rerere" tool can be +useful in such situations; it remembers how merge conflicts were resolved +so that you don't have to do the same work twice. + +One of the biggest recurring complaints about tools like git is this: the +mass movement of patches from one repository to another makes it easy to +slip in ill-advised changes which go into the mainline below the review +radar. Kernel developers tend to get unhappy when they see that kind of +thing happening; putting up a git tree with unreviewed or off-topic patches +can affect your ability to get trees pulled in the future. Quoting Linus: + + You can send me patches, but for me to pull a git patch from you, I + need to know that you know what you're doing, and I need to be able + to trust things *without* then having to go and check every + individual change by hand. + +(http://lwn.net/Articles/224135/). + +To avoid this kind of situation, ensure that all patches within a given +branch stick closely to the associated topic; a "driver fixes" branch +should not be making changes to the core memory management code. And, most +importantly, do not use a git tree to bypass the review process. Post an +occasional summary of the tree to the relevant list, and, when the time is +right, request that the tree be included in linux-next. + +If and when others start to send patches for inclusion into your tree, +don't forget to review them. Also ensure that you maintain the correct +authorship information; the git "am" tool does its best in this regard, but +you may have to add a "From:" line to the patch if it has been relayed to +you via a third party. + +When requesting a pull, be sure to give all the relevant information: where +your tree is, what branch to pull, and what changes will result from the +pull. The git request-pull command can be helpful in this regard; it will +format the request as other developers expect, and will also check to be +sure that you have remembered to push those changes to the public server. + + +7.2: REVIEWING PATCHES + +Some readers will certainly object to putting this section with "advanced +topics" on the grounds that even beginning kernel developers should be +reviewing patches. It is certainly true that there is no better way to +learn how to program in the kernel environment than by looking at code +posted by others. In addition, reviewers are forever in short supply; by +looking at code you can make a significant contribution to the process as a +whole. + +Reviewing code can be an intimidating prospect, especially for a new kernel +developer who may well feel nervous about questioning code - in public - +which has been posted by those with more experience. Even code written by +the most experienced developers can be improved, though. Perhaps the best +piece of advice for reviewers (all reviewers) is this: phrase review +comments as questions rather than criticisms. Asking "how does the lock +get released in this path?" will always work better than stating "the +locking here is wrong." + +Different developers will review code from different points of view. Some +are mostly concerned with coding style and whether code lines have trailing +white space. Others will focus primarily on whether the change implemented +by the patch as a whole is a good thing for the kernel or not. Yet others +will check for problematic locking, excessive stack usage, possible +security issues, duplication of code found elsewhere, adequate +documentation, adverse effects on performance, user-space ABI changes, etc. +All types of review, if they lead to better code going into the kernel, are +welcome and worthwhile. + + diff --git a/Documentation/development-process/8.Conclusion b/Documentation/development-process/8.Conclusion new file mode 100644 index 00000000000..1990ab4b494 --- /dev/null +++ b/Documentation/development-process/8.Conclusion @@ -0,0 +1,74 @@ +8: FOR MORE INFORMATION + +There are numerous sources of information on Linux kernel development and +related topics. First among those will always be the Documentation +directory found in the kernel source distribution. The top-level HOWTO +file is an important starting point; SubmittingPatches and +SubmittingDrivers are also something which all kernel developers should +read. Many internal kernel APIs are documented using the kerneldoc +mechanism; "make htmldocs" or "make pdfdocs" can be used to generate those +documents in HTML or PDF format (though the version of TeX shipped by some +distributions runs into internal limits and fails to process the documents +properly). + +Various web sites discuss kernel development at all levels of detail. Your +author would like to humbly suggest http://lwn.net/ as a source; +information on many specific kernel topics can be found via the LWN kernel +index at: + + http://lwn.net/Kernel/Index/ + +Beyond that, a valuable resource for kernel developers is: + + http://kernelnewbies.org/ + +Information about the linux-next tree gathers at: + + http://linux.f-seidel.de/linux-next/pmwiki/ + +And, of course, one should not forget http://kernel.org/, the definitive +location for kernel release information. + +There are a number of books on kernel development: + + Linux Device Drivers, 3rd Edition (Jonathan Corbet, Alessandro + Rubini, and Greg Kroah-Hartman). Online at + http://lwn.net/Kernel/LDD3/. + + Linux Kernel Development (Robert Love). + + Understanding the Linux Kernel (Daniel Bovet and Marco Cesati). + +All of these books suffer from a common fault, though: they tend to be +somewhat obsolete by the time they hit the shelves, and they have been on +the shelves for a while now. Still, there is quite a bit of good +information to be found there. + +Documentation for git can be found at: + + http://www.kernel.org/pub/software/scm/git/docs/ + + http://www.kernel.org/pub/software/scm/git/docs/user-manual.html + + +9: CONCLUSION + +Congratulations to anybody who has made it through this long-winded +document. Hopefully it has provided a helpful understanding of how the +Linux kernel is developed and how you can participate in that process. + +In the end, it's the participation that matters. Any open source software +project is no more than the sum of what its contributors put into it. The +Linux kernel has progressed as quickly and as well as it has because it has +been helped by an impressively large group of developers, all of whom are +working to make it better. The kernel is a premier example of what can be +done when thousands of people work together toward a common goal. + +The kernel can always benefit from a larger developer base, though. There +is always more work to do. But, just as importantly, most other +participants in the Linux ecosystem can benefit through contributing to the +kernel. Getting code into the mainline is the key to higher code quality, +lower maintenance and distribution costs, a higher level of influence over +the direction of kernel development, and more. It is a situation where +everybody involved wins. Fire up your editor and come join us; you will be +more than welcome. -- cgit v1.2.3-70-g09d2 From 58bae1f5cfd077f7f5f3af5d1ac50c3a82ac6411 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Tue, 13 May 2008 13:36:55 +0900 Subject: doc: Test-by? Commonly used is "Tested-by." Signed-off-by: Hidetoshi Seto Signed-off-by: Jonathan Corbet --- Documentation/SubmittingPatches | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index f79ad9ff603..0d601cba969 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -405,7 +405,7 @@ person it names. This tag documents that potentially interested parties have been included in the discussion -14) Using Test-by: and Reviewed-by: +14) Using Tested-by: and Reviewed-by: A Tested-by: tag indicates that the patch has been successfully tested (in some environment) by the person named. This tag informs maintainers that -- cgit v1.2.3-70-g09d2 From 7e3975617df8dd8b7fd94f14200abdec9f71729e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 16 Oct 2008 11:53:20 -0600 Subject: Remove videobook.tmpl This document describes the long-deprecated V4L1 interface. In-tree, it can only serve to encourage developers to write drivers to the wrong API. Remove it in favor of the V4L2 documentation which must surely show up someday. Acked-by: Alan Cox Acked-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- Documentation/DocBook/Makefile | 2 +- Documentation/DocBook/videobook.tmpl | 1654 ---------------------------------- 2 files changed, 1 insertion(+), 1655 deletions(-) delete mode 100644 Documentation/DocBook/videobook.tmpl (limited to 'Documentation') diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 1615350b7b5..fabc06466b9 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -6,7 +6,7 @@ # To add a new book the only step required is to add the book to the # list of DOCBOOKS. -DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \ +DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml \ kernel-hacking.xml kernel-locking.xml deviceiobook.xml \ procfs-guide.xml writing_usb_driver.xml networking.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl deleted file mode 100644 index 0bc25949b66..00000000000 --- a/Documentation/DocBook/videobook.tmpl +++ /dev/null @@ -1,1654 +0,0 @@ - - - - - - Video4Linux Programming - - - - Alan - Cox - -
- alan@redhat.com -
-
-
-
- - - 2000 - Alan Cox - - - - - This documentation 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 - - - - For more details see the file COPYING in the source - distribution of Linux. - - -
- - - - - Introduction - - Parts of this document first appeared in Linux Magazine under a - ninety day exclusivity. - - - Video4Linux is intended to provide a common programming interface - for the many TV and capture cards now on the market, as well as - parallel port and USB video cameras. Radio, teletext decoders and - vertical blanking data interfaces are also provided. - - - - Radio Devices - - There are a wide variety of radio interfaces available for PC's, and these - are generally very simple to program. The biggest problem with supporting - such devices is normally extracting documentation from the vendor. - - - The radio interface supports a simple set of control ioctls standardised - across all radio and tv interfaces. It does not support read or write, which - are used for video streams. The reason radio cards do not allow you to read - the audio stream into an application is that without exception they provide - a connection on to a soundcard. Soundcards can be used to read the radio - data just fine. - - - Registering Radio Devices - - The Video4linux core provides an interface for registering devices. The - first step in writing our radio card driver is to register it. - - - - -static struct video_device my_radio -{ - "My radio", - VID_TYPE_TUNER, - radio_open. - radio_close, - NULL, /* no read */ - NULL, /* no write */ - NULL, /* no poll */ - radio_ioctl, - NULL, /* no special init function */ - NULL /* no private data */ -}; - - - - - This declares our video4linux device driver interface. The VID_TYPE_ value - defines what kind of an interface we are, and defines basic capabilities. - - - The only defined value relevant for a radio card is VID_TYPE_TUNER which - indicates that the device can be tuned. Clearly our radio is going to have some - way to change channel so it is tuneable. - - - We declare an open and close routine, but we do not need read or write, - which are used to read and write video data to or from the card itself. As - we have no read or write there is no poll function. - - - The private initialise function is run when the device is registered. In - this driver we've already done all the work needed. The final pointer is a - private data pointer that can be used by the device driver to attach and - retrieve private data structures. We set this field "priv" to NULL for - the moment. - - - Having the structure defined is all very well but we now need to register it - with the kernel. - - - - -static int io = 0x320; - -int __init myradio_init(struct video_init *v) -{ - if(!request_region(io, MY_IO_SIZE, "myradio")) - { - printk(KERN_ERR - "myradio: port 0x%03X is in use.\n", io); - return -EBUSY; - } - - if(video_device_register(&my_radio, VFL_TYPE_RADIO)==-1) { - release_region(io, MY_IO_SIZE); - return -EINVAL; - } - return 0; -} - - - - The first stage of the initialisation, as is normally the case, is to check - that the I/O space we are about to fiddle with doesn't belong to some other - driver. If it is we leave well alone. If the user gives the address of the - wrong device then we will spot this. These policies will generally avoid - crashing the machine. - - - Now we ask the Video4Linux layer to register the device for us. We hand it - our carefully designed video_device structure and also tell it which group - of devices we want it registered with. In this case VFL_TYPE_RADIO. - - - The types available are - - Device Types - - - - VFL_TYPE_RADIO/dev/radio{n} - - Radio devices are assigned in this block. As with all of these - selections the actual number assignment is done by the video layer - accordijng to what is free. - - VFL_TYPE_GRABBER/dev/video{n} - Video capture devices and also -- counter-intuitively for the name -- - hardware video playback devices such as MPEG2 cards. - - VFL_TYPE_VBI/dev/vbi{n} - The VBI devices capture the hidden lines on a television picture - that carry further information like closed caption data, teletext - (primarily in Europe) and now Intercast and the ATVEC internet - television encodings. - - VFL_TYPE_VTX/dev/vtx[n} - VTX is 'Videotext' also known as 'Teletext'. This is a system for - sending numbered, 40x25, mostly textual page images over the hidden - lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder - chips. (The use of the word smart here has to be taken in context, - the smartest teletext chips are fairly dumb pieces of technology). - - - - -
- - We are most definitely a radio. - - - Finally we allocate our I/O space so that nobody treads on us and return 0 - to signify general happiness with the state of the universe. - -
- - Opening And Closing The Radio - - - The functions we declared in our video_device are mostly very simple. - Firstly we can drop in what is basically standard code for open and close. - - - - -static int users = 0; - -static int radio_open(struct video_device *dev, int flags) -{ - if(users) - return -EBUSY; - users++; - return 0; -} - - - - At open time we need to do nothing but check if someone else is also using - the radio card. If nobody is using it we make a note that we are using it, - then we ensure that nobody unloads our driver on us. - - - - -static int radio_close(struct video_device *dev) -{ - users--; -} - - - - At close time we simply need to reduce the user count and allow the module - to become unloadable. - - - If you are sharp you will have noticed neither the open nor the close - routines attempt to reset or change the radio settings. This is intentional. - It allows an application to set up the radio and exit. It avoids a user - having to leave an application running all the time just to listen to the - radio. - - - - The Ioctl Interface - - This leaves the ioctl routine, without which the driver will not be - terribly useful to anyone. - - - - -static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability v; - v.type = VID_TYPE_TUNER; - v.channels = 1; - v.audios = 1; - v.maxwidth = 0; - v.minwidth = 0; - v.maxheight = 0; - v.minheight = 0; - strcpy(v.name, "My Radio"); - if(copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - - - - VIDIOCGCAP is the first ioctl all video4linux devices must support. It - allows the applications to find out what sort of a card they have found and - to figure out what they want to do about it. The fields in the structure are - - struct video_capability fields - - - - nameThe device text name. This is intended for the user. - - channelsThe number of different channels you can tune on - this card. It could even by zero for a card that has - no tuning capability. For our simple FM radio it is 1. - An AM/FM radio would report 2. - - audiosThe number of audio inputs on this device. For our - radio there is only one audio input. - - minwidth,minheightThe smallest size the card is capable of capturing - images in. We set these to zero. Radios do not - capture pictures - - maxwidth,maxheightThe largest image size the card is capable of - capturing. For our radio we report 0. - - - typeThis reports the capabilities of the device, and - matches the field we filled in in the struct - video_device when registering. - - - -
- - Having filled in the fields, we use copy_to_user to copy the structure into - the users buffer. If the copy fails we return an EFAULT to the application - so that it knows it tried to feed us garbage. - - - The next pair of ioctl operations select which tuner is to be used and let - the application find the tuner properties. We have only a single FM band - tuner in our example device. - - - - - case VIDIOCGTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))!=0) - return -EFAULT; - if(v.tuner) - return -EINVAL; - v.rangelow=(87*16000); - v.rangehigh=(108*16000); - v.flags = VIDEO_TUNER_LOW; - v.mode = VIDEO_MODE_AUTO; - v.signal = 0xFFFF; - strcpy(v.name, "FM"); - if(copy_to_user(&v, arg, sizeof(v))!=0) - return -EFAULT; - return 0; - } - - - - The VIDIOCGTUNER ioctl allows applications to query a tuner. The application - sets the tuner field to the tuner number it wishes to query. The query does - not change the tuner that is being used, it merely enquires about the tuner - in question. - - - We have exactly one tuner so after copying the user buffer to our temporary - structure we complain if they asked for a tuner other than tuner 0. - - - The video_tuner structure has the following fields - - struct video_tuner fields - - - - int tunerThe number of the tuner in question - - char name[32]A text description of this tuner. "FM" will do fine. - This is intended for the application. - - u32 flags - Tuner capability flags - - - u16 modeThe current reception mode - - - u16 signalThe signal strength scaled between 0 and 65535. If - a device cannot tell the signal strength it should - report 65535. Many simple cards contain only a - signal/no signal bit. Such cards will report either - 0 or 65535. - - - u32 rangelow, rangehigh - The range of frequencies supported by the radio - or TV. It is scaled according to the VIDEO_TUNER_LOW - flag. - - - - -
- - struct video_tuner flags - - - - VIDEO_TUNER_PALA PAL TV tuner - - VIDEO_TUNER_NTSCAn NTSC (US) TV tuner - - VIDEO_TUNER_SECAMA SECAM (French) TV tuner - - VIDEO_TUNER_LOW - The tuner frequency is scaled in 1/16th of a KHz - steps. If not it is in 1/16th of a MHz steps - - - VIDEO_TUNER_NORMThe tuner can set its format - - VIDEO_TUNER_STEREO_ONThe tuner is currently receiving a stereo signal - - - -
- - struct video_tuner modes - - - - VIDEO_MODE_PALPAL Format - - VIDEO_MODE_NTSCNTSC Format (USA) - - VIDEO_MODE_SECAMFrench Format - - VIDEO_MODE_AUTOA device that does not need to do - TV format switching - - - -
- - The settings for the radio card are thus fairly simple. We report that we - are a tuner called "FM" for FM radio. In order to get the best tuning - resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its - unlikely our card can do that resolution but it is a fair bet the card can - do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all - radio usage. - - - We report that the tuner automatically handles deciding what format it is - receiving - true enough as it only handles FM radio. Our example card is - also incapable of detecting stereo or signal strengths so it reports a - strength of 0xFFFF (maximum) and no stereo detected. - - - To finish off we set the range that can be tuned to be 87-108Mhz, the normal - FM broadcast radio range. It is important to find out what the card is - actually capable of tuning. It is easy enough to simply use the FM broadcast - range. Unfortunately if you do this you will discover the FM broadcast - ranges in the USA, Europe and Japan are all subtly different and some users - cannot receive all the stations they wish. - - - The application also needs to be able to set the tuner it wishes to use. In - our case, with a single tuner this is rather simple to arrange. - - - - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.tuner != 0) - return -EINVAL; - return 0; - } - - - - We copy the user supplied structure into kernel memory so we can examine it. - If the user has selected a tuner other than zero we reject the request. If - they wanted tuner 0 then, surprisingly enough, that is the current tuner already. - - - The next two ioctls we need to provide are to get and set the frequency of - the radio. These both use an unsigned long argument which is the frequency. - The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I - mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in - 1/16ths of a KHz. - - - -static unsigned long current_freq; - - - - case VIDIOCGFREQ: - if(copy_to_user(arg, &current_freq, - sizeof(unsigned long)) - return -EFAULT; - return 0; - - - - Querying the frequency in our case is relatively simple. Our radio card is - too dumb to let us query the signal strength so we remember our setting if - we know it. All we have to do is copy it to the user. - - - - - case VIDIOCSFREQ: - { - u32 freq; - if(copy_from_user(arg, &freq, - sizeof(unsigned long))!=0) - return -EFAULT; - if(hardware_set_freq(freq)<0) - return -EINVAL; - current_freq = freq; - return 0; - } - - - - Setting the frequency is a little more complex. We begin by copying the - desired frequency into kernel space. Next we call a hardware specific routine - to set the radio up. This might be as simple as some scaling and a few - writes to an I/O port. For most radio cards it turns out a good deal more - complicated and may involve programming things like a phase locked loop on - the card. This is what documentation is for. - - - The final set of operations we need to provide for our radio are the - volume controls. Not all radio cards can even do volume control. After all - there is a perfectly good volume control on the sound card. We will assume - our radio card has a simple 4 step volume control. - - - There are two ioctls with audio we need to support - - - -static int current_volume=0; - - case VIDIOCGAUDIO: - { - struct video_audio v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.audio != 0) - return -EINVAL; - v.volume = 16384*current_volume; - v.step = 16384; - strcpy(v.name, "Radio"); - v.mode = VIDEO_SOUND_MONO; - v.balance = 0; - v.base = 0; - v.treble = 0; - - if(copy_to_user(arg. &v, sizeof(v))) - return -EFAULT; - return 0; - } - - - - Much like the tuner we start by copying the user structure into kernel - space. Again we check if the user has asked for a valid audio input. We have - only input 0 and we punt if they ask for another input. - - - Then we fill in the video_audio structure. This has the following format - - struct video_audio fields - - - - audioThe input the user wishes to query - - volumeThe volume setting on a scale of 0-65535 - - baseThe base level on a scale of 0-65535 - - trebleThe treble level on a scale of 0-65535 - - flagsThe features this audio device supports - - - nameA text name to display to the user. We picked - "Radio" as it explains things quite nicely. - - modeThe current reception mode for the audio - - We report MONO because our card is too stupid to know if it is in - mono or stereo. - - - balanceThe stereo balance on a scale of 0-65535, 32768 is - middle. - - stepThe step by which the volume control jumps. This is - used to help make it easy for applications to set - slider behaviour. - - - -
- - struct video_audio flags - - - - VIDEO_AUDIO_MUTEThe audio is currently muted. We - could fake this in our driver but we - choose not to bother. - - VIDEO_AUDIO_MUTABLEThe input has a mute option - - VIDEO_AUDIO_TREBLEThe input has a treble control - - VIDEO_AUDIO_BASSThe input has a base control - - - -
- - struct video_audio modes - - - - VIDEO_SOUND_MONOMono sound - - VIDEO_SOUND_STEREOStereo sound - - VIDEO_SOUND_LANG1Alternative language 1 (TV specific) - - VIDEO_SOUND_LANG2Alternative language 2 (TV specific) - - - -
- - Having filled in the structure we copy it back to user space. - - - The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the - video_audio structure. The driver does its best to honour the request. - - - - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.audio) - return -EINVAL; - current_volume = v/16384; - hardware_set_volume(current_volume); - return 0; - } - - - - In our case there is very little that the user can set. The volume is - basically the limit. Note that we could pretend to have a mute feature - by rewriting this to - - - - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.audio) - return -EINVAL; - current_volume = v/16384; - if(v.flags&VIDEO_AUDIO_MUTE) - hardware_set_volume(0); - else - hardware_set_volume(current_volume); - current_muted = v.flags & - VIDEO_AUDIO_MUTE; - return 0; - } - - - - This with the corresponding changes to the VIDIOCGAUDIO code to report the - state of the mute flag we save and to report the card has a mute function, - will allow applications to use a mute facility with this card. It is - questionable whether this is a good idea however. User applications can already - fake this themselves and kernel space is precious. - - - We now have a working radio ioctl handler. So we just wrap up the function - - - - - } - return -ENOIOCTLCMD; -} - - - - and pass the Video4Linux layer back an error so that it knows we did not - understand the request we got passed. - -
- - Module Wrapper - - Finally we add in the usual module wrapping and the driver is done. - - - -#ifndef MODULE - -static int io = 0x300; - -#else - -static int io = -1; - -#endif - -MODULE_AUTHOR("Alan Cox"); -MODULE_DESCRIPTION("A driver for an imaginary radio card."); -module_param(io, int, 0444); -MODULE_PARM_DESC(io, "I/O address of the card."); - -static int __init init(void) -{ - if(io==-1) - { - printk(KERN_ERR - "You must set an I/O address with io=0x???\n"); - return -EINVAL; - } - return myradio_init(NULL); -} - -static void __exit cleanup(void) -{ - video_unregister_device(&my_radio); - release_region(io, MY_IO_SIZE); -} - -module_init(init); -module_exit(cleanup); - - - - In this example we set the IO base by default if the driver is compiled into - the kernel: you can still set it using "my_radio.irq" if this file is called my_radio.c. For the module we require the - user sets the parameter. We set io to a nonsense port (-1) so that we can - tell if the user supplied an io parameter or not. - - - We use MODULE_ defines to give an author for the card driver and a - description. We also use them to declare that io is an integer and it is the - address of the card, and can be read by anyone from sysfs. - - - The clean-up routine unregisters the video_device we registered, and frees - up the I/O space. Note that the unregister takes the actual video_device - structure as its argument. Unlike the file operations structure which can be - shared by all instances of a device a video_device structure as an actual - instance of the device. If you are registering multiple radio devices you - need to fill in one structure per device (most likely by setting up a - template and copying it to each of the actual device structures). - - -
- - Video Capture Devices - - Video Capture Device Types - - The video capture devices share the same interfaces as radio devices. In - order to explain the video capture interface I will use the example of a - camera that has no tuners or audio input. This keeps the example relatively - clean. To get both combine the two driver examples. - - - Video capture devices divide into four categories. A little technology - backgrounder. Full motion video even at television resolution (which is - actually fairly low) is pretty resource-intensive. You are continually - passing megabytes of data every second from the capture card to the display. - several alternative approaches have emerged because copying this through the - processor and the user program is a particularly bad idea . - - - The first is to add the television image onto the video output directly. - This is also how some 3D cards work. These basic cards can generally drop the - video into any chosen rectangle of the display. Cards like this, which - include most mpeg1 cards that used the feature connector, aren't very - friendly in a windowing environment. They don't understand windows or - clipping. The video window is always on the top of the display. - - - Chroma keying is a technique used by cards to get around this. It is an old - television mixing trick where you mark all the areas you wish to replace - with a single clear colour that isn't used in the image - TV people use an - incredibly bright blue while computing people often use a particularly - virulent purple. Bright blue occurs on the desktop. Anyone with virulent - purple windows has another problem besides their TV overlay. - - - The third approach is to copy the data from the capture card to the video - card, but to do it directly across the PCI bus. This relieves the processor - from doing the work but does require some smartness on the part of the video - capture chip, as well as a suitable video card. Programming this kind of - card and more so debugging it can be extremely tricky. There are some quite - complicated interactions with the display and you may also have to cope with - various chipset bugs that show up when PCI cards start talking to each - other. - - - To keep our example fairly simple we will assume a card that supports - overlaying a flat rectangular image onto the frame buffer output, and which - can also capture stuff into processor memory. - - - - Registering Video Capture Devices - - This time we need to add more functions for our camera device. - - -static struct video_device my_camera -{ - "My Camera", - VID_TYPE_OVERLAY|VID_TYPE_SCALES|\ - VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY, - camera_open. - camera_close, - camera_read, /* no read */ - NULL, /* no write */ - camera_poll, /* no poll */ - camera_ioctl, - NULL, /* no special init function */ - NULL /* no private data */ -}; - - - We need a read() function which is used for capturing data from - the card, and we need a poll function so that a driver can wait for the next - frame to be captured. - - - We use the extra video capability flags that did not apply to the - radio interface. The video related flags are - - Capture Capabilities - - - -VID_TYPE_CAPTUREWe support image capture - -VID_TYPE_TELETEXTA teletext capture device (vbi{n]) - -VID_TYPE_OVERLAYThe image can be directly overlaid onto the - frame buffer - -VID_TYPE_CHROMAKEYChromakey can be used to select which parts - of the image to display - -VID_TYPE_CLIPPINGIt is possible to give the board a list of - rectangles to draw around. - -VID_TYPE_FRAMERAMThe video capture goes into the video memory - and actually changes it. Applications need - to know this so they can clean up after the - card - -VID_TYPE_SCALESThe image can be scaled to various sizes, - rather than being a single fixed size. - -VID_TYPE_MONOCHROMEThe capture will be monochrome. This isn't a - complete answer to the question since a mono - camera on a colour capture card will still - produce mono output. - -VID_TYPE_SUBCAPTUREThe card allows only part of its field of - view to be captured. This enables - applications to avoid copying all of a large - image into memory when only some section is - relevant. - - - -
- - We set VID_TYPE_CAPTURE so that we are seen as a capture card, - VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent - purple, and VID_TYPE_SCALES because we can be resized. - - - Our setup is fairly similar. This time we also want an interrupt line - for the 'frame captured' signal. Not all cards have this so some of them - cannot handle poll(). - - - - -static int io = 0x320; -static int irq = 11; - -int __init mycamera_init(struct video_init *v) -{ - if(!request_region(io, MY_IO_SIZE, "mycamera")) - { - printk(KERN_ERR - "mycamera: port 0x%03X is in use.\n", io); - return -EBUSY; - } - - if(video_device_register(&my_camera, - VFL_TYPE_GRABBER)==-1) { - release_region(io, MY_IO_SIZE); - return -EINVAL; - } - return 0; -} - - - - This is little changed from the needs of the radio card. We specify - VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name. - -
- - Opening And Closing The Capture Device - - - -static int users = 0; - -static int camera_open(struct video_device *dev, int flags) -{ - if(users) - return -EBUSY; - if(request_irq(irq, camera_irq, 0, "camera", dev)<0) - return -EBUSY; - users++; - return 0; -} - - -static int camera_close(struct video_device *dev) -{ - users--; - free_irq(irq, dev); -} - - - The open and close routines are also quite similar. The only real change is - that we now request an interrupt for the camera device interrupt line. If we - cannot get the interrupt we report EBUSY to the application and give up. - - - - Interrupt Handling - - Our example handler is for an ISA bus device. If it was PCI you would be - able to share the interrupt and would have set IRQF_SHARED to indicate a - shared IRQ. We pass the device pointer as the interrupt routine argument. We - don't need to since we only support one card but doing this will make it - easier to upgrade the driver for multiple devices in the future. - - - Our interrupt routine needs to do little if we assume the card can simply - queue one frame to be read after it captures it. - - - - -static struct wait_queue *capture_wait; -static int capture_ready = 0; - -static void camera_irq(int irq, void *dev_id, - struct pt_regs *regs) -{ - capture_ready=1; - wake_up_interruptible(&capture_wait); -} - - - The interrupt handler is nice and simple for this card as we are assuming - the card is buffering the frame for us. This means we have little to do but - wake up anybody interested. We also set a capture_ready flag, as we may - capture a frame before an application needs it. In this case we need to know - that a frame is ready. If we had to collect the frame on the interrupt life - would be more complex. - - - The two new routines we need to supply are camera_read which returns a - frame, and camera_poll which waits for a frame to become ready. - - - - -static int camera_poll(struct video_device *dev, - struct file *file, struct poll_table *wait) -{ - poll_wait(file, &capture_wait, wait); - if(capture_read) - return POLLIN|POLLRDNORM; - return 0; -} - - - - Our wait queue for polling is the capture_wait queue. This will cause the - task to be woken up by our camera_irq routine. We check capture_read to see - if there is an image present and if so report that it is readable. - - - - Reading The Video Image - - - -static long camera_read(struct video_device *dev, char *buf, - unsigned long count) -{ - struct wait_queue wait = { current, NULL }; - u8 *ptr; - int len; - int i; - - add_wait_queue(&capture_wait, &wait); - - while(!capture_ready) - { - if(file->flags&O_NDELAY) - { - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; - return -EWOULDBLOCK; - } - if(signal_pending(current)) - { - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; - return -ERESTARTSYS; - } - schedule(); - current->state = TASK_INTERRUPTIBLE; - } - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; - - - - The first thing we have to do is to ensure that the application waits until - the next frame is ready. The code here is almost identical to the mouse code - we used earlier in this chapter. It is one of the common building blocks of - Linux device driver code and probably one which you will find occurs in any - drivers you write. - - - We wait for a frame to be ready, or for a signal to interrupt our waiting. If a - signal occurs we need to return from the system call so that the signal can - be sent to the application itself. We also check to see if the user actually - wanted to avoid waiting - ie if they are using non-blocking I/O and have other things - to get on with. - - - Next we copy the data from the card to the user application. This is rarely - as easy as our example makes out. We will add capture_w, and capture_h here - to hold the width and height of the captured image. We assume the card only - supports 24bit RGB for now. - - - - - - capture_ready = 0; - - ptr=(u8 *)buf; - len = capture_w * 3 * capture_h; /* 24bit RGB */ - - if(len>count) - len=count; /* Doesn't all fit */ - - for(i=0; i<len; i++) - { - put_user(inb(io+IMAGE_DATA), ptr); - ptr++; - } - - hardware_restart_capture(); - - return i; -} - - - - For a real hardware device you would try to avoid the loop with put_user(). - Each call to put_user() has a time overhead checking whether the accesses to user - space are allowed. It would be better to read a line into a temporary buffer - then copy this to user space in one go. - - - Having captured the image and put it into user space we can kick the card to - get the next frame acquired. - - - - Video Ioctl Handling - - As with the radio driver the major control interface is via the ioctl() - function. Video capture devices support the same tuner calls as a radio - device and also support additional calls to control how the video functions - are handled. In this simple example the card has no tuners to avoid making - the code complex. - - - - - -static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability v; - v.type = VID_TYPE_CAPTURE|\ - VID_TYPE_CHROMAKEY|\ - VID_TYPE_SCALES|\ - VID_TYPE_OVERLAY; - v.channels = 1; - v.audios = 0; - v.maxwidth = 640; - v.minwidth = 16; - v.maxheight = 480; - v.minheight = 16; - strcpy(v.name, "My Camera"); - if(copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - - - - - The first ioctl we must support and which all video capture and radio - devices are required to support is VIDIOCGCAP. This behaves exactly the same - as with a radio device. This time, however, we report the extra capabilities - we outlined earlier on when defining our video_dev structure. - - - We now set the video flags saying that we support overlay, capture, - scaling and chromakey. We also report size limits - our smallest image is - 16x16 pixels, our largest is 640x480. - - - To keep things simple we report no audio and no tuning capabilities at all. - - - - case VIDIOCGCHAN: - { - struct video_channel v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.channel != 0) - return -EINVAL; - v.flags = 0; - v.tuners = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = VIDEO_MODE_AUTO; - strcpy(v.name, "Camera Input");break; - if(copy_to_user(&v, arg, sizeof(v))) - return -EFAULT; - return 0; - } - - - - - This follows what is very much the standard way an ioctl handler looks - in Linux. We copy the data into a kernel space variable and we check that the - request is valid (in this case that the input is 0). Finally we copy the - camera info back to the user. - - - The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is - inputs to the video card). Our example card has a single camera input. The - fields in the structure are - - struct video_channel fields - - - - - channelThe channel number we are selecting - - nameThe name for this channel. This is intended - to describe the port to the user. - Appropriate names are therefore things like - "Camera" "SCART input" - - flagsChannel properties - - typeInput type - - normThe current television encoding being used - if relevant for this channel. - - - - -
- struct video_channel flags - - - - VIDEO_VC_TUNERChannel has a tuner. - - VIDEO_VC_AUDIOChannel has audio. - - - -
- struct video_channel types - - - - VIDEO_TYPE_TVTelevision input. - - VIDEO_TYPE_CAMERAFixed camera input. - - 0Type is unknown. - - - -
- struct video_channel norms - - - - VIDEO_MODE_PALPAL encoded Television - - VIDEO_MODE_NTSCNTSC (US) encoded Television - - VIDEO_MODE_SECAMSECAM (French) Television - - VIDEO_MODE_AUTOAutomatic switching, or format does not - matter - - - -
- - The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to - request the norm is changed - for example to switch between a PAL or an NTSC - format camera. - - - - - case VIDIOCSCHAN: - { - struct video_channel v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.channel != 0) - return -EINVAL; - if(v.norm != VIDEO_MODE_AUTO) - return -EINVAL; - return 0; - } - - - - - The implementation of this call in our driver is remarkably easy. Because we - are assuming fixed format hardware we need only check that the user has not - tried to change anything. - - - The user also needs to be able to configure and adjust the picture they are - seeing. This is much like adjusting a television set. A user application - also needs to know the palette being used so that it knows how to display - the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl - calls provide this information. - - - - - case VIDIOCGPICT - { - struct video_picture v; - v.brightness = hardware_brightness(); - v.hue = hardware_hue(); - v.colour = hardware_saturation(); - v.contrast = hardware_brightness(); - /* Not settable */ - v.whiteness = 32768; - v.depth = 24; /* 24bit */ - v.palette = VIDEO_PALETTE_RGB24; - if(copy_to_user(&v, arg, - sizeof(v))) - return -EFAULT; - return 0; - } - - - - - The brightness, hue, color, and contrast provide the picture controls that - are akin to a conventional television. Whiteness provides additional - control for greyscale images. All of these values are scaled between 0-65535 - and have 32768 as the mid point setting. The scaling means that applications - do not have to worry about the capability range of the hardware but can let - it make a best effort attempt. - - - Our depth is 24, as this is in bits. We will be returning RGB24 format. This - has one byte of red, then one of green, then one of blue. This then repeats - for every other pixel in the image. The other common formats the interface - defines are - - Framebuffer Encodings - - - - GREYLinear greyscale. This is for simple cameras and the - like - - RGB565The top 5 bits hold 32 red levels, the next six bits - hold green and the low 5 bits hold blue. - - RGB555The top bit is clear. The red green and blue levels - each occupy five bits. - - - -
- - Additional modes are support for YUV capture formats. These are common for - TV and video conferencing applications. - - - The VIDIOCSPICT ioctl allows a user to set some of the picture parameters. - Exactly which ones are supported depends heavily on the card itself. It is - possible to support many modes and effects in software. In general doing - this in the kernel is a bad idea. Video capture is a performance-sensitive - application and the programs can often do better if they aren't being - 'helped' by an overkeen driver writer. Thus for our device we will report - RGB24 only and refuse to allow a change. - - - - - case VIDIOCSPICT: - { - struct video_picture v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.depth!=24 || - v.palette != VIDEO_PALETTE_RGB24) - return -EINVAL; - set_hardware_brightness(v.brightness); - set_hardware_hue(v.hue); - set_hardware_saturation(v.colour); - set_hardware_brightness(v.contrast); - return 0; - } - - - - - We check the user has not tried to change the palette or the depth. We do - not want to carry out some of the changes and then return an error. This may - confuse the application which will be assuming no change occurred. - - - In much the same way as you need to be able to set the picture controls to - get the right capture images, many cards need to know what they are - displaying onto when generating overlay output. In some cases getting this - wrong even makes a nasty mess or may crash the computer. For that reason - the VIDIOCSBUF ioctl used to set up the frame buffer information may well - only be usable by root. - - - We will assume our card is one of the old ISA devices with feature connector - and only supports a couple of standard video modes. Very common for older - cards although the PCI devices are way smarter than this. - - - - -static struct video_buffer capture_fb; - - case VIDIOCGFBUF: - { - if(copy_to_user(arg, &capture_fb, - sizeof(capture_fb))) - return -EFAULT; - return 0; - - } - - - - - We keep the frame buffer information in the format the ioctl uses. This - makes it nice and easy to work with in the ioctl calls. - - - - case VIDIOCSFBUF: - { - struct video_buffer v; - - if(!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.width!=320 && v.width!=640) - return -EINVAL; - if(v.height!=200 && v.height!=240 - && v.height!=400 - && v.height !=480) - return -EINVAL; - memcpy(&capture_fb, &v, sizeof(v)); - hardware_set_fb(&v); - return 0; - } - - - - - - The capable() function checks a user has the required capability. The Linux - operating system has a set of about 30 capabilities indicating privileged - access to services. The default set up gives the superuser (uid 0) all of - them and nobody else has any. - - - We check that the user has the SYS_ADMIN capability, that is they are - allowed to operate as the machine administrator. We don't want anyone but - the administrator making a mess of the display. - - - Next we check for standard PC video modes (320 or 640 wide with either - EGA or VGA depths). If the mode is not a standard video mode we reject it as - not supported by our card. If the mode is acceptable we save it so that - VIDIOCFBUF will give the right answer next time it is called. The - hardware_set_fb() function is some undescribed card specific function to - program the card for the desired mode. - - - Before the driver can display an overlay window it needs to know where the - window should be placed, and also how large it should be. If the card - supports clipping it needs to know which rectangles to omit from the - display. The video_window structure is used to describe the way the image - should be displayed. - - struct video_window fields - - - - widthThe width in pixels of the desired image. The card - may use a smaller size if this size is not available - - heightThe height of the image. The card may use a smaller - size if this size is not available. - - x The X position of the top left of the window. This - is in pixels relative to the left hand edge of the - picture. Not all cards can display images aligned on - any pixel boundary. If the position is unsuitable - the card adjusts the image right and reduces the - width. - - y The Y position of the top left of the window. This - is counted in pixels relative to the top edge of the - picture. As with the width if the card cannot - display starting on this line it will adjust the - values. - - chromakeyThe colour (expressed in RGB32 format) for the - chromakey colour if chroma keying is being used. - - clipsAn array of rectangles that must not be drawn - over. - - clipcountThe number of clips in this array. - - - -
- - Each clip is a struct video_clip which has the following fields - - video_clip fields - - - - x, yCo-ordinates relative to the display - - width, heightWidth and height in pixels - - nextA spare field for the application to use - - - -
- - The driver is required to ensure it always draws in the area requested or a smaller area, and that it never draws in any of the areas that are clipped. - This may well mean it has to leave alone. small areas the application wished to be - drawn. - - - Our example card uses chromakey so does not have to address most of the - clipping. We will add a video_window structure to our global variables to - remember our parameters, as we did with the frame buffer. - - - - - case VIDIOCGWIN: - { - if(copy_to_user(arg, &capture_win, - sizeof(capture_win))) - return -EFAULT; - return 0; - } - - - case VIDIOCSWIN: - { - struct video_window v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.width > 640 || v.height > 480) - return -EINVAL; - if(v.width < 16 || v.height < 16) - return -EINVAL; - hardware_set_key(v.chromakey); - hardware_set_window(v); - memcpy(&capture_win, &v, sizeof(v)); - capture_w = v.width; - capture_h = v.height; - return 0; - } - - - - - Because we are using Chromakey our setup is fairly simple. Mostly we have to - check the values are sane and load them into the capture card. - - - With all the setup done we can now turn on the actual capture/overlay. This - is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument - where 0 is on and 1 is off. - - - - - case VIDIOCCAPTURE: - { - int v; - if(get_user(v, (int *)arg)) - return -EFAULT; - if(v==0) - hardware_capture_off(); - else - { - if(capture_fb.width == 0 - || capture_w == 0) - return -EINVAL; - hardware_capture_on(); - } - return 0; - } - - - - - We grab the flag from user space and either enable or disable according to - its value. There is one small corner case we have to consider here. Suppose - that the capture was requested before the video window or the frame buffer - had been set up. In those cases there will be unconfigured fields in our - card data, as well as unconfigured hardware settings. We check for this case and - return an error if the frame buffer or the capture window width is zero. - - - - - default: - return -ENOIOCTLCMD; - } -} - - - - We don't need to support any other ioctls, so if we get this far, it is time - to tell the video layer that we don't now what the user is talking about. - -
- - Other Functionality - - The Video4Linux layer supports additional features, including a high - performance mmap() based capture mode and capturing part of the image. - These features are out of the scope of the book. You should however have enough - example code to implement most simple video4linux devices for radio and TV - cards. - - -
- - Known Bugs And Assumptions - - - Multiple Opens - - - The driver assumes multiple opens should not be allowed. A driver - can work around this but not cleanly. - - - - API Deficiencies - - - The existing API poorly reflects compression capable devices. There - are plans afoot to merge V4L, V4L2 and some other ideas into a - better interface. - - - - - - - - - Public Functions Provided -!Edrivers/media/video/v4l2-dev.c - - -
-- cgit v1.2.3-70-g09d2 From 12caa1b6fc3a9772535227c723c11878b5ca618e Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 7 Oct 2008 12:17:28 +0200 Subject: Add a reference to paper to SubmittingPatches Add a reference to paper to SubmittingPatches For this year's OLS I wrote a paper on successfull strategies to submit difficult kernel patches. Add a reference to it to SubmittingPatches. Signed-off-by: Andi Kleen Signed-off-by: Jonathan Corbet --- Documentation/SubmittingPatches | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 0d601cba969..98aa8b18739 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -672,4 +672,9 @@ Kernel Documentation/CodingStyle: Linus Torvalds's mail on the canonical patch format: + +Andi Kleen, "On submitting kernel patches" + Some strategies to get difficult or controversal changes in. + http://halobates.de/on-submitting-patches.pdf + -- -- cgit v1.2.3-70-g09d2 From b25f29b0da23f4f784f9bcae954b157e1f45cc69 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Wed, 15 Oct 2008 22:01:21 -0700 Subject: pm: document use of RTC in pm_trace As pm_trace uses the system's hardware clock to save its magic value, users of that option should be warned that using this debug option will result in an incorrect system time after resume. Signed-off-by: Frans Pop Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/power/s2ram.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Documentation') diff --git a/Documentation/power/s2ram.txt b/Documentation/power/s2ram.txt index b05f512130e..2ebdc6091ce 100644 --- a/Documentation/power/s2ram.txt +++ b/Documentation/power/s2ram.txt @@ -54,3 +54,21 @@ used to run with "radeonfb" (it's an ATI Radeon mobility). It turns out that "radeonfb" simply cannot resume that device - it tries to set the PLL's, and it just _hangs_. Using the regular VGA console and letting X resume it instead works fine. + +NOTE +==== +pm_trace uses the system's Real Time Clock (RTC) to save the magic number. +Reason for this is that the RTC is the only reliably available piece of +hardware during resume operations where a value can be set that will +survive a reboot. + +Consequence is that after a resume (even if it is successful) your system +clock will have a value corresponding to the magic mumber instead of the +correct date/time! It is therefore advisable to use a program like ntp-date +or rdate to reset the correct date/time from an external time source when +using this trace option. + +As the clock keeps ticking it is also essential that the reboot is done +quickly after the resume failure. The trace option does not use the seconds +or the low order bits of the minutes of the RTC, but a too long delay will +corrupt the magic value. -- cgit v1.2.3-70-g09d2 From dd1c53a64a48b6c16f349e46b71f0938d9a4fa1f Mon Sep 17 00:00:00 2001 From: frans Date: Wed, 15 Oct 2008 22:01:30 -0700 Subject: Fix Documentation/filesystems/ramfs-rootfs-initramfs.txt First a file hello.c is created, then the file hello2.c is compiled. Change this to hello.c Signed-off-by: Frans Meulenbroeks Signed-off-by: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/ramfs-rootfs-initramfs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.txt b/Documentation/filesystems/ramfs-rootfs-initramfs.txt index 7be232b44ee..62fe9b1e089 100644 --- a/Documentation/filesystems/ramfs-rootfs-initramfs.txt +++ b/Documentation/filesystems/ramfs-rootfs-initramfs.txt @@ -263,7 +263,7 @@ User Mode Linux, like so: sleep(999999999); } EOF - gcc -static hello2.c -o init + gcc -static hello.c -o init echo init | cpio -o -H newc | gzip > test.cpio.gz # Testing external initramfs using the initrd loading mechanism. qemu -kernel /boot/vmlinuz -initrd test.cpio.gz /dev/zero -- cgit v1.2.3-70-g09d2 From 404d0ae289f7a76ff233e8fbfde8b1e7b6e62ae3 Mon Sep 17 00:00:00 2001 From: Danny ter Haar Date: Wed, 15 Oct 2008 22:01:34 -0700 Subject: fix random typos Signed-off-by: Danny ter Haar Cc: Patrick McHardy Cc: Mikael Starvik Cc: Avi Kivity Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cris/README | 2 +- Documentation/ia64/kvm.txt | 9 +++++---- net/netfilter/nf_conntrack_acct.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cris/README b/Documentation/cris/README index 795a1dabe6c..d9b086869a6 100644 --- a/Documentation/cris/README +++ b/Documentation/cris/README @@ -27,7 +27,7 @@ operating system. The ETRAX 100LX chip -------------------- -For reference, plase see the press-release: +For reference, please see the press-release: http://www.axis.com/news/us/001101_etrax.htm diff --git a/Documentation/ia64/kvm.txt b/Documentation/ia64/kvm.txt index 914d07f4926..84f7cb3d5be 100644 --- a/Documentation/ia64/kvm.txt +++ b/Documentation/ia64/kvm.txt @@ -1,7 +1,8 @@ -Currently, kvm module in EXPERIMENTAL stage on IA64. This means that -interfaces are not stable enough to use. So, plase had better don't run -critical applications in virtual machine. We will try our best to make it -strong in future versions! +Currently, kvm module is in EXPERIMENTAL stage on IA64. This means that +interfaces are not stable enough to use. So, please don't run critical +applications in virtual machine. +We will try our best to improve it in future versions! + Guide: How to boot up guests on kvm/ia64 This guide is to describe how to enable kvm support for IA-64 systems. diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 03591d37b9c..b92df5c1dfc 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -115,7 +115,7 @@ int nf_conntrack_acct_init(struct net *net) if (net_eq(net, &init_net)) { #ifdef CONFIG_NF_CT_ACCT - printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Plase use\n"); + printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Please use\n"); printk(KERN_WARNING "nf_conntrack.acct=1 kernel paramater, acct=1 nf_conntrack module option or\n"); printk(KERN_WARNING "sysctl net.netfilter.nf_conntrack_acct=1 to enable it.\n"); #endif -- cgit v1.2.3-70-g09d2 From c80cfb0406c01bb5da91bfe30f5cb1fd96831138 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 15 Oct 2008 22:01:35 -0700 Subject: vsprintf: use new vsprintf symbolic function pointer format Use the '%pF' format to get rid of an "#ifdef DEBUG" and make some printks atomic. This removes the last in-tree uses of print_fn_descriptor_symbol(). I marked print_fn_descriptor_symbol() deprecated and scheduled it for removal next year to give time for out-of-tree modules to be updated. parisc's print_fn_descriptor_symbol() is currently broken there (it needs to dereference the function pointer similar to ia64 and power). This patch shouldn't make anything worse, but it means we need to fix dereference_function_descriptor() instead of print_fn_descriptor_symbol() to get meaningful initcall_debug output. Signed-off-by: Bjorn Helgaas Cc: Jesse Barnes Cc: Kyle McMartin Cc: "Rafael J. Wysocki" Cc: Kay Sievers Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 9 +++++++++ drivers/base/power/main.c | 7 ++----- include/linux/kallsyms.h | 8 +++----- 3 files changed, 14 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 4d2566a7d16..f5f812daf9f 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -294,6 +294,15 @@ Who: Jiri Slaby --------------------------- +What: print_fn_descriptor_symbol() +When: October 2009 +Why: The %pF vsprintf format provides the same functionality in a + simpler way. print_fn_descriptor_symbol() is deprecated but + still present to give out-of-tree modules time to change. +Who: Bjorn Helgaas + +--------------------------- + What: /sys/o2cb symlink When: January 2010 Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 273a944d404..03bde7524bc 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -778,10 +778,7 @@ EXPORT_SYMBOL_GPL(device_suspend); void __suspend_report_result(const char *function, void *fn, int ret) { - if (ret) { - printk(KERN_ERR "%s(): ", function); - print_fn_descriptor_symbol("%s returns ", fn); - printk("%d\n", ret); - } + if (ret) + printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); } EXPORT_SYMBOL_GPL(__suspend_report_result); diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index b9614488744..f3fe34391d8 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -93,12 +93,10 @@ static inline void print_symbol(const char *fmt, unsigned long addr) } /* - * Pretty-print a function pointer. - * - * ia64 and ppc64 function pointers are really function descriptors, - * which contain a pointer the real address. + * Pretty-print a function pointer. This function is deprecated. + * Please use the "%pF" vsprintf format instead. */ -static inline void print_fn_descriptor_symbol(const char *fmt, void *addr) +static inline void __deprecated print_fn_descriptor_symbol(const char *fmt, void *addr) { #if defined(CONFIG_IA64) || defined(CONFIG_PPC64) addr = *(void **)addr; -- cgit v1.2.3-70-g09d2 From 22b8ce94708f7cdf0b04965c6f7443dfd374c35c Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Wed, 15 Oct 2008 22:01:46 -0700 Subject: profiling: dynamically enable readprofile at runtime Way too often, I have a machine that exhibits some kind of crappy behavior. The CPU looks wedged in the kernel or it is spending way too much system time and I wonder what is responsible. I try to run readprofile. But, of course, Ubuntu doesn't enable it by default. Dang! The reason we boot-time enable it is that it takes a big bufffer that we generally can only bootmem alloc. But, does it hurt to at least try and runtime-alloc it? To use: echo 2 > /sys/kernel/profile Then run readprofile like normal. This should fix the compile issue with allmodconfig. I've compile-tested on a bunch more configs now including a few more architectures. Signed-off-by: Dave Hansen Acked-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-profiling | 13 ++++++++++ include/linux/profile.h | 8 +++--- kernel/ksysfs.c | 35 ++++++++++++++++++++++++++ kernel/profile.c | 41 +++++++++++++++++++++++-------- 4 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-profiling (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-profiling b/Documentation/ABI/testing/sysfs-profiling new file mode 100644 index 00000000000..b02d8b8c173 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-profiling @@ -0,0 +1,13 @@ +What: /sys/kernel/profile +Date: September 2008 +Contact: Dave Hansen +Description: + /sys/kernel/profile is the runtime equivalent + of the boot-time profile= option. + + You can get the same effect running: + + echo 2 > /sys/kernel/profile + + as you would by issuing profile=2 on the boot + command line. diff --git a/include/linux/profile.h b/include/linux/profile.h index 7e7087239af..570045053ce 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -35,7 +35,9 @@ enum profile_type { extern int prof_on __read_mostly; /* init basic kernel profiler */ -void __init profile_init(void); +int profile_init(void); +int profile_setup(char *str); +int create_proc_profile(void); void profile_tick(int type); /* @@ -84,9 +86,9 @@ struct pt_regs; #define prof_on 0 -static inline void profile_init(void) +static inline int profile_init(void) { - return; + return 0; } static inline void profile_tick(int type) diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index e53bc30e9ba..08dd8ed86c7 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define KERNEL_ATTR_RO(_name) \ @@ -53,6 +54,37 @@ static ssize_t uevent_helper_store(struct kobject *kobj, KERNEL_ATTR_RW(uevent_helper); #endif +#ifdef CONFIG_PROFILING +static ssize_t profiling_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", prof_on); +} +static ssize_t profiling_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int ret; + + if (prof_on) + return -EEXIST; + /* + * This eventually calls into get_option() which + * has a ton of callers and is not const. It is + * easiest to cast it away here. + */ + profile_setup((char *)buf); + ret = profile_init(); + if (ret) + return ret; + ret = create_proc_profile(); + if (ret) + return ret; + return count; +} +KERNEL_ATTR_RW(profiling); +#endif + #ifdef CONFIG_KEXEC static ssize_t kexec_loaded_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -109,6 +141,9 @@ static struct attribute * kernel_attrs[] = { &uevent_seqnum_attr.attr, &uevent_helper_attr.attr, #endif +#ifdef CONFIG_PROFILING + &profiling_attr.attr, +#endif #ifdef CONFIG_KEXEC &kexec_loaded_attr.attr, &kexec_crash_loaded_attr.attr, diff --git a/kernel/profile.c b/kernel/profile.c index cd26bed4cc2..a9e422df6bf 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -50,11 +52,11 @@ static DEFINE_PER_CPU(int, cpu_profile_flip); static DEFINE_MUTEX(profile_flip_mutex); #endif /* CONFIG_SMP */ -static int __init profile_setup(char *str) +int profile_setup(char *str) { - static char __initdata schedstr[] = "schedule"; - static char __initdata sleepstr[] = "sleep"; - static char __initdata kvmstr[] = "kvm"; + static char schedstr[] = "schedule"; + static char sleepstr[] = "sleep"; + static char kvmstr[] = "kvm"; int par; if (!strncmp(str, sleepstr, strlen(sleepstr))) { @@ -100,14 +102,33 @@ static int __init profile_setup(char *str) __setup("profile=", profile_setup); -void __init profile_init(void) +int profile_init(void) { + int buffer_bytes; if (!prof_on) - return; + return 0; /* only text is profiled */ prof_len = (_etext - _stext) >> prof_shift; - prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t)); + buffer_bytes = prof_len*sizeof(atomic_t); + if (!slab_is_available()) { + prof_buffer = alloc_bootmem(buffer_bytes); + return 0; + } + + prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL); + if (prof_buffer) + return 0; + + prof_buffer = alloc_pages_exact(buffer_bytes, GFP_KERNEL|__GFP_ZERO); + if (prof_buffer) + return 0; + + prof_buffer = vmalloc(buffer_bytes); + if (prof_buffer) + return 0; + + return -ENOMEM; } /* Profile event notifications */ @@ -527,7 +548,7 @@ static void __init profile_nop(void *unused) { } -static int __init create_hash_tables(void) +static int create_hash_tables(void) { int cpu; @@ -575,14 +596,14 @@ out_cleanup: #define create_hash_tables() ({ 0; }) #endif -static int __init create_proc_profile(void) +int create_proc_profile(void) { struct proc_dir_entry *entry; if (!prof_on) return 0; if (create_hash_tables()) - return -1; + return -ENOMEM; entry = proc_create("profile", S_IWUSR | S_IRUGO, NULL, &proc_profile_operations); if (!entry) -- cgit v1.2.3-70-g09d2 From 1c828320d2e063dd1ec84e873e6399bfc3d85d6f Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 15 Oct 2008 22:01:46 -0700 Subject: doc: typo in Documentation/filesystems/nfsroot.txt Add a missing word to the explanation of the purpose of the zdisk and bzdisk make targets. Signed-off-by: Shane McDonald Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/nfsroot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfsroot.txt b/Documentation/filesystems/nfsroot.txt index 31b32917234..68baddf3c3e 100644 --- a/Documentation/filesystems/nfsroot.txt +++ b/Documentation/filesystems/nfsroot.txt @@ -169,7 +169,7 @@ They depend on various facilities being available: 3.1) Booting from a floppy using syslinux When building kernels, an easy way to create a boot floppy that uses - syslinux is to use the zdisk or bzdisk make targets which use + syslinux is to use the zdisk or bzdisk make targets which use zimage and bzimage images respectively. Both targets accept the FDARGS parameter which can be used to set the kernel command line. -- cgit v1.2.3-70-g09d2 From 929f37cb3c3e0f4d23d7106693b7067cf72f4dbc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Oct 2008 22:01:48 -0700 Subject: dontdiff: more updates to be closer to gitignore defkeymap.c_shipped should be diffed if it is changed. Reported-by: Mike Galbraith COPYING, CREDITS, .mailmap should be diffed if they are changed. keywords.c_shipped & lex.c_shipped should be diffed when changed. parse.[ch]_shipped should be diffed when changed. Reported-by: Sam Ravnborg vsyscall* updates from a .gitignore patch by "Denis V. Lunev" . *.so.dbg from a .gitignore patch by Thomas Gleixner . binoffset from a .gitignore patch by Uwe Kleine-Koenig . Module.markers from a .gitignore patch by Matthew Wilcox . vmlinux*.lds* should be diffed if changed. Reported-by: Etienne Lorrain vmlinux.lds from a .gitignore patch by Daniel Guilak . *.scr should be diffed if changed. Lots of updates from http://lkml.org/lkml/2008/5/20/32 Reported-by: Bart Van Assche Use ncscope.* instead of *cscope* since the latter may catch too many files. Add *.elf, from a .gitignore patch by Eduard - Gabriel Munteanu . Make firmware entries match .gitignore entries. Make some entries less greedy by removing trailing '*'. Remove "make_times_h" (no such file). Remove "filelist" (no such file). Remove "dummy_sym.c" (no such file). Remove "gen-kdb_cmds.c" (no such file). Remove "gentbl" (no such file). Remove "kconfig.tk" (no such file). Remove "tkparse" (no such file). Remove "sim710_d.h" (no such file). Remove "53c8xx_d.h" (no such file). Add "syscalltab.h" (generated file). Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/dontdiff | 59 ++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dontdiff b/Documentation/dontdiff index 27809357da5..1e89a51ea49 100644 --- a/Documentation/dontdiff +++ b/Documentation/dontdiff @@ -2,11 +2,13 @@ *.aux *.bin *.cpio -*.css +*.csp +*.dsp *.dvi +*.elf *.eps -*.fw.gen.S *.fw +*.gen.S *.gif *.grep *.grp @@ -30,6 +32,7 @@ *.s *.sgml *.so +*.so.dbg *.symtypes *.tab.c *.tab.h @@ -38,24 +41,17 @@ *.xml *_MODULES *_vga16.c -*cscope* *~ *.9 *.9.gz .* -.cscope -.gitignore -.mailmap .mm 53c700_d.h -53c8xx_d.h* -COPYING -CREDITS CVS ChangeSet Image Kerntypes -MODS.txt +Module.markers Module.symvers PENDING SCCS @@ -73,7 +69,9 @@ autoconf.h* bbootsect bin2c binkernel.spec +binoffset bootsect +bounds.h bsetup btfixupprep build @@ -89,39 +87,36 @@ config_data.h* config_data.gz* conmakehash consolemap_deftbl.c* +cpustr.h crc32table.h* cscope.* -defkeymap.c* +defkeymap.c devlist.h* docproc -dummy_sym.c* elf2ecoff elfconfig.h* -filelist fixdep fore200e_mkfirm fore200e_pca_fw.c* gconf gen-devlist -gen-kdb_cmds.c* gen_crc32table gen_init_cpio genksyms -gentbl *_gray256.c +ihex2fw ikconfig.h* initramfs_data.cpio initramfs_data.cpio.gz initramfs_list kallsyms kconfig -kconfig.tk -keywords.c* +keywords.c ksym.c* ksym.h* kxgettext lkc_defs.h -lex.c* +lex.c lex.*.c logo_*.c logo_*_clut224.c @@ -130,7 +125,6 @@ lxdialog mach-types mach-types.h machtypes.h -make_times_h map maui_boot.h mconf @@ -138,6 +132,7 @@ miboot* mk_elfconfig mkboot mkbugboot +mkcpustr mkdep mkprep mktables @@ -145,11 +140,12 @@ mktree modpost modules.order modversions.h* +ncscope.* offset.h offsets.h oui.c* -parse.c* -parse.h* +parse.c +parse.h patches* pca200e.bin pca200e_ecd.bin2 @@ -157,7 +153,7 @@ piggy.gz piggyback pnmtologo ppc_defs.h* -promcon_tbl.c* +promcon_tbl.c pss_boot.h qconf raid6altivec*.c @@ -168,27 +164,38 @@ series setup setup.bin setup.elf -sim710_d.h* sImage sm_tbl* split-include +syscalltab.h tags tftpboot.img timeconst.h times.h* -tkparse trix_boot.h utsrelease.h* +vdso-syms.lds vdso.lds +vdso32-int80-syms.lds +vdso32-syms.lds +vdso32-syscall-syms.lds +vdso32-sysenter-syms.lds +vdso32.lds +vdso32.so.dbg +vdso64.lds +vdso64.so.dbg version.h* vmlinux vmlinux-* vmlinux.aout -vmlinux*.lds* -vmlinux*.scr +vmlinux.lds vsyscall.lds +vsyscall_32.lds wanxlfw.inc uImage unifdef +wakeup.bin +wakeup.elf +wakeup.lds zImage* zconf.hash.c -- cgit v1.2.3-70-g09d2 From 8033fe65a6d6c0e47ba9e3cb2e4e6902f9dfb8dd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Oct 2008 22:01:59 -0700 Subject: documentation: explain memory barriers We want all uses of memory barriers to be explained in the source code. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SubmitChecklist | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/SubmitChecklist b/Documentation/SubmitChecklist index 21f0795af20..ac5e0b2f109 100644 --- a/Documentation/SubmitChecklist +++ b/Documentation/SubmitChecklist @@ -85,3 +85,6 @@ 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: All memory barriers {e.g., barrier(), rmb(), wmb()} need a comment in the + source code that explains the logic of what they are doing and why. -- cgit v1.2.3-70-g09d2 From e1f8e87449147ffe5ea3de64a46af7de450ce279 Mon Sep 17 00:00:00 2001 From: Francois Cami Date: Wed, 15 Oct 2008 22:01:59 -0700 Subject: Remove Andrew Morton's old email accounts People can use the real name an an index into MAINTAINERS to find the current email address. Signed-off-by: Francois Cami Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SAK.txt | 2 +- Documentation/SubmittingDrivers | 2 +- Documentation/networking/cs89x0.txt | 4 ++-- Documentation/networking/vortex.txt | 4 ++-- Documentation/scsi/ChangeLog.megaraid | 6 +++--- drivers/char/vt.c | 2 +- drivers/net/3c509.c | 2 +- drivers/net/cs89x0.c | 13 ++++++------- drivers/parport/ChangeLog | 2 +- fs/direct-io.c | 4 ++-- fs/fs-writeback.c | 2 +- fs/mpage.c | 2 +- include/linux/journal-head.h | 2 +- include/linux/task_io_accounting.h | 2 +- kernel/printk.c | 2 +- kernel/workqueue.c | 2 +- mm/fadvise.c | 2 +- mm/page-writeback.c | 2 +- mm/pdflush.c | 2 +- mm/readahead.c | 2 +- mm/truncate.c | 2 +- 21 files changed, 31 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/SAK.txt b/Documentation/SAK.txt index b9019ca872e..74be14679ed 100644 --- a/Documentation/SAK.txt +++ b/Documentation/SAK.txt @@ -1,5 +1,5 @@ Linux 2.4.2 Secure Attention Key (SAK) handling -18 March 2001, Andrew Morton +18 March 2001, Andrew Morton An operating system's Secure Attention Key is a security tool which is provided as protection against trojan password capturing programs. It diff --git a/Documentation/SubmittingDrivers b/Documentation/SubmittingDrivers index 24f2eb40cae..99e72a81fa2 100644 --- a/Documentation/SubmittingDrivers +++ b/Documentation/SubmittingDrivers @@ -41,7 +41,7 @@ Linux 2.4: Linux 2.6: The same rules apply as 2.4 except that you should follow linux-kernel to track changes in API's. The final contact point for Linux 2.6 - submissions is Andrew Morton . + submissions is Andrew Morton. What Criteria Determine Acceptance ---------------------------------- diff --git a/Documentation/networking/cs89x0.txt b/Documentation/networking/cs89x0.txt index 6387d3decf8..c725d33b316 100644 --- a/Documentation/networking/cs89x0.txt +++ b/Documentation/networking/cs89x0.txt @@ -3,7 +3,7 @@ NOTE ---- This document was contributed by Cirrus Logic for kernel 2.2.5. This version -has been updated for 2.3.48 by Andrew Morton +has been updated for 2.3.48 by Andrew Morton. Cirrus make a copy of this driver available at their website, as described below. In general, you should use the driver version which @@ -690,7 +690,7 @@ latest drivers and technical publications. 6.4 Current maintainer In February 2000 the maintenance of this driver was assumed by Andrew -Morton +Morton. 6.5 Kernel module parameters diff --git a/Documentation/networking/vortex.txt b/Documentation/networking/vortex.txt index 6356d3faed3..bd874daabde 100644 --- a/Documentation/networking/vortex.txt +++ b/Documentation/networking/vortex.txt @@ -1,5 +1,5 @@ Documentation/networking/vortex.txt -Andrew Morton +Andrew Morton 30 April 2000 @@ -11,7 +11,7 @@ The driver was written by Donald Becker Don is no longer the prime maintainer of this version of the driver. Please report problems to one or more of: - Andrew Morton + Andrew Morton Netdev mailing list Linux kernel mailing list diff --git a/Documentation/scsi/ChangeLog.megaraid b/Documentation/scsi/ChangeLog.megaraid index 37796fe45bd..eaa4801f2ce 100644 --- a/Documentation/scsi/ChangeLog.megaraid +++ b/Documentation/scsi/ChangeLog.megaraid @@ -409,7 +409,7 @@ i. Function reordering so that inline functions are defined before they megaraid_mbox_prepare_pthru, megaraid_mbox_prepare_epthru, megaraid_busywait_mbox - - Andrew Morton , 08.19.2004 + - Andrew Morton, 08.19.2004 linux-scsi mailing list "Something else to clean up after inclusion: every instance of an @@ -471,13 +471,13 @@ vi. Add support for 64-bit applications. Current drivers assume only vii. Move the function declarations for the management module from megaraid_mm.h to megaraid_mm.c - - Andrew Morton , 08.19.2004 + - Andrew Morton, 08.19.2004 linux-scsi mailing list viii. Change default values for MEGARAID_NEWGEN, MEGARAID_MM, and MEGARAID_MAILBOX to 'n' in Kconfig.megaraid - - Andrew Morton , 08.19.2004 + - Andrew Morton, 08.19.2004 linux-scsi mailing list ix. replace udelay with msleep diff --git a/drivers/char/vt.c b/drivers/char/vt.c index a0f7ffb6808..d8f83e26e4a 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -59,7 +59,7 @@ * by Martin Mares , July 1998 * * Removed old-style timers, introduced console_timer, made timer - * deletion SMP-safe. 17Jun00, Andrew Morton + * deletion SMP-safe. 17Jun00, Andrew Morton * * Removed console_lock, enabled interrupts across all console operations * 13 March 2001, Andrew Morton diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index b9d097c9f6b..3a7bc524af3 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -40,7 +40,7 @@ v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb v1.15 1/31/98 Faster recovery for Tx errors. -djb v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb - v1.18 12Mar2001 Andrew Morton + v1.18 12Mar2001 Andrew Morton - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - Reviewed against 1.18 from scyld.com v1.18a 17Nov2001 Jeff Garzik diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index a28de818280..7107620f615 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -36,8 +36,7 @@ Alan Cox : Removed 1.2 support, added 2.1 extra counters. - Andrew Morton : andrewm@uow.edu.au - : Kernel 2.3.48 + Andrew Morton : Kernel 2.3.48 : Handle kmalloc() failures : Other resource allocation fixes : Add SMP locks @@ -49,7 +48,7 @@ : Fixed an out-of-mem bug in dma_rx() : Updated Documentation/networking/cs89x0.txt - Andrew Morton : andrewm@uow.edu.au / Kernel 2.3.99-pre1 + Andrew Morton : Kernel 2.3.99-pre1 : Use skb_reserve to longword align IP header (two places) : Remove a delay loop from dma_rx() : Replace '100' with HZ @@ -57,11 +56,11 @@ : Added 'cs89x0_dma=N' kernel boot option : Correctly initialise lp->lock in non-module compile - Andrew Morton : andrewm@uow.edu.au / Kernel 2.3.99-pre4-1 + Andrew Morton : Kernel 2.3.99-pre4-1 : MOD_INC/DEC race fix (see : http://www.uwsg.indiana.edu/hypermail/linux/kernel/0003.3/1532.html) - Andrew Morton : andrewm@uow.edu.au / Kernel 2.4.0-test7-pre2 + Andrew Morton : Kernel 2.4.0-test7-pre2 : Enhanced EEPROM support to cover more devices, : abstracted IRQ mapping to support CONFIG_ARCH_CLPS7500 arch : (Jason Gunthorpe ) @@ -156,7 +155,7 @@ #include "cs89x0.h" static char version[] __initdata = -"cs89x0.c: v2.4.3-pre1 Russell Nelson , Andrew Morton \n"; +"cs89x0.c: v2.4.3-pre1 Russell Nelson , Andrew Morton\n"; #define DRV_NAME "cs89x0" @@ -1877,7 +1876,7 @@ MODULE_PARM_DESC(dmasize , "(ignored)"); MODULE_PARM_DESC(use_dma , "(ignored)"); #endif -MODULE_AUTHOR("Mike Cruse, Russwll Nelson , Andrew Morton "); +MODULE_AUTHOR("Mike Cruse, Russwll Nelson , Andrew Morton"); MODULE_LICENSE("GPL"); diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index db717c1d62a..8565bbbeb6e 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -311,7 +311,7 @@ * ieee1284_ops.c (parport_ieee1284_read_nibble): Reset nAutoFd on timeout. Matches 2.2.x behaviour. -2001-03-02 Andrew Morton +2001-03-02 Andrew Morton * parport_pc.c (registered_parport): New static variable. (parport_pc_find_ports): Set it when we register PCI driver. diff --git a/fs/direct-io.c b/fs/direct-io.c index 9606ee848fd..af0558dbe8b 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -5,11 +5,11 @@ * * O_DIRECT * - * 04Jul2002 akpm@zip.com.au + * 04Jul2002 Andrew Morton * Initial version * 11Sep2002 janetinc@us.ibm.com * added readv/writev support. - * 29Oct2002 akpm@zip.com.au + * 29Oct2002 Andrew Morton * rewrote bio_add_page() support. * 30Oct2002 pbadari@us.ibm.com * added support for non-aligned IO. diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 25adfc3c693..d0ff0b8cf30 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -8,7 +8,7 @@ * pages against inodes. ie: data writeback. Writeout of the * inode itself is not handled here. * - * 10Apr2002 akpm@zip.com.au + * 10Apr2002 Andrew Morton * Split out of fs/inode.c * Additions for address_space-based writeback */ diff --git a/fs/mpage.c b/fs/mpage.c index dbcc7af76a1..552b80b3fac 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -6,7 +6,7 @@ * Contains functions related to preparing and submitting BIOs which contain * multiple pagecache pages. * - * 15May2002 akpm@zip.com.au + * 15May2002 Andrew Morton * Initial version * 27Jun2002 axboe@suse.de * use bio_add_page() to build bio's just the right size diff --git a/include/linux/journal-head.h b/include/linux/journal-head.h index 8a62d1e84b9..bb70ebb6a2d 100644 --- a/include/linux/journal-head.h +++ b/include/linux/journal-head.h @@ -3,7 +3,7 @@ * * buffer_head fields for JBD * - * 27 May 2001 Andrew Morton + * 27 May 2001 Andrew Morton * Created - pulled out of fs.h */ diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 5e88afc9a2f..bdf855c2856 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -5,7 +5,7 @@ * Don't include this header file directly - it is designed to be dragged in via * sched.h. * - * Blame akpm@osdl.org for all this. + * Blame Andrew Morton for all this. */ struct task_io_accounting { diff --git a/kernel/printk.c b/kernel/printk.c index a430fd04008..c3ab51dc9fa 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -13,7 +13,7 @@ * Fixed SMP synchronization, 08/08/99, Manfred Spraul * manfred@colorfullife.com * Rewrote bits to get rid of console_lock - * 01Mar01 Andrew Morton + * 01Mar01 Andrew Morton */ #include diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 4048e92aa04..714afad4653 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -9,7 +9,7 @@ * Derived from the taskqueue/keventd code by: * * David Woodhouse - * Andrew Morton + * Andrew Morton * Kai Petzke * Theodore Ts'o * diff --git a/mm/fadvise.c b/mm/fadvise.c index 343cfdfebd9..a1da969bd98 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c @@ -3,7 +3,7 @@ * * Copyright (C) 2002, Linus Torvalds * - * 11Jan2003 akpm@digeo.com + * 11Jan2003 Andrew Morton * Initial version. */ diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 24de8b65fdb..c130a137c12 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -7,7 +7,7 @@ * Contains functions related to writing back dirty pages at the * address_space level. * - * 10Apr2002 akpm@zip.com.au + * 10Apr2002 Andrew Morton * Initial version */ diff --git a/mm/pdflush.c b/mm/pdflush.c index 0cbe0c60c6b..a0a14c4d507 100644 --- a/mm/pdflush.c +++ b/mm/pdflush.c @@ -3,7 +3,7 @@ * * Copyright (C) 2002, Linus Torvalds. * - * 09Apr2002 akpm@zip.com.au + * 09Apr2002 Andrew Morton * Initial version * 29Feb2004 kaos@sgi.com * Move worker thread creation to kthread to avoid chewing diff --git a/mm/readahead.c b/mm/readahead.c index 77e8ddf945e..6cbd9a72fde 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -3,7 +3,7 @@ * * Copyright (C) 2002, Linus Torvalds * - * 09Apr2002 akpm@zip.com.au + * 09Apr2002 Andrew Morton * Initial version. */ diff --git a/mm/truncate.c b/mm/truncate.c index 6650c1d878b..e83e4b114ef 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -3,7 +3,7 @@ * * Copyright (C) 2002, Linus Torvalds * - * 10Sep2002 akpm@zip.com.au + * 10Sep2002 Andrew Morton * Initial version. */ -- cgit v1.2.3-70-g09d2 From 2223c65103d2aa8d0e9c48a956035a1e0353233d Mon Sep 17 00:00:00 2001 From: FD Cami Date: Wed, 15 Oct 2008 22:02:00 -0700 Subject: Remove Andrew Morton's http://www.zip.com.au/~akpm/ Remove Andrew Morton's http://www.zip.com.au/~akpm/ urls, update to new ones when necessary, delete references otherwise. There are still instances of that living in: Documentation/zh_CN/HOWTO Documentation/zh_CN/SubmittingPatches Documentation/ko_KR/HOWTO Documentation/ja_JP/SubmittingPatches Signed-off-by: Francois Cami Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/HOWTO | 4 ++-- Documentation/SubmittingPatches | 4 ++-- Documentation/filesystems/ext3.txt | 3 +-- Documentation/networking/vortex.txt | 5 ----- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/HOWTO b/Documentation/HOWTO index 48a3955f05f..8495fc97039 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -112,7 +112,7 @@ required reading: Other excellent descriptions of how to create patches properly are: "The Perfect Patch" - http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + http://userweb.kernel.org/~akpm/stuff/tpp.txt "Linux kernel patch submission format" http://linux.yyz.us/patch-format.html @@ -620,7 +620,7 @@ all time. It should describe the patch completely, containing: For more details on what this should all look like, please see the ChangeLog section of the document: "The Perfect Patch" - http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + http://userweb.kernel.org/~akpm/stuff/tpp.txt diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index f79ad9ff603..bee87f2842a 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -77,7 +77,7 @@ Quilt: http://savannah.nongnu.org/projects/quilt Andrew Morton's patch scripts: -http://www.zip.com.au/~akpm/linux/patches/ +http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz Instead of these scripts, quilt is the recommended patch management tool (see above). @@ -653,7 +653,7 @@ SECTION 3 - REFERENCES ---------------------- Andrew Morton, "The perfect patch" (tpp). - + Jeff Garzik, "Linux kernel patch submission format". diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt index b45f3c1b8b4..295f26cd895 100644 --- a/Documentation/filesystems/ext3.txt +++ b/Documentation/filesystems/ext3.txt @@ -193,6 +193,5 @@ kernel source: programs: http://e2fsprogs.sourceforge.net/ http://ext2resize.sourceforge.net -useful links: http://www.zip.com.au/~akpm/linux/ext3/ext3-usage.html - http://www-106.ibm.com/developerworks/linux/library/l-fs7/ +useful links: http://www-106.ibm.com/developerworks/linux/library/l-fs7/ http://www-106.ibm.com/developerworks/linux/library/l-fs8/ diff --git a/Documentation/networking/vortex.txt b/Documentation/networking/vortex.txt index bd874daabde..bd70976b816 100644 --- a/Documentation/networking/vortex.txt +++ b/Documentation/networking/vortex.txt @@ -305,11 +305,6 @@ Donald's wake-on-LAN page: ftp://ftp.3com.com/pub/nic/3c90x/3c90xx2.exe -Driver updates and a detailed changelog for the modifications which -were made for the 2.3/2,4 series kernel is available at - - http://www.zip.com.au/~akpm/linux/#3c59x-bc - Autonegotiation notes --------------------- -- cgit v1.2.3-70-g09d2 From 22b8ab66deb2600f93d24d30df17b9d9e5273d05 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Wed, 15 Oct 2008 22:02:01 -0700 Subject: Document panic_on_unrecovered_nmi sysctl This adds "panic_on_unrecovered_nmi" sysctl to Documentation/filesystems/proc.txt. The text is mainly taken from http://readlist.com/lists/vger.kernel.org/linux-kernel/43/217998.html. Signed-off-by: Bernhard Walle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index b488edad743..c032bf39e8b 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1321,6 +1321,18 @@ debugging information is displayed on console. NMI switch that most IA32 servers have fires unknown NMI up, for example. If a system hangs up, try pressing the NMI switch. +panic_on_unrecovered_nmi +------------------------ + +The default Linux behaviour on an NMI of either memory or unknown is to continue +operation. For many environments such as scientific computing it is preferable +that the box is taken out and the error dealt with than an uncorrected +parity/ECC error get propogated. + +A small number of systems do generate NMI's for bizarre random reasons such as +power management so the default is off. That sysctl works like the existing +panic controls already in that directory. + nmi_watchdog ------------ -- cgit v1.2.3-70-g09d2 From 9536727ef696861b205834dd2e01456b91088cb7 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 15 Oct 2008 22:02:02 -0700 Subject: SubmittingPatches: add a reference to Andi's OLS paper For this year's OLS I wrote a paper on successfull strategies to submit difficult kernel patches. Add a reference to it to SubmittingPatches. Signed-off-by: Andi Kleen Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SubmittingPatches | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index bee87f2842a..7b67f3bf8dd 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -672,4 +672,9 @@ Kernel Documentation/CodingStyle: Linus Torvalds's mail on the canonical patch format: + +Andi Kleen, "On submitting kernel patches" + Some strategies to get difficult or controversal changes in. + http://halobates.de/on-submitting-patches.pdf + -- -- cgit v1.2.3-70-g09d2 From f1f640a9c1d97a1a131879ab1efe3766443904d7 Mon Sep 17 00:00:00 2001 From: Vernon Sauder Date: Wed, 15 Oct 2008 22:02:43 -0700 Subject: pxa2xx_spi: fix chip_info defaults and documentation. Make the chip info structure data optional by providing reasonable defaults. Improve corresponding documentation, and highlight the drawback of not providing explicit chipselect control. DMA can determine appropriate dma_burst_size and thresholds automatically so use DMA even if dma_burst_size is not specified. Signed-off-by: Vernon Sauder Reviewed-by: Ned Forrester Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/spi/pxa2xx | 34 +++++++++++++++++++++++----------- drivers/spi/pxa2xx_spi.c | 46 +++++++++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/spi/pxa2xx b/Documentation/spi/pxa2xx index bbe8dee681a..6bb916d57c9 100644 --- a/Documentation/spi/pxa2xx +++ b/Documentation/spi/pxa2xx @@ -96,7 +96,7 @@ Each slave device attached to the PXA must provide slave specific configuration information via the structure "pxa2xx_spi_chip" found in "arch/arm/mach-pxa/include/mach/pxa2xx_spi.h". The pxa2xx_spi master controller driver will uses the configuration whenever the driver communicates with the slave -device. +device. All fields are optional. struct pxa2xx_spi_chip { u8 tx_threshold; @@ -112,14 +112,17 @@ used to configure the SSP hardware fifo. These fields are critical to the performance of pxa2xx_spi driver and misconfiguration will result in rx fifo overruns (especially in PIO mode transfers). Good default values are - .tx_threshold = 12, - .rx_threshold = 4, + .tx_threshold = 8, + .rx_threshold = 8, + +The range is 1 to 16 where zero indicates "use default". The "pxa2xx_spi_chip.dma_burst_size" field is used to configure PXA2xx DMA engine and is related the "spi_device.bits_per_word" field. Read and understand the PXA2xx "Developer Manual" sections on the DMA controller and SSP Controllers to determine the correct value. An SSP configured for byte-wide transfers would -use a value of 8. +use a value of 8. The driver will determine a reasonable default if +dma_burst_size == 0. The "pxa2xx_spi_chip.timeout" fields is used to efficiently handle trailing bytes in the SSP receiver fifo. The correct value for this field is @@ -137,7 +140,13 @@ function for asserting/deasserting a slave device chip select. If the field is NULL, the pxa2xx_spi master controller driver assumes that the SSP port is configured to use SSPFRM instead. -NSSP SALVE SAMPLE +NOTE: the SPI driver cannot control the chip select if SSPFRM is used, so the +chipselect is dropped after each spi_transfer. Most devices need chip select +asserted around the complete message. Use SSPFRM as a GPIO (through cs_control) +to accomodate these chips. + + +NSSP SLAVE SAMPLE ----------------- The pxa2xx_spi_chip structure is passed to the pxa2xx_spi driver in the "spi_board_info.controller_data" field. Below is a sample configuration using @@ -206,18 +215,21 @@ static void __init streetracer_init(void) DMA and PIO I/O Support ----------------------- -The pxa2xx_spi driver support both DMA and interrupt driven PIO message -transfers. The driver defaults to PIO mode and DMA transfers must enabled by -setting the "enable_dma" flag in the "pxa2xx_spi_master" structure and -ensuring that the "pxa2xx_spi_chip.dma_burst_size" field is non-zero. The DMA -mode support both coherent and stream based DMA mappings. +The pxa2xx_spi driver supports both DMA and interrupt driven PIO message +transfers. The driver defaults to PIO mode and DMA transfers must be enabled +by setting the "enable_dma" flag in the "pxa2xx_spi_master" structure. The DMA +mode supports both coherent and stream based DMA mappings. The following logic is used to determine the type of I/O to be used on a per "spi_transfer" basis: -if !enable_dma or dma_burst_size == 0 then +if !enable_dma then always use PIO transfers +if spi_message.len > 8191 then + print "rate limited" warning + use PIO transfers + if spi_message.is_dma_mapped and rx_dma_buf != 0 and tx_dma_buf != 0 then use coherent DMA mode diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 59ae3ed1665..dae87b1a4c6 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -47,6 +47,10 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define MAX_BUSES 3 +#define RX_THRESH_DFLT 8 +#define TX_THRESH_DFLT 8 +#define TIMOUT_DFLT 1000 + #define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) #define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) #define IS_DMA_ALIGNED(x) ((((u32)(x)) & 0x07) == 0) @@ -1171,6 +1175,8 @@ static int setup(struct spi_device *spi) struct driver_data *drv_data = spi_master_get_devdata(spi->master); struct ssp_device *ssp = drv_data->ssp; unsigned int clk_div; + uint tx_thres = TX_THRESH_DFLT; + uint rx_thres = RX_THRESH_DFLT; if (!spi->bits_per_word) spi->bits_per_word = 8; @@ -1209,8 +1215,7 @@ static int setup(struct spi_device *spi) chip->cs_control = null_cs_control; chip->enable_dma = 0; - chip->timeout = 1000; - chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1); + chip->timeout = TIMOUT_DFLT; chip->dma_burst_size = drv_data->master_info->enable_dma ? DCMD_BURST8 : 0; } @@ -1224,22 +1229,21 @@ static int setup(struct spi_device *spi) if (chip_info) { if (chip_info->cs_control) chip->cs_control = chip_info->cs_control; - - chip->timeout = chip_info->timeout; - - chip->threshold = (SSCR1_RxTresh(chip_info->rx_threshold) & - SSCR1_RFT) | - (SSCR1_TxTresh(chip_info->tx_threshold) & - SSCR1_TFT); - - chip->enable_dma = chip_info->dma_burst_size != 0 - && drv_data->master_info->enable_dma; + if (chip_info->timeout) + chip->timeout = chip_info->timeout; + if (chip_info->tx_threshold) + tx_thres = chip_info->tx_threshold; + if (chip_info->rx_threshold) + rx_thres = chip_info->rx_threshold; + chip->enable_dma = drv_data->master_info->enable_dma; chip->dma_threshold = 0; - if (chip_info->enable_loopback) chip->cr1 = SSCR1_LBM; } + chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | + (SSCR1_TxTresh(tx_thres) & SSCR1_TFT); + /* set dma burst and threshold outside of chip_info path so that if * chip_info goes away after setting chip->enable_dma, the * burst and threshold can still respond to changes in bits_per_word */ @@ -1268,17 +1272,19 @@ static int setup(struct spi_device *spi) /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (drv_data->ssp_type != PXA25x_SSP) - dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d, %s\n", spi->bits_per_word, clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), - spi->mode & 0x3); + spi->mode & 0x3, + chip->enable_dma ? "DMA" : "PIO"); else - dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d, %s\n", spi->bits_per_word, - clk_get_rate(ssp->clk) + clk_get_rate(ssp->clk) / 2 / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), - spi->mode & 0x3); + spi->mode & 0x3, + chip->enable_dma ? "DMA" : "PIO"); if (spi->bits_per_word <= 8) { chip->n_bytes = 1; @@ -1498,7 +1504,9 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) /* Load default SSP configuration */ write_SSCR0(0, drv_data->ioaddr); - write_SSCR1(SSCR1_RxTresh(4) | SSCR1_TxTresh(12), drv_data->ioaddr); + write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | + SSCR1_TxTresh(TX_THRESH_DFLT), + drv_data->ioaddr); write_SSCR0(SSCR0_SerClkDiv(2) | SSCR0_Motorola | SSCR0_DataSize(8), -- cgit v1.2.3-70-g09d2 From 4b22ff13415fa30b6282c88da790c82b4c6e5127 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Wed, 15 Oct 2008 22:02:53 -0700 Subject: autofs4: device node ioctl documentation Add documentation for the miscellaneous device module of autofs4. Signed-off-by: Ian Kent Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../filesystems/autofs4-mount-control.txt | 393 +++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 Documentation/filesystems/autofs4-mount-control.txt (limited to 'Documentation') diff --git a/Documentation/filesystems/autofs4-mount-control.txt b/Documentation/filesystems/autofs4-mount-control.txt new file mode 100644 index 00000000000..c6341745df3 --- /dev/null +++ b/Documentation/filesystems/autofs4-mount-control.txt @@ -0,0 +1,393 @@ + +Miscellaneous Device control operations for the autofs4 kernel module +==================================================================== + +The problem +=========== + +There is a problem with active restarts in autofs (that is to say +restarting autofs when there are busy mounts). + +During normal operation autofs uses a file descriptor opened on the +directory that is being managed in order to be able to issue control +operations. Using a file descriptor gives ioctl operations access to +autofs specific information stored in the super block. The operations +are things such as setting an autofs mount catatonic, setting the +expire timeout and requesting expire checks. As is explained below, +certain types of autofs triggered mounts can end up covering an autofs +mount itself which prevents us being able to use open(2) to obtain a +file descriptor for these operations if we don't already have one open. + +Currently autofs uses "umount -l" (lazy umount) to clear active mounts +at restart. While using lazy umount works for most cases, anything that +needs to walk back up the mount tree to construct a path, such as +getcwd(2) and the proc file system /proc//cwd, no longer works +because the point from which the path is constructed has been detached +from the mount tree. + +The actual problem with autofs is that it can't reconnect to existing +mounts. Immediately one thinks of just adding the ability to remount +autofs file systems would solve it, but alas, that can't work. This is +because autofs direct mounts and the implementation of "on demand mount +and expire" of nested mount trees have the file system mounted directly +on top of the mount trigger directory dentry. + +For example, there are two types of automount maps, direct (in the kernel +module source you will see a third type called an offset, which is just +a direct mount in disguise) and indirect. + +Here is a master map with direct and indirect map entries: + +/- /etc/auto.direct +/test /etc/auto.indirect + +and the corresponding map files: + +/etc/auto.direct: + +/automount/dparse/g6 budgie:/autofs/export1 +/automount/dparse/g1 shark:/autofs/export1 +and so on. + +/etc/auto.indirect: + +g1 shark:/autofs/export1 +g6 budgie:/autofs/export1 +and so on. + +For the above indirect map an autofs file system is mounted on /test and +mounts are triggered for each sub-directory key by the inode lookup +operation. So we see a mount of shark:/autofs/export1 on /test/g1, for +example. + +The way that direct mounts are handled is by making an autofs mount on +each full path, such as /automount/dparse/g1, and using it as a mount +trigger. So when we walk on the path we mount shark:/autofs/export1 "on +top of this mount point". Since these are always directories we can +use the follow_link inode operation to trigger the mount. + +But, each entry in direct and indirect maps can have offsets (making +them multi-mount map entries). + +For example, an indirect mount map entry could also be: + +g1 \ + / shark:/autofs/export5/testing/test \ + /s1 shark:/autofs/export/testing/test/s1 \ + /s2 shark:/autofs/export5/testing/test/s2 \ + /s1/ss1 shark:/autofs/export1 \ + /s2/ss2 shark:/autofs/export2 + +and a similarly a direct mount map entry could also be: + +/automount/dparse/g1 \ + / shark:/autofs/export5/testing/test \ + /s1 shark:/autofs/export/testing/test/s1 \ + /s2 shark:/autofs/export5/testing/test/s2 \ + /s1/ss1 shark:/autofs/export2 \ + /s2/ss2 shark:/autofs/export2 + +One of the issues with version 4 of autofs was that, when mounting an +entry with a large number of offsets, possibly with nesting, we needed +to mount and umount all of the offsets as a single unit. Not really a +problem, except for people with a large number of offsets in map entries. +This mechanism is used for the well known "hosts" map and we have seen +cases (in 2.4) where the available number of mounts are exhausted or +where the number of privileged ports available is exhausted. + +In version 5 we mount only as we go down the tree of offsets and +similarly for expiring them which resolves the above problem. There is +somewhat more detail to the implementation but it isn't needed for the +sake of the problem explanation. The one important detail is that these +offsets are implemented using the same mechanism as the direct mounts +above and so the mount points can be covered by a mount. + +The current autofs implementation uses an ioctl file descriptor opened +on the mount point for control operations. The references held by the +descriptor are accounted for in checks made to determine if a mount is +in use and is also used to access autofs file system information held +in the mount super block. So the use of a file handle needs to be +retained. + + +The Solution +============ + +To be able to restart autofs leaving existing direct, indirect and +offset mounts in place we need to be able to obtain a file handle +for these potentially covered autofs mount points. Rather than just +implement an isolated operation it was decided to re-implement the +existing ioctl interface and add new operations to provide this +functionality. + +In addition, to be able to reconstruct a mount tree that has busy mounts, +the uid and gid of the last user that triggered the mount needs to be +available because these can be used as macro substitution variables in +autofs maps. They are recorded at mount request time and an operation +has been added to retrieve them. + +Since we're re-implementing the control interface, a couple of other +problems with the existing interface have been addressed. First, when +a mount or expire operation completes a status is returned to the +kernel by either a "send ready" or a "send fail" operation. The +"send fail" operation of the ioctl interface could only ever send +ENOENT so the re-implementation allows user space to send an actual +status. Another expensive operation in user space, for those using +very large maps, is discovering if a mount is present. Usually this +involves scanning /proc/mounts and since it needs to be done quite +often it can introduce significant overhead when there are many entries +in the mount table. An operation to lookup the mount status of a mount +point dentry (covered or not) has also been added. + +Current kernel development policy recommends avoiding the use of the +ioctl mechanism in favor of systems such as Netlink. An implementation +using this system was attempted to evaluate its suitability and it was +found to be inadequate, in this case. The Generic Netlink system was +used for this as raw Netlink would lead to a significant increase in +complexity. There's no question that the Generic Netlink system is an +elegant solution for common case ioctl functions but it's not a complete +replacement probably because it's primary purpose in life is to be a +message bus implementation rather than specifically an ioctl replacement. +While it would be possible to work around this there is one concern +that lead to the decision to not use it. This is that the autofs +expire in the daemon has become far to complex because umount +candidates are enumerated, almost for no other reason than to "count" +the number of times to call the expire ioctl. This involves scanning +the mount table which has proved to be a big overhead for users with +large maps. The best way to improve this is try and get back to the +way the expire was done long ago. That is, when an expire request is +issued for a mount (file handle) we should continually call back to +the daemon until we can't umount any more mounts, then return the +appropriate status to the daemon. At the moment we just expire one +mount at a time. A Generic Netlink implementation would exclude this +possibility for future development due to the requirements of the +message bus architecture. + + +autofs4 Miscellaneous Device mount control interface +==================================================== + +The control interface is opening a device node, typically /dev/autofs. + +All the ioctls use a common structure to pass the needed parameter +information and return operation results: + +struct autofs_dev_ioctl { + __u32 ver_major; + __u32 ver_minor; + __u32 size; /* total size of data passed in + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + + __u32 arg1; /* Command parameters */ + __u32 arg2; + + char path[0]; +}; + +The ioctlfd field is a mount point file descriptor of an autofs mount +point. It is returned by the open call and is used by all calls except +the check for whether a given path is a mount point, where it may +optionally be used to check a specific mount corresponding to a given +mount point file descriptor, and when requesting the uid and gid of the +last successful mount on a directory within the autofs file system. + +The fields arg1 and arg2 are used to communicate parameters and results of +calls made as described below. + +The path field is used to pass a path where it is needed and the size field +is used account for the increased structure length when translating the +structure sent from user space. + +This structure can be initialized before setting specific fields by using +the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). + +All of the ioctls perform a copy of this structure from user space to +kernel space and return -EINVAL if the size parameter is smaller than +the structure size itself, -ENOMEM if the kernel memory allocation fails +or -EFAULT if the copy itself fails. Other checks include a version check +of the compiled in user space version against the module version and a +mismatch results in a -EINVAL return. If the size field is greater than +the structure size then a path is assumed to be present and is checked to +ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is +returned. Following these checks, for all ioctl commands except +AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and +AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is +not a valid descriptor or doesn't correspond to an autofs mount point +an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is +returned. + + +The ioctls +========== + +An example of an implementation which uses this interface can be seen +in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the +distribution tar available for download from kernel.org in directory +/pub/linux/daemons/autofs/v5. + +The device node ioctl operations implemented by this interface are: + + +AUTOFS_DEV_IOCTL_VERSION +------------------------ + +Get the major and minor version of the autofs4 device ioctl kernel module +implementation. It requires an initialized struct autofs_dev_ioctl as an +input parameter and sets the version information in the passed in structure. +It returns 0 on success or the error -EINVAL if a version mismatch is +detected. + + +AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD +------------------------------------------------------------------ + +Get the major and minor version of the autofs4 protocol version understood +by loaded module. This call requires an initialized struct autofs_dev_ioctl +with the ioctlfd field set to a valid autofs mount point descriptor +and sets the requested version number in structure field arg1. These +commands return 0 on success or one of the negative error codes if +validation fails. + + +AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT +---------------------------------------------------------- + +Obtain and release a file descriptor for an autofs managed mount point +path. The open call requires an initialized struct autofs_dev_ioctl with +the the path field set and the size field adjusted appropriately as well +as the arg1 field set to the device number of the autofs mount. The +device number can be obtained from the mount options shown in +/proc/mounts. The close call requires an initialized struct +autofs_dev_ioct with the ioctlfd field set to the descriptor obtained +from the open call. The release of the file descriptor can also be done +with close(2) so any open descriptors will also be closed at process exit. +The close call is included in the implemented operations largely for +completeness and to provide for a consistent user space implementation. + + +AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD +-------------------------------------------------------- + +Return mount and expire result status from user space to the kernel. +Both of these calls require an initialized struct autofs_dev_ioctl +with the ioctlfd field set to the descriptor obtained from the open +call and the arg1 field set to the wait queue token number, received +by user space in the foregoing mount or expire request. The arg2 field +is set to the status to be returned. For the ready call this is always +0 and for the fail call it is set to the errno of the operation. + + +AUTOFS_DEV_IOCTL_SETPIPEFD_CMD +------------------------------ + +Set the pipe file descriptor used for kernel communication to the daemon. +Normally this is set at mount time using an option but when reconnecting +to a existing mount we need to use this to tell the autofs mount about +the new kernel pipe descriptor. In order to protect mounts against +incorrectly setting the pipe descriptor we also require that the autofs +mount be catatonic (see next call). + +The call requires an initialized struct autofs_dev_ioctl with the +ioctlfd field set to the descriptor obtained from the open call and +the arg1 field set to descriptor of the pipe. On success the call +also sets the process group id used to identify the controlling process +(eg. the owning automount(8) daemon) to the process group of the caller. + + +AUTOFS_DEV_IOCTL_CATATONIC_CMD +------------------------------ + +Make the autofs mount point catatonic. The autofs mount will no longer +issue mount requests, the kernel communication pipe descriptor is released +and any remaining waits in the queue released. + +The call requires an initialized struct autofs_dev_ioctl with the +ioctlfd field set to the descriptor obtained from the open call. + + +AUTOFS_DEV_IOCTL_TIMEOUT_CMD +---------------------------- + +Set the expire timeout for mounts withing an autofs mount point. + +The call requires an initialized struct autofs_dev_ioctl with the +ioctlfd field set to the descriptor obtained from the open call. + + +AUTOFS_DEV_IOCTL_REQUESTER_CMD +------------------------------ + +Return the uid and gid of the last process to successfully trigger a the +mount on the given path dentry. + +The call requires an initialized struct autofs_dev_ioctl with the path +field set to the mount point in question and the size field adjusted +appropriately as well as the arg1 field set to the device number of the +containing autofs mount. Upon return the struct field arg1 contains the +uid and arg2 the gid. + +When reconstructing an autofs mount tree with active mounts we need to +re-connect to mounts that may have used the original process uid and +gid (or string variations of them) for mount lookups within the map entry. +This call provides the ability to obtain this uid and gid so they may be +used by user space for the mount map lookups. + + +AUTOFS_DEV_IOCTL_EXPIRE_CMD +--------------------------- + +Issue an expire request to the kernel for an autofs mount. Typically +this ioctl is called until no further expire candidates are found. + +The call requires an initialized struct autofs_dev_ioctl with the +ioctlfd field set to the descriptor obtained from the open call. In +addition an immediate expire, independent of the mount timeout, can be +requested by setting the arg1 field to 1. If no expire candidates can +be found the ioctl returns -1 with errno set to EAGAIN. + +This call causes the kernel module to check the mount corresponding +to the given ioctlfd for mounts that can be expired, issues an expire +request back to the daemon and waits for completion. + +AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD +------------------------------ + +Checks if an autofs mount point is in use. + +The call requires an initialized struct autofs_dev_ioctl with the +ioctlfd field set to the descriptor obtained from the open call and +it returns the result in the arg1 field, 1 for busy and 0 otherwise. + + +AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD +--------------------------------- + +Check if the given path is a mountpoint. + +The call requires an initialized struct autofs_dev_ioctl. There are two +possible variations. Both use the path field set to the path of the mount +point to check and the size field adjusted appropriately. One uses the +ioctlfd field to identify a specific mount point to check while the other +variation uses the path and optionaly arg1 set to an autofs mount type. +The call returns 1 if this is a mount point and sets arg1 to the device +number of the mount and field arg2 to the relevant super block magic +number (described below) or 0 if it isn't a mountpoint. In both cases +the the device number (as returned by new_encode_dev()) is returned +in field arg1. + +If supplied with a file descriptor we're looking for a specific mount, +not necessarily at the top of the mounted stack. In this case the path +the descriptor corresponds to is considered a mountpoint if it is itself +a mountpoint or contains a mount, such as a multi-mount without a root +mount. In this case we return 1 if the descriptor corresponds to a mount +point and and also returns the super magic of the covering mount if there +is one or 0 if it isn't a mountpoint. + +If a path is supplied (and the ioctlfd field is set to -1) then the path +is looked up and is checked to see if it is the root of a mount. If a +type is also given we are looking for a particular autofs mount and if +a match isn't found a fail is returned. If the the located path is the +root of a mount 1 is returned along with the super magic of the mount +or 0 otherwise. + -- cgit v1.2.3-70-g09d2 From 0f6d504e73b49374c6093efe6aa60ab55058248a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 15 Oct 2008 22:03:14 -0700 Subject: gpiolib: gpio_to_irq() hooks Add a new gpiolib mechanism: gpio_chip instances can provide mappings between their (input) GPIOs and any associated IRQs. This makes it easier for platforms to support IRQs that are provided by board-specific external chips instead of as part of their core (such as SOC-integrated GPIOs). Also update the irq_to_gpio() description, saying to avoid it because it's not always supported. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 5 +++-- drivers/gpio/gpiolib.c | 18 ++++++++++++++++++ include/asm-generic/gpio.h | 7 +++++++ 3 files changed, 28 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 18022e249c5..8a7c45956d2 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -264,7 +264,7 @@ map between them using calls like: /* map GPIO numbers to IRQ numbers */ int gpio_to_irq(unsigned gpio); - /* map IRQ numbers to GPIO numbers */ + /* map IRQ numbers to GPIO numbers (avoid using this) */ int irq_to_gpio(unsigned irq); Those return either the corresponding number in the other namespace, or @@ -284,7 +284,8 @@ system wakeup capabilities. Non-error values returned from irq_to_gpio() would most commonly be used with gpio_get_value(), for example to initialize or update driver state -when the IRQ is edge-triggered. +when the IRQ is edge-triggered. Note that some platforms don't support +this reverse mapping, so you should avoid using it. Emulating Open Drain Signals diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 317004fd94f..29a7e6b1be5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1010,6 +1010,24 @@ int __gpio_cansleep(unsigned gpio) } EXPORT_SYMBOL_GPL(__gpio_cansleep); +/** + * __gpio_to_irq() - return the IRQ corresponding to a GPIO + * @gpio: gpio whose IRQ will be returned (already requested) + * Context: any + * + * This is used directly or indirectly to implement gpio_to_irq(). + * It returns the number of the IRQ signaled by this (input) GPIO, + * or a negative errno. + */ +int __gpio_to_irq(unsigned gpio) +{ + struct gpio_chip *chip; + + chip = gpio_to_chip(gpio); + return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO; +} +EXPORT_SYMBOL_GPL(__gpio_to_irq); + /* There's no value in making it easy to inline GPIO calls that may sleep. diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2c5744b66de..04cb1d175df 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -40,6 +40,8 @@ struct module; * returns either the value actually sensed, or zero * @direction_output: configures signal "offset" as output, or returns error * @set: assigns output value for signal "offset" + * @to_irq: optional hook supporting non-static gpio_to_irq() mappings; + * implementation may not sleep * @dbg_show: optional routine to show contents in debugfs; default code * will be used when this is omitted, but custom code can show extra * state (such as pullup/pulldown configuration). @@ -73,6 +75,10 @@ struct gpio_chip { unsigned offset, int value); void (*set)(struct gpio_chip *chip, unsigned offset, int value); + + int (*to_irq)(struct gpio_chip *chip, + unsigned offset); + void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip); int base; @@ -112,6 +118,7 @@ extern void __gpio_set_value(unsigned gpio, int value); extern int __gpio_cansleep(unsigned gpio); +extern int __gpio_to_irq(unsigned gpio); #ifdef CONFIG_GPIO_SYSFS -- cgit v1.2.3-70-g09d2 From 35e8bb5175c1a6ff6253f1a2acb30bfe52a2f500 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 15 Oct 2008 22:03:16 -0700 Subject: gpiolib: request/free hooks Add a new internal mechanism to gpiolib to support low power operations by letting gpio_chip instances see when their GPIOs are in use. When no GPIOs are active, chips may be able to enter lower powered runtime states by disabling clocks and/or power domains. Signed-off-by: David Brownell Cc: "Magnus Damm" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 4 ++ drivers/gpio/gpiolib.c | 91 +++++++++++++++++++++++++++++++++++++++------- include/asm-generic/gpio.h | 9 +++++ 3 files changed, 91 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 8a7c45956d2..b1b98870124 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -240,6 +240,10 @@ signal, or (b) something wrongly believes it's safe to remove drivers needed to manage a signal that's in active use. That is, requesting a GPIO can serve as a kind of lock. +Some platforms may also use knowledge about what GPIOs are active for +power management, such as by powering down unused chip sectors and, more +easily, gating off unused clocks. + These two calls are optional because not not all current Linux platforms offer such functionality in their GPIO support; a valid implementation could return success for all gpio_request() calls. Unlike the other calls, diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 29a7e6b1be5..9112830107a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -67,17 +67,28 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) * when setting direction, and otherwise illegal. Until board setup code * and drivers use explicit requests everywhere (which won't happen when * those calls have no teeth) we can't avoid autorequesting. This nag - * message should motivate switching to explicit requests... + * message should motivate switching to explicit requests... so should + * the weaker cleanup after faults, compared to gpio_request(). */ -static void gpio_ensure_requested(struct gpio_desc *desc) +static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) { if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { - pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc)); + struct gpio_chip *chip = desc->chip; + int gpio = chip->base + offset; + + if (!try_module_get(chip->owner)) { + pr_err("GPIO-%d: module can't be gotten \n", gpio); + clear_bit(FLAG_REQUESTED, &desc->flags); + /* lose */ + return -EIO; + } + pr_warning("GPIO-%d autorequested\n", gpio); desc_set_label(desc, "[auto]"); - if (!try_module_get(desc->chip->owner)) - pr_err("GPIO-%d: module can't be gotten \n", - (int)(desc - gpio_desc)); + /* caller must chip->request() w/o spinlock */ + if (chip->request) + return 1; } + return 0; } /* caller holds gpio_lock *OR* gpio is marked as requested */ @@ -752,6 +763,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); int gpio_request(unsigned gpio, const char *label) { struct gpio_desc *desc; + struct gpio_chip *chip; int status = -EINVAL; unsigned long flags; @@ -760,14 +772,15 @@ int gpio_request(unsigned gpio, const char *label) if (!gpio_is_valid(gpio)) goto done; desc = &gpio_desc[gpio]; - if (desc->chip == NULL) + chip = desc->chip; + if (chip == NULL) goto done; - if (!try_module_get(desc->chip->owner)) + if (!try_module_get(chip->owner)) goto done; /* NOTE: gpio_request() can be called in early boot, - * before IRQs are enabled. + * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { @@ -775,7 +788,20 @@ int gpio_request(unsigned gpio, const char *label) status = 0; } else { status = -EBUSY; - module_put(desc->chip->owner); + module_put(chip->owner); + } + + if (chip->request) { + /* chip->request may sleep */ + spin_unlock_irqrestore(&gpio_lock, flags); + status = chip->request(chip, gpio - chip->base); + spin_lock_irqsave(&gpio_lock, flags); + + if (status < 0) { + desc_set_label(desc, NULL); + module_put(chip->owner); + clear_bit(FLAG_REQUESTED, &desc->flags); + } } done: @@ -791,6 +817,7 @@ void gpio_free(unsigned gpio) { unsigned long flags; struct gpio_desc *desc; + struct gpio_chip *chip; might_sleep(); @@ -804,9 +831,17 @@ void gpio_free(unsigned gpio) spin_lock_irqsave(&gpio_lock, flags); desc = &gpio_desc[gpio]; - if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) { + chip = desc->chip; + if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { + if (chip->free) { + spin_unlock_irqrestore(&gpio_lock, flags); + might_sleep_if(extra_checks && chip->can_sleep); + chip->free(chip, gpio - chip->base); + spin_lock_irqsave(&gpio_lock, flags); + } desc_set_label(desc, NULL); module_put(desc->chip->owner); + clear_bit(FLAG_REQUESTED, &desc->flags); } else WARN_ON(extra_checks); @@ -871,7 +906,9 @@ int gpio_direction_input(unsigned gpio) gpio -= chip->base; if (gpio >= chip->ngpio) goto fail; - gpio_ensure_requested(desc); + status = gpio_ensure_requested(desc, gpio); + if (status < 0) + goto fail; /* now we know the gpio is valid and chip won't vanish */ @@ -879,9 +916,22 @@ int gpio_direction_input(unsigned gpio) might_sleep_if(extra_checks && chip->can_sleep); + if (status) { + status = chip->request(chip, gpio); + if (status < 0) { + pr_debug("GPIO-%d: chip request fail, %d\n", + chip->base + gpio, status); + /* and it's not available to anyone else ... + * gpio_request() is the fully clean solution. + */ + goto lose; + } + } + status = chip->direction_input(chip, gpio); if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); +lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); @@ -909,7 +959,9 @@ int gpio_direction_output(unsigned gpio, int value) gpio -= chip->base; if (gpio >= chip->ngpio) goto fail; - gpio_ensure_requested(desc); + status = gpio_ensure_requested(desc, gpio); + if (status < 0) + goto fail; /* now we know the gpio is valid and chip won't vanish */ @@ -917,9 +969,22 @@ int gpio_direction_output(unsigned gpio, int value) might_sleep_if(extra_checks && chip->can_sleep); + if (status) { + status = chip->request(chip, gpio); + if (status < 0) { + pr_debug("GPIO-%d: chip request fail, %d\n", + chip->base + gpio, status); + /* and it's not available to anyone else ... + * gpio_request() is the fully clean solution. + */ + goto lose; + } + } + status = chip->direction_output(chip, gpio, value); if (status == 0) set_bit(FLAG_IS_OUT, &desc->flags); +lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 04cb1d175df..81797ec9ab2 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -35,6 +35,10 @@ struct module; * @label: for diagnostics * @dev: optional device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs + * @request: optional hook for chip-specific activation, such as + * enabling module power and clock; may sleep + * @free: optional hook for chip-specific deactivation, such as + * disabling module power and clock; may sleep * @direction_input: configures signal "offset" as input, or returns error * @get: returns value for signal "offset"; for output signals this * returns either the value actually sensed, or zero @@ -67,6 +71,11 @@ struct gpio_chip { struct device *dev; struct module *owner; + int (*request)(struct gpio_chip *chip, + unsigned offset); + void (*free)(struct gpio_chip *chip, + unsigned offset); + int (*direction_input)(struct gpio_chip *chip, unsigned offset); int (*get)(struct gpio_chip *chip, -- cgit v1.2.3-70-g09d2 From 09a525ec1cf5a142f2e73f15527c169dafc6ff52 Mon Sep 17 00:00:00 2001 From: Joseph Chan Date: Wed, 15 Oct 2008 22:03:19 -0700 Subject: viafb: viafb.modes, viafb.txt Correct via_fb_ to viafb_ and remove the Kconfig part in viafb.txt. viafb.modes: supported mode table viafb.txt: documentation of viafb driver Signed-off-by: Joseph Chan Cc: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/viafb.modes | 870 +++++++++++++++++++++++++++++++++++++++++++ Documentation/fb/viafb.txt | 214 +++++++++++ 2 files changed, 1084 insertions(+) create mode 100644 Documentation/fb/viafb.modes create mode 100644 Documentation/fb/viafb.txt (limited to 'Documentation') diff --git a/Documentation/fb/viafb.modes b/Documentation/fb/viafb.modes new file mode 100644 index 00000000000..02e5b487f00 --- /dev/null +++ b/Documentation/fb/viafb.modes @@ -0,0 +1,870 @@ +# +# +# These data are based on the CRTC parameters in +# +# VIA Integration Graphics Chip +# (C) 2004 VIA Technologies Inc. +# + +# +# 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) +# +# Horizontal Vertical +# Resolution 640 480 +# Scan Frequency 31.469 kHz 59.94 Hz +# Sync Width 3.813 us 0.064 ms +# 12 chars 2 lines +# Front Porch 0.636 us 0.318 ms +# 2 chars 10 lines +# Back Porch 1.907 us 1.048 ms +# 6 chars 33 lines +# Active Time 25.422 us 15.253 ms +# 80 chars 480 lines +# Blank Time 6.356 us 1.430 ms +# 20 chars 45 lines +# Polarity negative negative +# + +mode "640x480-60" +# D: 25.175 MHz, H: 31.469 kHz, V: 59.94 Hz + geometry 640 480 640 480 32 + timings 39722 48 16 33 10 96 2 endmode mode "480x640-60" +# D: 24.823 MHz, H: 39.780 kHz, V: 60.00 Hz + geometry 480 640 480 640 32 timings 39722 72 24 19 1 48 3 endmode +# +# 640x480, 75 Hz, Non-Interlaced (31.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 640 480 +# Scan Frequency 37.500 kHz 75.00 Hz +# Sync Width 2.032 us 0.080 ms +# 8 chars 3 lines +# Front Porch 0.508 us 0.027 ms +# 2 chars 1 lines +# Back Porch 3.810 us 0.427 ms +# 15 chars 16 lines +# Active Time 20.317 us 12.800 ms +# 80 chars 480 lines +# Blank Time 6.349 us 0.533 ms +# 25 chars 20 lines +# Polarity negative negative +# + mode "640x480-75" +# D: 31.50 MHz, H: 37.500 kHz, V: 75.00 Hz + geometry 640 480 640 480 32 timings 31747 120 16 16 1 64 3 endmode +# +# 640x480, 85 Hz, Non-Interlaced (36.000 MHz dotclock) +# +# Horizontal Vertical +# Resolution 640 480 +# Scan Frequency 43.269 kHz 85.00 Hz +# Sync Width 1.556 us 0.069 ms +# 7 chars 3 lines +# Front Porch 1.556 us 0.023 ms +# 7 chars 1 lines +# Back Porch 2.222 us 0.578 ms +# 10 chars 25 lines +# Active Time 17.778 us 11.093 ms +# 80 chars 480 lines +# Blank Time 5.333 us 0.670 ms +# 24 chars 29 lines +# Polarity negative negative +# + mode "640x480-85" +# D: 36.000 MHz, H: 43.269 kHz, V: 85.00 Hz + geometry 640 480 640 480 32 timings 27777 80 56 25 1 56 3 endmode +# +# 640x480, 100 Hz, Non-Interlaced (43.163 MHz dotclock) +# +# Horizontal Vertical +# Resolution 640 480 +# Scan Frequency 50.900 kHz 100.00 Hz +# Sync Width 1.483 us 0.058 ms +# 8 chars 3 lines +# Front Porch 0.927 us 0.019 ms +# 5 chars 1 lines +# Back Porch 2.409 us 0.475 ms +# 13 chars 25 lines +# Active Time 14.827 us 9.430 ms +# 80 chars 480 lines +# Blank Time 4.819 us 0.570 ms +# 26 chars 29 lines +# Polarity positive positive +# + mode "640x480-100" +# D: 43.163 MHz, H: 50.900 kHz, V: 100.00 Hz + geometry 640 480 640 480 32 timings 23168 104 40 25 1 64 3 endmode +# +# 640x480, 120 Hz, Non-Interlaced (52.406 MHz dotclock) +# +# Horizontal Vertical +# Resolution 640 480 +# Scan Frequency 61.800 kHz 120.00 Hz +# Sync Width 1.221 us 0.048 ms +# 8 chars 3 lines +# Front Porch 0.763 us 0.016 ms +# 5 chars 1 lines +# Back Porch 1.984 us 0.496 ms +# 13 chars 31 lines +# Active Time 12.212 us 7.767 ms +# 80 chars 480 lines +# Blank Time 3.969 us 0.566 ms +# 26 chars 35 lines +# Polarity positive positive +# + mode "640x480-120" +# D: 52.406 MHz, H: 61.800 kHz, V: 120.00 Hz + geometry 640 480 640 480 32 timings 19081 104 40 31 1 64 3 endmode +# +# 720x480, 60 Hz, Non-Interlaced (26.880 MHz dotclock) +# +# Horizontal Vertical +# Resolution 720 480 +# Scan Frequency 30.000 kHz 60.241 Hz +# Sync Width 2.679 us 0.099 ms +# 9 chars 3 lines +# Front Porch 0.595 us 0.033 ms +# 2 chars 1 lines +# Back Porch 3.274 us 0.462 ms +# 11 chars 14 lines +# Active Time 26.786 us 16.000 ms +# 90 chars 480 lines +# Blank Time 6.548 us 0.600 ms +# 22 chars 18 lines +# Polarity positive positive +# + mode "720x480-60" +# D: 26.880 MHz, H: 30.000 kHz, V: 60.24 Hz + geometry 720 480 720 480 32 timings 37202 88 16 14 1 72 3 endmode +# +# 800x480, 60 Hz, Non-Interlaced (29.581 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 480 +# Scan Frequency 29.892 kHz 60.00 Hz +# Sync Width 2.704 us 100.604 us +# 10 chars 3 lines +# Front Porch 0.541 us 33.535 us +# 2 chars 1 lines +# Back Porch 3.245 us 435.949 us +# 12 chars 13 lines +# Active Time 27.044 us 16.097 ms +# 100 chars 480 lines +# Blank Time 6.491 us 0.570 ms +# 24 chars 17 lines +# Polarity positive positive +# + mode "800x480-60" +# D: 29.500 MHz, H: 29.738 kHz, V: 60.00 Hz + geometry 800 480 800 480 32 timings 33805 96 24 10 3 72 7 endmode +# +# 720x576, 60 Hz, Non-Interlaced (32.668 MHz dotclock) +# +# Horizontal Vertical +# Resolution 720 576 +# Scan Frequency 35.820 kHz 60.00 Hz +# Sync Width 2.204 us 0.083 ms +# 9 chars 3 lines +# Front Porch 0.735 us 0.027 ms +# 3 chars 1 lines +# Back Porch 2.939 us 0.459 ms +# 12 chars 17 lines +# Active Time 22.040 us 16.080 ms +# 90 chars 476 lines +# Blank Time 5.877 us 0.586 ms +# 24 chars 21 lines +# Polarity positive positive +# + mode "720x576-60" +# D: 32.668 MHz, H: 35.820 kHz, V: 60.00 Hz + geometry 720 576 720 576 32 timings 30611 96 24 17 1 72 3 endmode +# +# 800x600, 60 Hz, Non-Interlaced (40.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 600 +# Scan Frequency 37.879 kHz 60.32 Hz +# Sync Width 3.200 us 0.106 ms +# 16 chars 4 lines +# Front Porch 1.000 us 0.026 ms +# 5 chars 1 lines +# Back Porch 2.200 us 0.607 ms +# 11 chars 23 lines +# Active Time 20.000 us 15.840 ms +# 100 chars 600 lines +# Blank Time 6.400 us 0.739 ms +# 32 chars 28 lines +# Polarity positive positive +# + mode "800x600-60" +# D: 40.00 MHz, H: 37.879 kHz, V: 60.32 Hz + geometry 800 600 800 600 32 + timings 25000 88 40 23 1 128 4 hsync high vsync high endmode +# +# 800x600, 75 Hz, Non-Interlaced (49.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 600 +# Scan Frequency 46.875 kHz 75.00 Hz +# Sync Width 1.616 us 0.064 ms +# 10 chars 3 lines +# Front Porch 0.323 us 0.021 ms +# 2 chars 1 lines +# Back Porch 3.232 us 0.448 ms +# 20 chars 21 lines +# Active Time 16.162 us 12.800 ms +# 100 chars 600 lines +# Blank Time 5.172 us 0.533 ms +# 32 chars 25 lines +# Polarity positive positive +# + mode "800x600-75" +# D: 49.50 MHz, H: 46.875 kHz, V: 75.00 Hz + geometry 800 600 800 600 32 + timings 20203 160 16 21 1 80 3 hsync high vsync high endmode +# +# 800x600, 85 Hz, Non-Interlaced (56.25 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 600 +# Scan Frequency 53.674 kHz 85.061 Hz +# Sync Width 1.138 us 0.056 ms +# 8 chars 3 lines +# Front Porch 0.569 us 0.019 ms +# 4 chars 1 lines +# Back Porch 2.702 us 0.503 ms +# 19 chars 27 lines +# Active Time 14.222 us 11.179 ms +# 100 chars 600 lines +# Blank Time 4.409 us 0.578 ms +# 31 chars 31 lines +# Polarity positive positive +# + mode "800x600-85" +# D: 56.25 MHz, H: 53.674 kHz, V: 85.061 Hz + geometry 800 600 800 600 32 + timings 17777 152 32 27 1 64 3 hsync high vsync high endmode +# +# 800x600, 100 Hz, Non-Interlaced (67.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 600 +# Scan Frequency 62.500 kHz 100.00 Hz +# Sync Width 0.948 us 0.064 ms +# 8 chars 4 lines +# Front Porch 0.000 us 0.112 ms +# 0 chars 7 lines +# Back Porch 3.200 us 0.224 ms +# 27 chars 14 lines +# Active Time 11.852 us 9.600 ms +# 100 chars 600 lines +# Blank Time 4.148 us 0.400 ms +# 35 chars 25 lines +# Polarity positive positive +# + mode "800x600-100" +# D: 67.50 MHz, H: 62.500 kHz, V: 100.00 Hz + geometry 800 600 800 600 32 + timings 14667 216 0 14 7 64 4 hsync high vsync high endmode +# +# 800x600, 120 Hz, Non-Interlaced (83.950 MHz dotclock) +# +# Horizontal Vertical +# Resolution 800 600 +# Scan Frequency 77.160 kHz 120.00 Hz +# Sync Width 1.048 us 0.039 ms +# 11 chars 3 lines +# Front Porch 0.667 us 0.013 ms +# 7 chars 1 lines +# Back Porch 1.715 us 0.507 ms +# 18 chars 39 lines +# Active Time 9.529 us 7.776 ms +# 100 chars 600 lines +# Blank Time 3.431 us 0.557 ms +# 36 chars 43 lines +# Polarity positive positive +# + mode "800x600-120" +# D: 83.950 MHz, H: 77.160 kHz, V: 120.00 Hz + geometry 800 600 800 600 32 + timings 11912 144 56 39 1 88 3 hsync high vsync high endmode +# +# 848x480, 60 Hz, Non-Interlaced (31.490 MHz dotclock) +# +# Horizontal Vertical +# Resolution 848 480 +# Scan Frequency 29.820 kHz 60.00 Hz +# Sync Width 2.795 us 0.099 ms +# 11 chars 3 lines +# Front Porch 0.508 us 0.033 ms +# 2 chars 1 lines +# Back Porch 3.303 us 0.429 ms +# 13 chars 13 lines +# Active Time 26.929 us 16.097 ms +# 106 chars 480 lines +# Blank Time 6.605 us 0.570 ms +# 26 chars 17 lines +# Polarity positive positive +# + mode "848x480-60" +# D: 31.500 MHz, H: 29.830 kHz, V: 60.00 Hz + geometry 848 480 848 480 32 + timings 31746 104 24 12 3 80 5 hsync high vsync high endmode +# +# 856x480, 60 Hz, Non-Interlaced (31.728 MHz dotclock) +# +# Horizontal Vertical +# Resolution 856 480 +# Scan Frequency 29.820 kHz 60.00 Hz +# Sync Width 2.774 us 0.099 ms +# 11 chars 3 lines +# Front Porch 0.504 us 0.033 ms +# 2 chars 1 lines +# Back Porch 3.728 us 0.429 ms +# 13 chars 13 lines +# Active Time 26.979 us 16.097 ms +# 107 chars 480 lines +# Blank Time 6.556 us 0.570 ms +# 26 chars 17 lines +# Polarity positive positive +# + mode "856x480-60" +# D: 31.728 MHz, H: 29.820 kHz, V: 60.00 Hz + geometry 856 480 856 480 32 + timings 31518 104 16 13 1 88 3 + hsync high vsync high endmode mode "960x600-60" +# D: 45.250 MHz, H: 37.212 kHz, V: 60.00 Hz + geometry 960 600 960 600 32 timings 22099 128 32 15 3 96 6 endmode +# +# 1000x600, 60 Hz, Non-Interlaced (48.068 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1000 600 +# Scan Frequency 37.320 kHz 60.00 Hz +# Sync Width 2.164 us 0.080 ms +# 13 chars 3 lines +# Front Porch 0.832 us 0.027 ms +# 5 chars 1 lines +# Back Porch 2.996 us 0.483 ms +# 18 chars 18 lines +# Active Time 20.804 us 16.077 ms +# 125 chars 600 lines +# Blank Time 5.991 us 0.589 ms +# 36 chars 22 lines +# Polarity negative positive +# + mode "1000x600-60" +# D: 48.068 MHz, H: 37.320 kHz, V: 60.00 Hz + geometry 1000 600 1000 600 32 + timings 20834 144 40 18 1 104 3 endmode mode "1024x576-60" +# D: 46.996 MHz, H: 35.820 kHz, V: 60.00 Hz + geometry 1024 576 1024 576 32 + timings 21278 144 40 17 1 104 3 endmode mode "1024x600-60" +# D: 48.964 MHz, H: 37.320 kHz, V: 60.00 Hz + geometry 1024 600 1024 600 32 + timings 20461 144 40 18 1 104 3 endmode mode "1088x612-60" +# D: 52.952 MHz, H: 38.040 kHz, V: 60.00 Hz + geometry 1088 612 1088 612 32 timings 18877 152 48 16 3 104 5 endmode +# +# 1024x512, 60 Hz, Non-Interlaced (41.291 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 512 +# Scan Frequency 31.860 kHz 60.00 Hz +# Sync Width 2.519 us 0.094 ms +# 13 chars 3 lines +# Front Porch 0.775 us 0.031 ms +# 4 chars 1 lines +# Back Porch 3.294 us 0.465 ms +# 17 chars 15 lines +# Active Time 24.800 us 16.070 ms +# 128 chars 512 lines +# Blank Time 6.587 us 0.596 ms +# 34 chars 19 lines +# Polarity positive positive +# + mode "1024x512-60" +# D: 41.291 MHz, H: 31.860 kHz, V: 60.00 Hz + geometry 1024 512 1024 512 32 + timings 24218 126 32 15 1 104 3 hsync high vsync high endmode +# +# 1024x600, 60 Hz, Non-Interlaced (48.875 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 768 +# Scan Frequency 37.252 kHz 60.00 Hz +# Sync Width 2.128 us 80.532us +# 13 chars 3 lines +# Front Porch 0.818 us 26.844 us +# 5 chars 1 lines +# Back Porch 2.946 us 483.192 us +# 18 chars 18 lines +# Active Time 20.951 us 16.697 ms +# 128 chars 622 lines +# Blank Time 5.893 us 0.591 ms +# 36 chars 22 lines +# Polarity negative positive +# +#mode "1024x600-60" +# # D: 48.875 MHz, H: 37.252 kHz, V: 60.00 Hz +# geometry 1024 600 1024 600 32 +# timings 20460 144 40 18 1 104 3 +# endmode +# +# 1024x768, 60 Hz, Non-Interlaced (65.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 768 +# Scan Frequency 48.363 kHz 60.00 Hz +# Sync Width 2.092 us 0.124 ms +# 17 chars 6 lines +# Front Porch 0.369 us 0.062 ms +# 3 chars 3 lines +# Back Porch 2.462 us 0.601 ms +# 20 chars 29 lines +# Active Time 15.754 us 15.880 ms +# 128 chars 768 lines +# Blank Time 4.923 us 0.786 ms +# 40 chars 38 lines +# Polarity negative negative +# + mode "1024x768-60" +# D: 65.00 MHz, H: 48.363 kHz, V: 60.00 Hz + geometry 1024 768 1024 768 32 timings 15385 160 24 29 3 136 6 endmode +# +# 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 768 +# Scan Frequency 60.023 kHz 75.03 Hz +# Sync Width 1.219 us 0.050 ms +# 12 chars 3 lines +# Front Porch 0.203 us 0.017 ms +# 2 chars 1 lines +# Back Porch 2.235 us 0.466 ms +# 22 chars 28 lines +# Active Time 13.003 us 12.795 ms +# 128 chars 768 lines +# Blank Time 3.657 us 0.533 ms +# 36 chars 32 lines +# Polarity positive positive +# + mode "1024x768-75" +# D: 78.75 MHz, H: 60.023 kHz, V: 75.03 Hz + geometry 1024 768 1024 768 32 + timings 12699 176 16 28 1 96 3 hsync high vsync high endmode +# +# 1024x768, 85 Hz, Non-Interlaced (94.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 768 +# Scan Frequency 68.677 kHz 85.00 Hz +# Sync Width 1.016 us 0.044 ms +# 12 chars 3 lines +# Front Porch 0.508 us 0.015 ms +# 6 chars 1 lines +# Back Porch 2.201 us 0.524 ms +# 26 chars 36 lines +# Active Time 10.836 us 11.183 ms +# 128 chars 768 lines +# Blank Time 3.725 us 0.582 ms +# 44 chars 40 lines +# Polarity positive positive +# + mode "1024x768-85" +# D: 94.50 MHz, H: 68.677 kHz, V: 85.00 Hz + geometry 1024 768 1024 768 32 + timings 10582 208 48 36 1 96 3 hsync high vsync high endmode +# +# 1024x768, 100 Hz, Non-Interlaced (110.0 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1024 768 +# Scan Frequency 79.023 kHz 99.78 Hz +# Sync Width 0.800 us 0.101 ms +# 11 chars 8 lines +# Front Porch 0.000 us 0.000 ms +# 0 chars 0 lines +# Back Porch 2.545 us 0.202 ms +# 35 chars 16 lines +# Active Time 9.309 us 9.719 ms +# 128 chars 768 lines +# Blank Time 3.345 us 0.304 ms +# 46 chars 24 lines +# Polarity negative negative +# + mode "1024x768-100" +# D: 113.3 MHz, H: 79.023 kHz, V: 99.78 Hz + geometry 1024 768 1024 768 32 + timings 8825 280 0 16 0 88 8 endmode mode "1152x720-60" +# D: 66.750 MHz, H: 44.859 kHz, V: 60.00 Hz + geometry 1152 720 1152 720 32 timings 14981 168 56 19 3 112 6 endmode +# +# 1152x864, 75 Hz, Non-Interlaced (110.0 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1152 864 +# Scan Frequency 75.137 kHz 74.99 Hz +# Sync Width 1.309 us 0.106 ms +# 18 chars 8 lines +# Front Porch 0.245 us 0.599 ms +# 3 chars 45 lines +# Back Porch 1.282 us 1.132 ms +# 18 chars 85 lines +# Active Time 10.473 us 11.499 ms +# 144 chars 864 lines +# Blank Time 2.836 us 1.837 ms +# 39 chars 138 lines +# Polarity positive positive +# + mode "1152x864-75" +# D: 110.0 MHz, H: 75.137 kHz, V: 74.99 Hz + geometry 1152 864 1152 864 32 + timings 9259 144 24 85 45 144 8 + hsync high vsync high endmode mode "1200x720-60" +# D: 70.184 MHz, H: 44.760 kHz, V: 60.00 Hz + geometry 1200 720 1200 720 32 + timings 14253 184 28 22 1 128 3 endmode mode "1280x600-60" +# D: 61.503 MHz, H: 37.320 kHz, V: 60.00 Hz + geometry 1280 600 1280 600 32 + timings 16260 184 28 18 1 128 3 endmode mode "1280x720-50" +# D: 60.466 MHz, H: 37.050 kHz, V: 50.00 Hz + geometry 1280 720 1280 720 32 + timings 16538 176 48 17 1 128 3 endmode mode "1280x768-50" +# D: 65.178 MHz, H: 39.550 kHz, V: 50.00 Hz + geometry 1280 768 1280 768 32 timings 15342 184 28 19 1 128 3 endmode +# +# 1280x768, 60 Hz, Non-Interlaced (80.136 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 768 +# Scan Frequency 47.700 kHz 60.00 Hz +# Sync Width 1.697 us 0.063 ms +# 17 chars 3 lines +# Front Porch 0.799 us 0.021 ms +# 8 chars 1 lines +# Back Porch 2.496 us 0.483 ms +# 25 chars 23 lines +# Active Time 15.973 us 16.101 ms +# 160 chars 768 lines +# Blank Time 4.992 us 0.566 ms +# 50 chars 27 lines +# Polarity positive positive +# + mode "1280x768-60" +# D: 80.13 MHz, H: 47.700 kHz, V: 60.00 Hz + geometry 1280 768 1280 768 32 + timings 12480 200 48 23 1 126 3 hsync high vsync high endmode +# +# 1280x800, 60 Hz, Non-Interlaced (83.375 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 800 +# Scan Frequency 49.628 kHz 60.00 Hz +# Sync Width 1.631 us 60.450 us +# 17 chars 3 lines +# Front Porch 0.768 us 20.15 us +# 8 chars 1 lines +# Back Porch 2.399 us 0.483 ms +# 25 chars 24 lines +# Active Time 15.352 us 16.120 ms +# 160 chars 800 lines +# Blank Time 4.798 us 0.564 ms +# 50 chars 28 lines +# Polarity negtive positive +# + mode "1280x800-60" +# D: 83.500 MHz, H: 49.702 kHz, V: 60.00 Hz + geometry 1280 800 1280 800 32 timings 11994 200 72 22 3 128 6 endmode +# +# 1280x960, 60 Hz, Non-Interlaced (108.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 960 +# Scan Frequency 60.000 kHz 60.00 Hz +# Sync Width 1.037 us 0.050 ms +# 14 chars 3 lines +# Front Porch 0.889 us 0.017 ms +# 12 chars 1 lines +# Back Porch 2.889 us 0.600 ms +# 39 chars 36 lines +# Active Time 11.852 us 16.000 ms +# 160 chars 960 lines +# Blank Time 4.815 us 0.667 ms +# 65 chars 40 lines +# Polarity positive positive +# + mode "1280x960-60" +# D: 108.00 MHz, H: 60.000 kHz, V: 60.00 Hz + geometry 1280 960 1280 960 32 + timings 9259 312 96 36 1 112 3 hsync high vsync high endmode +# +# 1280x1024, 60 Hz, Non-Interlaced (108.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 1024 +# Scan Frequency 63.981 kHz 60.02 Hz +# Sync Width 1.037 us 0.047 ms +# 14 chars 3 lines +# Front Porch 0.444 us 0.015 ms +# 6 chars 1 lines +# Back Porch 2.297 us 0.594 ms +# 31 chars 38 lines +# Active Time 11.852 us 16.005 ms +# 160 chars 1024 lines +# Blank Time 3.778 us 0.656 ms +# 51 chars 42 lines +# Polarity positive positive +# + mode "1280x1024-60" +# D: 108.00 MHz, H: 63.981 kHz, V: 60.02 Hz + geometry 1280 1024 1280 1024 32 + timings 9260 248 48 38 1 112 3 hsync high vsync high endmode +# +# 1280x1024, 75 Hz, Non-Interlaced (135.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 1024 +# Scan Frequency 79.976 kHz 75.02 Hz +# Sync Width 1.067 us 0.038 ms +# 18 chars 3 lines +# Front Porch 0.119 us 0.012 ms +# 2 chars 1 lines +# Back Porch 1.837 us 0.475 ms +# 31 chars 38 lines +# Active Time 9.481 us 12.804 ms +# 160 chars 1024 lines +# Blank Time 3.022 us 0.525 ms +# 51 chars 42 lines +# Polarity positive positive +# + mode "1280x1024-75" +# D: 135.00 MHz, H: 79.976 kHz, V: 75.02 Hz + geometry 1280 1024 1280 1024 32 + timings 7408 248 16 38 1 144 3 hsync high vsync high endmode +# +# 1280x1024, 85 Hz, Non-Interlaced (157.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 1024 +# Scan Frequency 91.146 kHz 85.02 Hz +# Sync Width 1.016 us 0.033 ms +# 20 chars 3 lines +# Front Porch 0.406 us 0.011 ms +# 8 chars 1 lines +# Back Porch 1.422 us 0.483 ms +# 28 chars 44 lines +# Active Time 8.127 us 11.235 ms +# 160 chars 1024 lines +# Blank Time 2.844 us 0.527 ms +# 56 chars 48 lines +# Polarity positive positive +# + mode "1280x1024-85" +# D: 157.50 MHz, H: 91.146 kHz, V: 85.02 Hz + geometry 1280 1024 1280 1024 32 + timings 6349 224 64 44 1 160 3 + hsync high vsync high endmode mode "1440x900-60" +# D: 106.500 MHz, H: 55.935 kHz, V: 60.00 Hz + geometry 1440 900 1440 900 32 + timings 9390 232 80 25 3 152 6 + hsync high vsync high endmode mode "1440x900-75" +# D: 136.750 MHz, H: 70.635 kHz, V: 75.00 Hz + geometry 1440 900 1440 900 32 + timings 7315 248 96 33 3 152 6 hsync high vsync high endmode +# +# 1440x1050, 60 Hz, Non-Interlaced (125.10 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1440 1050 +# Scan Frequency 65.220 kHz 60.00 Hz +# Sync Width 1.204 us 0.046 ms +# 19 chars 3 lines +# Front Porch 0.760 us 0.015 ms +# 12 chars 1 lines +# Back Porch 1.964 us 0.495 ms +# 31 chars 33 lines +# Active Time 11.405 us 16.099 ms +# 180 chars 1050 lines +# Blank Time 3.928 us 0.567 ms +# 62 chars 37 lines +# Polarity positive positive +# + mode "1440x1050-60" +# D: 125.10 MHz, H: 65.220 kHz, V: 60.00 Hz + geometry 1440 1050 1440 1050 32 + timings 7993 248 96 33 1 152 3 + hsync high vsync high endmode mode "1600x900-60" +# D: 118.250 MHz, H: 55.990 kHz, V: 60.00 Hz + geometry 1600 900 1600 900 32 + timings 8415 256 88 26 3 168 5 endmode mode "1600x1024-60" +# D: 136.358 MHz, H: 63.600 kHz, V: 60.00 Hz + geometry 1600 1024 1600 1024 32 timings 7315 272 104 32 1 168 3 endmode +# +# 1600x1200, 60 Hz, Non-Interlaced (156.00 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1600 1200 +# Scan Frequency 76.200 kHz 60.00 Hz +# Sync Width 1.026 us 0.105 ms +# 20 chars 8 lines +# Front Porch 0.205 us 0.131 ms +# 4 chars 10 lines +# Back Porch 1.636 us 0.682 ms +# 32 chars 52 lines +# Active Time 10.256 us 15.748 ms +# 200 chars 1200 lines +# Blank Time 2.872 us 0.866 ms +# 56 chars 66 lines +# Polarity negative negative +# + mode "1600x1200-60" +# D: 156.00 MHz, H: 76.200 kHz, V: 60.00 Hz + geometry 1600 1200 1600 1200 32 timings 6172 256 32 52 10 160 8 endmode +# +# 1600x1200, 75 Hz, Non-Interlaced (202.50 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1600 1200 +# Scan Frequency 93.750 kHz 75.00 Hz +# Sync Width 0.948 us 0.032 ms +# 24 chars 3 lines +# Front Porch 0.316 us 0.011 ms +# 8 chars 1 lines +# Back Porch 1.501 us 0.491 ms +# 38 chars 46 lines +# Active Time 7.901 us 12.800 ms +# 200 chars 1200 lines +# Blank Time 2.765 us 0.533 ms +# 70 chars 50 lines +# Polarity positive positive +# + mode "1600x1200-75" +# D: 202.50 MHz, H: 93.750 kHz, V: 75.00 Hz + geometry 1600 1200 1600 1200 32 + timings 4938 304 64 46 1 192 3 + hsync high vsync high endmode mode "1680x1050-60" +# D: 146.250 MHz, H: 65.290 kHz, V: 59.954 Hz + geometry 1680 1050 1680 1050 32 + timings 6814 280 104 30 3 176 6 + hsync high vsync high endmode mode "1680x1050-75" +# D: 187.000 MHz, H: 82.306 kHz, V: 74.892 Hz + geometry 1680 1050 1680 1050 32 + timings 5348 296 120 40 3 176 6 + hsync high vsync high endmode mode "1792x1344-60" +# D: 202.975 MHz, H: 83.460 kHz, V: 60.00 Hz + geometry 1792 1344 1792 1344 32 + timings 4902 320 128 43 1 192 3 + hsync high vsync high endmode mode "1856x1392-60" +# D: 218.571 MHz, H: 86.460 kHz, V: 60.00 Hz + geometry 1856 1392 1856 1392 32 + timings 4577 336 136 45 1 200 3 + hsync high vsync high endmode mode "1920x1200-60" +# D: 193.250 MHz, H: 74.556 kHz, V: 60.00 Hz + geometry 1920 1200 1920 1200 32 + timings 5173 336 136 36 3 200 6 + hsync high vsync high endmode mode "1920x1440-60" +# D: 234.000 MHz, H:90.000 kHz, V: 60.00 Hz + geometry 1920 1440 1920 1440 32 + timings 4274 344 128 56 1 208 3 + hsync high vsync high endmode mode "1920x1440-75" +# D: 297.000 MHz, H:112.500 kHz, V: 75.00 Hz + geometry 1920 1440 1920 1440 32 + timings 3367 352 144 56 1 224 3 + hsync high vsync high endmode mode "2048x1536-60" +# D: 267.250 MHz, H: 95.446 kHz, V: 60.00 Hz + geometry 2048 1536 2048 1536 32 + timings 3742 376 152 49 3 224 4 hsync high vsync high endmode +# +# 1280x720, 60 Hz, Non-Interlaced (74.481 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1280 720 +# Scan Frequency 44.760 kHz 60.00 Hz +# Sync Width 1.826 us 67.024 ms +# 17 chars 3 lines +# Front Porch 0.752 us 22.341 ms +# 7 chars 1 lines +# Back Porch 2.578 us 491.510 ms +# 24 chars 22 lines +# Active Time 17.186 us 16.086 ms +# 160 chars 720 lines +# Blank Time 5.156 us 0.581 ms +# 48 chars 26 lines +# Polarity negative negative +# + mode "1280x720-60" +# D: 74.481 MHz, H: 44.760 kHz, V: 60.00 Hz + geometry 1280 720 1280 720 32 timings 13426 192 64 22 1 136 3 endmode +# +# 1920x1080, 60 Hz, Non-Interlaced (172.798 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1920 1080 +# Scan Frequency 67.080 kHz 60.00 Hz +# Sync Width 1.204 us 44.723 ms +# 26 chars 3 lines +# Front Porch 0.694 us 14.908 ms +# 15 chars 1 lines +# Back Porch 1.898 us 506.857 ms +# 41 chars 34 lines +# Active Time 11.111 us 16.100 ms +# 240 chars 1080 lines +# Blank Time 3.796 us 0.566 ms +# 82 chars 38 lines +# Polarity negative negative +# + mode "1920x1080-60" +# D: 74.481 MHz, H: 67.080 kHz, V: 60.00 Hz + geometry 1920 1080 1920 1080 32 timings 5787 328 120 34 1 208 3 endmode +# +# 1400x1050, 60 Hz, Non-Interlaced (122.61 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1400 1050 +# Scan Frequency 65.218 kHz 59.99 Hz +# Sync Width 1.037 us 0.047 ms +# 19 chars 3 lines +# Front Porch 0.444 us 0.015 ms +# 11 chars 1 lines +# Back Porch 1.185 us 0.188 ms +# 30 chars 33 lines +# Active Time 12.963 us 16.411 ms +# 175 chars 1050 lines +# Blank Time 2.667 us 0.250 ms +# 60 chars 37 lines +# Polarity negative positive +# + mode "1400x1050-60" +# D: 122.750 MHz, H: 65.317 kHz, V: 59.99 Hz + geometry 1400 1050 1408 1050 32 + timings 8214 232 88 32 3 144 4 endmode mode "1400x1050-75" +# D: 156.000 MHz, H: 82.278 kHz, V: 74.867 Hz + geometry 1400 1050 1408 1050 32 timings 6410 248 104 42 3 144 4 endmode +# +# 1366x768, 60 Hz, Non-Interlaced (85.86 MHz dotclock) +# +# Horizontal Vertical +# Resolution 1366 768 +# Scan Frequency 47.700 kHz 60.00 Hz +# Sync Width 1.677 us 0.063 ms +# 18 chars 3 lines +# Front Porch 0.839 us 0.021 ms +# 9 chars 1 lines +# Back Porch 2.516 us 0.482 ms +# 27 chars 23 lines +# Active Time 15.933 us 16.101 ms +# 171 chars 768 lines +# Blank Time 5.031 us 0.566 ms +# 54 chars 27 lines +# Polarity negative positive +# + mode "1360x768-60" +# D: 84.750 MHz, H: 47.720 kHz, V: 60.00 Hz + geometry 1360 768 1360 768 32 + timings 11799 208 72 22 3 136 5 endmode mode "1366x768-60" +# D: 85.86 MHz, H: 47.700 kHz, V: 60.00 Hz + geometry 1366 768 1366 768 32 + timings 11647 216 72 23 1 144 3 endmode mode "1366x768-50" +# D: 69,924 MHz, H: 39.550 kHz, V: 50.00 Hz + geometry 1366 768 1366 768 32 timings 14301 200 56 19 1 144 3 endmode diff --git a/Documentation/fb/viafb.txt b/Documentation/fb/viafb.txt new file mode 100644 index 00000000000..67dbf442b0b --- /dev/null +++ b/Documentation/fb/viafb.txt @@ -0,0 +1,214 @@ + + VIA Integration Graphic Chip Console Framebuffer Driver + +[Platform] +----------------------- + The console framebuffer driver is for graphics chips of + VIA UniChrome Family(CLE266, PM800 / CN400 / CN300, + P4M800CE / P4M800Pro / CN700 / VN800, + CX700 / VX700, K8M890, P4M890, + CN896 / P4M900, VX800) + +[Driver features] +------------------------ + Device: CRT, LCD, DVI + + Support viafb_mode: + CRT: + 640x480(60, 75, 85, 100, 120 Hz), 720x480(60 Hz), + 720x576(60 Hz), 800x600(60, 75, 85, 100, 120 Hz), + 848x480(60 Hz), 856x480(60 Hz), 1024x512(60 Hz), + 1024x768(60, 75, 85, 100 Hz), 1152x864(75 Hz), + 1280x768(60 Hz), 1280x960(60 Hz), 1280x1024(60, 75, 85 Hz), + 1440x1050(60 Hz), 1600x1200(60, 75 Hz), 1280x720(60 Hz), + 1920x1080(60 Hz), 1400x1050(60 Hz), 800x480(60 Hz) + + color depth: 8 bpp, 16 bpp, 32 bpp supports. + + Support 2D hardware accelerator. + +[Using the viafb module] +-- -- -------------------- + Start viafb with default settings: + #modprobe viafb + + Start viafb with with user options: + #modprobe viafb viafb_mode=800x600 viafb_bpp=16 viafb_refresh=60 + viafb_active_dev=CRT+DVI viafb_dvi_port=DVP1 + viafb_mode1=1024x768 viafb_bpp=16 viafb_refresh1=60 + viafb_SAMM_ON=1 + + viafb_mode: + 640x480 (default) + 720x480 + 800x600 + 1024x768 + ...... + + viafb_bpp: + 8, 16, 32 (default:32) + + viafb_refresh: + 60, 75, 85, 100, 120 (default:60) + + viafb_lcd_dsp_method: + 0 : expansion (default) + 1 : centering + + viafb_lcd_mode: + 0 : LCD panel with LSB data format input (default) + 1 : LCD panel with MSB data format input + + viafb_lcd_panel_id: + 0 : Resolution: 640x480, Channel: single, Dithering: Enable + 1 : Resolution: 800x600, Channel: single, Dithering: Enable + 2 : Resolution: 1024x768, Channel: single, Dithering: Enable (default) + 3 : Resolution: 1280x768, Channel: single, Dithering: Enable + 4 : Resolution: 1280x1024, Channel: dual, Dithering: Enable + 5 : Resolution: 1400x1050, Channel: dual, Dithering: Enable + 6 : Resolution: 1600x1200, Channel: dual, Dithering: Enable + + 8 : Resolution: 800x480, Channel: single, Dithering: Enable + 9 : Resolution: 1024x768, Channel: dual, Dithering: Enable + 10: Resolution: 1024x768, Channel: single, Dithering: Disable + 11: Resolution: 1024x768, Channel: dual, Dithering: Disable + 12: Resolution: 1280x768, Channel: single, Dithering: Disable + 13: Resolution: 1280x1024, Channel: dual, Dithering: Disable + 14: Resolution: 1400x1050, Channel: dual, Dithering: Disable + 15: Resolution: 1600x1200, Channel: dual, Dithering: Disable + 16: Resolution: 1366x768, Channel: single, Dithering: Disable + 17: Resolution: 1024x600, Channel: single, Dithering: Enable + 18: Resolution: 1280x768, Channel: dual, Dithering: Enable + 19: Resolution: 1280x800, Channel: single, Dithering: Enable + + viafb_accel: + 0 : No 2D Hardware Acceleration + 1 : 2D Hardware Acceleration (default) + + viafb_SAMM_ON: + 0 : viafb_SAMM_ON disable (default) + 1 : viafb_SAMM_ON enable + + viafb_mode1: (secondary display device) + 640x480 (default) + 720x480 + 800x600 + 1024x768 + ... ... + + viafb_bpp1: (secondary display device) + 8, 16, 32 (default:32) + + viafb_refresh1: (secondary display device) + 60, 75, 85, 100, 120 (default:60) + + viafb_active_dev: + This option is used to specify active devices.(CRT, DVI, CRT+LCD...) + DVI stands for DVI or HDMI, E.g., If you want to enable HDMI, + set viafb_active_dev=DVI. In SAMM case, the previous of + viafb_active_dev is primary device, and the following is + secondary device. + + For example: + To enable one device, such as DVI only, we can use: + modprobe viafb viafb_active_dev=DVI + To enable two devices, such as CRT+DVI: + modprobe viafb viafb_active_dev=CRT+DVI; + + For DuoView case, we can use: + modprobe viafb viafb_active_dev=CRT+DVI + OR + modprobe viafb viafb_active_dev=DVI+CRT... + + For SAMM case: + If CRT is primary and DVI is secondary, we should use: + modprobe viafb viafb_active_dev=CRT+DVI viafb_SAMM_ON=1... + If DVI is primary and CRT is secondary, we should use: + modprobe viafb viafb_active_dev=DVI+CRT viafb_SAMM_ON=1... + + viafb_display_hardware_layout: + This option is used to specify display hardware layout for CX700 chip. + 1 : LCD only + 2 : DVI only + 3 : LCD+DVI (default) + 4 : LCD1+LCD2 (internal + internal) + 16: LCD1+ExternalLCD2 (internal + external) + + viafb_second_size: + This option is used to set second device memory size(MB) in SAMM case. + The minimal size is 16. + + viafb_platform_epia_dvi: + This option is used to enable DVI on EPIA - M + 0 : No DVI on EPIA - M (default) + 1 : DVI on EPIA - M + + viafb_bus_width: + When using 24 - Bit Bus Width Digital Interface, + this option should be set. + 12: 12-Bit LVDS or 12-Bit TMDS (default) + 24: 24-Bit LVDS or 24-Bit TMDS + + viafb_device_lcd_dualedge: + When using Dual Edge Panel, this option should be set. + 0 : No Dual Edge Panel (default) + 1 : Dual Edge Panel + + viafb_video_dev: + This option is used to specify video output devices(CRT, DVI, LCD) for + duoview case. + For example: + To output video on DVI, we should use: + modprobe viafb viafb_video_dev=DVI... + + viafb_lcd_port: + This option is used to specify LCD output port, + available values are "DVP0" "DVP1" "DFP_HIGHLOW" "DFP_HIGH" "DFP_LOW". + for external LCD + external DVI on CX700(External LCD is on DVP0), + we should use: + modprobe viafb viafb_lcd_port=DVP0... + +Notes: + 1. CRT may not display properly for DuoView CRT & DVI display at + the "640x480" PAL mode with DVI overscan enabled. + 2. SAMM stands for single adapter multi monitors. It is different from + multi-head since SAMM support multi monitor at driver layers, thus fbcon + layer doesn't even know about it; SAMM's second screen doesn't have a + device node file, thus a user mode application can't access it directly. + When SAMM is enabled, viafb_mode and viafb_mode1, viafb_bpp and + viafb_bpp1, viafb_refresh and viafb_refresh1 can be different. + 3. When console is depending on viafbinfo1, dynamically change resolution + and bpp, need to call VIAFB specified ioctl interface VIAFB_SET_DEVICE + instead of calling common ioctl function FBIOPUT_VSCREENINFO since + viafb doesn't support multi-head well, or it will cause screen crush. + 4. VX800 2D accelerator hasn't been supported in this driver yet. When + using driver on VX800, the driver will disable the acceleration + function as default. + + +[Configure viafb with "fbset" tool] +----------------------------------- + "fbset" is an inbox utility of Linux. + 1. Inquire current viafb information, type, + # fbset -i + + 2. Set various resolutions and viafb_refresh rates, + # fbset + + example, + # fbset "1024x768-75" + or + # fbset -g 1024 768 1024 768 32 + Check the file "/etc/fb.modes" to find display modes available. + + 3. Set the color depth, + # fbset -depth + + example, + # fbset -depth 16 + +[Bootup with viafb]: +-------------------- + Add the following line to your grub.conf: + append = "video=viafb:viafb_mode=1024x768,viafb_bpp=32,viafb_refresh=85" + -- cgit v1.2.3-70-g09d2 From 3f7a26b4b9768fe31597d1af35106aa512dc3742 Mon Sep 17 00:00:00 2001 From: Phil Endecott Date: Wed, 15 Oct 2008 22:03:35 -0700 Subject: intelfb: support 945GME (as used in ASUS Eee 901) Add support for Intel's 945GME graphics chip to the intelfb driver. I have assumed that the 945GME is identical to the already-supported 945GM apart from its PCI IDs; this is based on a quick look at the X driver for these chips which seems to treat them identically. The 945GME is used in the ASUS Eee 901, and I coded this in the hope that I'd be able to use it to get a console at the native 1024x600 resolution which is not known to the BIOS. I realised too late that the intelfb driver does not support mode changing on laptops, so it won't be any use for me. Signed-off-by: Phil Endecott Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/intelfb.txt | 1 + drivers/video/intelfb/intelfb.h | 7 +++++-- drivers/video/intelfb/intelfb_i2c.c | 1 + drivers/video/intelfb/intelfbdrv.c | 7 ++++++- drivers/video/intelfb/intelfbhw.c | 7 +++++++ 5 files changed, 20 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/fb/intelfb.txt b/Documentation/fb/intelfb.txt index 27a3160650a..dd9e944ea62 100644 --- a/Documentation/fb/intelfb.txt +++ b/Documentation/fb/intelfb.txt @@ -14,6 +14,7 @@ graphics devices. These would include: Intel 915GM Intel 945G Intel 945GM + Intel 945GME Intel 965G Intel 965GM diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index 3325fbd68ab..a50bea61480 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h @@ -12,9 +12,9 @@ #endif /*** Version/name ***/ -#define INTELFB_VERSION "0.9.5" +#define INTELFB_VERSION "0.9.6" #define INTELFB_MODULE_NAME "intelfb" -#define SUPPORTED_CHIPSETS "830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM/965G/965GM" +#define SUPPORTED_CHIPSETS "830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM/945GME/965G/965GM" /*** Debug/feature defines ***/ @@ -58,6 +58,7 @@ #define PCI_DEVICE_ID_INTEL_915GM 0x2592 #define PCI_DEVICE_ID_INTEL_945G 0x2772 #define PCI_DEVICE_ID_INTEL_945GM 0x27A2 +#define PCI_DEVICE_ID_INTEL_945GME 0x27AE #define PCI_DEVICE_ID_INTEL_965G 0x29A2 #define PCI_DEVICE_ID_INTEL_965GM 0x2A02 @@ -160,6 +161,7 @@ enum intel_chips { INTEL_915GM, INTEL_945G, INTEL_945GM, + INTEL_945GME, INTEL_965G, INTEL_965GM, }; @@ -363,6 +365,7 @@ struct intelfb_info { ((dinfo)->chipset == INTEL_915GM) || \ ((dinfo)->chipset == INTEL_945G) || \ ((dinfo)->chipset == INTEL_945GM) || \ + ((dinfo)->chipset == INTEL_945GME) || \ ((dinfo)->chipset == INTEL_965G) || \ ((dinfo)->chipset == INTEL_965GM)) diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c index fcf9fadbf57..5d896b81f4e 100644 --- a/drivers/video/intelfb/intelfb_i2c.c +++ b/drivers/video/intelfb/intelfb_i2c.c @@ -171,6 +171,7 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo) /* has some LVDS + tv-out */ case INTEL_945G: case INTEL_945GM: + case INTEL_945GME: case INTEL_965G: case INTEL_965GM: /* SDVO ports have a single control bus - 2 devices */ diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index e44303f9bc5..a09e2364935 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -2,7 +2,7 @@ * intelfb * * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G/915G/915GM/ - * 945G/945GM/965G/965GM integrated graphics chips. + * 945G/945GM/945GME/965G/965GM integrated graphics chips. * * Copyright © 2002, 2003 David Dawes * 2004 Sylvain Meyer @@ -102,6 +102,9 @@ * * 04/2008 - Version 0.9.5 * Add support for 965G/965GM. (Maik Broemme ) + * + * 08/2008 - Version 0.9.6 + * Add support for 945GME. (Phil Endecott ) */ #include @@ -183,6 +186,7 @@ static struct pci_device_id intelfb_pci_table[] __devinitdata = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_915GM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_915GM }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_945G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_945G }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_945GM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_945GM }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_945GME, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_945GME }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_965G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_965G }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_965GM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_965GM }, { 0, } @@ -555,6 +559,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, (ent->device == PCI_DEVICE_ID_INTEL_915GM) || (ent->device == PCI_DEVICE_ID_INTEL_945G) || (ent->device == PCI_DEVICE_ID_INTEL_945GM) || + (ent->device == PCI_DEVICE_ID_INTEL_945GME) || (ent->device == PCI_DEVICE_ID_INTEL_965G) || (ent->device == PCI_DEVICE_ID_INTEL_965GM)) { diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 8e6d6a4db0a..8b26b27c2db 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -143,6 +143,12 @@ int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) dinfo->mobile = 1; dinfo->pll_index = PLLS_I9xx; return 0; + case PCI_DEVICE_ID_INTEL_945GME: + dinfo->name = "Intel(R) 945GME"; + dinfo->chipset = INTEL_945GME; + dinfo->mobile = 1; + dinfo->pll_index = PLLS_I9xx; + return 0; case PCI_DEVICE_ID_INTEL_965G: dinfo->name = "Intel(R) 965G"; dinfo->chipset = INTEL_965G; @@ -186,6 +192,7 @@ int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, case PCI_DEVICE_ID_INTEL_915GM: case PCI_DEVICE_ID_INTEL_945G: case PCI_DEVICE_ID_INTEL_945GM: + case PCI_DEVICE_ID_INTEL_945GME: case PCI_DEVICE_ID_INTEL_965G: case PCI_DEVICE_ID_INTEL_965GM: /* 915, 945 and 965 chipsets support a 256MB aperture. -- cgit v1.2.3-70-g09d2 From c0dd504cea3703c3ec9bfd810e6bd649680afd37 Mon Sep 17 00:00:00 2001 From: Mike Pagano Date: Wed, 15 Oct 2008 22:03:46 -0700 Subject: uvesafb: document mode to mode_option parameter change Document the change from the old "mode" parameter to the "mode_option" parameter. Signed-off-by: Mike Pagano Cc: Krzysztof Halasa Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/uvesafb.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/fb/uvesafb.txt b/Documentation/fb/uvesafb.txt index bcfc233a008..7ac3c4078ff 100644 --- a/Documentation/fb/uvesafb.txt +++ b/Documentation/fb/uvesafb.txt @@ -52,7 +52,7 @@ are either given on the kernel command line or as module parameters, e.g.: video=uvesafb:1024x768-32,mtrr:3,ywrap (compiled into the kernel) - # modprobe uvesafb mode=1024x768-32 mtrr=3 scroll=ywrap (module) + # modprobe uvesafb mode_option=1024x768-32 mtrr=3 scroll=ywrap (module) Accepted options: @@ -105,7 +105,7 @@ vtotal:n The mode you want to set, in the standard modedb format. Refer to modedb.txt for a detailed description. When uvesafb is compiled as a module, the mode string should be provided as a value of the - 'mode' option. + 'mode_option' option. vbemode:x Force the use of VBE mode x. The mode will only be set if it's -- cgit v1.2.3-70-g09d2 From 758222f84261a6a808c4d1dcd443f90c1baaa875 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Oct 2008 22:04:14 -0700 Subject: docbook: update procfs credits Update Erik Mouw's email address & affiliation in DocBook. Signed-off-by: Randy Dunlap Acked-by: Erik Mouw Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/DocBook/procfs-guide.tmpl | 29 ++++++++++------------------- Documentation/DocBook/procfs_example.c | 20 ++++---------------- 2 files changed, 14 insertions(+), 35 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/procfs-guide.tmpl b/Documentation/DocBook/procfs-guide.tmpl index 8a5dc6e021f..9eba4b7af73 100644 --- a/Documentation/DocBook/procfs-guide.tmpl +++ b/Documentation/DocBook/procfs-guide.tmpl @@ -14,17 +14,20 @@ (J.A.K.) Mouw - Delft University of Technology - Faculty of Information Technology and Systems
- J.A.K.Mouw@its.tudelft.nl - PO BOX 5031 - 2600 GA - Delft - The Netherlands + mouw@nl.linux.org
+ + + This software and documentation were written while working on the + LART computing board + (http://www.lartmaker.nl/), + which was sponsored by the Delt University of Technology projects + Mobile Multi-media Communications and Ubiquitous Communications. + + @@ -108,18 +111,6 @@ proofreading. - - This documentation was written while working on the LART - computing board (http://www.lart.tudelft.nl/), - which is sponsored by the Mobile Multi-media Communications - (http://www.mmc.tudelft.nl/) - and Ubiquitous Communications (http://www.ubicom.tudelft.nl/) - projects. - - Erik diff --git a/Documentation/DocBook/procfs_example.c b/Documentation/DocBook/procfs_example.c index 2f3de0fb836..8c6396e4bf3 100644 --- a/Documentation/DocBook/procfs_example.c +++ b/Documentation/DocBook/procfs_example.c @@ -1,28 +1,16 @@ /* * procfs_example.c: an example proc interface * - * Copyright (C) 2001, Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * Copyright (C) 2001, Erik Mouw (mouw@nl.linux.org) * * This file accompanies the procfs-guide in the Linux kernel * source. Its main use is to demonstrate the concepts and * functions described in the guide. * * This software has been developed while working on the LART - * computing board (http://www.lart.tudelft.nl/), which is - * sponsored by the Mobile Multi-media Communications - * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications - * (http://www.ubicom.tudelft.nl/) projects. - * - * The author can be reached at: - * - * Erik Mouw - * Information and Communication Theory Group - * Faculty of Information Technology and Systems - * Delft University of Technology - * P.O. Box 5031 - * 2600 GA Delft - * The Netherlands - * + * computing board (http://www.lartmaker.nl), which was sponsored + * by the Delt University of Technology projects Mobile Multi-media + * Communications and Ubiquitous Communications. * * This program is free software; you can redistribute * it and/or modify it under the terms of the GNU General -- cgit v1.2.3-70-g09d2 From 6cd159744eaf212f3729d154f3881230a7c19eb2 Mon Sep 17 00:00:00 2001 From: David Fries Date: Wed, 15 Oct 2008 22:04:43 -0700 Subject: W1: feature, w1_therm.c use strong pullup and documentation Added strong pullup to thermal sensor driver and general documentation on the sensor. Signed-off-by: David Fries Signed-off-by: Evgeniy Polyakov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/w1/00-INDEX | 2 ++ Documentation/w1/slaves/00-INDEX | 4 ++++ Documentation/w1/slaves/w1_therm | 41 ++++++++++++++++++++++++++++++++++++++++ drivers/w1/slaves/w1_therm.c | 15 +++++++++++++-- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 Documentation/w1/slaves/00-INDEX create mode 100644 Documentation/w1/slaves/w1_therm (limited to 'Documentation') diff --git a/Documentation/w1/00-INDEX b/Documentation/w1/00-INDEX index 5270cf4cb10..cb49802745d 100644 --- a/Documentation/w1/00-INDEX +++ b/Documentation/w1/00-INDEX @@ -1,5 +1,7 @@ 00-INDEX - This file +slaves/ + - Drivers that provide support for specific family codes. masters/ - Individual chips providing 1-wire busses. w1.generic diff --git a/Documentation/w1/slaves/00-INDEX b/Documentation/w1/slaves/00-INDEX new file mode 100644 index 00000000000..f8101d6b07b --- /dev/null +++ b/Documentation/w1/slaves/00-INDEX @@ -0,0 +1,4 @@ +00-INDEX + - This file +w1_therm + - The Maxim/Dallas Semiconductor ds18*20 temperature sensor. diff --git a/Documentation/w1/slaves/w1_therm b/Documentation/w1/slaves/w1_therm new file mode 100644 index 00000000000..0403aaaba87 --- /dev/null +++ b/Documentation/w1/slaves/w1_therm @@ -0,0 +1,41 @@ +Kernel driver w1_therm +==================== + +Supported chips: + * Maxim ds18*20 based temperature sensors. + +Author: Evgeniy Polyakov + + +Description +----------- + +w1_therm provides basic temperature conversion for ds18*20 devices. +supported family codes: +W1_THERM_DS18S20 0x10 +W1_THERM_DS1822 0x22 +W1_THERM_DS18B20 0x28 + +Support is provided through the sysfs w1_slave file. Each open and +read sequence will initiate a temperature conversion then provide two +lines of ASCII output. The first line contains the nine hex bytes +read along with a calculated crc value and YES or NO if it matched. +If the crc matched the returned values are retained. The second line +displays the retained values along with a temperature in millidegrees +Centigrade after t=. + +Parasite powered devices are limited to one slave performing a +temperature conversion at a time. If none of the devices are parasite +powered it would be possible to convert all the devices at the same +time and then go back to read individual sensors. That isn't +currently supported. The driver also doesn't support reduced +precision (which would also reduce the conversion time). + +The module parameter strong_pullup can be set to 0 to disable the +strong pullup or 1 to enable. If enabled the 5V strong pullup will be +enabled when the conversion is taking place provided the master driver +must support the strong pullup (or it falls back to a pullup +resistor). The DS18b20 temperature sensor specification lists a +maximum current draw of 1.5mA and that a 5k pullup resistor is not +sufficient. The strong pullup is designed to provide the additional +current required. diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index fb28acaeed6..e87f464a6fb 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -37,6 +37,14 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); +/* Allow the strong pullup to be disabled, but default to enabled. + * If it was disabled a parasite powered device might not get the require + * current to do a temperature conversion. If it is enabled parasite powered + * devices have a better chance of getting the current required. + */ +static int w1_strong_pullup = 1; +module_param_named(strong_pullup, w1_strong_pullup, int, 0); + static u8 bad_roms[][9] = { {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87}, {} @@ -192,9 +200,12 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, int count = 0; unsigned int tm = 750; + /* 750ms strong pullup (or delay) after the convert */ + if (w1_strong_pullup) + w1_next_pullup(dev, tm); w1_write_8(dev, W1_CONVERT_TEMP); - - msleep(tm); + if (!w1_strong_pullup) + msleep(tm); if (!w1_reset_select_slave(sl)) { -- cgit v1.2.3-70-g09d2 From eba3b06da4bd8b79fe6c8ed922a319362c1a40c0 Mon Sep 17 00:00:00 2001 From: David Fries Date: Wed, 15 Oct 2008 22:04:47 -0700 Subject: W1: Document add, remove, search_count, and pullup. Document w1_master_add, w1_master_remove, search_count, and pullup. Signed-off-by: David Fries Signed-off-by: Evgeniy Polyakov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/w1/w1.generic | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/w1/w1.generic b/Documentation/w1/w1.generic index 4c6509dd478..e3333eec432 100644 --- a/Documentation/w1/w1.generic +++ b/Documentation/w1/w1.generic @@ -79,10 +79,13 @@ w1 master sysfs interface - a directory for a found device. The format is family-serial bus - (standard) symlink to the w1 bus driver - (standard) symlink to the w1 driver +w1_master_add - Manually register a slave device w1_master_attempts - the number of times a search was attempted w1_master_max_slave_count - the maximum slaves that may be attached to a master w1_master_name - the name of the device (w1_bus_masterX) +w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled +w1_master_remove - Manually remove a slave device w1_master_search - the number of searches left to do, -1=continual (default) w1_master_slave_count - the number of slaves found @@ -90,7 +93,13 @@ w1_master_slaves - the names of the slaves, one per line w1_master_timeout - the delay in seconds between searches If you have a w1 bus that never changes (you don't add or remove devices), -you can set w1_master_search to a positive value to disable searches. +you can set the module parameter search_count to a small positive number +for an initially small number of bus searches. Alternatively it could be +set to zero, then manually add the slave device serial numbers by +w1_master_add device file. The w1_master_add and w1_master_remove files +generally only make sense when searching is disabled, as a search will +redetect manually removed devices that are present and timeout manually +added devices that aren't on the bus. w1 slave sysfs interface -- cgit v1.2.3-70-g09d2 From 3823ee44cfa8b0e6edbc0c21b81b49b95a27ca0d Mon Sep 17 00:00:00 2001 From: David Fries Date: Wed, 15 Oct 2008 22:05:09 -0700 Subject: W1: Documentation/w1/masters/ds2490 update Provide some additional details about the status of the driver and the ds2490 hardware. Signed-off-by: David Fries Signed-off-by: Evgeniy Polyakov Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/w1/masters/ds2490 | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'Documentation') diff --git a/Documentation/w1/masters/ds2490 b/Documentation/w1/masters/ds2490 index 239f9ae0184..28176def3d6 100644 --- a/Documentation/w1/masters/ds2490 +++ b/Documentation/w1/masters/ds2490 @@ -16,3 +16,55 @@ which allows to build USB <-> W1 bridges. DS9490(R) is a USB <-> W1 bus master device which has 0x81 family ID integrated chip and DS2490 low-level operational chip. + +Notes and limitations. +- The weak pullup current is a minimum of 0.9mA and maximum of 6.0mA. +- The 5V strong pullup is supported with a minimum of 5.9mA and a + maximum of 30.4 mA. (From DS2490.pdf) +- While the ds2490 supports a hardware search the code doesn't take + advantage of it (in tested case it only returned first device). +- The hardware will detect when devices are attached to the bus on the + next bus (reset?) operation, however only a message is printed as + the core w1 code doesn't make use of the information. Connecting + one device tends to give multiple new device notifications. +- The number of USB bus transactions could be reduced if w1_reset_send + was added to the API. The name is just a suggestion. It would take + a write buffer and a read buffer (along with sizes) as arguments. + The ds2490 block I/O command supports reset, write buffer, read + buffer, and strong pullup all in one command, instead of the current + 1 reset bus, 2 write the match rom command and slave rom id, 3 block + write and read data. The write buffer needs to have the match rom + command and slave rom id prepended to the front of the requested + write buffer, both of which are known to the driver. +- The hardware supports normal, flexible, and overdrive bus + communication speeds, but only the normal is supported. +- The registered w1_bus_master functions don't define error + conditions. If a bus search is in progress and the ds2490 is + removed it can produce a good amount of error output before the bus + search finishes. +- The hardware supports detecting some error conditions, such as + short, alarming presence on reset, and no presence on reset, but the + driver doesn't query those values. +- The ds2490 specification doesn't cover short bulk in reads in + detail, but my observation is if fewer bytes are requested than are + available, the bulk read will return an error and the hardware will + clear the entire bulk in buffer. It would be possible to read the + maximum buffer size to not run into this error condition, only extra + bytes in the buffer is a logic error in the driver. The code should + should match reads and writes as well as data sizes. Reads and + writes are serialized and the status verifies that the chip is idle + (and data is available) before the read is executed, so it should + not happen. +- Running x86_64 2.6.24 UHCI under qemu 0.9.0 under x86_64 2.6.22-rc6 + with a OHCI controller, ds2490 running in the guest would operate + normally the first time the module was loaded after qemu attached + the ds2490 hardware, but if the module was unloaded, then reloaded + most of the time one of the bulk out or in, and usually the bulk in + would fail. qemu sets a 50ms timeout and the bulk in would timeout + even when the status shows data available. A bulk out write would + show a successful completion, but the ds2490 status register would + show 0 bytes written. Detaching qemu from the ds2490 hardware and + reattaching would clear the problem. usbmon output in the guest and + host did not explain the problem. My guess is a bug in either qemu + or the host OS and more likely the host OS. +-- 03-06-2008 David Fries -- cgit v1.2.3-70-g09d2 From 656e6c0050fd63ce42c55a6cb454a9b4b2f9ccf7 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Tue, 7 Oct 2008 13:21:56 +0200 Subject: Document panic_on_unrecovered_nmi sysctl This adds "panic_on_unrecovered_nmi" sysctl to Documentation/filesystems/proc.txt. The text is mainly taken from http://readlist.com/lists/vger.kernel.org/linux-kernel/43/217998.html. Signed-off-by: Bernhard Walle Signed-off-by: Jonathan Corbet --- Documentation/filesystems/proc.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index f566ad9bcb7..d2f77d95956 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1322,6 +1322,18 @@ debugging information is displayed on console. NMI switch that most IA32 servers have fires unknown NMI up, for example. If a system hangs up, try pressing the NMI switch. +panic_on_unrecovered_nmi +------------------------ + +The default Linux behaviour on an NMI of either memory or unknown is to continue +operation. For many environments such as scientific computing it is preferable +that the box is taken out and the error dealt with than an uncorrected +parity/ECC error get propogated. + +A small number of systems do generate NMI's for bizarre random reasons such as +power management so the default is off. That sysctl works like the existing +panic controls already in that directory. + nmi_watchdog ------------ -- cgit v1.2.3-70-g09d2 From 22359f5745eb26bd3205a1ede7968c8944398220 Mon Sep 17 00:00:00 2001 From: Diego Calleja Date: Fri, 17 Oct 2008 09:15:14 -0400 Subject: ext4: Update Documentation/filesystems/ext4.txt Since Ext4 is supposed to be stable in 2.6.28-rc, ext4's documentation file should be updated. [ More updates also added by Theodore Ts'o. ] Signed-off-by: Diego Calleja Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index eb154ef36c2..174eaff7ded 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -2,19 +2,24 @@ Ext4 Filesystem =============== -This is a development version of the ext4 filesystem, an advanced level -of the ext3 filesystem which incorporates scalability and reliability -enhancements for supporting large filesystems (64 bit) in keeping with -increasing disk capacities and state-of-the-art feature requirements. +Ext4 is an an advanced level of the ext3 filesystem which incorporates +scalability and reliability enhancements for supporting large filesystems +(64 bit) in keeping with increasing disk capacities and state-of-the-art +feature requirements. -Mailing list: linux-ext4@vger.kernel.org +Mailing list: linux-ext4@vger.kernel.org +Web site: http://ext4.wiki.kernel.org 1. Quick usage instructions: =========================== +Note: More extensive information for getting started with ext4 can be + found at the ext4 wiki site at the URL: + http://ext4.wiki.kernel.org/index.php/Ext4_Howto + - Compile and install the latest version of e2fsprogs (as of this - writing version 1.41) from: + writing version 1.41.3) from: http://sourceforge.net/project/showfiles.php?group_id=2406 @@ -36,11 +41,9 @@ Mailing list: linux-ext4@vger.kernel.org # mke2fs -t ext4 /dev/hda1 - Or configure an existing ext3 filesystem to support extents and set - the test_fs flag to indicate that it's ok for an in-development - filesystem to touch this filesystem: + Or to configure an existing ext3 filesystem to support extents: - # tune2fs -O extents -E test_fs /dev/hda1 + # tune2fs -O extents /dev/hda1 If the filesystem was created with 128 byte inodes, it can be converted to use 256 byte for greater efficiency via: @@ -104,8 +107,8 @@ exist yet so I'm not sure they're in the near-term roadmap. The big performance win will come with mballoc, delalloc and flex_bg grouping of bitmaps and inode tables. Some test results available here: - - http://www.bullopensource.org/ext4/20080530/ffsb-write-2.6.26-rc2.html - - http://www.bullopensource.org/ext4/20080530/ffsb-readwrite-2.6.26-rc2.html + - http://www.bullopensource.org/ext4/20080818-ffsb/ffsb-write-2.6.27-rc1.html + - http://www.bullopensource.org/ext4/20080818-ffsb/ffsb-readwrite-2.6.27-rc1.html 3. Options ========== @@ -214,9 +217,6 @@ noreservation bsddf (*) Make 'df' act like BSD. minixdf Make 'df' act like Minix. -check=none Don't do extra checking of bitmaps on mount. -nocheck - debug Extra debugging information is sent to syslog. errors=remount-ro(*) Remount the filesystem read-only on an error. @@ -253,8 +253,6 @@ nobh (a) cache disk block mapping information "nobh" option tries to avoid associating buffer heads (supported only for "writeback" mode). -mballoc (*) Use the multiple block allocator for block allocation -nomballoc disabled multiple block allocator for block allocation. stripe=n Number of filesystem blocks that mballoc will try to use for allocation size and alignment. For RAID5/6 systems this should be the number of data -- cgit v1.2.3-70-g09d2 From f65e17086fc141bee1592bbf6e709e9c7a43541b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Support the extra resolution bits of MAX6657 The Maxim MAX6657, MAX6658 and MAX6659 have extra resolution bits for the local temperature measurement. Let the lm90 driver read them and export them to user-space. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 10 +++++---- drivers/hwmon/lm90.c | 54 +++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index aa4a0ec2008..0b3e8bb7c1f 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -86,9 +86,8 @@ family is that it features critical limits with hysteresis, and an increased resolution of the remote temperature measurement. The different chipsets of the family are not strictly identical, although -very similar. This driver doesn't handle any specific feature for now, -with the exception of SMBus PEC. For reference, here comes a non-exhaustive -list of specific features: +very similar. For reference, here comes a non-exhaustive list of specific +features: LM90: * Filter and alert configuration register at 0xBF. @@ -114,9 +113,11 @@ ADT7461: * Lower resolution for remote temperature MAX6657 and MAX6658: + * Better local resolution * Remote sensor type selection MAX6659: + * Better local resolution * Selectable address * Second critical temperature limit * Remote sensor type selection @@ -127,7 +128,8 @@ MAX6680 and MAX6681: All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote -temperature. +temperature, except for the MAX6657, MAX6658 and MAX6659 which have a +resolution of 0.125 degree for both temperatures. Each sensor has its own high and low limits, plus a critical limit. Additionally, there is a relative hysteresis value common to both critical diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 73a1c622fb7..16b99e0bdff 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -149,6 +149,10 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 +/* MAX6657-specific registers */ + +#define MAX6657_REG_R_LOCAL_TEMPL 0x11 + /* * Conversions and various macros * For local temperatures and limits, critical limits and the hysteresis @@ -239,15 +243,15 @@ struct lm90_data { int kind; /* registers values */ - s8 temp8[5]; /* 0: local input - 1: local low limit - 2: local high limit - 3: local critical limit - 4: remote critical limit */ - s16 temp11[4]; /* 0: remote input + s8 temp8[4]; /* 0: local low limit + 1: local high limit + 2: local critical limit + 3: remote critical limit */ + s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) */ + 3: remote offset (except max6657) + 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ }; @@ -285,7 +289,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); else data->temp8[nr] = TEMP1_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); + i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; } @@ -347,7 +351,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[3]) - val; + hyst = TEMP1_FROM_REG(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, HYST_TO_REG(hyst)); mutex_unlock(&data->update_lock); @@ -371,23 +375,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); + set_temp8, 0); static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); + set_temp8, 3); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 3); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); + set_temphyst, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 3); @@ -779,13 +783,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); + if (data->kind == max6657) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); -- cgit v1.2.3-70-g09d2 From a874a10cf0b7105ae5eeb98b4860eae0fc78fcdd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Update datasheet links Update the links to the datasheet of some of the devices supported by the lm90 driver. Also remove the links from the driver itself, so that we don't have to update them twice each time they change. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 12 ++++++------ drivers/hwmon/lm90.c | 30 +++++++----------------------- 2 files changed, 13 insertions(+), 29 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0b3e8bb7c1f..0c08c5c5637 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -11,7 +11,7 @@ Supported chips: Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM89.html + http://www.national.com/mpf/LM/LM89.html * National Semiconductor LM99 Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d @@ -21,17 +21,17 @@ Supported chips: Prefix: 'lm86' Addresses scanned: I2C 0x4c Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM86.html + http://www.national.com/mpf/LM/LM86.html * Analog Devices ADM1032 Prefix: 'adm1032' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADM1032,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADM1032 * Analog Devices ADT7461 Prefix: 'adt7461' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADT7461,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 90489b8f5c8..d96e403d59e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -6,9 +6,7 @@ * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to * one external one) with a 0.125 deg resolution (1 deg for local - * temperature) and a 3-4 deg accuracy. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM90.html + * temperature) and a 3-4 deg accuracy. * * This driver also supports the LM89 and LM99, two other sensor chips * made by National Semiconductor. Both have an increased remote @@ -16,29 +14,19 @@ * additionally shifts remote temperatures (measured and limits) by 16 * degrees, which allows for higher temperatures measurement. The * driver doesn't handle it since it can be done easily in user-space. - * Complete datasheets can be obtained from National's website at: - * http://www.national.com/pf/LM/LM89.html - * http://www.national.com/pf/LM/LM99.html * Note that there is no way to differentiate between both chips. * * This driver also supports the LM86, another sensor chip made by * National Semiconductor. It is exactly similar to the LM90 except it * has a higher accuracy. - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM86.html * * This driver also supports the ADM1032, a sensor chip made by Analog * Devices. That chip is similar to the LM90, with a few differences - * that are not handled by this driver. Complete datasheet can be - * obtained from Analog's website at: - * http://www.analog.com/en/prod/0,2877,ADM1032,00.html - * Among others, it has a higher accuracy than the LM90, much like the - * LM86 does. + * that are not handled by this driver. Among others, it has a higher + * accuracy than the LM90, much like the LM86 does. * * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor - * chips made by Maxim. These chips are similar to the LM86. Complete - * datasheet can be obtained at Maxim's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three * variants. The extra address and features of the MAX6659 are not * supported by this driver. These chips lack the remote temperature @@ -46,18 +34,14 @@ * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim - * chips. Complete datasheet can be obtained at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 - * The MAX6680 and MAX6681 only differ in the pinout so they can be - * treated identically. + * chips. The MAX6680 and MAX6681 only differ in the pinout so they can + * be treated identically. * * This driver also supports the ADT7461 chip from Analog Devices but * only in its "compatability mode". If an ADT7461 chip is found but * is configured in non-compatible mode (where its temperature * register values are decoded differently) it is ignored by this - * driver. Complete datasheet can be obtained from Analog's website - * at: - * http://www.analog.com/en/prod/0,2877,ADT7461,00.html + * driver. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and -- cgit v1.2.3-70-g09d2 From 23b2d4778ad33ee6bfe60439fb73c16580f204f2 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Support ADT7461 in extended mode Support ADT7461 in extended temperature range mode, which will change the range of readings from 0..127 to -64..191 degC. Adjust the register conversion functions accordingly. Signed-off-by: Nate Case Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- Documentation/hwmon/lm90 | 8 +--- drivers/hwmon/Kconfig | 7 +-- drivers/hwmon/lm90.c | 120 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 97 insertions(+), 38 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0c08c5c5637..53cd829f3f4 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,7 +32,6 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 - Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c @@ -70,16 +69,13 @@ Description The LM90 is a digital temperature sensor. It senses its own temperature as well as the temperature of up to one external diode. It is compatible -with many other devices such as the LM86, the LM89, the LM99, the ADM1032, -the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are -supported by this driver. +with many other devices, many of which are supported by this driver. Note that there is no easy way to differentiate between the MAX6657, MAX6658 and MAX6659 variants. The extra address and features of the MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously can't (and don't need to) -be distinguished. Additionally, the ADT7461 is supported if found in -ADM1032 compatibility mode. +be distinguished. The specificity of this family of chipsets over the ADM1021/LM84 family is that it features critical limits with hysteresis, and an diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ebacc0af40f..96701e099e8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,11 +510,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, - MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. - - The Analog Devices ADT7461 sensor chip is also supported, but only - if found in ADM1032 compatibility mode. + LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim + MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b2d9b3f0946..fe5d860fc83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -37,11 +37,10 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * - * This driver also supports the ADT7461 chip from Analog Devices but - * only in its "compatability mode". If an ADT7461 chip is found but - * is configured in non-compatible mode (where its temperature - * register values are decoded differently) it is ignored by this - * driver. + * This driver also supports the ADT7461 chip from Analog Devices. + * It's supported in both compatibility and extended mode. It is mostly + * compatible with LM90 except for a data format difference for the + * temperature value registers. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and @@ -137,6 +136,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +/* + * Device flags + */ +#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ + /* * Functions declaration */ @@ -191,6 +195,7 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; + int flags; /* registers values */ s8 temp8[4]; /* 0: local low limit @@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val) } /* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. + * ADT7461 in compatibility mode is almost identical to LM90 except that + * attempts to write values that are outside the range 0 < temp < 127 are + * treated as the boundary value. + * + * ADT7461 in "extended mode" operation uses unsigned integers offset by + * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. */ -static u8 temp1_to_reg_adt7461(long val) +static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val) { - if (val <= 0) - return 0; - if (val >= 127000) - return 127; - return (val + 500) / 1000; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 64) * 1000; + else + return temp1_from_reg(val); } -static u16 temp2_to_reg_adt7461(long val) +static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val) { - if (val <= 0) - return 0; - if (val >= 127750) - return 0x7FC0; - return (val + 125) / 250 * 64; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 0x4000) / 64 * 250; + else + return temp2_from_reg(val); +} + +static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191000) + return 0xFF; + return (val + 500 + 64000) / 1000; + } else { + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; + } +} + +static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191750) + return 0xFFC0; + return (val + 64000 + 125) / 250 * 64; + } else { + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; + } } /* @@ -287,7 +327,14 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -308,7 +355,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = temp1_to_reg_adt7461(val); + data->temp8[nr] = temp1_to_reg_adt7461(data, val); else data->temp8[nr] = temp1_to_reg(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -321,7 +368,14 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]); + else + temp = temp2_from_reg(data->temp11[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -344,7 +398,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp2_to_reg_adt7461(val); + data->temp11[nr] = temp2_to_reg_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp1_to_reg(val) << 8; else @@ -364,8 +418,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) - - temp1_from_reg(data->temp_hyst)); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -598,7 +658,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind, kind = adm1032; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && (reg_config1 & 0x1B) == 0x00 && reg_convrate <= 0x0A) { kind = adt7461; } @@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client) } config_orig = config; + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + /* * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range -- cgit v1.2.3-70-g09d2 From 271dabf5bbf6ae6e2792cd5cf6f0434230e5c18c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm90) Support MAX6646, MAX6647 and MAX6649 These Maxim chips are similar to MAX6657 but use unsigned temperature values to allow for readings up to 145 degrees. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 15 ++++++++++ drivers/hwmon/Kconfig | 3 +- drivers/hwmon/lm90.c | 77 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 82 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 53cd829f3f4..e0d5206d1de 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,6 +32,21 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 + * Maxim MAX6646 + Prefix: 'max6646' + Addresses scanned: I2C 0x4d + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6647 + Prefix: 'max6646' + Addresses scanned: I2C 0x4e + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6649 + Prefix: 'max6646' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 96701e099e8..6de1e0ffd39 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -511,7 +511,8 @@ config SENSORS_LM90 help If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim - MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. + MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and + MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 85ba2c4feb4..fe09f82c42e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -32,6 +32,11 @@ * supported by this driver. These chips lack the remote temperature * offset feature. * + * This driver also supports the MAX6646, MAX6647 and MAX6649 chips + * made by Maxim. These are again similar to the LM86, but they use + * unsigned temperature values and can report temperatures from 0 to + * 145 degrees. + * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim * chips. The MAX6680 and MAX6681 only differ in the pinout so they can @@ -76,9 +81,10 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658 - * have address 0x4c. - * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 + * and MAX6658 have address 0x4c. + * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. + * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. @@ -91,7 +97,8 @@ static const unsigned short normal_i2c[] = { * Insmod parameters */ -I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); +I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, + max6646); /* * The LM90 registers @@ -132,7 +139,7 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6657-specific registers */ +/* MAX6646/6647/6649/6657/6658/6659 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 @@ -164,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = { { "lm86", lm86 }, { "lm89", lm99 }, { "lm99", lm99 }, /* Missing temperature offset */ + { "max6646", max6646 }, + { "max6647", max6646 }, + { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, { "max6659", max6657 }, @@ -205,7 +215,7 @@ struct lm90_data { s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) + 3: remote offset (except max6646 and max6657) 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ @@ -216,7 +226,8 @@ struct lm90_data { * For local temperatures and limits, critical limits and the hysteresis * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some + * Maxim chips use unsigned values. */ static inline int temp_from_s8(s8 val) @@ -224,11 +235,21 @@ static inline int temp_from_s8(s8 val) return val * 1000; } +static inline int temp_from_u8(u8 val) +{ + return val * 1000; +} + static inline int temp_from_s16(s16 val) { return val / 32 * 125; } +static inline int temp_from_u16(u16 val) +{ + return val / 32 * 125; +} + static s8 temp_to_s8(long val) { if (val <= -128000) @@ -240,6 +261,15 @@ static s8 temp_to_s8(long val) return (val + 500) / 1000; } +static u8 temp_to_u8(long val) +{ + if (val <= 0) + return 0; + if (val >= 255000) + return 255; + return (val + 500) / 1000; +} + static s16 temp_to_s16(long val) { if (val <= -128000) @@ -331,6 +361,8 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[attr->index]); else temp = temp_from_s8(data->temp8[attr->index]); @@ -356,6 +388,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp8[nr] = temp_to_u8_adt7461(data, val); + else if (data->kind == max6646) + data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -372,6 +406,8 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u16(data->temp11[attr->index]); else temp = temp_from_s16(data->temp11[attr->index]); @@ -401,12 +437,15 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, data->temp11[nr] = temp_to_u16_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp_to_s8(val) << 8; + else if (data->kind == max6646) + data->temp11[nr] = temp_to_u8(val) << 8; else data->temp11[nr] = temp_to_s16(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - if (data->kind != max6657 && data->kind != max6680) + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646) i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); @@ -689,6 +728,16 @@ static int lm90_detect(struct i2c_client *new_client, int kind, && (reg_config1 & 0x03) == 0x00 && reg_convrate <= 0x07) { kind = max6680; + } else + /* The chip_id register of the MAX6646/6647/6649 + * holds the revision of the chip. + * The lowest 6 bits of the config1 register are + * unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + kind = max6646; } } @@ -719,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind, name = "max6680"; } else if (kind == adt7461) { name = "adt7461"; + } else if (kind == max6646) { + name = "max6646"; } strlcpy(info->type, name, I2C_NAME_SIZE); @@ -758,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client, &dev_attr_pec))) goto exit_remove_files; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if ((err = device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr))) goto exit_remove_files; @@ -824,7 +875,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm90_group); device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657) + if (data->kind != max6657 && data->kind != max6646) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -881,7 +932,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - if (data->kind == max6657) { + if (data->kind == max6657 || data->kind == max6646) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, MAX6657_REG_R_LOCAL_TEMPL, &data->temp11[4]); @@ -896,6 +947,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { data->temp11[1] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) data->temp11[1] |= l; @@ -903,12 +955,13 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { data->temp11[2] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) data->temp11[2] |= l; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, -- cgit v1.2.3-70-g09d2 From 47064d645bc55863c7887a7c96cde39c9a37ee5f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (lm87) Add support for configuration through platform_data The lm87 driver normally assumes that firmware configured the chip correctly. Since this is not always the case, alllow platform code to set the channel register value via platform_data. All other configuration registers can be changed after driver initialisation. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm87 | 9 ++++----- drivers/hwmon/lm87.c | 17 +++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm87 b/Documentation/hwmon/lm87 index ec27aa1b94c..6b47b67fd96 100644 --- a/Documentation/hwmon/lm87 +++ b/Documentation/hwmon/lm87 @@ -65,11 +65,10 @@ The LM87 has four pins which can serve one of two possible functions, depending on the hardware configuration. Some functions share pins, so not all functions are available at the same -time. Which are depends on the hardware setup. This driver assumes that -the BIOS configured the chip correctly. In that respect, it differs from -the original driver (from lm_sensors for Linux 2.4), which would force the -LM87 to an arbitrary, compile-time chosen mode, regardless of the actual -chipset wiring. +time. Which are depends on the hardware setup. This driver normally +assumes that firmware configured the chip correctly. Where this is not +the case, platform code must set the I2C client's platform_data to point +to a u8 value to be written to the channel register. For reference, here is the list of exclusive functions: - in0+in5 (default) or temp3 diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index fa0e3794d9a..2e4a3cea95f 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -21,11 +21,10 @@ * http://www.national.com/pf/LM/LM87.html * * Some functions share pins, so not all functions are available at the same - * time. Which are depends on the hardware setup. This driver assumes that - * the BIOS configured the chip correctly. In that respect, it differs from - * the original driver (from lm_sensors for Linux 2.4), which would force the - * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual - * chipset wiring. + * time. Which are depends on the hardware setup. This driver normally + * assumes that firmware configured the chip correctly. Where this is not + * the case, platform code must set the I2C client's platform_data to point + * to a u8 value to be written to the channel register. * For reference, here is the list of exclusive functions: * - in0+in5 (default) or temp3 * - fan1 (default) or in6 @@ -843,7 +842,13 @@ static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + if (client->dev.platform_data) { + data->channel = *(u8 *)client->dev.platform_data; + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; if (!(data->config & 0x01)) { -- cgit v1.2.3-70-g09d2 From 34e7dc6ca4a663a1bb0a0a4e118426849dccd72d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Implement the standard PWM frequency interface Implement the standard PWM frequency interface: pwm[1-*]_freq in units of 1 Hz, instead of the non-standard pwm[1-*]_auto_pwm_freq in units of 0.1 Hz. The old naming was not only non-standard, it was also confusing, because it suggested that the frequency value only applied in automatic fan speed mode, which isn't true. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- Documentation/hwmon/lm85 | 10 ------- drivers/hwmon/lm85.c | 77 ++++++++++++++++++++++++------------------------ 2 files changed, 38 insertions(+), 49 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index 6d41db7f17f..40062074129 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -163,16 +163,6 @@ configured individually according to the following options. * pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off temperature. (PWM value from 0 to 255) -* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select - in range of 10.0 to 94.0 Hz in .1 Hz units. - (Values 100 to 940). - -The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the -frequency to a value not on this list, will result in the next higher frequency -being selected. The actual device frequency may vary slightly from this -specification as designed by the manufacturer. Consult the datasheet for more -details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940) - * pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature the bahaviour of fans. Write 1 to let fans spinning at pwm#_auto_pwm_min or write 0 to let them off. diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3594a02f281..6b676df3547 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -191,8 +191,8 @@ static int RANGE_TO_REG(int range) #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] /* These are the PWM frequency encodings */ -static const int lm85_freq_map[] = { /* .1 Hz */ - 100, 150, 230, 300, 380, 470, 620, 940 +static const int lm85_freq_map[8] = { /* 1 Hz */ + 10, 15, 23, 30, 38, 47, 62, 94 }; static int FREQ_TO_REG(int freq) @@ -275,7 +275,6 @@ struct lm85_zone { struct lm85_autofan { u8 config; /* Register value */ - u8 freq; /* PWM frequency, encoded */ u8 min_pwm; /* Minimum PWM value, encoded */ u8 min_off; /* Min PWM or OFF below "limit", flag */ }; @@ -301,6 +300,7 @@ struct lm85_data { u16 fan[4]; /* Register value */ u16 fan_min[4]; /* Register value */ u8 pwm[3]; /* Register value */ + u8 pwm_freq[3]; /* Register encoding */ u8 temp_ext[3]; /* Decoded values */ u8 in_ext[8]; /* Decoded values */ u8 vid; /* Register value */ @@ -528,11 +528,38 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute return count; } +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%d\n", FREQ_FROM_REG(data->pwm_freq[nr])); +} + +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->pwm_freq[nr] = FREQ_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + mutex_unlock(&data->update_lock); + return count; +} + #define show_pwm_reg(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1) + show_pwm_enable, set_pwm_enable, offset - 1); \ +static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \ + show_pwm_freq, set_pwm_freq, offset - 1) show_pwm_reg(1); show_pwm_reg(2); @@ -761,31 +788,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, return count; } -static ssize_t show_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->autofan[nr].freq)); -} - -static ssize_t set_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->autofan[nr].freq = FREQ_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->autofan[nr].freq); - mutex_unlock(&data->update_lock); - return count; -} - #define pwm_auto(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \ S_IRUGO | S_IWUSR, show_pwm_auto_channels, \ @@ -795,10 +797,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \ set_pwm_auto_pwm_min, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \ S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \ - set_pwm_auto_pwm_minctl, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \ - S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \ - set_pwm_auto_pwm_freq, offset - 1); + set_pwm_auto_pwm_minctl, offset - 1) pwm_auto(1); pwm_auto(2); @@ -867,7 +866,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, TEMP_FROM_REG(data->zone[nr].limit)); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); /* Update temp_auto_hyst and temp_auto_off */ data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( @@ -910,7 +909,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, val - min); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); mutex_unlock(&data->update_lock); return count; } @@ -984,6 +983,9 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, @@ -1026,9 +1028,6 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, @@ -1458,7 +1457,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) data->autofan[i].config = lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); - data->autofan[i].freq = val & 0x07; + data->pwm_freq[i] = val & 0x07; data->zone[i].range = val >> 4; data->autofan[i].min_pwm = lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); -- cgit v1.2.3-70-g09d2 From 4ed1077953f531b3fef4af4b4ade48a828c48869 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (it87) Fix thermal sensor type values The it87 driver doesn't follow the standard sensor type values as documented in Documentation/hwmon/sysfs-interface. It uses value 2 for thermistors instead of value 4. This causes "sensors" to tell the user that the chip is setup for a transistor while it is actually setup for a thermistor. Using value 4 for thermistors solves the problem. For compatibility reasons, we still accept value 2 but emit a warning message so that users update their configuration files. Signed-off-by: Jean Delvare Acked-by: Hans de Goede --- Documentation/hwmon/it87 | 4 ++-- drivers/hwmon/it87.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 3496b7020e7..042c0415140 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -136,10 +136,10 @@ once-only alarms. The IT87xx only updates its values each 1.5 seconds; reading it more often will do no harm, but will return 'old' values. -To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, +To change sensor N to a thermistor, 'echo 4 > tempN_type' where N is 1, 2, or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. Give 0 for unused sensor. Any other value is invalid. To configure this at -startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; +startup, consult lm_sensors's /etc/sensors.conf. (4 = thermistor; 3 = thermal diode) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index d793cc01199..b74c95735f9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) - return sprintf(buf, "2\n"); /* thermistor */ + return sprintf(buf, "4\n"); /* thermistor */ return sprintf(buf, "0\n"); /* disabled */ } static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, @@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, data->sensor &= ~(1 << nr); data->sensor &= ~(8 << nr); - /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ + if (val == 2) { /* backwards compatibility */ + dev_warn(dev, "Sensor type 2 is deprecated, please use 4 " + "instead\n"); + val = 4; + } + /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ if (val == 3) data->sensor |= 1 << nr; - else if (val == 2) + else if (val == 4) data->sensor |= 8 << nr; else if (val != 0) { mutex_unlock(&data->update_lock); -- cgit v1.2.3-70-g09d2 From 6495ce184033d5e70dfdf5bb8d149e9e02feaaa9 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add manual PWM support Add PWM manual control. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 15 ++++++---- drivers/hwmon/w83791d.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index a67d3b7a709..49c0e94a118 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -58,29 +58,32 @@ internal state that allows no clean access (Bank with ID register is not currently selected). If you know the address of the chip, use a 'force' parameter; this will put it into a more well-behaved state first. -The driver implements three temperature sensors, five fan rotation speed -sensors, and ten voltage sensors. +The driver implements three temperature sensors, ten voltage sensors, +five fan rotation speed sensors and manual PWM control of each fan. Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when the temperature gets higher than the Overtemperature Shutdown value; it stays on until the temperature falls below the Hysteresis value. +Voltage sensors (also known as IN sensors) report their values in millivolts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. + Fan rotation speeds are reported in RPM (rotations per minute). An alarm is triggered if the rotation speed has dropped below a programmable limit. Fan readings can be divided by a programmable divider (1, 2, 4, 8, 16, 32, 64 or 128 for all fans) to give the readings more range or accuracy. -Voltage sensors (also known as IN sensors) report their values in millivolts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. +Each fan controlled is controlled by PWM. The PWM duty cycle can be read and +set for each fan separately. Valid values range from 0 (stop) to 255 (full). The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for specific alarms. You need both the global beep enable bit and the corresponding beep bit to be on for a triggered alarm to sound a beep. -The sysfs interface to the gloabal enable is via the sysfs beep_enable file. +The sysfs interface to the global enable is via the sysfs beep_enable file. This file is used for both legacy and new code. The sysfs interface to the beep bitmask has migrated from the original legacy diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 6b1cec9950f..a8ff4e12671 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -23,7 +23,7 @@ Supports following chips: Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83791d 10 5 3 3 0x71 0x5ca3 yes no + w83791d 10 5 5 3 0x71 0x5ca3 yes no The w83791d chip appears to be part way between the 83781d and the 83792d. Thus, this file is derived from both the w83792d.c and @@ -45,6 +45,7 @@ #define NUMBER_OF_VIN 10 #define NUMBER_OF_FANIN 5 #define NUMBER_OF_TEMPIN 3 +#define NUMBER_OF_PWM 5 /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -116,6 +117,14 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { 0xBD, /* FAN 5 Count Low Limit in DataSheet */ }; +static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { + 0x81, /* PWM 1 duty cycle register in DataSheet */ + 0x83, /* PWM 2 duty cycle register in DataSheet */ + 0x94, /* PWM 3 duty cycle register in DataSheet */ + 0xA0, /* PWM 4 duty cycle register in DataSheet */ + 0xA1, /* PWM 5 duty cycle register in DataSheet */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -276,6 +285,9 @@ struct w83791d_data { two sensors with three values (cur, over, hyst) */ + /* PWMs */ + u8 pwm[5]; /* pwm duty cycle */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -653,6 +665,48 @@ static struct sensor_device_attribute sda_fan_alarm[] = { SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), }; +/* read/write PWMs */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm[nr]); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm[] = { + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2), + SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3), + SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -917,6 +971,9 @@ static struct attribute *w83791d_attributes[] = { &sda_beep_ctrl[1].dev_attr.attr, &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, + &sda_pwm[0].dev_attr.attr, + &sda_pwm[1].dev_attr.attr, + &sda_pwm[2].dev_attr.attr, NULL }; @@ -930,6 +987,8 @@ static const struct attribute_group w83791d_group = { static struct attribute *w83791d_attributes_fanpwm45[] = { FAN_UNIT_ATTRS(3), FAN_UNIT_ATTRS(4), + &sda_pwm[3].dev_attr.attr, + &sda_pwm[4].dev_attr.attr, NULL }; @@ -1260,6 +1319,12 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) for (i = 0; i < 3; i++) data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; + /* Update PWM duty cycle */ + for (i = 0; i < NUMBER_OF_PWM; i++) { + data->pwm[i] = w83791d_read(client, + W83791D_REG_PWM[i]); + } + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From b5938f8c4a530b2fad18f2293ffaf79ac9f5a148 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add pwm_enable support Add support for pwm_enable. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 13 +++++++- drivers/hwmon/w83791d.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index 49c0e94a118..b1e4798764e 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -108,6 +108,17 @@ going forward. The driver reads the hardware chip values at most once every three seconds. User mode code requesting values more often will receive cached values. +/sys files +---------- +The sysfs-interface is documented in the 'sysfs-interface' file. Only +chip-specific options are documented here. + +pwm[1-3]_enable - this file controls mode of fan/temperature control for + fan 1-3. Fan/PWM 4-5 only support manual mode. + * 1 Manual mode + * 2 Thermal Cruise mode (no further support) + * 3 Fan Speed Cruise mode (no further support) + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -138,4 +149,4 @@ global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) W83791D TODO: --------------- -Provide a patch for smart-fan control (still need appropriate motherboard/fans) +Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a8ff4e12671..a4d2b02d9e0 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -287,6 +287,8 @@ struct w83791d_data { /* PWMs */ u8 pwm[5]; /* pwm duty cycle */ + u8 pwm_enable[3]; /* pwm enable status for fan 1-3 + (fan 4-5 only support manual mode) */ /* Misc */ u32 alarms; /* realtime status register encoding,combined */ @@ -707,6 +709,71 @@ static struct sensor_device_attribute sda_pwm[] = { show_pwm, store_pwm, 4), }; +static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1); +} + +static ssize_t store_pwmenable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 reg_cfg_tmp; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + int ret = strict_strtoul(buf, 10, &val); + + if (ret || val < 1 || val > 3) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val - 1; + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 2; + keep_mask = 0xf3; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0xcf; + break; + case 2: + reg_idx = 1; + val_shift = 2; + keep_mask = 0xf3; + break; + } + + reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]); + reg_cfg_tmp = (reg_cfg_tmp & keep_mask) | + data->pwm_enable[nr] << val_shift; + + w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp); + mutex_unlock(&data->update_lock); + + return count; +} +static struct sensor_device_attribute sda_pwmenable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 0), + SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 1), + SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -974,6 +1041,9 @@ static struct attribute *w83791d_attributes[] = { &sda_pwm[0].dev_attr.attr, &sda_pwm[1].dev_attr.attr, &sda_pwm[2].dev_attr.attr, + &sda_pwmenable[0].dev_attr.attr, + &sda_pwmenable[1].dev_attr.attr, + &sda_pwmenable[2].dev_attr.attr, NULL }; @@ -1325,6 +1395,15 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) W83791D_REG_PWM[i]); } + /* Update PWM enable status */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_FAN_CFG[i]); + } + data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03; + data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; + data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From a5a4598cd2e7cae456a7f2a100bf0e5c3c7811c7 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add support for thermal cruise mode Add support to set target temperature and tolerance for thermal cruise mode. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 19 ++++-- drivers/hwmon/w83791d.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index b1e4798764e..5663e491655 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -77,6 +77,9 @@ readings can be divided by a programmable divider (1, 2, 4, 8, 16, Each fan controlled is controlled by PWM. The PWM duty cycle can be read and set for each fan separately. Valid values range from 0 (stop) to 255 (full). +PWM 1-3 support Thermal Cruise mode, in which the PWMs are automatically +regulated to keep respectively temp 1-3 at a certain target temperature. +See below for the description of the sysfs-interface. The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for @@ -116,9 +119,19 @@ chip-specific options are documented here. pwm[1-3]_enable - this file controls mode of fan/temperature control for fan 1-3. Fan/PWM 4-5 only support manual mode. * 1 Manual mode - * 2 Thermal Cruise mode (no further support) + * 2 Thermal Cruise mode * 3 Fan Speed Cruise mode (no further support) +temp[1-3]_target - defines the target temperature for Thermal Cruise mode. + Unit: millidegree Celsius + RW + +temp[1-3]_tolerance - temperature tolerance for Thermal Cruise mode. + Specifies an interval around the target temperature + in which the fan speed is not changed. + Unit: millidegree Celsius + RW + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -146,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch case_open : alarms: 0x001000 beep_mask: 0x001000 global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) - -W83791D TODO: ---------------- -Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a4d2b02d9e0..5768def8a4f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -125,6 +125,17 @@ static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { 0xA1, /* PWM 5 duty cycle register in DataSheet */ }; +static const u8 W83791D_REG_TEMP_TARGET[3] = { + 0x85, /* PWM 1 target temperature for temp 1 */ + 0x86, /* PWM 2 target temperature for temp 2 */ + 0x96, /* PWM 3 target temperature for temp 3 */ +}; + +static const u8 W83791D_REG_TEMP_TOL[2] = { + 0x87, /* PWM 1/2 temperature tolerance */ + 0x97, /* PWM 3 temperature tolerance */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -234,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 250) / 500 * 128 : \ ((val) + 250) / 500 * 128) +/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */ +#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) + +/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */ +#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 15000 ? 15 : \ + ((val) + 500) / 1000) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) @@ -290,6 +310,9 @@ struct w83791d_data { u8 pwm_enable[3]; /* pwm enable status for fan 1-3 (fan 4-5 only support manual mode) */ + u8 temp_target[3]; /* pwm 1-3 target temperature */ + u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -774,6 +797,110 @@ static struct sensor_device_attribute sda_pwmenable[] = { show_pwmenable, store_pwmenable, 2), }; +/* For Smart Fan I / Thermal Cruise */ +static ssize_t show_temp_target(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr])); +} + +static ssize_t store_temp_target(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp_target[nr] = TARGET_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TARGET[nr]) & 0x80; + w83791d_write(client, W83791D_REG_TEMP_TARGET[nr], + data->temp_target[nr] | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_target[] = { + SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 0), + SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 1), + SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 2), +}; + +static ssize_t show_temp_tolerance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr])); +} + +static ssize_t store_temp_tolerance(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 0; + keep_mask = 0xf0; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0x0f; + break; + case 2: + reg_idx = 1; + val_shift = 0; + keep_mask = 0xf0; + break; + } + + mutex_lock(&data->update_lock); + data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask; + w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx], + (data->temp_tolerance[nr] << val_shift) | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_tolerance[] = { + SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0), + SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1), + SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1044,6 +1171,12 @@ static struct attribute *w83791d_attributes[] = { &sda_pwmenable[0].dev_attr.attr, &sda_pwmenable[1].dev_attr.attr, &sda_pwmenable[2].dev_attr.attr, + &sda_temp_target[0].dev_attr.attr, + &sda_temp_target[1].dev_attr.attr, + &sda_temp_target[2].dev_attr.attr, + &sda_temp_tolerance[0].dev_attr.attr, + &sda_temp_tolerance[1].dev_attr.attr, + &sda_temp_tolerance[2].dev_attr.attr, NULL }; @@ -1404,6 +1537,21 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update PWM target temperature */ + for (i = 0; i < 3; i++) { + data->temp_target[i] = w83791d_read(client, + W83791D_REG_TEMP_TARGET[i]) & 0x7f; + } + + /* Update PWM temperature tolerance */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_TEMP_TOL[i]); + } + data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f; + data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f; + data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From 6aa693b85257cd41fdb3554016b663519dbf9d14 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: Drop dead links to old National Semiconductor chip datasheets Signed-off-by: Jean Delvare --- Documentation/hwmon/pc87360 | 7 +------ Documentation/hwmon/pc87427 | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/pc87360 b/Documentation/hwmon/pc87360 index 89a8fcfa78d..cbac32b59c8 100644 --- a/Documentation/hwmon/pc87360 +++ b/Documentation/hwmon/pc87360 @@ -5,12 +5,7 @@ Supported chips: * National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366 Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366' Addresses scanned: none, address read from Super I/O config space - Datasheets: - http://www.national.com/pf/PC/PC87360.html - http://www.national.com/pf/PC/PC87363.html - http://www.national.com/pf/PC/PC87364.html - http://www.national.com/pf/PC/PC87365.html - http://www.national.com/pf/PC/PC87366.html + Datasheets: No longer available Authors: Jean Delvare diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 index 9a0708f9f49..d1ebbe510f3 100644 --- a/Documentation/hwmon/pc87427 +++ b/Documentation/hwmon/pc87427 @@ -5,7 +5,7 @@ Supported chips: * National Semiconductor PC87427 Prefix: 'pc87427' Addresses scanned: none, address read from Super I/O config space - Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html + Datasheet: No longer available Author: Jean Delvare -- cgit v1.2.3-70-g09d2 From 10c08f937d832e1d5a77e65767a6e2c05bc25c69 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Additional information about AS99127F PWM This information was provided in lm-sensors ticket #2350: http://www.lm-sensors.org/ticket/2350 This is IMHO still not enough to be able to safely implement fan control support for the AS99127F, but this is valuable information so I am adding it to the documentation. Signed-off-by: Jean Delvare --- Documentation/hwmon/w83781d | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83781d b/Documentation/hwmon/w83781d index 6f800a0283e..c91e0b63ea1 100644 --- a/Documentation/hwmon/w83781d +++ b/Documentation/hwmon/w83781d @@ -353,7 +353,7 @@ in6=255 # PWM -Additional info about PWM on the AS99127F (may apply to other Asus +* Additional info about PWM on the AS99127F (may apply to other Asus chips as well) by Jean Delvare as of 2004-04-09: AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A, @@ -396,7 +396,7 @@ Please contact us if you can figure out how it is supposed to work. As long as we don't know more, the w83781d driver doesn't handle PWM on AS99127F chips at all. -Additional info about PWM on the AS99127F rev.1 by Hector Martin: +* Additional info about PWM on the AS99127F rev.1 by Hector Martin: I've been fiddling around with the (in)famous 0x59 register and found out the following values do work as a form of coarse pwm: @@ -418,3 +418,36 @@ change. My mobo is an ASUS A7V266-E. This behavior is similar to what I got with speedfan under Windows, where 0-15% would be off, 15-2x% (can't remember the exact value) would be 70% and higher would be full on. + +* Additional info about PWM on the AS99127F rev.1 from lm-sensors + ticket #2350: + +I conducted some experiment on Asus P3B-F motherboard with AS99127F +(Ver. 1). + +I confirm that 0x59 register control the CPU_Fan Header on this +motherboard, and 0x5a register control PWR_Fan. + +In order to reduce the dependency of specific fan, the measurement is +conducted with a digital scope without fan connected. I found out that +P3B-F actually output variable DC voltage on fan header center pin, +looks like PWM is filtered on this motherboard. + +Here are some of measurements: + +0x80 20 mV +0x81 20 mV +0x82 232 mV +0x83 1.2 V +0x84 2.31 V +0x85 3.44 V +0x86 4.62 V +0x87 5.81 V +0x88 7.01 V +9x89 8.22 V +0x8a 9.42 V +0x8b 10.6 V +0x8c 11.9 V +0x8d 12.4 V +0x8e 12.4 V +0x8f 12.4 V -- cgit v1.2.3-70-g09d2 From d664a4809e73c878a43607d584b2e2b60fd07468 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adt7470) Add documentation Add at least the bare minimum of documentation for this chip. Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- Documentation/hwmon/adt7470 | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/hwmon/adt7470 (limited to 'Documentation') diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470 new file mode 100644 index 00000000000..75d13ca147c --- /dev/null +++ b/Documentation/hwmon/adt7470 @@ -0,0 +1,76 @@ +Kernel driver adt7470 +===================== + +Supported chips: + * Analog Devices ADT7470 + Prefix: 'adt7470' + Addresses scanned: I2C 0x2C, 0x2E, 0x2F + Datasheet: Publicly available at the Analog Devices website + +Author: Darrick J. Wong + +Description +----------- + +This driver implements support for the Analog Devices ADT7470 chip. There may +be other chips that implement this interface. + +The ADT7470 uses the 2-wire interface compatible with the SMBus 2.0 +specification. Using an analog to digital converter it measures up to ten (10) +external temperatures. It has four (4) 16-bit counters for measuring fan speed. +There are four (4) PWM outputs that can be used to control fan speed. + +A sophisticated control system for the PWM outputs is designed into the ADT7470 +that allows fan speed to be adjusted automatically based on any of the ten +temperature sensors. Each PWM output is individually adjustable and +programmable. Once configured, the ADT7470 will adjust the PWM outputs in +response to the measured temperatures with further host intervention. This +feature can also be disabled for manual control of the PWM's. + +Each of the measured inputs (temperature, fan speed) has corresponding high/low +limit values. The ADT7470 will signal an ALARM if any measured value exceeds +either limit. + +The ADT7470 DOES NOT sample all inputs continuously. A single pin on the +ADT7470 is connected to a multitude of thermal diodes, but the chip must be +instructed explicitly to read the multitude of diodes. If you want to use +automatic fan control mode, you must manually read any of the temperature +sensors or the fan control algorithm will not run. The chip WILL NOT DO THIS +AUTOMATICALLY; this must be done from userspace. This may be a bug in the chip +design, given that many other AD chips take care of this. The driver will not +read the registers more often than once every 5 seconds. Further, +configuration data is only read once per minute. + +Special Features +---------------- + +The ADT7470 has a 8-bit ADC and is capable of measuring temperatures with 1 +degC resolution. + +The Analog Devices datasheet is very detailed and describes a procedure for +determining an optimal configuration for the automatic PWM control. + +Configuration Notes +------------------- + +Besides standard interfaces driver adds the following: + +* PWM Control + +* pwm#_auto_point1_pwm and pwm#_auto_point1_temp and +* pwm#_auto_point2_pwm and pwm#_auto_point2_temp - + +point1: Set the pwm speed at a lower temperature bound. +point2: Set the pwm speed at a higher temperature bound. + +The ADT7470 will scale the pwm between the lower and higher pwm speed when +the temperature is between the two temperature boundaries. PWM values range +from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the +temperature sensor associated with the PWM control exceeds +pwm#_auto_point2_temp. + +Notes +----- + +As stated above, the temperature inputs must be read periodically from +userspace in order for the automatic pwm algorithm to run. -- cgit v1.2.3-70-g09d2 From a636da6bab3307fc8c6e6a22a63b0b25ba0687be Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 15 Oct 2008 17:00:31 -0300 Subject: V4L/DVB (9247): au0828: add support for another USB id for Hauppauge HVR950Q Add autodetection support for a new revision of the Hauppauge HVR950Q (2040:721e) Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.au0828 | 2 +- drivers/media/video/au0828/au0828-cards.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.au0828 b/Documentation/video4linux/CARDLIST.au0828 index aa05e5bb22f..d5cb4ea287b 100644 --- a/Documentation/video4linux/CARDLIST.au0828 +++ b/Documentation/video4linux/CARDLIST.au0828 @@ -1,5 +1,5 @@ 0 -> Unknown board (au0828) - 1 -> Hauppauge HVR950Q (au0828) [2040:7200,2040:7210,2040:7217,2040:721b,2040:721f,2040:7280,0fd9:0008] + 1 -> Hauppauge HVR950Q (au0828) [2040:7200,2040:7210,2040:7217,2040:721b,2040:721e,2040:721f,2040:7280,0fd9:0008] 2 -> Hauppauge HVR850 (au0828) [2040:7240] 3 -> DViCO FusionHDTV USB (au0828) [0fe9:d620] 4 -> Hauppauge HVR950Q rev xxF8 (au0828) [2040:7201,2040:7211,2040:7281] diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 5f07a8a072b..66e0edd5861 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -90,6 +90,7 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ + case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; @@ -198,6 +199,8 @@ struct usb_device_id au0828_usb_id_table [] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x721b), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721e), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x721f), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7280), -- cgit v1.2.3-70-g09d2 From 953cafc04e9ef9d2fd9f8afb3b3bbde1f8bb9317 Mon Sep 17 00:00:00 2001 From: Darron Broad Date: Wed, 15 Oct 2008 14:14:30 -0300 Subject: V4L/DVB (9268): tuner: add FMD1216MEX tuner This tuner was already supported by proxy as an FMD1216ME, however, the MEX uses a different FM Radio IF so this addition is now required. Signed-off-by: Darron Broad Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.tuner | 1 + drivers/media/common/tuners/tuner-simple.c | 2 ++ drivers/media/common/tuners/tuner-types.c | 33 +++++++++++++++++++++++++++++- drivers/media/video/tveeprom.c | 2 +- include/media/tuner.h | 1 + 5 files changed, 37 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner index 30bbdda68d0..691d2f37dc5 100644 --- a/Documentation/video4linux/CARDLIST.tuner +++ b/Documentation/video4linux/CARDLIST.tuner @@ -75,3 +75,4 @@ tuner=73 - Samsung TCPG 6121P30A tuner=75 - Philips TEA5761 FM Radio tuner=76 - Xceive 5000 tuner tuner=77 - TCL tuner MF02GIP-5N-E +tuner=78 - Philips FMD1216MEX MK3 Hybrid Tuner diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index 2a1aac1cc75..fb3f3b3adab 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -493,6 +493,7 @@ static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_PHILIPS_FMD1216MEX_MK3: case TUNER_LG_NTSC_TAPE: case TUNER_PHILIPS_FM1256_IH3: case TUNER_TCL_MF02GIP_5N: @@ -767,6 +768,7 @@ static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf, switch (priv->type) { case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_PHILIPS_FMD1216MEX_MK3: if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ && params->frequency >= 158870000) buf[3] |= 0x08; diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index 04961a1f44b..7c0bc064c00 100644 --- a/drivers/media/common/tuners/tuner-types.c +++ b/drivers/media/common/tuners/tuner-types.c @@ -946,7 +946,7 @@ static struct tuner_params tuner_tena_9533_di_params[] = { }, }; -/* ------------ TUNER_PHILIPS_FMD1216ME_MK3 - Philips PAL ------------ */ +/* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */ static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { { 16 * 160.00 /*MHz*/, 0x86, 0x51, }, @@ -984,6 +984,27 @@ static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { }, }; +static struct tuner_params tuner_philips_fmd1216mex_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_fm_high_sensitivity = 1, + .port2_invert_for_secam_lc = 1, + .port1_set_for_fm_mono = 1, + .radio_if = 1, + .fm_gain_normal = 1, + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, +}; /* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */ @@ -1663,6 +1684,16 @@ struct tunertype tuners[] = { .params = tuner_tcl_mf02gip_5n_params, .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params), }, + [TUNER_PHILIPS_FMD1216MEX_MK3] = { /* Philips PAL */ + .name = "Philips FMD1216MEX MK3 Hybrid Tuner", + .params = tuner_philips_fmd1216mex_mk3_params, + .count = ARRAY_SIZE(tuner_philips_fmd1216mex_mk3_params), + .min = 16 * 50.87, + .max = 16 * 858.00, + .stepsize = 166667, + .initdata = tua603x_agc112, + .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index bcc32fa92a8..3b0b84c2e45 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -242,7 +242,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "TCL M2523_3DBH_E"}, { TUNER_ABSENT, "TCL M2523_3DIH_E"}, { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216MEX"}, + { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, { TUNER_ABSENT, "Philips FRH2036B"}, { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, { TUNER_ABSENT, "MaxLinear MXL5005"}, diff --git a/include/media/tuner.h b/include/media/tuner.h index 67c1f514d0e..7d4e2db7807 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -123,6 +123,7 @@ #define TUNER_TEA5761 75 /* Only FM Radio Tuner */ #define TUNER_XC5000 76 /* Xceive Silicon Tuner */ #define TUNER_TCL_MF02GIP_5N 77 /* TCL MF02GIP_5N */ +#define TUNER_PHILIPS_FMD1216MEX_MK3 78 /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- cgit v1.2.3-70-g09d2 From eb86be5424d4c08e686d5e578b72a26c516ae58a Mon Sep 17 00:00:00 2001 From: Harrison Metzger Date: Thu, 14 Aug 2008 11:29:32 -0500 Subject: USB: Added driver for a Delcom USB 7-segment LED Display Added basic support for a Delcom USB 7-segment LED Display Signed-off by: Harrison Metzger Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-usb-devices-usbsevseg | 43 +++ Documentation/usb/misc_usbsevseg.txt | 46 +++ drivers/usb/misc/Kconfig | 9 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/usbsevseg.c | 394 +++++++++++++++++++++ 5 files changed, 493 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg create mode 100644 Documentation/usb/misc_usbsevseg.txt create mode 100644 drivers/usb/misc/usbsevseg.c (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg b/Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg new file mode 100644 index 00000000000..cb830df8777 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg @@ -0,0 +1,43 @@ +Where: /sys/bus/usb/.../powered +Date: August 2008 +Kernel Version: 2.6.26 +Contact: Harrison Metzger +Description: Controls whether the device's display will powered. + A value of 0 is off and a non-zero value is on. + +Where: /sys/bus/usb/.../mode_msb +Where: /sys/bus/usb/.../mode_lsb +Date: August 2008 +Kernel Version: 2.6.26 +Contact: Harrison Metzger +Description: Controls the devices display mode. + For a 6 character display the values are + MSB 0x06; LSB 0x3F, and + for an 8 character display the values are + MSB 0x08; LSB 0xFF. + +Where: /sys/bus/usb/.../textmode +Date: August 2008 +Kernel Version: 2.6.26 +Contact: Harrison Metzger +Description: Controls the way the device interprets its text buffer. + raw: each character controls its segment manually + hex: each character is between 0-15 + ascii: each character is between '0'-'9' and 'A'-'F'. + +Where: /sys/bus/usb/.../text +Date: August 2008 +Kernel Version: 2.6.26 +Contact: Harrison Metzger +Description: The text (or data) for the device to display + +Where: /sys/bus/usb/.../decimals +Date: August 2008 +Kernel Version: 2.6.26 +Contact: Harrison Metzger +Description: Controls the decimal places on the device. + To set the nth decimal place, give this field + the value of 10 ** n. Assume this field has + the value k and has 1 or more decimal places set, + to set the mth place (where m is not already set), + change this fields value to k + 10 ** m. \ No newline at end of file diff --git a/Documentation/usb/misc_usbsevseg.txt b/Documentation/usb/misc_usbsevseg.txt new file mode 100644 index 00000000000..0f6be4f9930 --- /dev/null +++ b/Documentation/usb/misc_usbsevseg.txt @@ -0,0 +1,46 @@ +USB 7-Segment Numeric Display +Manufactured by Delcom Engineering + +Device Information +------------------ +USB VENDOR_ID 0x0fc5 +USB PRODUCT_ID 0x1227 +Both the 6 character and 8 character displays have PRODUCT_ID, +and according to Delcom Engineering no queryable information +can be obtained from the device to tell them apart. + +Device Modes +------------ +By default, the driver assumes the display is only 6 characters +The mode for 6 characters is: + MSB 0x06; LSB 0x3f +For the 8 character display: + MSB 0x08; LSB 0xff +The device can accept "text" either in raw, hex, or ascii textmode. +raw controls each segment manually, +hex expects a value between 0-15 per character, +ascii expects a value between '0'-'9' and 'A'-'F'. +The default is ascii. + +Device Operation +---------------- +1. Turn on the device: + echo 1 > /sys/bus/usb/.../powered +2. Set the device's mode: + echo $mode_msb > /sys/bus/usb/.../mode_msb + echo $mode_lsb > /sys/bus/usb/.../mode_lsb +3. Set the textmode: + echo $textmode > /sys/bus/usb/.../textmode +4. set the text (for example): + echo "123ABC" > /sys/bus/usb/.../text (ascii) + echo "A1B2" > /sys/bus/usb/.../text (ascii) + echo -ne "\x01\x02\x03" > /sys/bus/usb/.../text (hex) +5. Set the decimal places. + The device has either 6 or 8 decimal points. + to set the nth decimal place calculate 10 ** n + and echo it in to /sys/bus/usb/.../decimals + To set multiple decimals points sum up each power. + For example, to set the 0th and 3rd decimal place + echo 1001 > /sys/bus/usb/.../decimals + + diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 4ea50e0abcb..25e1157ab17 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -42,6 +42,15 @@ config USB_ADUTUX To compile this driver as a module, choose M here. The module will be called adutux. +config USB_SEVSEG + tristate "USB 7-Segment LED Display" + depends on USB + help + Say Y here if you have a USB 7-Segment Display by Delcom + + To compile this driver as a module, choose M here: the + module will be called usbsevseg. + config USB_RIO500 tristate "USB Diamond Rio500 support" depends on USB diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 45b4e12afb0..39ce4a16b3d 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c new file mode 100644 index 00000000000..28a6a3a0953 --- /dev/null +++ b/drivers/usb/misc/usbsevseg.c @@ -0,0 +1,394 @@ +/* + * USB 7 Segment Driver + * + * Copyright (C) 2008 Harrison Metzger + * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.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, version 2. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_AUTHOR "Harrison Metzger " +#define DRIVER_DESC "USB 7 Segment Driver" + +#define VENDOR_ID 0x0fc5 +#define PRODUCT_ID 0x1227 +#define MAXLEN 6 + +/* table of devices that work with this driver */ +static struct usb_device_id id_table[] = { + { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* the different text display modes the device is capable of */ +static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; + +struct usb_sevsegdev { + struct usb_device *udev; + + u8 powered; + u8 mode_msb; + u8 mode_lsb; + u8 decimals[MAXLEN]; + u8 textmode; + u8 text[MAXLEN]; + u16 textlength; +}; + +/* sysfs_streq can't replace this completely + * If the device was in hex mode, and the user wanted a 0, + * if str commands are used, we would assume the end of string + * so mem commands are used. + */ +inline size_t my_memlen(const char *buf, size_t count) +{ + if (count > 0 && buf[count-1] == '\n') + return count - 1; + else + return count; +} + +static void update_display_powered(struct usb_sevsegdev *mydev) +{ + int rc; + + rc = usb_control_msg(mydev->udev, + usb_sndctrlpipe(mydev->udev, 0), + 0x12, + 0x48, + (80 * 0x100) + 10, /* (power mode) */ + (0x00 * 0x100) + (mydev->powered ? 1 : 0), + NULL, + 0, + 2000); + if (rc < 0) + dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); +} + +static void update_display_mode(struct usb_sevsegdev *mydev) +{ + int rc; + + rc = usb_control_msg(mydev->udev, + usb_sndctrlpipe(mydev->udev, 0), + 0x12, + 0x48, + (82 * 0x100) + 10, /* (set mode) */ + (mydev->mode_msb * 0x100) + mydev->mode_lsb, + NULL, + 0, + 2000); + + if (rc < 0) + dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); +} + +static void update_display_visual(struct usb_sevsegdev *mydev) +{ + int rc; + int i; + unsigned char *buffer; + u8 decimals = 0; + + buffer = kzalloc(MAXLEN, GFP_KERNEL); + if (!buffer) { + dev_err(&mydev->udev->dev, "out of memory\n"); + return; + } + + /* The device is right to left, where as you write left to right */ + for (i = 0; i < mydev->textlength; i++) + buffer[i] = mydev->text[mydev->textlength-1-i]; + + rc = usb_control_msg(mydev->udev, + usb_sndctrlpipe(mydev->udev, 0), + 0x12, + 0x48, + (85 * 0x100) + 10, /* (write text) */ + (0 * 0x100) + mydev->textmode, /* mode */ + buffer, + mydev->textlength, + 2000); + + if (rc < 0) + dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); + + kfree(buffer); + + /* The device is right to left, where as you write left to right */ + for (i = 0; i < sizeof(mydev->decimals); i++) + decimals |= mydev->decimals[i] << i; + + rc = usb_control_msg(mydev->udev, + usb_sndctrlpipe(mydev->udev, 0), + 0x12, + 0x48, + (86 * 0x100) + 10, /* (set decimal) */ + (0 * 0x100) + decimals, /* decimals */ + NULL, + 0, + 2000); + + if (rc < 0) + dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); +} + +#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \ +static ssize_t show_attr_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%u\n", mydev->name); \ +} \ + \ +static ssize_t set_attr_##name(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ + \ + mydev->name = simple_strtoul(buf, NULL, 10); \ + update_fcn(mydev); \ + \ + return count; \ +} \ +static DEVICE_ATTR(name, S_IWUGO | S_IRUGO, show_attr_##name, set_attr_##name); + +static ssize_t show_attr_text(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + + return snprintf(buf, mydev->textlength, "%s\n", mydev->text); +} + +static ssize_t set_attr_text(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + size_t end = my_memlen(buf, count); + + if (end > sizeof(mydev->text)) + return -EINVAL; + + memset(mydev->text, 0, sizeof(mydev->text)); + mydev->textlength = end; + + if (end > 0) + memcpy(mydev->text, buf, end); + + update_display_visual(mydev); + return count; +} + +static DEVICE_ATTR(text, S_IWUGO | S_IRUGO, show_attr_text, set_attr_text); + +static ssize_t show_attr_decimals(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + int i; + int pos; + + for (i = 0; i < sizeof(mydev->decimals); i++) { + pos = sizeof(mydev->decimals) - 1 - i; + if (mydev->decimals[i] == 0) + buf[pos] = '0'; + else if (mydev->decimals[i] == 1) + buf[pos] = '1'; + else + buf[pos] = 'x'; + } + + buf[sizeof(mydev->decimals)] = '\n'; + return sizeof(mydev->decimals) + 1; +} + +static ssize_t set_attr_decimals(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + size_t end = my_memlen(buf, count); + int i; + + if (end > sizeof(mydev->decimals)) + return -EINVAL; + + for (i = 0; i < end; i++) + if (buf[i] != '0' && buf[i] != '1') + return -EINVAL; + + memset(mydev->decimals, 0, sizeof(mydev->decimals)); + for (i = 0; i < end; i++) + if (buf[i] == '1') + mydev->decimals[end-1-i] = 1; + + update_display_visual(mydev); + + return count; +} + +static DEVICE_ATTR(decimals, S_IWUGO | S_IRUGO, + show_attr_decimals, set_attr_decimals); + +static ssize_t show_attr_textmode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + int i; + + buf[0] = 0; + + for (i = 0; display_textmodes[i]; i++) { + if (mydev->textmode == i) { + strcat(buf, " ["); + strcat(buf, display_textmodes[i]); + strcat(buf, "] "); + } else { + strcat(buf, " "); + strcat(buf, display_textmodes[i]); + strcat(buf, " "); + } + } + strcat(buf, "\n"); + + + return strlen(buf); +} + +static ssize_t set_attr_textmode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + int i; + + for (i = 0; display_textmodes[i]; i++) { + if (sysfs_streq(display_textmodes[i], buf)) { + mydev->textmode = i; + update_display_visual(mydev); + return count; + } + } + + return -EINVAL; +} + +static DEVICE_ATTR(textmode, S_IWUGO | S_IRUGO, + show_attr_textmode, set_attr_textmode); + + +MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); +MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); +MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); + +static struct attribute *dev_attrs[] = { + &dev_attr_powered.attr, + &dev_attr_text.attr, + &dev_attr_textmode.attr, + &dev_attr_decimals.attr, + &dev_attr_mode_msb.attr, + &dev_attr_mode_lsb.attr, + NULL +}; + +static struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static int sevseg_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_sevsegdev *mydev = NULL; + int rc = -ENOMEM; + + mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); + if (mydev == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + goto error_mem; + } + + mydev->udev = usb_get_dev(udev); + usb_set_intfdata(interface, mydev); + + /*set defaults */ + mydev->textmode = 0x02; /* ascii mode */ + mydev->mode_msb = 0x06; /* 6 characters */ + mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ + + rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); + if (rc) + goto error; + + dev_info(&interface->dev, "USB 7 Segment device now attached\n"); + return 0; + +error: + usb_set_intfdata(interface, NULL); + usb_put_dev(mydev->udev); + kfree(mydev); +error_mem: + return rc; +} + +static void sevseg_disconnect(struct usb_interface *interface) +{ + struct usb_sevsegdev *mydev; + + mydev = usb_get_intfdata(interface); + sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); + usb_set_intfdata(interface, NULL); + usb_put_dev(mydev->udev); + kfree(mydev); + dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); +} + +static struct usb_driver sevseg_driver = { + .name = "usbsevseg", + .probe = sevseg_probe, + .disconnect = sevseg_disconnect, + .id_table = id_table, +}; + +static int __init usb_sevseg_init(void) +{ + int rc = 0; + + rc = usb_register(&sevseg_driver); + if (rc) + err("usb_register failed. Error number %d", rc); + return rc; +} + +static void __exit usb_sevseg_exit(void) +{ + usb_deregister(&sevseg_driver); +} + +module_init(usb_sevseg_init); +module_exit(usb_sevseg_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 5b775f672cc993ba9dba5626811ab1f2ac42883b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 26 Aug 2008 16:22:06 -0700 Subject: USB: add USB test and measurement class driver This driver was originaly written by Stefan Kopp, but massively reworked by Greg for submission. Thanks to Felipe Balbi for lots of work in cleaning up this driver. Thanks to Oliver Neukum for reviewing previous versions and pointing out problems. Cc: Stefan Kopp Cc: Marcel Janssen Cc: Felipe Balbi Cc: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-driver-usb-usbtmc | 62 ++ Documentation/devices.txt | 3 + Documentation/ioctl-number.txt | 2 + drivers/usb/class/Kconfig | 10 + drivers/usb/class/Makefile | 1 + drivers/usb/class/usbtmc.c | 1087 ++++++++++++++++++++++ include/linux/usb/Kbuild | 2 +- include/linux/usb/tmc.h | 43 + 8 files changed, 1209 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/stable/sysfs-driver-usb-usbtmc create mode 100644 drivers/usb/class/usbtmc.c create mode 100644 include/linux/usb/tmc.h (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-driver-usb-usbtmc b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc new file mode 100644 index 00000000000..9a75fb22187 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc @@ -0,0 +1,62 @@ +What: /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities +What: /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities +Date: August 2008 +Contact: Greg Kroah-Hartman +Description: + These files show the various USB TMC capabilities as described + by the device itself. The full description of the bitfields + can be found in the USB TMC documents from the USB-IF entitled + "Universal Serial Bus Test and Measurement Class Specification + (USBTMC) Revision 1.0" section 4.2.1.8. + + The files are read only. + + +What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities +What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities +Date: August 2008 +Contact: Greg Kroah-Hartman +Description: + These files show the various USB TMC capabilities as described + by the device itself. The full description of the bitfields + can be found in the USB TMC documents from the USB-IF entitled + "Universal Serial Bus Test and Measurement Class, Subclass + USB488 Specification (USBTMC-USB488) Revision 1.0" section + 4.2.2. + + The files are read only. + + +What: /sys/bus/usb/drivers/usbtmc/devices/*/TermChar +Date: August 2008 +Contact: Greg Kroah-Hartman +Description: + This file is the TermChar value to be sent to the USB TMC + device as described by the document, "Universal Serial Bus Test + and Measurement Class Specification + (USBTMC) Revision 1.0" as published by the USB-IF. + + Note that the TermCharEnabled file determines if this value is + sent to the device or not. + + +What: /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled +Date: August 2008 +Contact: Greg Kroah-Hartman +Description: + This file determines if the TermChar is to be sent to the + device on every transaction or not. For more details about + this, please see the document, "Universal Serial Bus Test and + Measurement Class Specification (USBTMC) Revision 1.0" as + published by the USB-IF. + + +What: /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort +Date: August 2008 +Contact: Greg Kroah-Hartman +Description: + This file determines if the the transaction of the USB TMC + device is to be automatically aborted if there is any error. + For more details about this, please see the document, + "Universal Serial Bus Test and Measurement Class Specification + (USBTMC) Revision 1.0" as published by the USB-IF. diff --git a/Documentation/devices.txt b/Documentation/devices.txt index 05c80645e4e..2be08240ee8 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -2571,6 +2571,9 @@ Your cooperation is appreciated. 160 = /dev/usb/legousbtower0 1st USB Legotower device ... 175 = /dev/usb/legousbtower15 16th USB Legotower device + 176 = /dev/usb/usbtmc1 First USB TMC device + ... + 192 = /dev/usb/usbtmc16 16th USB TMC device 240 = /dev/usb/dabusb0 First daubusb device ... 243 = /dev/usb/dabusb3 Fourth dabusb device diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 1c6b545635a..f8deb85eef6 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -110,6 +110,8 @@ Code Seq# Include File Comments 'W' 00-1F linux/wanrouter.h conflict! 'X' all linux/xfs_fs.h 'Y' all linux/cyclades.h +'[' 00-07 linux/usb/usbtmc.h USB Test and Measurement Devices + 'a' all ATM on linux 'b' 00-FF bit3 vme host bridge diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 66f17ed88cb..2519e320098 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -40,3 +40,13 @@ config USB_WDM To compile this driver as a module, choose M here: the module will be called cdc-wdm. +config USB_TMC + tristate "USB Test and Measurement Class support" + depends on USB + help + Say Y here if you want to connect a USB device that follows + the USB.org specification for USB Test and Measurement devices + to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called usbtmc. diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 535d59a3060..32e85277b5c 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_USB_ACM) += cdc-acm.o obj-$(CONFIG_USB_PRINTER) += usblp.o obj-$(CONFIG_USB_WDM) += cdc-wdm.o +obj-$(CONFIG_USB_TMC) += usbtmc.o diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c new file mode 100644 index 00000000000..543811f6e6e --- /dev/null +++ b/drivers/usb/class/usbtmc.c @@ -0,0 +1,1087 @@ +/** + * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver + * + * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Greg Kroah-Hartman + * + * 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. + * + * The GNU General Public License is available at + * http://www.gnu.org/copyleft/gpl.html. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define USBTMC_MINOR_BASE 176 + +/* + * Size of driver internal IO buffer. Must be multiple of 4 and at least as + * large as wMaxPacketSize (which is usually 512 bytes). + */ +#define USBTMC_SIZE_IOBUFFER 2048 + +/* Default USB timeout (in milliseconds) */ +#define USBTMC_TIMEOUT 10 + +/* + * Maximum number of read cycles to empty bulk in endpoint during CLEAR and + * ABORT_BULK_IN requests. Ends the loop if (for whatever reason) a short + * packet is never read. + */ +#define USBTMC_MAX_READS_TO_CLEAR_BULK_IN 100 + +static struct usb_device_id usbtmc_devices[] = { + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 0), }, + { 0, } /* terminating entry */ +}; + +/* + * This structure is the capabilities for the device + * See section 4.2.1.8 of the USBTMC specification for details. + */ +struct usbtmc_dev_capabilities { + __u8 interface_capabilities; + __u8 device_capabilities; + __u8 usb488_interface_capabilities; + __u8 usb488_device_capabilities; +}; + +/* This structure holds private data for each USBTMC device. One copy is + * allocated for each USBTMC device in the driver's probe function. + */ +struct usbtmc_device_data { + const struct usb_device_id *id; + struct usb_device *usb_dev; + struct usb_interface *intf; + + unsigned int bulk_in; + unsigned int bulk_out; + + u8 bTag; + u8 bTag_last_write; /* needed for abort */ + u8 bTag_last_read; /* needed for abort */ + + /* attributes from the USB TMC spec for this device */ + u8 TermChar; + bool TermCharEnabled; + bool auto_abort; + + struct usbtmc_dev_capabilities capabilities; + struct kref kref; + struct mutex io_mutex; /* only one i/o function running at a time */ +}; +#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) + +/* Forward declarations */ +static struct usb_driver usbtmc_driver; + +static void usbtmc_delete(struct kref *kref) +{ + struct usbtmc_device_data *data = to_usbtmc_data(kref); + + usb_put_dev(data->usb_dev); + kfree(data); +} + +static int usbtmc_open(struct inode *inode, struct file *filp) +{ + struct usb_interface *intf; + struct usbtmc_device_data *data; + int retval = -ENODEV; + + intf = usb_find_interface(&usbtmc_driver, iminor(inode)); + if (!intf) { + printk(KERN_ERR KBUILD_MODNAME + ": can not find device for minor %d", iminor(inode)); + goto exit; + } + + data = usb_get_intfdata(intf); + kref_get(&data->kref); + + /* Store pointer in file structure's private data field */ + filp->private_data = data; + +exit: + return retval; +} + +static int usbtmc_release(struct inode *inode, struct file *file) +{ + struct usbtmc_device_data *data = file->private_data; + + kref_put(&data->kref, usbtmc_delete); + return 0; +} + +static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) +{ + char *buffer; + struct device *dev; + int rv; + int n; + int actual; + struct usb_host_interface *current_setting; + int max_size; + + dev = &data->intf->dev; + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_ABORT_BULK_IN, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + data->bTag_last_read, data->bulk_in, + buffer, 2, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_FAILED) { + rv = 0; + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", + buffer[0]); + rv = -EPERM; + goto exit; + } + + max_size = 0; + current_setting = data->intf->cur_altsetting; + for (n = 0; n < current_setting->desc.bNumEndpoints; n++) + if (current_setting->endpoint[n].desc.bEndpointAddress == + data->bulk_in) + max_size = le16_to_cpu(current_setting->endpoint[n]. + desc.wMaxPacketSize); + + if (max_size == 0) { + dev_err(dev, "Couldn't get wMaxPacketSize\n"); + rv = -EPERM; + goto exit; + } + + dev_dbg(&data->intf->dev, "wMaxPacketSize is %d\n", max_size); + + n = 0; + + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + + n++; + + if (rv < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", rv); + goto exit; + } + } while ((actual == max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + n = 0; + +usbtmc_abort_bulk_in_status: + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, data->bulk_in, buffer, 0x08, + USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) { + rv = 0; + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_PENDING) { + dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + if (buffer[1] == 1) + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + + n++; + + if (rv < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", rv); + goto exit; + } + } while ((actual = max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + goto usbtmc_abort_bulk_in_status; + +exit: + kfree(buffer); + return rv; + +} + +static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) +{ + struct device *dev; + u8 *buffer; + int rv; + int n; + + dev = &data->intf->dev; + + buffer = kmalloc(8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + data->bTag_last_write, data->bulk_out, + buffer, 2, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", + buffer[0]); + rv = -EPERM; + goto exit; + } + + n = 0; + +usbtmc_abort_bulk_out_check_status: + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, data->bulk_out, buffer, 0x08, + USBTMC_TIMEOUT); + n++; + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "CHECK_ABORT_BULK_OUT returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) + goto usbtmc_abort_bulk_out_clear_halt; + + if ((buffer[0] == USBTMC_STATUS_PENDING) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)) + goto usbtmc_abort_bulk_out_check_status; + + rv = -EPERM; + goto exit; + +usbtmc_abort_bulk_out_clear_halt: + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_out, buffer, + 0, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static ssize_t usbtmc_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct usbtmc_device_data *data; + struct device *dev; + unsigned long int n_characters; + u8 *buffer; + int actual; + int done; + int remaining; + int retval; + int this_part; + + /* Get pointer to private data structure */ + data = filp->private_data; + dev = &data->intf->dev; + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + mutex_lock(&data->io_mutex); + + remaining = count; + done = 0; + + while (remaining > 0) { + if (remaining > USBTMC_SIZE_IOBUFFER - 12 - 3) + this_part = USBTMC_SIZE_IOBUFFER - 12 - 3; + else + this_part = remaining; + + /* Setup IO buffer for DEV_DEP_MSG_IN message + * Refer to class specs for details + */ + buffer[0] = 2; + buffer[1] = data->bTag; + buffer[2] = ~(data->bTag); + buffer[3] = 0; /* Reserved */ + buffer[4] = (this_part - 12 - 3) & 255; + buffer[5] = ((this_part - 12 - 3) >> 8) & 255; + buffer[6] = ((this_part - 12 - 3) >> 16) & 255; + buffer[7] = ((this_part - 12 - 3) >> 24) & 255; + buffer[8] = data->TermCharEnabled * 2; + /* Use term character? */ + buffer[9] = data->TermChar; + buffer[10] = 0; /* Reserved */ + buffer[11] = 0; /* Reserved */ + + /* Send bulk URB */ + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, 12, &actual, USBTMC_TIMEOUT); + + /* Store bTag (in case we need to abort) */ + data->bTag_last_write = data->bTag; + + /* Increment bTag -- and increment again if zero */ + data->bTag++; + if (!data->bTag) + (data->bTag)++; + + if (retval < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; + } + + /* Send bulk URB */ + retval = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, &actual, + USBTMC_TIMEOUT); + + /* Store bTag (in case we need to abort) */ + data->bTag_last_read = data->bTag; + + if (retval < 0) { + dev_err(dev, "Unable to read data, error %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } + + /* How many characters did the instrument send? */ + n_characters = buffer[4] + + (buffer[5] << 8) + + (buffer[6] << 16) + + (buffer[7] << 24); + + /* Copy buffer to user space */ + if (copy_to_user(buf + done, &buffer[12], n_characters)) { + /* There must have been an addressing problem */ + retval = -EFAULT; + goto exit; + } + + done += n_characters; + if (n_characters < USBTMC_SIZE_IOBUFFER) + remaining = 0; + } + + /* Update file position value */ + *f_pos = *f_pos + done; + retval = done; + +exit: + mutex_unlock(&data->io_mutex); + kfree(buffer); + return retval; +} + +static ssize_t usbtmc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct usbtmc_device_data *data; + u8 *buffer; + int retval; + int actual; + unsigned long int n_bytes; + int n; + int remaining; + int done; + int this_part; + + data = filp->private_data; + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + mutex_lock(&data->io_mutex); + + remaining = count; + done = 0; + + while (remaining > 0) { + if (remaining > USBTMC_SIZE_IOBUFFER - 12) { + this_part = USBTMC_SIZE_IOBUFFER - 12; + buffer[8] = 0; + } else { + this_part = remaining; + buffer[8] = 1; + } + + /* Setup IO buffer for DEV_DEP_MSG_OUT message */ + buffer[0] = 1; + buffer[1] = data->bTag; + buffer[2] = ~(data->bTag); + buffer[3] = 0; /* Reserved */ + buffer[4] = this_part & 255; + buffer[5] = (this_part >> 8) & 255; + buffer[6] = (this_part >> 16) & 255; + buffer[7] = (this_part >> 24) & 255; + /* buffer[8] is set above... */ + buffer[9] = 0; /* Reserved */ + buffer[10] = 0; /* Reserved */ + buffer[11] = 0; /* Reserved */ + + if (copy_from_user(&buffer[12], buf + done, this_part)) { + retval = -EFAULT; + goto exit; + } + + n_bytes = 12 + this_part; + if (this_part % 4) + n_bytes += 4 - this_part % 4; + for (n = 12 + this_part; n < n_bytes; n++) + buffer[n] = 0; + + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, n_bytes, &actual, USBTMC_TIMEOUT); + + data->bTag_last_write = data->bTag; + data->bTag++; + + if (!data->bTag) + data->bTag++; + + if (retval < 0) { + dev_err(&data->intf->dev, + "Unable to send data, error %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; + } + + remaining -= this_part; + done += this_part; + } + + retval = count; +exit: + mutex_unlock(&data->io_mutex); + kfree(buffer); + return retval; +} + +static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) +{ + struct usb_host_interface *current_setting; + struct usb_endpoint_descriptor *desc; + struct device *dev; + u8 *buffer; + int rv; + int n; + int actual; + int max_size; + + dev = &data->intf->dev; + + dev_dbg(dev, "Sending INITIATE_CLEAR request\n"); + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_CLEAR, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 1, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + max_size = 0; + current_setting = data->intf->cur_altsetting; + for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { + desc = ¤t_setting->endpoint[n].desc; + if (desc->bEndpointAddress == data->bulk_in) + max_size = le16_to_cpu(desc->wMaxPacketSize); + } + + if (max_size == 0) { + dev_err(dev, "Couldn't get wMaxPacketSize\n"); + rv = -EPERM; + goto exit; + } + + dev_dbg(dev, "wMaxPacketSize is %d\n", max_size); + + n = 0; + +usbtmc_clear_check_status: + + dev_dbg(dev, "Sending CHECK_CLEAR_STATUS request\n"); + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_CLEAR_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 2, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) + goto usbtmc_clear_bulk_out_halt; + + if (buffer[0] != USBTMC_STATUS_PENDING) { + dev_err(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + if (buffer[1] == 1) + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + n++; + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + } while ((actual == max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + goto usbtmc_clear_check_status; + +usbtmc_clear_bulk_out_halt: + + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, + data->bulk_out, buffer, 0, + USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data) +{ + u8 *buffer; + int rv; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_out, + buffer, 0, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) +{ + u8 *buffer; + int rv; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_in, buffer, 0, + USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int get_capabilities(struct usbtmc_device_data *data) +{ + struct device *dev = &data->usb_dev->dev; + char *buffer; + int rv; + + buffer = kmalloc(0x18, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_GET_CAPABILITIES, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 0x18, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + return rv; + } + + dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); + dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]); + dev_dbg(dev, "Device capabilities are %x\n", buffer[5]); + dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]); + dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); + return -EPERM; + } + + data->capabilities.interface_capabilities = buffer[4]; + data->capabilities.device_capabilities = buffer[5]; + data->capabilities.usb488_interface_capabilities = buffer[14]; + data->capabilities.usb488_device_capabilities = buffer[15]; + + kfree(buffer); + return 0; +} + +#define capability_attribute(name) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", data->capabilities.name); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +capability_attribute(interface_capabilities); +capability_attribute(device_capabilities); +capability_attribute(usb488_interface_capabilities); +capability_attribute(usb488_device_capabilities); + +static struct attribute *capability_attrs[] = { + &dev_attr_interface_capabilities.attr, + &dev_attr_device_capabilities.attr, + &dev_attr_usb488_interface_capabilities.attr, + &dev_attr_usb488_device_capabilities.attr, + NULL, +}; + +static struct attribute_group capability_attr_grp = { + .attrs = capability_attrs, +}; + +static ssize_t show_TermChar(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtmc_device_data *data = usb_get_intfdata(intf); + + return sprintf(buf, "%c\n", data->TermChar); +} + +static ssize_t store_TermChar(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtmc_device_data *data = usb_get_intfdata(intf); + + if (count < 1) + return -EINVAL; + data->TermChar = buf[0]; + return count; +} +static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar); + +#define data_attribute(name) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", data->name); \ +} \ +static ssize_t store_##name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + ssize_t result; \ + unsigned val; \ + \ + result = sscanf(buf, "%u\n", &val); \ + if (result != 1) \ + result = -EINVAL; \ + data->name = val; \ + if (result < 0) \ + return result; \ + else \ + return count; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name) + +data_attribute(TermCharEnabled); +data_attribute(auto_abort); + +static struct attribute *data_attrs[] = { + &dev_attr_TermChar.attr, + &dev_attr_TermCharEnabled.attr, + &dev_attr_auto_abort.attr, + NULL, +}; + +static struct attribute_group data_attr_grp = { + .attrs = data_attrs, +}; + +static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) +{ + struct device *dev; + u8 *buffer; + int rv; + + dev = &data->intf->dev; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INDICATOR_PULSE, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 0x01, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usbtmc_device_data *data; + int retval = -EBADRQC; + + data = file->private_data; + mutex_lock(&data->io_mutex); + + switch (cmd) { + case USBTMC_IOCTL_CLEAR_OUT_HALT: + retval = usbtmc_ioctl_clear_out_halt(data); + + case USBTMC_IOCTL_CLEAR_IN_HALT: + retval = usbtmc_ioctl_clear_in_halt(data); + + case USBTMC_IOCTL_INDICATOR_PULSE: + retval = usbtmc_ioctl_indicator_pulse(data); + + case USBTMC_IOCTL_CLEAR: + retval = usbtmc_ioctl_clear(data); + + case USBTMC_IOCTL_ABORT_BULK_OUT: + retval = usbtmc_ioctl_abort_bulk_out(data); + + case USBTMC_IOCTL_ABORT_BULK_IN: + retval = usbtmc_ioctl_abort_bulk_in(data); + } + + mutex_unlock(&data->io_mutex); + return retval; +} + +static struct file_operations fops = { + .owner = THIS_MODULE, + .read = usbtmc_read, + .write = usbtmc_write, + .open = usbtmc_open, + .release = usbtmc_release, + .unlocked_ioctl = usbtmc_ioctl, +}; + +static struct usb_class_driver usbtmc_class = { + .name = "usbtmc%d", + .fops = &fops, + .minor_base = USBTMC_MINOR_BASE, +}; + + +static int usbtmc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usbtmc_device_data *data; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int n; + int retcode; + + dev_dbg(&intf->dev, "%s called\n", __func__); + + data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL); + if (!data) { + dev_err(&intf->dev, "Unable to allocate kernel memory\n"); + return -ENOMEM; + } + + data->intf = intf; + data->id = id; + data->usb_dev = usb_get_dev(interface_to_usbdev(intf)); + usb_set_intfdata(intf, data); + kref_init(&data->kref); + mutex_init(&data->io_mutex); + + /* Initialize USBTMC bTag and other fields */ + data->bTag = 1; + data->TermCharEnabled = 0; + data->TermChar = '\n'; + + /* USBTMC devices have only one setting, so use that */ + iface_desc = data->intf->cur_altsetting; + + /* Find bulk in endpoint */ + for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { + endpoint = &iface_desc->endpoint[n].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) { + data->bulk_in = endpoint->bEndpointAddress; + dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", + data->bulk_in); + break; + } + } + + /* Find bulk out endpoint */ + for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { + endpoint = &iface_desc->endpoint[n].desc; + + if (usb_endpoint_is_bulk_out(endpoint)) { + data->bulk_out = endpoint->bEndpointAddress; + dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", + data->bulk_out); + break; + } + } + + retcode = get_capabilities(data); + if (retcode) + dev_err(&intf->dev, "can't read capabilities\n"); + else + retcode = sysfs_create_group(&intf->dev.kobj, + &capability_attr_grp); + + retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp); + + retcode = usb_register_dev(intf, &usbtmc_class); + if (retcode) { + dev_err(&intf->dev, "Not able to get a minor" + " (base %u, slice default): %d\n", USBTMC_MINOR_BASE, + retcode); + goto error_register; + } + dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor); + + return 0; + +error_register: + sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + kref_put(&data->kref, usbtmc_delete); + return retcode; +} + +static void usbtmc_disconnect(struct usb_interface *intf) +{ + struct usbtmc_device_data *data; + + dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); + + data = usb_get_intfdata(intf); + usb_deregister_dev(intf, &usbtmc_class); + sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + kref_put(&data->kref, usbtmc_delete); +} + +static struct usb_driver usbtmc_driver = { + .name = "usbtmc", + .id_table = usbtmc_devices, + .probe = usbtmc_probe, + .disconnect = usbtmc_disconnect +}; + +static int __init usbtmc_init(void) +{ + int retcode; + + retcode = usb_register(&usbtmc_driver); + if (retcode) + printk(KERN_ERR KBUILD_MODNAME": Unable to register driver\n"); + return retcode; +} +module_init(usbtmc_init); + +static void __exit usbtmc_exit(void) +{ + usb_deregister(&usbtmc_driver); +} +module_exit(usbtmc_exit); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild index 42e84fc315e..29fd73b0bff 100644 --- a/include/linux/usb/Kbuild +++ b/include/linux/usb/Kbuild @@ -4,4 +4,4 @@ header-y += ch9.h header-y += gadgetfs.h header-y += midi.h header-y += g_printer.h - +header-y += tmc.h diff --git a/include/linux/usb/tmc.h b/include/linux/usb/tmc.h new file mode 100644 index 00000000000..c045ae12556 --- /dev/null +++ b/include/linux/usb/tmc.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Greg Kroah-Hartman + * + * This file holds USB constants defined by the USB Device Class + * Definition for Test and Measurement devices published by the USB-IF. + * + * It also has the ioctl definitions for the usbtmc kernel driver that + * userspace needs to know about. + */ + +#ifndef __LINUX_USB_TMC_H +#define __LINUX_USB_TMC_H + +/* USB TMC status values */ +#define USBTMC_STATUS_SUCCESS 0x01 +#define USBTMC_STATUS_PENDING 0x02 +#define USBTMC_STATUS_FAILED 0x80 +#define USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS 0x81 +#define USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS 0x82 +#define USBTMC_STATUS_SPLIT_IN_PROGRESS 0x83 + +/* USB TMC requests values */ +#define USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT 1 +#define USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS 2 +#define USBTMC_REQUEST_INITIATE_ABORT_BULK_IN 3 +#define USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS 4 +#define USBTMC_REQUEST_INITIATE_CLEAR 5 +#define USBTMC_REQUEST_CHECK_CLEAR_STATUS 6 +#define USBTMC_REQUEST_GET_CAPABILITIES 7 +#define USBTMC_REQUEST_INDICATOR_PULSE 64 + +/* Request values for USBTMC driver's ioctl entry point */ +#define USBTMC_IOC_NR 91 +#define USBTMC_IOCTL_INDICATOR_PULSE _IO(USBTMC_IOC_NR, 1) +#define USBTMC_IOCTL_CLEAR _IO(USBTMC_IOC_NR, 2) +#define USBTMC_IOCTL_ABORT_BULK_OUT _IO(USBTMC_IOC_NR, 3) +#define USBTMC_IOCTL_ABORT_BULK_IN _IO(USBTMC_IOC_NR, 4) +#define USBTMC_IOCTL_CLEAR_OUT_HALT _IO(USBTMC_IOC_NR, 6) +#define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7) + +#endif -- cgit v1.2.3-70-g09d2 From 3086775a4916b0fe128d924d83f4e7d7c39e4d0e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 18 Aug 2008 17:39:30 -0700 Subject: usb gadget: cdc obex glue The following patch introduces a new f_obex.c function driver. It allows userspace obex servers to use usb as transport layer for their messages. [ dbrownell@users.sourceforge.net: various fixes and cleanups ] Signed-off-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/gadget.tmpl | 3 + drivers/usb/gadget/Kconfig | 8 +- drivers/usb/gadget/f_obex.c | 446 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/serial.c | 14 ++ drivers/usb/gadget/u_serial.h | 1 + include/linux/usb/cdc.h | 9 + 6 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/gadget/f_obex.c (limited to 'Documentation') diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index ea3bc9565e6..6ef2f0073e5 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -557,6 +557,9 @@ Near-term plans include converting all of them, except for "gadgetfs". !Edrivers/usb/gadget/f_acm.c +!Edrivers/usb/gadget/f_ecm.c +!Edrivers/usb/gadget/f_subset.c +!Edrivers/usb/gadget/f_obex.c !Edrivers/usb/gadget/f_serial.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5dda2dc708d..80a7c02dc95 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -576,19 +576,23 @@ config USB_FILE_STORAGE_TEST normal operation. config USB_G_SERIAL - tristate "Serial Gadget (with CDC ACM support)" + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" help The Serial Gadget talks to the Linux-USB generic serial driver. This driver supports a CDC-ACM module option, which can be used to interoperate with MS-Windows hosts or with the Linux-USB "cdc-acm" driver. + This driver also supports a CDC-OBEX option. You will need a + user space OBEX server talking to /dev/ttyGS*, since the kernel + itself doesn't implement the OBEX protocol. + Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_serial". For more information, see Documentation/usb/gadget_serial.txt which includes instructions and a "driver info file" needed to - make MS-Windows work with this driver. + make MS-Windows work with CDC ACM. config USB_MIDI_GADGET tristate "MIDI Gadget (EXPERIMENTAL)" diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c new file mode 100644 index 00000000000..86241b2ca8f --- /dev/null +++ b/drivers/usb/gadget/f_obex.c @@ -0,0 +1,446 @@ +/* + * f_obex.c -- USB CDC OBEX function driver + * + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi + * + * Based on f_acm.c by Al Borchers and David Brownell. + * + * 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 + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This CDC OBEX function support just packages a TTY-ish byte stream. + * A user mode server will put it into "raw" mode and handle all the + * relevant protocol details ... this is just a kernel passthrough. + * + * REVISIT this driver shouldn't actually activate before that user mode + * server is ready to respond! When the "serial gadget" utility code + * adds open/close notifications, this driver should use them with new + * (TBS) composite gadget hooks that wrap usb_gadget_disconnect() and + * usb_gadget_connect() calls with refcounts ... disconnect() when we + * bind, then connect() when the user server code is ready to respond. + */ + +struct obex_ep_descs { + struct usb_endpoint_descriptor *obex_in; + struct usb_endpoint_descriptor *obex_out; +}; + +struct f_obex { + struct gserial port; + u8 ctrl_id; + u8 data_id; + u8 port_num; + + struct obex_ep_descs fs; + struct obex_ep_descs hs; +}; + +static inline struct f_obex *func_to_obex(struct usb_function *f) +{ + return container_of(f, struct f_obex, port.func); +} + +/*-------------------------------------------------------------------------*/ + +#define OBEX_CTRL_IDX 0 +#define OBEX_DATA_IDX 1 + +static struct usb_string obex_string_defs[] = { + [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)", + [OBEX_DATA_IDX].s = "CDC OBEX Data", + { }, /* end of list */ +}; + +static struct usb_gadget_strings obex_string_table = { + .language = 0x0409, /* en-US */ + .strings = obex_string_defs, +}; + +static struct usb_gadget_strings *obex_strings[] = { + &obex_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor obex_control_intf __initdata = { + .bLength = sizeof(obex_control_intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, +}; + +static struct usb_interface_descriptor obex_data_nop_intf __initdata = { + .bLength = sizeof(obex_data_nop_intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + +static struct usb_interface_descriptor obex_data_intf __initdata = { + .bLength = sizeof(obex_data_intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 2, + + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + +static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = { + .bLength = sizeof(obex_cdc_header_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + .bcdCDC = __constant_cpu_to_le16(0x0120), +}; + +static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { + .bLength = sizeof(obex_cdc_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + .bMasterInterface0 = 1, + .bSlaveInterface0 = 2, +}; + +static struct usb_cdc_obex_desc obex_desc __initdata = { + .bLength = sizeof(obex_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_OBEX_TYPE, + .bcdVersion = __constant_cpu_to_le16(0x0100), +}; + +/* High-Speed Support */ + +static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_function[] __initdata = { + (struct usb_descriptor_header *) &obex_control_intf, + (struct usb_descriptor_header *) &obex_cdc_header_desc, + (struct usb_descriptor_header *) &obex_desc, + (struct usb_descriptor_header *) &obex_cdc_union_desc, + + (struct usb_descriptor_header *) &obex_data_nop_intf, + (struct usb_descriptor_header *) &obex_data_intf, + (struct usb_descriptor_header *) &obex_hs_ep_in_desc, + (struct usb_descriptor_header *) &obex_hs_ep_out_desc, + NULL, +}; + +/* Full-Speed Support */ + +static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_function[] __initdata = { + (struct usb_descriptor_header *) &obex_control_intf, + (struct usb_descriptor_header *) &obex_cdc_header_desc, + (struct usb_descriptor_header *) &obex_desc, + (struct usb_descriptor_header *) &obex_cdc_union_desc, + + (struct usb_descriptor_header *) &obex_data_nop_intf, + (struct usb_descriptor_header *) &obex_data_intf, + (struct usb_descriptor_header *) &obex_fs_ep_in_desc, + (struct usb_descriptor_header *) &obex_fs_ep_out_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_obex *obex = func_to_obex(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (intf == obex->ctrl_id) { + if (alt != 0) + goto fail; + /* NOP */ + DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num); + + } else if (intf == obex->data_id) { + if (alt > 1) + goto fail; + + if (obex->port.in->driver_data) { + DBG(cdev, "reset obex ttyGS%d\n", obex->port_num); + gserial_disconnect(&obex->port); + } + + if (!obex->port.in_desc) { + DBG(cdev, "init obex ttyGS%d\n", obex->port_num); + obex->port.in_desc = ep_choose(cdev->gadget, + obex->hs.obex_in, obex->fs.obex_in); + obex->port.out_desc = ep_choose(cdev->gadget, + obex->hs.obex_out, obex->fs.obex_out); + } + + if (alt == 1) { + DBG(cdev, "activate obex ttyGS%d\n", obex->port_num); + gserial_connect(&obex->port, obex->port_num); + } + + } else + goto fail; + + return 0; + +fail: + return -EINVAL; +} + +static int obex_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_obex *obex = func_to_obex(f); + + if (intf == obex->ctrl_id) + return 0; + + return obex->port.in->driver_data ? 1 : 0; +} + +static void obex_disable(struct usb_function *f) +{ + struct f_obex *obex = func_to_obex(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "obex ttyGS%d disable\n", obex->port_num); + gserial_disconnect(&obex->port); +} + +/*-------------------------------------------------------------------------*/ + +static int __init +obex_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_obex *obex = func_to_obex(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs, and patch descriptors */ + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + obex->ctrl_id = status; + + obex_control_intf.bInterfaceNumber = status; + obex_cdc_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + obex->data_id = status; + + obex_data_nop_intf.bInterfaceNumber = status; + obex_data_intf.bInterfaceNumber = status; + obex_cdc_union_desc.bSlaveInterface0 = status; + + /* allocate instance-specific endpoints */ + + ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); + if (!ep) + goto fail; + obex->port.in = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); + if (!ep) + goto fail; + obex->port.out = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(fs_function); + + obex->fs.obex_in = usb_find_endpoint(fs_function, + f->descriptors, &obex_fs_ep_in_desc); + obex->fs.obex_out = usb_find_endpoint(fs_function, + f->descriptors, &obex_fs_ep_out_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + + obex_hs_ep_in_desc.bEndpointAddress = + obex_fs_ep_in_desc.bEndpointAddress; + obex_hs_ep_out_desc.bEndpointAddress = + obex_fs_ep_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(hs_function); + + obex->hs.obex_in = usb_find_endpoint(hs_function, + f->descriptors, &obex_hs_ep_in_desc); + obex->hs.obex_out = usb_find_endpoint(hs_function, + f->descriptors, &obex_hs_ep_out_desc); + } + + DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", + obex->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + obex->port.in->name, obex->port.out->name); + + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (obex->port.out) + obex->port.out->driver_data = NULL; + if (obex->port.in) + obex->port.in->driver_data = NULL; + + ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); + + return status; +} + +static void +obex_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + kfree(func_to_obex(f)); +} + +/* Some controllers can't support CDC OBEX ... */ +static inline bool can_support_obex(struct usb_configuration *c) +{ + /* Since the first interface is a NOP, we can ignore the + * issue of multi-interface support on most controllers. + * + * Altsettings are mandatory, however... + */ + if (!gadget_supports_altsettings(c->cdev->gadget)) + return false; + + /* everything else is *probably* fine ... */ + return true; +} + +/** + * obex_bind_config - add a CDC OBEX function to a configuration + * @c: the configuration to support the CDC OBEX instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds. Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init obex_bind_config(struct usb_configuration *c, u8 port_num) +{ + struct f_obex *obex; + int status; + + if (!can_support_obex(c)) + return -EINVAL; + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + obex_string_defs[OBEX_CTRL_IDX].id = status; + + obex_control_intf.iInterface = status; + + status = usb_string_id(c->cdev); + if (status < 0) + return status; + obex_string_defs[OBEX_DATA_IDX].id = status; + + obex_data_nop_intf.iInterface = + obex_data_intf.iInterface = status; + } + + /* allocate and initialize one new instance */ + obex = kzalloc(sizeof *obex, GFP_KERNEL); + if (!obex) + return -ENOMEM; + + obex->port_num = port_num; + + obex->port.func.name = "obex"; + obex->port.func.strings = obex_strings; + /* descriptors are per-instance copies */ + obex->port.func.bind = obex_bind; + obex->port.func.unbind = obex_unbind; + obex->port.func.set_alt = obex_set_alt; + obex->port.func.get_alt = obex_get_alt; + obex->port.func.disable = obex_disable; + + status = usb_add_function(c, &obex->port.func); + if (status) + kfree(obex); + + return status; +} + +MODULE_AUTHOR("Felipe Balbi"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 3faa7a7022d..2dee848b2f5 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -43,6 +43,7 @@ #include "epautoconf.c" #include "f_acm.c" +#include "f_obex.c" #include "f_serial.c" #include "u_serial.c" @@ -56,6 +57,7 @@ #define GS_VENDOR_ID 0x0525 /* NetChip */ #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ +#define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */ /* string IDs are assigned dynamically */ @@ -125,6 +127,10 @@ static int use_acm = true; module_param(use_acm, bool, 0); MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); +static int use_obex = false; +module_param(use_obex, bool, 0); +MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no"); + static unsigned n_ports = 1; module_param(n_ports, uint, 0); MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); @@ -139,6 +145,8 @@ static int __init serial_bind_config(struct usb_configuration *c) for (i = 0; i < n_ports && status == 0; i++) { if (use_acm) status = acm_bind_config(c, i); + else if (use_obex) + status = obex_bind_config(c, i); else status = gser_bind_config(c, i); } @@ -249,6 +257,12 @@ static int __init init(void) device_desc.bDeviceClass = USB_CLASS_COMM; device_desc.idProduct = __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); + } else if (use_obex) { + serial_config_driver.label = "CDC OBEX config"; + serial_config_driver.bConfigurationValue = 3; + device_desc.bDeviceClass = USB_CLASS_COMM; + device_desc.idProduct = + __constant_cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID); } else { serial_config_driver.label = "Generic Serial config"; serial_config_driver.bConfigurationValue = 1; diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index af3910d01ae..300f0ed9475 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -62,5 +62,6 @@ void gserial_disconnect(struct gserial *); /* functions are bound to configurations by a config or gadget driver */ int acm_bind_config(struct usb_configuration *c, u8 port_num); int gser_bind_config(struct usb_configuration *c, u8 port_num); +int obex_bind_config(struct usb_configuration *c, u8 port_num); #endif /* __U_SERIAL_H */ diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index ca228bb9421..18a729343ff 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -160,6 +160,15 @@ struct usb_cdc_mdlm_detail_desc { __u8 bDetailData[0]; } __attribute__ ((packed)); +/* "OBEX Control Model Functional Descriptor" */ +struct usb_cdc_obex_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdVersion; +} __attribute__ ((packed)); + /*-------------------------------------------------------------------------*/ /* -- cgit v1.2.3-70-g09d2 From d1b1944085ab2345fae4a5fbb614f1a4d0732d3e Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 2 Sep 2008 14:16:11 +0200 Subject: USB: Documentation/usb/anchors.txt #2 This adds Documentation for the extensions of the anchor API. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/anchors.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/anchors.txt b/Documentation/usb/anchors.txt index 5e6b64c20d2..6f24f566955 100644 --- a/Documentation/usb/anchors.txt +++ b/Documentation/usb/anchors.txt @@ -52,6 +52,11 @@ Therefore no guarantee is made that the URBs have been unlinked when the call returns. They may be unlinked later but will be unlinked in finite time. +usb_scuttle_anchored_urbs() +--------------------------- + +All URBs of an anchor are unanchored en masse. + usb_wait_anchor_empty_timeout() ------------------------------- @@ -59,4 +64,16 @@ This function waits for all URBs associated with an anchor to finish or a timeout, whichever comes first. Its return value will tell you whether the timeout was reached. +usb_anchor_empty() +------------------ + +Returns true if no URBs are associated with an anchor. Locking +is the caller's responsibility. + +usb_get_from_anchor() +--------------------- +Returns the oldest anchored URB of an anchor. The URB is unanchored +and returned with a reference. As you may mix URBs to several +destinations in one anchor you have no guarantee the chronologically +first submitted URB is returned. \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 81ab5b8ee6b8cd5fe8cfdf0eea84eea0aa7b4da9 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Sat, 20 Sep 2008 14:41:47 -0700 Subject: USB: Fix doc for usb_autopm_enable Correct errors in the descriptions for usb_autopm_enable and usb_autopm_disable in the USB PM doc. Signed-off-by: Geoff Levand Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/power-management.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index 9d31140e3f5..e48ea1d5101 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -350,12 +350,12 @@ without holding the mutex. There also are a couple of utility routines drivers can use: - usb_autopm_enable() sets pm_usage_cnt to 1 and then calls - usb_autopm_set_interface(), which will attempt an autoresume. - - usb_autopm_disable() sets pm_usage_cnt to 0 and then calls + usb_autopm_enable() sets pm_usage_cnt to 0 and then calls usb_autopm_set_interface(), which will attempt an autosuspend. + usb_autopm_disable() sets pm_usage_cnt to 1 and then calls + usb_autopm_set_interface(), which will attempt an autoresume. + The conventional usage pattern is that a driver calls usb_autopm_get_interface() in its open routine and usb_autopm_put_interface() in its close or release routine. But -- cgit v1.2.3-70-g09d2 From cbc30118d7a376dab4113f299c0c8f035737a5c3 Mon Sep 17 00:00:00 2001 From: Stephen Ware Date: Tue, 30 Sep 2008 11:39:38 -0700 Subject: usb: vstusb.c : new driver for spectrometers used by Vernier Software & Technology, Inc. This patch adds the vstusb driver to the drivers/usb/misc directory. This driver provides support for Vernier Software & Technology spectrometers, all made by Ocean Optics. The driver provides both IOCTL and read()/write() methods for sending raw data to spectrometers across the bulk channel. Each method allows for a configured timeout. From: Stephen Ware Signed-off-by: Dennis O'Brien Signed-off-by: Greg Kroah-Hartman --- Documentation/ioctl-number.txt | 1 + drivers/usb/misc/Kconfig | 15 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/vstusb.c | 768 +++++++++++++++++++++++++++++++++++++++++ include/linux/usb/Kbuild | 1 + include/linux/usb/vstusb.h | 71 ++++ 6 files changed, 857 insertions(+) create mode 100644 drivers/usb/misc/vstusb.c create mode 100644 include/linux/usb/vstusb.h (limited to 'Documentation') diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index f8deb85eef6..b880ce5dbd3 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -92,6 +92,7 @@ Code Seq# Include File Comments 'J' 00-1F drivers/scsi/gdth_ioctl.h 'K' all linux/kd.h 'L' 00-1F linux/loop.h +'L' 20-2F driver/usb/misc/vstusb.h 'L' E0-FF linux/ppdd.h encrypted disk device driver 'M' all linux/soundcard.h diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 25e1157ab17..e463db5d818 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -280,3 +280,18 @@ config USB_ISIGHTFW The firmware for this driver must be extracted from the MacOS driver beforehand. Tools for doing so are available at http://bersace03.free.fr + +config USB_VST + tristate "USB VST driver" + depends on USB + help + This driver is intended for Vernier Software Technologies + bulk usb devices such as their Ocean-Optics spectrometers or + Labquest. + It is a bulk channel driver with configurable read and write + timeouts. + + To compile this driver as a module, choose M here: the + module will be called vstusb. + + diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 39ce4a16b3d..1334f7bdd7b 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o +obj-$(CONFIG_USB_VST) += vstusb.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ diff --git a/drivers/usb/misc/vstusb.c b/drivers/usb/misc/vstusb.c new file mode 100644 index 00000000000..5ad75e4a032 --- /dev/null +++ b/drivers/usb/misc/vstusb.c @@ -0,0 +1,768 @@ +/***************************************************************************** + * File: drivers/usb/misc/vstusb.c + * + * Purpose: Support for the bulk USB Vernier Spectrophotometers + * + * Author: Johnnie Peters + * Axian Consulting + * Beaverton, OR, USA 97005 + * + * Modified by: EQware Engineering, Inc. + * Oregon City, OR, USA 97045 + * + * Copyright: 2007, 2008 + * Vernier Software & Technology + * Beaverton, OR, USA 97005 + * + * Web: www.vernier.com + * + * 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 + +#define DRIVER_VERSION "VST USB Driver Version 1.5" +#define DRIVER_DESC "Vernier Software Technology Bulk USB Driver" + +#ifdef CONFIG_USB_DYNAMIC_MINORS + #define VSTUSB_MINOR_BASE 0 +#else + #define VSTUSB_MINOR_BASE 199 +#endif + +#define USB_VENDOR_OCEANOPTICS 0x2457 +#define USB_VENDOR_VERNIER 0x08F7 /* Vernier Software & Technology */ + +#define USB_PRODUCT_USB2000 0x1002 +#define USB_PRODUCT_ADC1000_FW 0x1003 /* firmware download (renumerates) */ +#define USB_PRODUCT_ADC1000 0x1004 +#define USB_PRODUCT_HR2000_FW 0x1009 /* firmware download (renumerates) */ +#define USB_PRODUCT_HR2000 0x100A +#define USB_PRODUCT_HR4000_FW 0x1011 /* firmware download (renumerates) */ +#define USB_PRODUCT_HR4000 0x1012 +#define USB_PRODUCT_USB650 0x1014 /* "Red Tide" */ +#define USB_PRODUCT_QE65000 0x1018 +#define USB_PRODUCT_USB4000 0x1022 +#define USB_PRODUCT_USB325 0x1024 /* "Vernier Spectrometer" */ + +#define USB_PRODUCT_LABPRO 0x0001 +#define USB_PRODUCT_LABQUEST 0x0005 + +static struct usb_device_id id_table[] = { + { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)}, + { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)}, + { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB650)}, + { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB4000)}, + { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB325)}, + { USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABQUEST)}, + { USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABPRO)}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +struct vstusb_device { + struct mutex lock; + struct usb_device *usb_dev; + char present; + char isopen; + struct usb_anchor submitted; + int rd_pipe; + int rd_timeout_ms; + int wr_pipe; + int wr_timeout_ms; +}; + +static struct usb_driver vstusb_driver; + +static int vstusb_open(struct inode *inode, struct file *file) +{ + struct vstusb_device *vstdev; + struct usb_interface *interface; + + interface = usb_find_interface(&vstusb_driver, iminor(inode)); + + if (!interface) { + printk(KERN_ERR KBUILD_MODNAME + ": %s - error, can't find device for minor %d\n", + __func__, iminor(inode)); + return -ENODEV; + } + + vstdev = usb_get_intfdata(interface); + + if (!vstdev) + return -ENODEV; + + /* lock this device */ + mutex_lock(&vstdev->lock); + + /* can only open one time */ + if ((!vstdev->present) || (vstdev->isopen)) { + mutex_unlock(&vstdev->lock); + return -EBUSY; + } + + vstdev->isopen = 1; + + /* save device in the file's private structure */ + file->private_data = vstdev; + + dev_dbg(&vstdev->usb_dev->dev, "%s: opened\n", __func__); + + mutex_unlock(&vstdev->lock); + + return 0; +} + +static int vstusb_close(struct inode *inode, struct file *file) +{ + struct vstusb_device *vstdev; + + vstdev = file->private_data; + + if (vstdev == NULL) + return -ENODEV; + + mutex_lock(&vstdev->lock); + + vstdev->isopen = 0; + file->private_data = NULL; + + /* if device is no longer present */ + if (!vstdev->present) { + mutex_unlock(&vstdev->lock); + kfree(vstdev); + } else + mutex_unlock(&vstdev->lock); + + return 0; +} + +static void usb_api_blocking_completion(struct urb *urb) +{ + struct completion *completeit = urb->context; + + complete(completeit); +} + +static int vstusb_fill_and_send_urb(struct urb *urb, + struct usb_device *usb_dev, + unsigned int pipe, void *data, + unsigned int len, struct completion *done) +{ + struct usb_host_endpoint *ep; + struct usb_host_endpoint **hostep; + unsigned int pipend; + + int status; + + hostep = usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out; + pipend = usb_pipeendpoint(pipe); + ep = hostep[pipend]; + + if (!ep || (len == 0)) + return -EINVAL; + + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT) { + pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); + usb_fill_int_urb(urb, usb_dev, pipe, data, len, + (usb_complete_t)usb_api_blocking_completion, + NULL, ep->desc.bInterval); + } else + usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, + (usb_complete_t)usb_api_blocking_completion, + NULL); + + init_completion(done); + urb->context = done; + urb->actual_length = 0; + status = usb_submit_urb(urb, GFP_KERNEL); + + return status; +} + +static int vstusb_complete_urb(struct urb *urb, struct completion *done, + int timeout, int *actual_length) +{ + unsigned long expire; + int status; + + expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; + if (!wait_for_completion_interruptible_timeout(done, expire)) { + usb_kill_urb(urb); + status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status; + + dev_dbg(&urb->dev->dev, + "%s timed out on ep%d%s len=%d/%d, urb status = %d\n", + current->comm, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->actual_length, + urb->transfer_buffer_length, + urb->status); + + } else { + if (signal_pending(current)) { + /* if really an error */ + if (urb->status && !((urb->status == -ENOENT) || + (urb->status == -ECONNRESET) || + (urb->status == -ESHUTDOWN))) { + status = -EINTR; + usb_kill_urb(urb); + } else { + status = 0; + } + + dev_dbg(&urb->dev->dev, + "%s: signal pending on ep%d%s len=%d/%d," + "urb status = %d\n", + current->comm, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->actual_length, + urb->transfer_buffer_length, + urb->status); + + } else { + status = urb->status; + } + } + + if (actual_length) + *actual_length = urb->actual_length; + + return status; +} + +static ssize_t vstusb_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct vstusb_device *vstdev; + int cnt = -1; + void *buf; + int retval = 0; + + struct urb *urb; + struct usb_device *dev; + unsigned int pipe; + int timeout; + + DECLARE_COMPLETION_ONSTACK(done); + + vstdev = file->private_data; + + if (vstdev == NULL) + return -ENODEV; + + /* verify that we actually want to read some data */ + if (count == 0) + return -EINVAL; + + /* lock this object */ + if (mutex_lock_interruptible(&vstdev->lock)) + return -ERESTARTSYS; + + /* anyone home */ + if (!vstdev->present) { + mutex_unlock(&vstdev->lock); + printk(KERN_ERR KBUILD_MODNAME + ": %s: device not present\n", __func__); + return -ENODEV; + } + + /* pull out the necessary data */ + dev = vstdev->usb_dev; + pipe = usb_rcvbulkpipe(dev, vstdev->rd_pipe); + timeout = vstdev->rd_timeout_ms; + + buf = kmalloc(count, GFP_KERNEL); + if (buf == NULL) { + mutex_unlock(&vstdev->lock); + return -ENOMEM; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buf); + mutex_unlock(&vstdev->lock); + return -ENOMEM; + } + + usb_anchor_urb(urb, &vstdev->submitted); + retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done); + mutex_unlock(&vstdev->lock); + if (retval) { + usb_unanchor_urb(urb); + dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n", + __func__, retval, pipe); + goto exit; + } + + retval = vstusb_complete_urb(urb, &done, timeout, &cnt); + if (retval) { + dev_err(&dev->dev, "%s: error %d completing urb %d\n", + __func__, retval, pipe); + goto exit; + } + + if (copy_to_user(buffer, buf, cnt)) { + dev_err(&dev->dev, "%s: can't copy_to_user\n", __func__); + retval = -EFAULT; + } else { + retval = cnt; + dev_dbg(&dev->dev, "%s: read %d bytes from pipe %d\n", + __func__, cnt, pipe); + } + +exit: + usb_free_urb(urb); + kfree(buf); + return retval; +} + +static ssize_t vstusb_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct vstusb_device *vstdev; + int cnt = -1; + void *buf; + int retval = 0; + + struct urb *urb; + struct usb_device *dev; + unsigned int pipe; + int timeout; + + DECLARE_COMPLETION_ONSTACK(done); + + vstdev = file->private_data; + + if (vstdev == NULL) + return -ENODEV; + + /* verify that we actually have some data to write */ + if (count == 0) + return retval; + + /* lock this object */ + if (mutex_lock_interruptible(&vstdev->lock)) + return -ERESTARTSYS; + + /* anyone home */ + if (!vstdev->present) { + mutex_unlock(&vstdev->lock); + printk(KERN_ERR KBUILD_MODNAME + ": %s: device not present\n", __func__); + return -ENODEV; + } + + /* pull out the necessary data */ + dev = vstdev->usb_dev; + pipe = usb_sndbulkpipe(dev, vstdev->wr_pipe); + timeout = vstdev->wr_timeout_ms; + + buf = kmalloc(count, GFP_KERNEL); + if (buf == NULL) { + mutex_unlock(&vstdev->lock); + return -ENOMEM; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buf); + mutex_unlock(&vstdev->lock); + return -ENOMEM; + } + + if (copy_from_user(buf, buffer, count)) { + dev_err(&dev->dev, "%s: can't copy_from_user\n", __func__); + retval = -EFAULT; + goto exit; + } + + usb_anchor_urb(urb, &vstdev->submitted); + retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done); + mutex_unlock(&vstdev->lock); + if (retval) { + usb_unanchor_urb(urb); + dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n", + __func__, retval, pipe); + goto exit; + } + + retval = vstusb_complete_urb(urb, &done, timeout, &cnt); + if (retval) { + dev_err(&dev->dev, "%s: error %d completing urb %d\n", + __func__, retval, pipe); + goto exit; + } else { + retval = cnt; + dev_dbg(&dev->dev, "%s: sent %d bytes to pipe %d\n", + __func__, cnt, pipe); + } + +exit: + usb_free_urb(urb); + kfree(buf); + return retval; +} + +static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + int cnt = -1; + void __user *data = (void __user *)arg; + struct vstusb_args usb_data; + + struct vstusb_device *vstdev; + void *buffer = NULL; /* must be initialized. buffer is + * referenced on exit but not all + * ioctls allocate it */ + + struct urb *urb = NULL; /* must be initialized. urb is + * referenced on exit but not all + * ioctls allocate it */ + struct usb_device *dev; + unsigned int pipe; + int timeout; + + DECLARE_COMPLETION_ONSTACK(done); + + vstdev = file->private_data; + + if (_IOC_TYPE(cmd) != VST_IOC_MAGIC) { + dev_warn(&vstdev->usb_dev->dev, + "%s: ioctl command %x, bad ioctl magic %x, " + "expected %x\n", __func__, cmd, + _IOC_TYPE(cmd), VST_IOC_MAGIC); + return -EINVAL; + } + + if (vstdev == NULL) + return -ENODEV; + + if (copy_from_user(&usb_data, data, sizeof(struct vstusb_args))) { + dev_err(&vstdev->usb_dev->dev, "%s: can't copy_from_user\n", + __func__); + return -EFAULT; + } + + /* lock this object */ + if (mutex_lock_interruptible(&vstdev->lock)) { + retval = -ERESTARTSYS; + goto exit; + } + + /* anyone home */ + if (!vstdev->present) { + mutex_unlock(&vstdev->lock); + dev_err(&vstdev->usb_dev->dev, "%s: device not present\n", + __func__); + retval = -ENODEV; + goto exit; + } + + /* pull out the necessary data */ + dev = vstdev->usb_dev; + + switch (cmd) { + + case IOCTL_VSTUSB_CONFIG_RW: + + vstdev->rd_pipe = usb_data.rd_pipe; + vstdev->rd_timeout_ms = usb_data.rd_timeout_ms; + vstdev->wr_pipe = usb_data.wr_pipe; + vstdev->wr_timeout_ms = usb_data.wr_timeout_ms; + + mutex_unlock(&vstdev->lock); + + dev_dbg(&dev->dev, "%s: setting pipes/timeouts, " + "rdpipe = %d, rdtimeout = %d, " + "wrpipe = %d, wrtimeout = %d\n", __func__, + vstdev->rd_pipe, vstdev->rd_timeout_ms, + vstdev->wr_pipe, vstdev->wr_timeout_ms); + break; + + case IOCTL_VSTUSB_SEND_PIPE: + + if (usb_data.count == 0) { + mutex_unlock(&vstdev->lock); + retval = -EINVAL; + goto exit; + } + + buffer = kmalloc(usb_data.count, GFP_KERNEL); + if (buffer == NULL) { + mutex_unlock(&vstdev->lock); + retval = -ENOMEM; + goto exit; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + mutex_unlock(&vstdev->lock); + retval = -ENOMEM; + goto exit; + } + + timeout = usb_data.timeout_ms; + + pipe = usb_sndbulkpipe(dev, usb_data.pipe); + + if (copy_from_user(buffer, usb_data.buffer, usb_data.count)) { + dev_err(&dev->dev, "%s: can't copy_from_user\n", + __func__); + mutex_unlock(&vstdev->lock); + retval = -EFAULT; + goto exit; + } + + usb_anchor_urb(urb, &vstdev->submitted); + retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer, + usb_data.count, &done); + mutex_unlock(&vstdev->lock); + if (retval) { + usb_unanchor_urb(urb); + dev_err(&dev->dev, + "%s: error %d filling and sending urb %d\n", + __func__, retval, pipe); + goto exit; + } + + retval = vstusb_complete_urb(urb, &done, timeout, &cnt); + if (retval) { + dev_err(&dev->dev, "%s: error %d completing urb %d\n", + __func__, retval, pipe); + } + + break; + case IOCTL_VSTUSB_RECV_PIPE: + + if (usb_data.count == 0) { + mutex_unlock(&vstdev->lock); + retval = -EINVAL; + goto exit; + } + + buffer = kmalloc(usb_data.count, GFP_KERNEL); + if (buffer == NULL) { + mutex_unlock(&vstdev->lock); + retval = -ENOMEM; + goto exit; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + mutex_unlock(&vstdev->lock); + retval = -ENOMEM; + goto exit; + } + + timeout = usb_data.timeout_ms; + + pipe = usb_rcvbulkpipe(dev, usb_data.pipe); + + usb_anchor_urb(urb, &vstdev->submitted); + retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer, + usb_data.count, &done); + mutex_unlock(&vstdev->lock); + if (retval) { + usb_unanchor_urb(urb); + dev_err(&dev->dev, + "%s: error %d filling and sending urb %d\n", + __func__, retval, pipe); + goto exit; + } + + retval = vstusb_complete_urb(urb, &done, timeout, &cnt); + if (retval) { + dev_err(&dev->dev, "%s: error %d completing urb %d\n", + __func__, retval, pipe); + goto exit; + } + + if (copy_to_user(usb_data.buffer, buffer, cnt)) { + dev_err(&dev->dev, "%s: can't copy_to_user\n", + __func__); + retval = -EFAULT; + goto exit; + } + + usb_data.count = cnt; + if (copy_to_user(data, &usb_data, sizeof(struct vstusb_args))) { + dev_err(&dev->dev, "%s: can't copy_to_user\n", + __func__); + retval = -EFAULT; + } else { + dev_dbg(&dev->dev, "%s: recv %d bytes from pipe %d\n", + __func__, usb_data.count, usb_data.pipe); + } + + break; + + default: + mutex_unlock(&vstdev->lock); + dev_warn(&dev->dev, "ioctl_vstusb: invalid ioctl cmd %x\n", + cmd); + return -EINVAL; + break; + } +exit: + usb_free_urb(urb); + kfree(buffer); + return retval; +} + +static const struct file_operations vstusb_fops = { + .owner = THIS_MODULE, + .read = vstusb_read, + .write = vstusb_write, + .unlocked_ioctl = vstusb_ioctl, + .compat_ioctl = vstusb_ioctl, + .open = vstusb_open, + .release = vstusb_close, +}; + +static struct usb_class_driver usb_vstusb_class = { + .name = "usb/vstusb%d", + .fops = &vstusb_fops, + .minor_base = VSTUSB_MINOR_BASE, +}; + +static int vstusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct vstusb_device *vstdev; + int i; + int retval = 0; + + /* allocate memory for our device state and intialize it */ + + vstdev = kzalloc(sizeof(*vstdev), GFP_KERNEL); + if (vstdev == NULL) + return -ENOMEM; + + mutex_init(&vstdev->lock); + + i = dev->descriptor.bcdDevice; + + dev_dbg(&intf->dev, "Version %1d%1d.%1d%1d found at address %d\n", + (i & 0xF000) >> 12, (i & 0xF00) >> 8, + (i & 0xF0) >> 4, (i & 0xF), dev->devnum); + + vstdev->present = 1; + vstdev->isopen = 0; + vstdev->usb_dev = dev; + init_usb_anchor(&vstdev->submitted); + + usb_set_intfdata(intf, vstdev); + retval = usb_register_dev(intf, &usb_vstusb_class); + if (retval) { + dev_err(&intf->dev, + "%s: Not able to get a minor for this device.\n", + __func__); + usb_set_intfdata(intf, NULL); + kfree(vstdev); + return retval; + } + + /* let the user know what node this device is now attached to */ + dev_info(&intf->dev, + "VST USB Device #%d now attached to major %d minor %d\n", + (intf->minor - VSTUSB_MINOR_BASE), USB_MAJOR, intf->minor); + + dev_info(&intf->dev, "%s, %s\n", DRIVER_DESC, DRIVER_VERSION); + + return retval; +} + +static void vstusb_disconnect(struct usb_interface *intf) +{ + struct vstusb_device *vstdev = usb_get_intfdata(intf); + + usb_deregister_dev(intf, &usb_vstusb_class); + usb_set_intfdata(intf, NULL); + + if (vstdev) { + + mutex_lock(&vstdev->lock); + vstdev->present = 0; + + usb_kill_anchored_urbs(&vstdev->submitted); + + /* if the device is not opened, then we clean up right now */ + if (!vstdev->isopen) { + mutex_unlock(&vstdev->lock); + kfree(vstdev); + } else + mutex_unlock(&vstdev->lock); + + } +} + +static int vstusb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct vstusb_device *vstdev = usb_get_intfdata(intf); + int time; + if (!vstdev) + return 0; + + mutex_lock(&vstdev->lock); + time = usb_wait_anchor_empty_timeout(&vstdev->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&vstdev->submitted); + mutex_unlock(&vstdev->lock); + + return 0; +} + +static int vstusb_resume(struct usb_interface *intf) +{ + return 0; +} + +static struct usb_driver vstusb_driver = { + .name = "vstusb", + .probe = vstusb_probe, + .disconnect = vstusb_disconnect, + .suspend = vstusb_suspend, + .resume = vstusb_resume, + .id_table = id_table, +}; + +static int __init vstusb_init(void) +{ + int rc; + + rc = usb_register(&vstusb_driver); + if (rc) + printk(KERN_ERR "%s: failed to register (%d)", __func__, rc); + + return rc; +} + +static void __exit vstusb_exit(void) +{ + usb_deregister(&vstusb_driver); +} + +module_init(vstusb_init); +module_exit(vstusb_exit); + +MODULE_AUTHOR("Dennis O'Brien/Stephen Ware"); +MODULE_DESCRIPTION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild index 29fd73b0bff..54c446309a2 100644 --- a/include/linux/usb/Kbuild +++ b/include/linux/usb/Kbuild @@ -5,3 +5,4 @@ header-y += gadgetfs.h header-y += midi.h header-y += g_printer.h header-y += tmc.h +header-y += vstusb.h diff --git a/include/linux/usb/vstusb.h b/include/linux/usb/vstusb.h new file mode 100644 index 00000000000..1cfac67191f --- /dev/null +++ b/include/linux/usb/vstusb.h @@ -0,0 +1,71 @@ +/***************************************************************************** + * File: drivers/usb/misc/vstusb.h + * + * Purpose: Support for the bulk USB Vernier Spectrophotometers + * + * Author: EQware Engineering, Inc. + * Oregon City, OR, USA 97045 + * + * Copyright: 2007, 2008 + * Vernier Software & Technology + * Beaverton, OR, USA 97005 + * + * Web: www.vernier.com + * + * 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. + * + *****************************************************************************/ +/***************************************************************************** + * + * The vstusb module is a standard usb 'client' driver running on top of the + * standard usb host controller stack. + * + * In general, vstusb supports standard bulk usb pipes. It supports multiple + * devices and multiple pipes per device. + * + * The vstusb driver supports two interfaces: + * 1 - ioctl SEND_PIPE/RECV_PIPE - a general bulk write/read msg + * interface to any pipe with timeout support; + * 2 - standard read/write with ioctl config - offers standard read/write + * interface with ioctl configured pipes and timeouts. + * + * Both interfaces can be signal from other process and will abort its i/o + * operation. + * + * A timeout of 0 means NO timeout. The user can still terminate the read via + * signal. + * + * If using multiple threads with this driver, the user should ensure that + * any reads, writes, or ioctls are complete before closing the device. + * Changing read/write timeouts or pipes takes effect on next read/write. + * + *****************************************************************************/ + +struct vstusb_args { + union { + /* this struct is used for IOCTL_VSTUSB_SEND_PIPE, * + * IOCTL_VSTUSB_RECV_PIPE, and read()/write() fops */ + struct { + void __user *buffer; + size_t count; + unsigned int timeout_ms; + int pipe; + }; + + /* this one is used for IOCTL_VSTUSB_CONFIG_RW */ + struct { + int rd_pipe; + int rd_timeout_ms; + int wr_pipe; + int wr_timeout_ms; + }; + }; +}; + +#define VST_IOC_MAGIC 'L' +#define VST_IOC_FIRST 0x20 +#define IOCTL_VSTUSB_SEND_PIPE _IO(VST_IOC_MAGIC, VST_IOC_FIRST) +#define IOCTL_VSTUSB_RECV_PIPE _IO(VST_IOC_MAGIC, VST_IOC_FIRST + 1) +#define IOCTL_VSTUSB_CONFIG_RW _IO(VST_IOC_MAGIC, VST_IOC_FIRST + 2) -- cgit v1.2.3-70-g09d2 From 49e7cc84a86784ef2ab4e651f1824093be8f5b2b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 6 Oct 2008 14:45:46 -0700 Subject: USB: Export if an interface driver supports autosuspend. Create a new sysfs file per interface named supports_autosuspend. This file returns true if an interface driver's .supports_autosuspend flag is set. It also returns true if the interface is unclaimed (since the USB core will autosuspend a device if an interface is not claimed). This new sysfs file will be useful for user space scripts to test whether a USB device correctly auto-suspends. Signed-off-by: Sarah Sharp Cc: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 16 ++++++++++++++++ drivers/usb/core/sysfs.c | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 11a3c1682ce..df6c8a0159f 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -85,3 +85,19 @@ Description: Users: PowerTOP http://www.lesswatts.org/projects/powertop/ + +What: /sys/bus/usb/device/-...:-/supports_autosuspend +Date: January 2008 +KernelVersion: 2.6.27 +Contact: Sarah Sharp +Description: + When read, this file returns 1 if the interface driver + for this interface supports autosuspend. It also + returns 1 if no driver has claimed this interface, as an + unclaimed interface will not stop the device from being + autosuspended if all other interface drivers are idle. + The file returns 0 if autosuspend support has not been + added to the driver. +Users: + USB PM tool + git://git.moblin.org/users/sarah/usb-pm-tool/ diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5e1f5d55bf0..f66fba11fbd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -743,6 +743,29 @@ static ssize_t show_modalias(struct device *dev, } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); +static ssize_t show_supports_autosuspend(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf; + struct usb_device *udev; + int ret; + + intf = to_usb_interface(dev); + udev = interface_to_usbdev(intf); + + usb_lock_device(udev); + /* Devices will be autosuspended even when an interface isn't claimed */ + if (!intf->dev.driver || + to_usb_driver(intf->dev.driver)->supports_autosuspend) + ret = sprintf(buf, "%u\n", 1); + else + ret = sprintf(buf, "%u\n", 0); + usb_unlock_device(udev); + + return ret; +} +static DEVICE_ATTR(supports_autosuspend, S_IRUGO, show_supports_autosuspend, NULL); + static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, &dev_attr_bAlternateSetting.attr, @@ -751,6 +774,7 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, &dev_attr_modalias.attr, + &dev_attr_supports_autosuspend.attr, NULL, }; static struct attribute_group intf_attr_grp = { -- cgit v1.2.3-70-g09d2 From fd7c519dd40a0d561280bb797386143fb2026949 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 10 Oct 2008 16:24:45 +0200 Subject: USB: hub.c: Add initial_descriptor_timeout module parameter for usbcore This patch adds initial_descriptor_timeout module parameter for usbcore.ko to allow modify initial 64-byte USB_REQ_GET_DESCRIPTOR timeout for non-standard devices. For example, the SATA8000 device from DATAST0R Technology Corp requires about 10 seconds to send reply (probably it waits until inserted disk is ready for operation). Also, this patch adds missing usbcore parameters to Documentation/kernel-parameters.txt. Signed-off-by: Jaroslav Kysela Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 19 +++++++++++++++++++ drivers/usb/core/hub.c | 11 ++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index dd28a0d5698..d4f4875fc7c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2253,6 +2253,25 @@ and is between 256 and 4096 characters. It is defined in the file autosuspended. Devices for which the delay is set to a negative value won't be autosuspended at all. + usbcore.usbfs_snoop= + [USB] Set to log all usbfs traffic (default 0 = off). + + usbcore.blinkenlights= + [USB] Set to cycle leds on hubs (default 0 = off). + + usbcore.old_scheme_first= + [USB] Start with the old device initialization + scheme (default 0 = off). + + usbcore.use_both_schemes= + [USB] Try the other device initialization scheme + if the first one fails (default 1 = enabled). + + usbcore.initial_descriptor_timeout= + [USB] Specifies timeout for the initial 64-byte + USB_REQ_GET_DESCRIPTOR request in milliseconds + (default 5000 = 5.0 seconds). + usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b97110ca352..d73ce262c36 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -100,6 +100,15 @@ static int blinkenlights = 0; module_param (blinkenlights, bool, S_IRUGO); MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs"); +/* + * Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about + * 10 seconds to send reply for the initial 64-byte descriptor request. + */ +/* define initial 64-byte descriptor request timeout in milliseconds */ +static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT; +module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)"); + /* * As of 2.6.10 we introduce a new USB device initialization scheme which * closely resembles the way Windows works. Hopefully it will be compatible @@ -2538,7 +2547,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, - USB_CTRL_GET_TIMEOUT); + initial_descriptor_timeout); switch (buf->bMaxPacketSize0) { case 8: case 16: case 32: case 64: case 255: if (buf->bDescriptorType == -- cgit v1.2.3-70-g09d2 From 24bdeb4598b9560c8ffecb8ba5cefa01f3a12a54 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 18 Oct 2008 20:27:27 -0700 Subject: Fix documentation of sysrq-q I fell into the trap recently that it only dumps hrtimers instead of all timers. Fix the documentation. Signed-off-by: Andi Kleen Cc: torvalds@linux-foundation.org Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- Documentation/sysrq.txt | 3 ++- drivers/char/sysrq.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 5ce0952aa06..49378a9f2b5 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -95,7 +95,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'p' - Will dump the current registers and flags to your console. -'q' - Will dump a list of all running timers. +'q' - Will dump a list of all running hrtimers. + WARNING: Does not cover any other timers 'r' - Turns off keyboard raw mode and sets it to XLATE. diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index dce4cc0e695..d0c0d64ed36 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -168,7 +168,7 @@ static void sysrq_handle_show_timers(int key, struct tty_struct *tty) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, .help_msg = "show-all-timers(Q)", - .action_msg = "Show Pending Timers", + .action_msg = "Show pending hrtimers (no others)", }; static void sysrq_handle_mountro(int key, struct tty_struct *tty) -- cgit v1.2.3-70-g09d2 From 322acf6585f3c4e82ee32a246b0483ca0f6ad3f4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 20 Oct 2008 12:33:14 +0200 Subject: fix documentation of sysrq-q really SysRq-Q also dumps information about the clockevent devices. Signed-off-by: Thomas Gleixner --- Documentation/sysrq.txt | 4 ++-- drivers/char/sysrq.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 49378a9f2b5..7b3b069c376 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -95,8 +95,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'p' - Will dump the current registers and flags to your console. -'q' - Will dump a list of all running hrtimers. - WARNING: Does not cover any other timers +'q' - Will dump per CPU lists of all armed hrtimers (not timer_list timers) + and detailed information about all clockevent devices. 'r' - Turns off keyboard raw mode and sets it to XLATE. diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index d0c0d64ed36..ce0d9da52a8 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -168,7 +168,7 @@ static void sysrq_handle_show_timers(int key, struct tty_struct *tty) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, .help_msg = "show-all-timers(Q)", - .action_msg = "Show pending hrtimers (no others)", + .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; static void sysrq_handle_mountro(int key, struct tty_struct *tty) -- cgit v1.2.3-70-g09d2 From fa07e787733416c42938a310a8e717295934e33c Mon Sep 17 00:00:00 2001 From: Lee Schermerhorn Date: Sat, 18 Oct 2008 20:26:47 -0700 Subject: doc: unevictable LRU and mlocked pages documentation Documentation for unevictable lru list and its usage. Signed-off-by: Lee Schermerhorn Signed-off-by: Rik van Riel Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/unevictable-lru.txt | 615 +++++++++++++++++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 Documentation/vm/unevictable-lru.txt (limited to 'Documentation') diff --git a/Documentation/vm/unevictable-lru.txt b/Documentation/vm/unevictable-lru.txt new file mode 100644 index 00000000000..125eed560e5 --- /dev/null +++ b/Documentation/vm/unevictable-lru.txt @@ -0,0 +1,615 @@ + +This document describes the Linux memory management "Unevictable LRU" +infrastructure and the use of this infrastructure to manage several types +of "unevictable" pages. The document attempts to provide the overall +rationale behind this mechanism and the rationale for some of the design +decisions that drove the implementation. The latter design rationale is +discussed in the context of an implementation description. Admittedly, one +can obtain the implementation details--the "what does it do?"--by reading the +code. One hopes that the descriptions below add value by provide the answer +to "why does it do that?". + +Unevictable LRU Infrastructure: + +The Unevictable LRU adds an additional LRU list to track unevictable pages +and to hide these pages from vmscan. This mechanism is based on a patch by +Larry Woodman of Red Hat to address several scalability problems with page +reclaim in Linux. The problems have been observed at customer sites on large +memory x86_64 systems. For example, a non-numal x86_64 platform with 128GB +of main memory will have over 32 million 4k pages in a single zone. When a +large fraction of these pages are not evictable for any reason [see below], +vmscan will spend a lot of time scanning the LRU lists looking for the small +fraction of pages that are evictable. This can result in a situation where +all cpus are spending 100% of their time in vmscan for hours or days on end, +with the system completely unresponsive. + +The Unevictable LRU infrastructure addresses the following classes of +unevictable pages: + ++ page owned by ramfs ++ page mapped into SHM_LOCKed shared memory regions ++ page mapped into VM_LOCKED [mlock()ed] vmas + +The infrastructure might be able to handle other conditions that make pages +unevictable, either by definition or by circumstance, in the future. + + +The Unevictable LRU List + +The Unevictable LRU infrastructure consists of an additional, per-zone, LRU list +called the "unevictable" list and an associated page flag, PG_unevictable, to +indicate that the page is being managed on the unevictable list. The +PG_unevictable flag is analogous to, and mutually exclusive with, the PG_active +flag in that it indicates on which LRU list a page resides when PG_lru is set. +The unevictable LRU list is source configurable based on the UNEVICTABLE_LRU +Kconfig option. + +The Unevictable LRU infrastructure maintains unevictable pages on an additional +LRU list for a few reasons: + +1) We get to "treat unevictable pages just like we treat other pages in the + system, which means we get to use the same code to manipulate them, the + same code to isolate them (for migrate, etc.), the same code to keep track + of the statistics, etc..." [Rik van Riel] + +2) We want to be able to migrate unevictable pages between nodes--for memory + defragmentation, workload management and memory hotplug. The linux kernel + can only migrate pages that it can successfully isolate from the lru lists. + If we were to maintain pages elsewise than on an lru-like list, where they + can be found by isolate_lru_page(), we would prevent their migration, unless + we reworked migration code to find the unevictable pages. + + +The unevictable LRU list does not differentiate between file backed and swap +backed [anon] pages. This differentiation is only important while the pages +are, in fact, evictable. + +The unevictable LRU list benefits from the "arrayification" of the per-zone +LRU lists and statistics originally proposed and posted by Christoph Lameter. + +The unevictable list does not use the lru pagevec mechanism. Rather, +unevictable pages are placed directly on the page's zone's unevictable +list under the zone lru_lock. The reason for this is to prevent stranding +of pages on the unevictable list when one task has the page isolated from the +lru and other tasks are changing the "evictability" state of the page. + + +Unevictable LRU and Memory Controller Interaction + +The memory controller data structure automatically gets a per zone unevictable +lru list as a result of the "arrayification" of the per-zone LRU lists. The +memory controller tracks the movement of pages to and from the unevictable list. +When a memory control group comes under memory pressure, the controller will +not attempt to reclaim pages on the unevictable list. This has a couple of +effects. Because the pages are "hidden" from reclaim on the unevictable list, +the reclaim process can be more efficient, dealing only with pages that have +a chance of being reclaimed. On the other hand, if too many of the pages +charged to the control group are unevictable, the evictable portion of the +working set of the tasks in the control group may not fit into the available +memory. This can cause the control group to thrash or to oom-kill tasks. + + +Unevictable LRU: Detecting Unevictable Pages + +The function page_evictable(page, vma) in vmscan.c determines whether a +page is evictable or not. For ramfs pages and pages in SHM_LOCKed regions, +page_evictable() tests a new address space flag, AS_UNEVICTABLE, in the page's +address space using a wrapper function. Wrapper functions are used to set, +clear and test the flag to reduce the requirement for #ifdef's throughout the +source code. AS_UNEVICTABLE is set on ramfs inode/mapping when it is created. +This flag remains for the life of the inode. + +For shared memory regions, AS_UNEVICTABLE is set when an application +successfully SHM_LOCKs the region and is removed when the region is +SHM_UNLOCKed. Note that shmctl(SHM_LOCK, ...) does not populate the page +tables for the region as does, for example, mlock(). So, we make no special +effort to push any pages in the SHM_LOCKed region to the unevictable list. +Vmscan will do this when/if it encounters the pages during reclaim. On +SHM_UNLOCK, shmctl() scans the pages in the region and "rescues" them from the +unevictable list if no other condition keeps them unevictable. If a SHM_LOCKed +region is destroyed, the pages are also "rescued" from the unevictable list in +the process of freeing them. + +page_evictable() detects mlock()ed pages by testing an additional page flag, +PG_mlocked via the PageMlocked() wrapper. If the page is NOT mlocked, and a +non-NULL vma is supplied, page_evictable() will check whether the vma is +VM_LOCKED via is_mlocked_vma(). is_mlocked_vma() will SetPageMlocked() and +update the appropriate statistics if the vma is VM_LOCKED. This method allows +efficient "culling" of pages in the fault path that are being faulted in to +VM_LOCKED vmas. + + +Unevictable Pages and Vmscan [shrink_*_list()] + +If unevictable pages are culled in the fault path, or moved to the unevictable +list at mlock() or mmap() time, vmscan will never encounter the pages until +they have become evictable again, for example, via munlock() and have been +"rescued" from the unevictable list. However, there may be situations where we +decide, for the sake of expediency, to leave a unevictable page on one of the +regular active/inactive LRU lists for vmscan to deal with. Vmscan checks for +such pages in all of the shrink_{active|inactive|page}_list() functions and +will "cull" such pages that it encounters--that is, it diverts those pages to +the unevictable list for the zone being scanned. + +There may be situations where a page is mapped into a VM_LOCKED vma, but the +page is not marked as PageMlocked. Such pages will make it all the way to +shrink_page_list() where they will be detected when vmscan walks the reverse +map in try_to_unmap(). If try_to_unmap() returns SWAP_MLOCK, shrink_page_list() +will cull the page at that point. + +Note that for anonymous pages, shrink_page_list() attempts to add the page to +the swap cache before it tries to unmap the page. To avoid this unnecessary +consumption of swap space, shrink_page_list() calls try_to_munlock() to check +whether any VM_LOCKED vmas map the page without attempting to unmap the page. +If try_to_munlock() returns SWAP_MLOCK, shrink_page_list() will cull the page +without consuming swap space. try_to_munlock() will be described below. + +To "cull" an unevictable page, vmscan simply puts the page back on the lru +list using putback_lru_page()--the inverse operation to isolate_lru_page()-- +after dropping the page lock. Because the condition which makes the page +unevictable may change once the page is unlocked, putback_lru_page() will +recheck the unevictable state of a page that it places on the unevictable lru +list. If the page has become unevictable, putback_lru_page() removes it from +the list and retries, including the page_unevictable() test. Because such a +race is a rare event and movement of pages onto the unevictable list should be +rare, these extra evictabilty checks should not occur in the majority of calls +to putback_lru_page(). + + +Mlocked Page: Prior Work + +The "Unevictable Mlocked Pages" infrastructure is based on work originally +posted by Nick Piggin in an RFC patch entitled "mm: mlocked pages off LRU". +Nick posted his patch as an alternative to a patch posted by Christoph +Lameter to achieve the same objective--hiding mlocked pages from vmscan. +In Nick's patch, he used one of the struct page lru list link fields as a count +of VM_LOCKED vmas that map the page. This use of the link field for a count +prevented the management of the pages on an LRU list. Thus, mlocked pages were +not migratable as isolate_lru_page() could not find them and the lru list link +field was not available to the migration subsystem. Nick resolved this by +putting mlocked pages back on the lru list before attempting to isolate them, +thus abandoning the count of VM_LOCKED vmas. When Nick's patch was integrated +with the Unevictable LRU work, the count was replaced by walking the reverse +map to determine whether any VM_LOCKED vmas mapped the page. More on this +below. + + +Mlocked Pages: Basic Management + +Mlocked pages--pages mapped into a VM_LOCKED vma--represent one class of +unevictable pages. When such a page has been "noticed" by the memory +management subsystem, the page is marked with the PG_mlocked [PageMlocked()] +flag. A PageMlocked() page will be placed on the unevictable LRU list when +it is added to the LRU. Pages can be "noticed" by memory management in +several places: + +1) in the mlock()/mlockall() system call handlers. +2) in the mmap() system call handler when mmap()ing a region with the + MAP_LOCKED flag, or mmap()ing a region in a task that has called + mlockall() with the MCL_FUTURE flag. Both of these conditions result + in the VM_LOCKED flag being set for the vma. +3) in the fault path, if mlocked pages are "culled" in the fault path, + and when a VM_LOCKED stack segment is expanded. +4) as mentioned above, in vmscan:shrink_page_list() with attempting to + reclaim a page in a VM_LOCKED vma--via try_to_unmap() or try_to_munlock(). + +Mlocked pages become unlocked and rescued from the unevictable list when: + +1) mapped in a range unlocked via the munlock()/munlockall() system calls. +2) munmapped() out of the last VM_LOCKED vma that maps the page, including + unmapping at task exit. +3) when the page is truncated from the last VM_LOCKED vma of an mmap()ed file. +4) before a page is COWed in a VM_LOCKED vma. + + +Mlocked Pages: mlock()/mlockall() System Call Handling + +Both [do_]mlock() and [do_]mlockall() system call handlers call mlock_fixup() +for each vma in the range specified by the call. In the case of mlockall(), +this is the entire active address space of the task. Note that mlock_fixup() +is used for both mlock()ing and munlock()ing a range of memory. A call to +mlock() an already VM_LOCKED vma, or to munlock() a vma that is not VM_LOCKED +is treated as a no-op--mlock_fixup() simply returns. + +If the vma passes some filtering described in "Mlocked Pages: Filtering Vmas" +below, mlock_fixup() will attempt to merge the vma with its neighbors or split +off a subset of the vma if the range does not cover the entire vma. Once the +vma has been merged or split or neither, mlock_fixup() will call +__mlock_vma_pages_range() to fault in the pages via get_user_pages() and +to mark the pages as mlocked via mlock_vma_page(). + +Note that the vma being mlocked might be mapped with PROT_NONE. In this case, +get_user_pages() will be unable to fault in the pages. That's OK. If pages +do end up getting faulted into this VM_LOCKED vma, we'll handle them in the +fault path or in vmscan. + +Also note that a page returned by get_user_pages() could be truncated or +migrated out from under us, while we're trying to mlock it. To detect +this, __mlock_vma_pages_range() tests the page_mapping after acquiring +the page lock. If the page is still associated with its mapping, we'll +go ahead and call mlock_vma_page(). If the mapping is gone, we just +unlock the page and move on. Worse case, this results in page mapped +in a VM_LOCKED vma remaining on a normal LRU list without being +PageMlocked(). Again, vmscan will detect and cull such pages. + +mlock_vma_page(), called with the page locked [N.B., not "mlocked"], will +TestSetPageMlocked() for each page returned by get_user_pages(). We use +TestSetPageMlocked() because the page might already be mlocked by another +task/vma and we don't want to do extra work. We especially do not want to +count an mlocked page more than once in the statistics. If the page was +already mlocked, mlock_vma_page() is done. + +If the page was NOT already mlocked, mlock_vma_page() attempts to isolate the +page from the LRU, as it is likely on the appropriate active or inactive list +at that time. If the isolate_lru_page() succeeds, mlock_vma_page() will +putback the page--putback_lru_page()--which will notice that the page is now +mlocked and divert the page to the zone's unevictable LRU list. If +mlock_vma_page() is unable to isolate the page from the LRU, vmscan will handle +it later if/when it attempts to reclaim the page. + + +Mlocked Pages: Filtering Special Vmas + +mlock_fixup() filters several classes of "special" vmas: + +1) vmas with VM_IO|VM_PFNMAP set are skipped entirely. The pages behind + these mappings are inherently pinned, so we don't need to mark them as + mlocked. In any case, most of the pages have no struct page in which to + so mark the page. Because of this, get_user_pages() will fail for these + vmas, so there is no sense in attempting to visit them. + +2) vmas mapping hugetlbfs page are already effectively pinned into memory. + We don't need nor want to mlock() these pages. However, to preserve the + prior behavior of mlock()--before the unevictable/mlock changes--mlock_fixup() + will call make_pages_present() in the hugetlbfs vma range to allocate the + huge pages and populate the ptes. + +3) vmas with VM_DONTEXPAND|VM_RESERVED are generally user space mappings of + kernel pages, such as the vdso page, relay channel pages, etc. These pages + are inherently unevictable and are not managed on the LRU lists. + mlock_fixup() treats these vmas the same as hugetlbfs vmas. It calls + make_pages_present() to populate the ptes. + +Note that for all of these special vmas, mlock_fixup() does not set the +VM_LOCKED flag. Therefore, we won't have to deal with them later during +munlock() or munmap()--for example, at task exit. Neither does mlock_fixup() +account these vmas against the task's "locked_vm". + +Mlocked Pages: Downgrading the Mmap Semaphore. + +mlock_fixup() must be called with the mmap semaphore held for write, because +it may have to merge or split vmas. However, mlocking a large region of +memory can take a long time--especially if vmscan must reclaim pages to +satisfy the regions requirements. Faulting in a large region with the mmap +semaphore held for write can hold off other faults on the address space, in +the case of a multi-threaded task. It can also hold off scans of the task's +address space via /proc. While testing under heavy load, it was observed that +the ps(1) command could be held off for many minutes while a large segment was +mlock()ed down. + +To address this issue, and to make the system more responsive during mlock()ing +of large segments, mlock_fixup() downgrades the mmap semaphore to read mode +during the call to __mlock_vma_pages_range(). This works fine. However, the +callers of mlock_fixup() expect the semaphore to be returned in write mode. +So, mlock_fixup() "upgrades" the semphore to write mode. Linux does not +support an atomic upgrade_sem() call, so mlock_fixup() must drop the semaphore +and reacquire it in write mode. In a multi-threaded task, it is possible for +the task memory map to change while the semaphore is dropped. Therefore, +mlock_fixup() looks up the vma at the range start address after reacquiring +the semaphore in write mode and verifies that it still covers the original +range. If not, mlock_fixup() returns an error [-EAGAIN]. All callers of +mlock_fixup() have been changed to deal with this new error condition. + +Note: when munlocking a region, all of the pages should already be resident-- +unless we have racing threads mlocking() and munlocking() regions. So, +unlocking should not have to wait for page allocations nor faults of any kind. +Therefore mlock_fixup() does not downgrade the semaphore for munlock(). + + +Mlocked Pages: munlock()/munlockall() System Call Handling + +The munlock() and munlockall() system calls are handled by the same functions-- +do_mlock[all]()--as the mlock() and mlockall() system calls with the unlock +vs lock operation indicated by an argument. So, these system calls are also +handled by mlock_fixup(). Again, if called for an already munlock()ed vma, +mlock_fixup() simply returns. Because of the vma filtering discussed above, +VM_LOCKED will not be set in any "special" vmas. So, these vmas will be +ignored for munlock. + +If the vma is VM_LOCKED, mlock_fixup() again attempts to merge or split off +the specified range. The range is then munlocked via the function +__mlock_vma_pages_range()--the same function used to mlock a vma range-- +passing a flag to indicate that munlock() is being performed. + +Because the vma access protections could have been changed to PROT_NONE after +faulting in and mlocking some pages, get_user_pages() was unreliable for visiting +these pages for munlocking. Because we don't want to leave pages mlocked(), +get_user_pages() was enhanced to accept a flag to ignore the permissions when +fetching the pages--all of which should be resident as a result of previous +mlock()ing. + +For munlock(), __mlock_vma_pages_range() unlocks individual pages by calling +munlock_vma_page(). munlock_vma_page() unconditionally clears the PG_mlocked +flag using TestClearPageMlocked(). As with mlock_vma_page(), munlock_vma_page() +use the Test*PageMlocked() function to handle the case where the page might +have already been unlocked by another task. If the page was mlocked, +munlock_vma_page() updates that zone statistics for the number of mlocked +pages. Note, however, that at this point we haven't checked whether the page +is mapped by other VM_LOCKED vmas. + +We can't call try_to_munlock(), the function that walks the reverse map to check +for other VM_LOCKED vmas, without first isolating the page from the LRU. +try_to_munlock() is a variant of try_to_unmap() and thus requires that the page +not be on an lru list. [More on these below.] However, the call to +isolate_lru_page() could fail, in which case we couldn't try_to_munlock(). +So, we go ahead and clear PG_mlocked up front, as this might be the only chance +we have. If we can successfully isolate the page, we go ahead and +try_to_munlock(), which will restore the PG_mlocked flag and update the zone +page statistics if it finds another vma holding the page mlocked. If we fail +to isolate the page, we'll have left a potentially mlocked page on the LRU. +This is fine, because we'll catch it later when/if vmscan tries to reclaim the +page. This should be relatively rare. + +Mlocked Pages: Migrating Them... + +A page that is being migrated has been isolated from the lru lists and is +held locked across unmapping of the page, updating the page's mapping +[address_space] entry and copying the contents and state, until the +page table entry has been replaced with an entry that refers to the new +page. Linux supports migration of mlocked pages and other unevictable +pages. This involves simply moving the PageMlocked and PageUnevictable states +from the old page to the new page. + +Note that page migration can race with mlocking or munlocking of the same +page. This has been discussed from the mlock/munlock perspective in the +respective sections above. Both processes [migration, m[un]locking], hold +the page locked. This provides the first level of synchronization. Page +migration zeros out the page_mapping of the old page before unlocking it, +so m[un]lock can skip these pages by testing the page mapping under page +lock. + +When completing page migration, we place the new and old pages back onto the +lru after dropping the page lock. The "unneeded" page--old page on success, +new page on failure--will be freed when the reference count held by the +migration process is released. To ensure that we don't strand pages on the +unevictable list because of a race between munlock and migration, page +migration uses the putback_lru_page() function to add migrated pages back to +the lru. + + +Mlocked Pages: mmap(MAP_LOCKED) System Call Handling + +In addition the the mlock()/mlockall() system calls, an application can request +that a region of memory be mlocked using the MAP_LOCKED flag with the mmap() +call. Furthermore, any mmap() call or brk() call that expands the heap by a +task that has previously called mlockall() with the MCL_FUTURE flag will result +in the newly mapped memory being mlocked. Before the unevictable/mlock changes, +the kernel simply called make_pages_present() to allocate pages and populate +the page table. + +To mlock a range of memory under the unevictable/mlock infrastructure, the +mmap() handler and task address space expansion functions call +mlock_vma_pages_range() specifying the vma and the address range to mlock. +mlock_vma_pages_range() filters vmas like mlock_fixup(), as described above in +"Mlocked Pages: Filtering Vmas". It will clear the VM_LOCKED flag, which will +have already been set by the caller, in filtered vmas. Thus these vma's need +not be visited for munlock when the region is unmapped. + +For "normal" vmas, mlock_vma_pages_range() calls __mlock_vma_pages_range() to +fault/allocate the pages and mlock them. Again, like mlock_fixup(), +mlock_vma_pages_range() downgrades the mmap semaphore to read mode before +attempting to fault/allocate and mlock the pages; and "upgrades" the semaphore +back to write mode before returning. + +The callers of mlock_vma_pages_range() will have already added the memory +range to be mlocked to the task's "locked_vm". To account for filtered vmas, +mlock_vma_pages_range() returns the number of pages NOT mlocked. All of the +callers then subtract a non-negative return value from the task's locked_vm. +A negative return value represent an error--for example, from get_user_pages() +attempting to fault in a vma with PROT_NONE access. In this case, we leave +the memory range accounted as locked_vm, as the protections could be changed +later and pages allocated into that region. + + +Mlocked Pages: munmap()/exit()/exec() System Call Handling + +When unmapping an mlocked region of memory, whether by an explicit call to +munmap() or via an internal unmap from exit() or exec() processing, we must +munlock the pages if we're removing the last VM_LOCKED vma that maps the pages. +Before the unevictable/mlock changes, mlocking did not mark the pages in any way, +so unmapping them required no processing. + +To munlock a range of memory under the unevictable/mlock infrastructure, the +munmap() hander and task address space tear down function call +munlock_vma_pages_all(). The name reflects the observation that one always +specifies the entire vma range when munlock()ing during unmap of a region. +Because of the vma filtering when mlocking() regions, only "normal" vmas that +actually contain mlocked pages will be passed to munlock_vma_pages_all(). + +munlock_vma_pages_all() clears the VM_LOCKED vma flag and, like mlock_fixup() +for the munlock case, calls __munlock_vma_pages_range() to walk the page table +for the vma's memory range and munlock_vma_page() each resident page mapped by +the vma. This effectively munlocks the page, only if this is the last +VM_LOCKED vma that maps the page. + + +Mlocked Page: try_to_unmap() + +[Note: the code changes represented by this section are really quite small +compared to the text to describe what happening and why, and to discuss the +implications.] + +Pages can, of course, be mapped into multiple vmas. Some of these vmas may +have VM_LOCKED flag set. It is possible for a page mapped into one or more +VM_LOCKED vmas not to have the PG_mlocked flag set and therefore reside on one +of the active or inactive LRU lists. This could happen if, for example, a +task in the process of munlock()ing the page could not isolate the page from +the LRU. As a result, vmscan/shrink_page_list() might encounter such a page +as described in "Unevictable Pages and Vmscan [shrink_*_list()]". To +handle this situation, try_to_unmap() has been enhanced to check for VM_LOCKED +vmas while it is walking a page's reverse map. + +try_to_unmap() is always called, by either vmscan for reclaim or for page +migration, with the argument page locked and isolated from the LRU. BUG_ON() +assertions enforce this requirement. Separate functions handle anonymous and +mapped file pages, as these types of pages have different reverse map +mechanisms. + + try_to_unmap_anon() + +To unmap anonymous pages, each vma in the list anchored in the anon_vma must be +visited--at least until a VM_LOCKED vma is encountered. If the page is being +unmapped for migration, VM_LOCKED vmas do not stop the process because mlocked +pages are migratable. However, for reclaim, if the page is mapped into a +VM_LOCKED vma, the scan stops. try_to_unmap() attempts to acquire the mmap +semphore of the mm_struct to which the vma belongs in read mode. If this is +successful, try_to_unmap() will mlock the page via mlock_vma_page()--we +wouldn't have gotten to try_to_unmap() if the page were already mlocked--and +will return SWAP_MLOCK, indicating that the page is unevictable. If the +mmap semaphore cannot be acquired, we are not sure whether the page is really +unevictable or not. In this case, try_to_unmap() will return SWAP_AGAIN. + + try_to_unmap_file() -- linear mappings + +Unmapping of a mapped file page works the same, except that the scan visits +all vmas that maps the page's index/page offset in the page's mapping's +reverse map priority search tree. It must also visit each vma in the page's +mapping's non-linear list, if the list is non-empty. As for anonymous pages, +on encountering a VM_LOCKED vma for a mapped file page, try_to_unmap() will +attempt to acquire the associated mm_struct's mmap semaphore to mlock the page, +returning SWAP_MLOCK if this is successful, and SWAP_AGAIN, if not. + + try_to_unmap_file() -- non-linear mappings + +If a page's mapping contains a non-empty non-linear mapping vma list, then +try_to_un{map|lock}() must also visit each vma in that list to determine +whether the page is mapped in a VM_LOCKED vma. Again, the scan must visit +all vmas in the non-linear list to ensure that the pages is not/should not be +mlocked. If a VM_LOCKED vma is found in the list, the scan could terminate. +However, there is no easy way to determine whether the page is actually mapped +in a given vma--either for unmapping or testing whether the VM_LOCKED vma +actually pins the page. + +So, try_to_unmap_file() handles non-linear mappings by scanning a certain +number of pages--a "cluster"--in each non-linear vma associated with the page's +mapping, for each file mapped page that vmscan tries to unmap. If this happens +to unmap the page we're trying to unmap, try_to_unmap() will notice this on +return--(page_mapcount(page) == 0)--and return SWAP_SUCCESS. Otherwise, it +will return SWAP_AGAIN, causing vmscan to recirculate this page. We take +advantage of the cluster scan in try_to_unmap_cluster() as follows: + +For each non-linear vma, try_to_unmap_cluster() attempts to acquire the mmap +semaphore of the associated mm_struct for read without blocking. If this +attempt is successful and the vma is VM_LOCKED, try_to_unmap_cluster() will +retain the mmap semaphore for the scan; otherwise it drops it here. Then, +for each page in the cluster, if we're holding the mmap semaphore for a locked +vma, try_to_unmap_cluster() calls mlock_vma_page() to mlock the page. This +call is a no-op if the page is already locked, but will mlock any pages in +the non-linear mapping that happen to be unlocked. If one of the pages so +mlocked is the page passed in to try_to_unmap(), try_to_unmap_cluster() will +return SWAP_MLOCK, rather than the default SWAP_AGAIN. This will allow vmscan +to cull the page, rather than recirculating it on the inactive list. Again, +if try_to_unmap_cluster() cannot acquire the vma's mmap sem, it returns +SWAP_AGAIN, indicating that the page is mapped by a VM_LOCKED vma, but +couldn't be mlocked. + + +Mlocked pages: try_to_munlock() Reverse Map Scan + +TODO/FIXME: a better name might be page_mlocked()--analogous to the +page_referenced() reverse map walker--especially if we continue to call this +from shrink_page_list(). See related TODO/FIXME below. + +When munlock_vma_page()--see "Mlocked Pages: munlock()/munlockall() System +Call Handling" above--tries to munlock a page, or when shrink_page_list() +encounters an anonymous page that is not yet in the swap cache, they need to +determine whether or not the page is mapped by any VM_LOCKED vma, without +actually attempting to unmap all ptes from the page. For this purpose, the +unevictable/mlock infrastructure introduced a variant of try_to_unmap() called +try_to_munlock(). + +try_to_munlock() calls the same functions as try_to_unmap() for anonymous and +mapped file pages with an additional argument specifing unlock versus unmap +processing. Again, these functions walk the respective reverse maps looking +for VM_LOCKED vmas. When such a vma is found for anonymous pages and file +pages mapped in linear VMAs, as in the try_to_unmap() case, the functions +attempt to acquire the associated mmap semphore, mlock the page via +mlock_vma_page() and return SWAP_MLOCK. This effectively undoes the +pre-clearing of the page's PG_mlocked done by munlock_vma_page() and informs +shrink_page_list() that the anonymous page should be culled rather than added +to the swap cache in preparation for a try_to_unmap() that will almost +certainly fail. + +If try_to_unmap() is unable to acquire a VM_LOCKED vma's associated mmap +semaphore, it will return SWAP_AGAIN. This will allow shrink_page_list() +to recycle the page on the inactive list and hope that it has better luck +with the page next time. + +For file pages mapped into non-linear vmas, the try_to_munlock() logic works +slightly differently. On encountering a VM_LOCKED non-linear vma that might +map the page, try_to_munlock() returns SWAP_AGAIN without actually mlocking +the page. munlock_vma_page() will just leave the page unlocked and let +vmscan deal with it--the usual fallback position. + +Note that try_to_munlock()'s reverse map walk must visit every vma in a pages' +reverse map to determine that a page is NOT mapped into any VM_LOCKED vma. +However, the scan can terminate when it encounters a VM_LOCKED vma and can +successfully acquire the vma's mmap semphore for read and mlock the page. +Although try_to_munlock() can be called many [very many!] times when +munlock()ing a large region or tearing down a large address space that has been +mlocked via mlockall(), overall this is a fairly rare event. In addition, +although shrink_page_list() calls try_to_munlock() for every anonymous page that +it handles that is not yet in the swap cache, on average anonymous pages will +have very short reverse map lists. + +Mlocked Page: Page Reclaim in shrink_*_list() + +shrink_active_list() culls any obviously unevictable pages--i.e., +!page_evictable(page, NULL)--diverting these to the unevictable lru +list. However, shrink_active_list() only sees unevictable pages that +made it onto the active/inactive lru lists. Note that these pages do not +have PageUnevictable set--otherwise, they would be on the unevictable list and +shrink_active_list would never see them. + +Some examples of these unevictable pages on the LRU lists are: + +1) ramfs pages that have been placed on the lru lists when first allocated. + +2) SHM_LOCKed shared memory pages. shmctl(SHM_LOCK) does not attempt to + allocate or fault in the pages in the shared memory region. This happens + when an application accesses the page the first time after SHM_LOCKing + the segment. + +3) Mlocked pages that could not be isolated from the lru and moved to the + unevictable list in mlock_vma_page(). + +3) Pages mapped into multiple VM_LOCKED vmas, but try_to_munlock() couldn't + acquire the vma's mmap semaphore to test the flags and set PageMlocked. + munlock_vma_page() was forced to let the page back on to the normal + LRU list for vmscan to handle. + +shrink_inactive_list() also culls any unevictable pages that it finds +on the inactive lists, again diverting them to the appropriate zone's unevictable +lru list. shrink_inactive_list() should only see SHM_LOCKed pages that became +SHM_LOCKed after shrink_active_list() had moved them to the inactive list, or +pages mapped into VM_LOCKED vmas that munlock_vma_page() couldn't isolate from +the lru to recheck via try_to_munlock(). shrink_inactive_list() won't notice +the latter, but will pass on to shrink_page_list(). + +shrink_page_list() again culls obviously unevictable pages that it could +encounter for similar reason to shrink_inactive_list(). As already discussed, +shrink_page_list() proactively looks for anonymous pages that should have +PG_mlocked set but don't--these would not be detected by page_evictable()--to +avoid adding them to the swap cache unnecessarily. File pages mapped into +VM_LOCKED vmas but without PG_mlocked set will make it all the way to +try_to_unmap(). shrink_page_list() will divert them to the unevictable list when +try_to_unmap() returns SWAP_MLOCK, as discussed above. + +TODO/FIXME: If we can enhance the swap cache to reliably remove entries +with page_count(page) > 2, as long as all ptes are mapped to the page and +not the swap entry, we can probably remove the call to try_to_munlock() in +shrink_page_list() and just remove the page from the swap cache when +try_to_unmap() returns SWAP_MLOCK. Currently, remove_exclusive_swap_page() +doesn't seem to allow that. + + -- cgit v1.2.3-70-g09d2 From e575f111dc0f27044e170580e7de50985ab3e011 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Sat, 18 Oct 2008 20:27:08 -0700 Subject: coredump_filter: add hugepage dumping Presently hugepage's vma has a VM_RESERVED flag in order not to be swapped. But a VM_RESERVED vma isn't core dumped because this flag is often used for some kernel vmas (e.g. vmalloc, sound related). Thus hugepages are never dumped and it can't be debugged easily. Many developers want hugepages to be included into core-dump. However, We can't read generic VM_RESERVED area because this area is often IO mapping area. then these area reading may change device state. it is definitly undesiable side-effect. So adding a hugepage specific bit to the coredump filter is better. It will be able to hugepage core dumping and doesn't cause any side-effect to any i/o devices. In additional, libhugetlb use hugetlb private mapping pages as anonymous page. Then, hugepage private mapping pages should be core dumped by default. Then, /proc/[pid]/core_dump_filter has two new bits. - bit 5 mean hugetlb private mapping pages are dumped or not. (default: yes) - bit 6 mean hugetlb shared mapping pages are dumped or not. (default: no) I tested by following method. % ulimit -c unlimited % ./crash_hugepage 50 % ./crash_hugepage 50 -p % ls -lh % gdb ./crash_hugepage core % % echo 0x43 > /proc/self/coredump_filter % ./crash_hugepage 50 % ./crash_hugepage 50 -p % ls -lh % gdb ./crash_hugepage core #include #include #include #include #include #include "hugetlbfs.h" int main(int argc, char** argv){ char* p; int ch; int mmap_flags = MAP_SHARED; int fd; int nr_pages; while((ch = getopt(argc, argv, "p")) != -1) { switch (ch) { case 'p': mmap_flags &= ~MAP_SHARED; mmap_flags |= MAP_PRIVATE; break; default: /* nothing*/ break; } } argc -= optind; argv += optind; if (argc == 0){ printf("need # of pages\n"); exit(1); } nr_pages = atoi(argv[0]); if (nr_pages < 2) { printf("nr_pages must >2\n"); exit(1); } fd = hugetlbfs_unlinked_fd(); p = mmap(NULL, nr_pages * gethugepagesize(), PROT_READ|PROT_WRITE, mmap_flags, fd, 0); sleep(2); *(p + gethugepagesize()) = 1; /* COW */ sleep(2); /* crash! */ *(int*)0 = 1; return 0; } Signed-off-by: KOSAKI Motohiro Reviewed-by: Kawai Hidehiro Cc: Hugh Dickins Cc: William Irwin Cc: Adam Litke Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 15 ++++++++++----- fs/binfmt_elf.c | 12 ++++++++++-- include/linux/sched.h | 7 +++++-- 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index c032bf39e8b..02cb7faeed6 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -2412,24 +2412,29 @@ will be dumped when the process is dumped. coredump_filter is a bitmask of memory types. If a bit of the bitmask is set, memory segments of the corresponding memory type are dumped, otherwise they are not dumped. -The following 4 memory types are supported: +The following 7 memory types are supported: - (bit 0) anonymous private memory - (bit 1) anonymous shared memory - (bit 2) file-backed private memory - (bit 3) file-backed shared memory - (bit 4) ELF header pages in file-backed private memory areas (it is effective only if the bit 2 is cleared) + - (bit 5) hugetlb private memory + - (bit 6) hugetlb shared memory Note that MMIO pages such as frame buffer are never dumped and vDSO pages are always dumped regardless of the bitmask status. -Default value of coredump_filter is 0x3; this means all anonymous memory -segments are dumped. + Note bit 0-4 doesn't effect any hugetlb memory. hugetlb memory are only + effected by bit 5-6. + +Default value of coredump_filter is 0x23; this means all anonymous memory +segments and hugetlb private memory are dumped. If you don't want to dump all shared memory segments attached to pid 1234, -write 1 to the process's proc file. +write 0x21 to the process's proc file. - $ echo 0x1 > /proc/1234/coredump_filter + $ echo 0x21 > /proc/1234/coredump_filter When a new process is created, the process inherits the bitmask status from its parent. It is useful to set up coredump_filter before the program runs. diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index c76afa26edf..e2159063198 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1156,16 +1156,24 @@ static int dump_seek(struct file *file, loff_t off) static unsigned long vma_dump_size(struct vm_area_struct *vma, unsigned long mm_flags) { +#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type)) + /* The vma can be set up to tell us the answer directly. */ if (vma->vm_flags & VM_ALWAYSDUMP) goto whole; + /* Hugetlb memory check */ + if (vma->vm_flags & VM_HUGETLB) { + if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED)) + goto whole; + if (!(vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_PRIVATE)) + goto whole; + } + /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; -#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type)) - /* By default, dump shared memory if mapped from an anonymous file. */ if (vma->vm_flags & VM_SHARED) { if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0 ? diff --git a/include/linux/sched.h b/include/linux/sched.h index c226c7b8294..017cc914ef1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -403,12 +403,15 @@ extern int get_dumpable(struct mm_struct *mm); #define MMF_DUMP_MAPPED_PRIVATE 4 #define MMF_DUMP_MAPPED_SHARED 5 #define MMF_DUMP_ELF_HEADERS 6 +#define MMF_DUMP_HUGETLB_PRIVATE 7 +#define MMF_DUMP_HUGETLB_SHARED 8 #define MMF_DUMP_FILTER_SHIFT MMF_DUMPABLE_BITS -#define MMF_DUMP_FILTER_BITS 5 +#define MMF_DUMP_FILTER_BITS 7 #define MMF_DUMP_FILTER_MASK \ (((1 << MMF_DUMP_FILTER_BITS) - 1) << MMF_DUMP_FILTER_SHIFT) #define MMF_DUMP_FILTER_DEFAULT \ - ((1 << MMF_DUMP_ANON_PRIVATE) | (1 << MMF_DUMP_ANON_SHARED)) + ((1 << MMF_DUMP_ANON_PRIVATE) | (1 << MMF_DUMP_ANON_SHARED) |\ + (1 << MMF_DUMP_HUGETLB_PRIVATE)) struct sighand_struct { atomic_t count; -- cgit v1.2.3-70-g09d2 From 7a6560e02556b6f0a798c247f3a557c523d9701b Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Sat, 18 Oct 2008 20:27:13 -0700 Subject: documentation: clarify dirty_ratio and dirty_background_ratio description The current documentation of dirty_ratio and dirty_background_ratio is a bit misleading. In the documentation we say that they are "a percentage of total system memory", but the current page writeback policy, intead, is to apply the percentages to the dirtyable memory, that means free pages + reclaimable pages. Better to be more explicit to clarify this concept. Signed-off-by: Andrea Righi Signed-off-by: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 02cb7faeed6..bcceb99b81d 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1384,15 +1384,18 @@ causes the kernel to prefer to reclaim dentries and inodes. dirty_background_ratio ---------------------- -Contains, as a percentage of total system memory, the number of pages at which -the pdflush background writeback daemon will start writing out dirty data. +Contains, as a percentage of the dirtyable system memory (free pages + mapped +pages + file cache, not including locked pages and HugePages), the number of +pages at which the pdflush background writeback daemon will start writing out +dirty data. dirty_ratio ----------------- -Contains, as a percentage of total system memory, the number of pages at which -a process which is generating disk writes will itself start writing out dirty -data. +Contains, as a percentage of the dirtyable system memory (free pages + mapped +pages + file cache, not including locked pages and HugePages), the number of +pages at which a process which is generating disk writes will itself start +writing out dirty data. dirty_writeback_centisecs ------------------------- -- cgit v1.2.3-70-g09d2 From bde5ab65581a63e9f4f4bacfae8f201d04d25bed Mon Sep 17 00:00:00 2001 From: Matt Helsley Date: Sat, 18 Oct 2008 20:27:24 -0700 Subject: container freezer: document the cgroup freezer subsystem. Describe why we need the freezer subsystem and how to use it in a documentation file. Since the cgroups.txt file is focused on the subsystem-agnostic portions of cgroups make a directory and move the old cgroups.txt file at the same time. Signed-off-by: Matt Helsley Cc: Paul Menage Cc: containers@lists.linux-foundation.org Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups.txt | 548 ---------------------------- Documentation/cgroups/cgroups.txt | 548 ++++++++++++++++++++++++++++ Documentation/cgroups/freezer-subsystem.txt | 99 +++++ Documentation/cpusets.txt | 2 +- 4 files changed, 648 insertions(+), 549 deletions(-) delete mode 100644 Documentation/cgroups.txt create mode 100644 Documentation/cgroups/cgroups.txt create mode 100644 Documentation/cgroups/freezer-subsystem.txt (limited to 'Documentation') diff --git a/Documentation/cgroups.txt b/Documentation/cgroups.txt deleted file mode 100644 index d9014aa0eb6..00000000000 --- a/Documentation/cgroups.txt +++ /dev/null @@ -1,548 +0,0 @@ - CGROUPS - ------- - -Written by Paul Menage based on Documentation/cpusets.txt - -Original copyright statements from cpusets.txt: -Portions Copyright (C) 2004 BULL SA. -Portions Copyright (c) 2004-2006 Silicon Graphics, Inc. -Modified by Paul Jackson -Modified by Christoph Lameter - -CONTENTS: -========= - -1. Control Groups - 1.1 What are cgroups ? - 1.2 Why are cgroups needed ? - 1.3 How are cgroups implemented ? - 1.4 What does notify_on_release do ? - 1.5 How do I use cgroups ? -2. Usage Examples and Syntax - 2.1 Basic Usage - 2.2 Attaching processes -3. Kernel API - 3.1 Overview - 3.2 Synchronization - 3.3 Subsystem API -4. Questions - -1. Control Groups -================= - -1.1 What are cgroups ? ----------------------- - -Control Groups provide a mechanism for aggregating/partitioning sets of -tasks, and all their future children, into hierarchical groups with -specialized behaviour. - -Definitions: - -A *cgroup* associates a set of tasks with a set of parameters for one -or more subsystems. - -A *subsystem* is a module that makes use of the task grouping -facilities provided by cgroups to treat groups of tasks in -particular ways. A subsystem is typically a "resource controller" that -schedules a resource or applies per-cgroup limits, but it may be -anything that wants to act on a group of processes, e.g. a -virtualization subsystem. - -A *hierarchy* is a set of cgroups arranged in a tree, such that -every task in the system is in exactly one of the cgroups in the -hierarchy, and a set of subsystems; each subsystem has system-specific -state attached to each cgroup in the hierarchy. Each hierarchy has -an instance of the cgroup virtual filesystem associated with it. - -At any one time there may be multiple active hierachies of task -cgroups. Each hierarchy is a partition of all tasks in the system. - -User level code may create and destroy cgroups by name in an -instance of the cgroup virtual file system, specify and query to -which cgroup a task is assigned, and list the task pids assigned to -a cgroup. Those creations and assignments only affect the hierarchy -associated with that instance of the cgroup file system. - -On their own, the only use for cgroups is for simple job -tracking. The intention is that other subsystems hook into the generic -cgroup support to provide new attributes for cgroups, such as -accounting/limiting the resources which processes in a cgroup can -access. For example, cpusets (see Documentation/cpusets.txt) allows -you to associate a set of CPUs and a set of memory nodes with the -tasks in each cgroup. - -1.2 Why are cgroups needed ? ----------------------------- - -There are multiple efforts to provide process aggregations in the -Linux kernel, mainly for resource tracking purposes. Such efforts -include cpusets, CKRM/ResGroups, UserBeanCounters, and virtual server -namespaces. These all require the basic notion of a -grouping/partitioning of processes, with newly forked processes ending -in the same group (cgroup) as their parent process. - -The kernel cgroup patch provides the minimum essential kernel -mechanisms required to efficiently implement such groups. It has -minimal impact on the system fast paths, and provides hooks for -specific subsystems such as cpusets to provide additional behaviour as -desired. - -Multiple hierarchy support is provided to allow for situations where -the division of tasks into cgroups is distinctly different for -different subsystems - having parallel hierarchies allows each -hierarchy to be a natural division of tasks, without having to handle -complex combinations of tasks that would be present if several -unrelated subsystems needed to be forced into the same tree of -cgroups. - -At one extreme, each resource controller or subsystem could be in a -separate hierarchy; at the other extreme, all subsystems -would be attached to the same hierarchy. - -As an example of a scenario (originally proposed by vatsa@in.ibm.com) -that can benefit from multiple hierarchies, consider a large -university server with various users - students, professors, system -tasks etc. The resource planning for this server could be along the -following lines: - - CPU : Top cpuset - / \ - CPUSet1 CPUSet2 - | | - (Profs) (Students) - - In addition (system tasks) are attached to topcpuset (so - that they can run anywhere) with a limit of 20% - - Memory : Professors (50%), students (30%), system (20%) - - Disk : Prof (50%), students (30%), system (20%) - - Network : WWW browsing (20%), Network File System (60%), others (20%) - / \ - Prof (15%) students (5%) - -Browsers like firefox/lynx go into the WWW network class, while (k)nfsd go -into NFS network class. - -At the same time firefox/lynx will share an appropriate CPU/Memory class -depending on who launched it (prof/student). - -With the ability to classify tasks differently for different resources -(by putting those resource subsystems in different hierarchies) then -the admin can easily set up a script which receives exec notifications -and depending on who is launching the browser he can - - # echo browser_pid > /mnt///tasks - -With only a single hierarchy, he now would potentially have to create -a separate cgroup for every browser launched and associate it with -approp network and other resource class. This may lead to -proliferation of such cgroups. - -Also lets say that the administrator would like to give enhanced network -access temporarily to a student's browser (since it is night and the user -wants to do online gaming :)) OR give one of the students simulation -apps enhanced CPU power, - -With ability to write pids directly to resource classes, it's just a -matter of : - - # echo pid > /mnt/network//tasks - (after some time) - # echo pid > /mnt/network//tasks - -Without this ability, he would have to split the cgroup into -multiple separate ones and then associate the new cgroups with the -new resource classes. - - - -1.3 How are cgroups implemented ? ---------------------------------- - -Control Groups extends the kernel as follows: - - - Each task in the system has a reference-counted pointer to a - css_set. - - - A css_set contains a set of reference-counted pointers to - cgroup_subsys_state objects, one for each cgroup subsystem - registered in the system. There is no direct link from a task to - the cgroup of which it's a member in each hierarchy, but this - can be determined by following pointers through the - cgroup_subsys_state objects. This is because accessing the - subsystem state is something that's expected to happen frequently - and in performance-critical code, whereas operations that require a - task's actual cgroup assignments (in particular, moving between - cgroups) are less common. A linked list runs through the cg_list - field of each task_struct using the css_set, anchored at - css_set->tasks. - - - A cgroup hierarchy filesystem can be mounted for browsing and - manipulation from user space. - - - You can list all the tasks (by pid) attached to any cgroup. - -The implementation of cgroups requires a few, simple hooks -into the rest of the kernel, none in performance critical paths: - - - in init/main.c, to initialize the root cgroups and initial - css_set at system boot. - - - in fork and exit, to attach and detach a task from its css_set. - -In addition a new file system, of type "cgroup" may be mounted, to -enable browsing and modifying the cgroups presently known to the -kernel. When mounting a cgroup hierarchy, you may specify a -comma-separated list of subsystems to mount as the filesystem mount -options. By default, mounting the cgroup filesystem attempts to -mount a hierarchy containing all registered subsystems. - -If an active hierarchy with exactly the same set of subsystems already -exists, it will be reused for the new mount. If no existing hierarchy -matches, and any of the requested subsystems are in use in an existing -hierarchy, the mount will fail with -EBUSY. Otherwise, a new hierarchy -is activated, associated with the requested subsystems. - -It's not currently possible to bind a new subsystem to an active -cgroup hierarchy, or to unbind a subsystem from an active cgroup -hierarchy. This may be possible in future, but is fraught with nasty -error-recovery issues. - -When a cgroup filesystem is unmounted, if there are any -child cgroups created below the top-level cgroup, that hierarchy -will remain active even though unmounted; if there are no -child cgroups then the hierarchy will be deactivated. - -No new system calls are added for cgroups - all support for -querying and modifying cgroups is via this cgroup file system. - -Each task under /proc has an added file named 'cgroup' displaying, -for each active hierarchy, the subsystem names and the cgroup name -as the path relative to the root of the cgroup file system. - -Each cgroup is represented by a directory in the cgroup file system -containing the following files describing that cgroup: - - - tasks: list of tasks (by pid) attached to that cgroup - - releasable flag: cgroup currently removeable? - - notify_on_release flag: run the release agent on exit? - - release_agent: the path to use for release notifications (this file - exists in the top cgroup only) - -Other subsystems such as cpusets may add additional files in each -cgroup dir. - -New cgroups are created using the mkdir system call or shell -command. The properties of a cgroup, such as its flags, are -modified by writing to the appropriate file in that cgroups -directory, as listed above. - -The named hierarchical structure of nested cgroups allows partitioning -a large system into nested, dynamically changeable, "soft-partitions". - -The attachment of each task, automatically inherited at fork by any -children of that task, to a cgroup allows organizing the work load -on a system into related sets of tasks. A task may be re-attached to -any other cgroup, if allowed by the permissions on the necessary -cgroup file system directories. - -When a task is moved from one cgroup to another, it gets a new -css_set pointer - if there's an already existing css_set with the -desired collection of cgroups then that group is reused, else a new -css_set is allocated. Note that the current implementation uses a -linear search to locate an appropriate existing css_set, so isn't -very efficient. A future version will use a hash table for better -performance. - -To allow access from a cgroup to the css_sets (and hence tasks) -that comprise it, a set of cg_cgroup_link objects form a lattice; -each cg_cgroup_link is linked into a list of cg_cgroup_links for -a single cgroup on its cgrp_link_list field, and a list of -cg_cgroup_links for a single css_set on its cg_link_list. - -Thus the set of tasks in a cgroup can be listed by iterating over -each css_set that references the cgroup, and sub-iterating over -each css_set's task set. - -The use of a Linux virtual file system (vfs) to represent the -cgroup hierarchy provides for a familiar permission and name space -for cgroups, with a minimum of additional kernel code. - -1.4 What does notify_on_release do ? ------------------------------------- - -If the notify_on_release flag is enabled (1) in a cgroup, then -whenever the last task in the cgroup leaves (exits or attaches to -some other cgroup) and the last child cgroup of that cgroup -is removed, then the kernel runs the command specified by the contents -of the "release_agent" file in that hierarchy's root directory, -supplying the pathname (relative to the mount point of the cgroup -file system) of the abandoned cgroup. This enables automatic -removal of abandoned cgroups. The default value of -notify_on_release in the root cgroup at system boot is disabled -(0). The default value of other cgroups at creation is the current -value of their parents notify_on_release setting. The default value of -a cgroup hierarchy's release_agent path is empty. - -1.5 How do I use cgroups ? --------------------------- - -To start a new job that is to be contained within a cgroup, using -the "cpuset" cgroup subsystem, the steps are something like: - - 1) mkdir /dev/cgroup - 2) mount -t cgroup -ocpuset cpuset /dev/cgroup - 3) Create the new cgroup by doing mkdir's and write's (or echo's) in - the /dev/cgroup virtual file system. - 4) Start a task that will be the "founding father" of the new job. - 5) Attach that task to the new cgroup by writing its pid to the - /dev/cgroup tasks file for that cgroup. - 6) fork, exec or clone the job tasks from this founding father task. - -For example, the following sequence of commands will setup a cgroup -named "Charlie", containing just CPUs 2 and 3, and Memory Node 1, -and then start a subshell 'sh' in that cgroup: - - mount -t cgroup cpuset -ocpuset /dev/cgroup - cd /dev/cgroup - mkdir Charlie - cd Charlie - /bin/echo 2-3 > cpuset.cpus - /bin/echo 1 > cpuset.mems - /bin/echo $$ > tasks - sh - # The subshell 'sh' is now running in cgroup Charlie - # The next line should display '/Charlie' - cat /proc/self/cgroup - -2. Usage Examples and Syntax -============================ - -2.1 Basic Usage ---------------- - -Creating, modifying, using the cgroups can be done through the cgroup -virtual filesystem. - -To mount a cgroup hierarchy will all available subsystems, type: -# mount -t cgroup xxx /dev/cgroup - -The "xxx" is not interpreted by the cgroup code, but will appear in -/proc/mounts so may be any useful identifying string that you like. - -To mount a cgroup hierarchy with just the cpuset and numtasks -subsystems, type: -# mount -t cgroup -o cpuset,numtasks hier1 /dev/cgroup - -To change the set of subsystems bound to a mounted hierarchy, just -remount with different options: - -# mount -o remount,cpuset,ns /dev/cgroup - -Note that changing the set of subsystems is currently only supported -when the hierarchy consists of a single (root) cgroup. Supporting -the ability to arbitrarily bind/unbind subsystems from an existing -cgroup hierarchy is intended to be implemented in the future. - -Then under /dev/cgroup you can find a tree that corresponds to the -tree of the cgroups in the system. For instance, /dev/cgroup -is the cgroup that holds the whole system. - -If you want to create a new cgroup under /dev/cgroup: -# cd /dev/cgroup -# mkdir my_cgroup - -Now you want to do something with this cgroup. -# cd my_cgroup - -In this directory you can find several files: -# ls -notify_on_release releasable tasks -(plus whatever files added by the attached subsystems) - -Now attach your shell to this cgroup: -# /bin/echo $$ > tasks - -You can also create cgroups inside your cgroup by using mkdir in this -directory. -# mkdir my_sub_cs - -To remove a cgroup, just use rmdir: -# rmdir my_sub_cs - -This will fail if the cgroup is in use (has cgroups inside, or -has processes attached, or is held alive by other subsystem-specific -reference). - -2.2 Attaching processes ------------------------ - -# /bin/echo PID > tasks - -Note that it is PID, not PIDs. You can only attach ONE task at a time. -If you have several tasks to attach, you have to do it one after another: - -# /bin/echo PID1 > tasks -# /bin/echo PID2 > tasks - ... -# /bin/echo PIDn > tasks - -You can attach the current shell task by echoing 0: - -# echo 0 > tasks - -3. Kernel API -============= - -3.1 Overview ------------- - -Each kernel subsystem that wants to hook into the generic cgroup -system needs to create a cgroup_subsys object. This contains -various methods, which are callbacks from the cgroup system, along -with a subsystem id which will be assigned by the cgroup system. - -Other fields in the cgroup_subsys object include: - -- subsys_id: a unique array index for the subsystem, indicating which - entry in cgroup->subsys[] this subsystem should be managing. - -- name: should be initialized to a unique subsystem name. Should be - no longer than MAX_CGROUP_TYPE_NAMELEN. - -- early_init: indicate if the subsystem needs early initialization - at system boot. - -Each cgroup object created by the system has an array of pointers, -indexed by subsystem id; this pointer is entirely managed by the -subsystem; the generic cgroup code will never touch this pointer. - -3.2 Synchronization -------------------- - -There is a global mutex, cgroup_mutex, used by the cgroup -system. This should be taken by anything that wants to modify a -cgroup. It may also be taken to prevent cgroups from being -modified, but more specific locks may be more appropriate in that -situation. - -See kernel/cgroup.c for more details. - -Subsystems can take/release the cgroup_mutex via the functions -cgroup_lock()/cgroup_unlock(). - -Accessing a task's cgroup pointer may be done in the following ways: -- while holding cgroup_mutex -- while holding the task's alloc_lock (via task_lock()) -- inside an rcu_read_lock() section via rcu_dereference() - -3.3 Subsystem API ------------------ - -Each subsystem should: - -- add an entry in linux/cgroup_subsys.h -- define a cgroup_subsys object called _subsys - -Each subsystem may export the following methods. The only mandatory -methods are create/destroy. Any others that are null are presumed to -be successful no-ops. - -struct cgroup_subsys_state *create(struct cgroup_subsys *ss, - struct cgroup *cgrp) -(cgroup_mutex held by caller) - -Called to create a subsystem state object for a cgroup. The -subsystem should allocate its subsystem state object for the passed -cgroup, returning a pointer to the new object on success or a -negative error code. On success, the subsystem pointer should point to -a structure of type cgroup_subsys_state (typically embedded in a -larger subsystem-specific object), which will be initialized by the -cgroup system. Note that this will be called at initialization to -create the root subsystem state for this subsystem; this case can be -identified by the passed cgroup object having a NULL parent (since -it's the root of the hierarchy) and may be an appropriate place for -initialization code. - -void destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) -(cgroup_mutex held by caller) - -The cgroup system is about to destroy the passed cgroup; the subsystem -should do any necessary cleanup and free its subsystem state -object. By the time this method is called, the cgroup has already been -unlinked from the file system and from the child list of its parent; -cgroup->parent is still valid. (Note - can also be called for a -newly-created cgroup if an error occurs after this subsystem's -create() method has been called for the new cgroup). - -void pre_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp); -(cgroup_mutex held by caller) - -Called before checking the reference count on each subsystem. This may -be useful for subsystems which have some extra references even if -there are not tasks in the cgroup. - -int can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct task_struct *task) -(cgroup_mutex held by caller) - -Called prior to moving a task into a cgroup; if the subsystem -returns an error, this will abort the attach operation. If a NULL -task is passed, then a successful result indicates that *any* -unspecified task can be moved into the cgroup. Note that this isn't -called on a fork. If this method returns 0 (success) then this should -remain valid while the caller holds cgroup_mutex. - -void attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup *old_cgrp, struct task_struct *task) - -Called after the task has been attached to the cgroup, to allow any -post-attachment activity that requires memory allocations or blocking. - -void fork(struct cgroup_subsy *ss, struct task_struct *task) - -Called when a task is forked into a cgroup. - -void exit(struct cgroup_subsys *ss, struct task_struct *task) - -Called during task exit. - -int populate(struct cgroup_subsys *ss, struct cgroup *cgrp) - -Called after creation of a cgroup to allow a subsystem to populate -the cgroup directory with file entries. The subsystem should make -calls to cgroup_add_file() with objects of type cftype (see -include/linux/cgroup.h for details). Note that although this -method can return an error code, the error code is currently not -always handled well. - -void post_clone(struct cgroup_subsys *ss, struct cgroup *cgrp) - -Called at the end of cgroup_clone() to do any paramater -initialization which might be required before a task could attach. For -example in cpusets, no task may attach before 'cpus' and 'mems' are set -up. - -void bind(struct cgroup_subsys *ss, struct cgroup *root) -(cgroup_mutex held by caller) - -Called when a cgroup subsystem is rebound to a different hierarchy -and root cgroup. Currently this will only involve movement between -the default hierarchy (which never has sub-cgroups) and a hierarchy -that is being created/destroyed (and hence has no sub-cgroups). - -4. Questions -============ - -Q: what's up with this '/bin/echo' ? -A: bash's builtin 'echo' command does not check calls to write() against - errors. If you use it in the cgroup file system, you won't be - able to tell whether a command succeeded or failed. - -Q: When I attach processes, only the first of the line gets really attached ! -A: We can only return one error code per call to write(). So you should also - put only ONE pid. - diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt new file mode 100644 index 00000000000..d9014aa0eb6 --- /dev/null +++ b/Documentation/cgroups/cgroups.txt @@ -0,0 +1,548 @@ + CGROUPS + ------- + +Written by Paul Menage based on Documentation/cpusets.txt + +Original copyright statements from cpusets.txt: +Portions Copyright (C) 2004 BULL SA. +Portions Copyright (c) 2004-2006 Silicon Graphics, Inc. +Modified by Paul Jackson +Modified by Christoph Lameter + +CONTENTS: +========= + +1. Control Groups + 1.1 What are cgroups ? + 1.2 Why are cgroups needed ? + 1.3 How are cgroups implemented ? + 1.4 What does notify_on_release do ? + 1.5 How do I use cgroups ? +2. Usage Examples and Syntax + 2.1 Basic Usage + 2.2 Attaching processes +3. Kernel API + 3.1 Overview + 3.2 Synchronization + 3.3 Subsystem API +4. Questions + +1. Control Groups +================= + +1.1 What are cgroups ? +---------------------- + +Control Groups provide a mechanism for aggregating/partitioning sets of +tasks, and all their future children, into hierarchical groups with +specialized behaviour. + +Definitions: + +A *cgroup* associates a set of tasks with a set of parameters for one +or more subsystems. + +A *subsystem* is a module that makes use of the task grouping +facilities provided by cgroups to treat groups of tasks in +particular ways. A subsystem is typically a "resource controller" that +schedules a resource or applies per-cgroup limits, but it may be +anything that wants to act on a group of processes, e.g. a +virtualization subsystem. + +A *hierarchy* is a set of cgroups arranged in a tree, such that +every task in the system is in exactly one of the cgroups in the +hierarchy, and a set of subsystems; each subsystem has system-specific +state attached to each cgroup in the hierarchy. Each hierarchy has +an instance of the cgroup virtual filesystem associated with it. + +At any one time there may be multiple active hierachies of task +cgroups. Each hierarchy is a partition of all tasks in the system. + +User level code may create and destroy cgroups by name in an +instance of the cgroup virtual file system, specify and query to +which cgroup a task is assigned, and list the task pids assigned to +a cgroup. Those creations and assignments only affect the hierarchy +associated with that instance of the cgroup file system. + +On their own, the only use for cgroups is for simple job +tracking. The intention is that other subsystems hook into the generic +cgroup support to provide new attributes for cgroups, such as +accounting/limiting the resources which processes in a cgroup can +access. For example, cpusets (see Documentation/cpusets.txt) allows +you to associate a set of CPUs and a set of memory nodes with the +tasks in each cgroup. + +1.2 Why are cgroups needed ? +---------------------------- + +There are multiple efforts to provide process aggregations in the +Linux kernel, mainly for resource tracking purposes. Such efforts +include cpusets, CKRM/ResGroups, UserBeanCounters, and virtual server +namespaces. These all require the basic notion of a +grouping/partitioning of processes, with newly forked processes ending +in the same group (cgroup) as their parent process. + +The kernel cgroup patch provides the minimum essential kernel +mechanisms required to efficiently implement such groups. It has +minimal impact on the system fast paths, and provides hooks for +specific subsystems such as cpusets to provide additional behaviour as +desired. + +Multiple hierarchy support is provided to allow for situations where +the division of tasks into cgroups is distinctly different for +different subsystems - having parallel hierarchies allows each +hierarchy to be a natural division of tasks, without having to handle +complex combinations of tasks that would be present if several +unrelated subsystems needed to be forced into the same tree of +cgroups. + +At one extreme, each resource controller or subsystem could be in a +separate hierarchy; at the other extreme, all subsystems +would be attached to the same hierarchy. + +As an example of a scenario (originally proposed by vatsa@in.ibm.com) +that can benefit from multiple hierarchies, consider a large +university server with various users - students, professors, system +tasks etc. The resource planning for this server could be along the +following lines: + + CPU : Top cpuset + / \ + CPUSet1 CPUSet2 + | | + (Profs) (Students) + + In addition (system tasks) are attached to topcpuset (so + that they can run anywhere) with a limit of 20% + + Memory : Professors (50%), students (30%), system (20%) + + Disk : Prof (50%), students (30%), system (20%) + + Network : WWW browsing (20%), Network File System (60%), others (20%) + / \ + Prof (15%) students (5%) + +Browsers like firefox/lynx go into the WWW network class, while (k)nfsd go +into NFS network class. + +At the same time firefox/lynx will share an appropriate CPU/Memory class +depending on who launched it (prof/student). + +With the ability to classify tasks differently for different resources +(by putting those resource subsystems in different hierarchies) then +the admin can easily set up a script which receives exec notifications +and depending on who is launching the browser he can + + # echo browser_pid > /mnt///tasks + +With only a single hierarchy, he now would potentially have to create +a separate cgroup for every browser launched and associate it with +approp network and other resource class. This may lead to +proliferation of such cgroups. + +Also lets say that the administrator would like to give enhanced network +access temporarily to a student's browser (since it is night and the user +wants to do online gaming :)) OR give one of the students simulation +apps enhanced CPU power, + +With ability to write pids directly to resource classes, it's just a +matter of : + + # echo pid > /mnt/network//tasks + (after some time) + # echo pid > /mnt/network//tasks + +Without this ability, he would have to split the cgroup into +multiple separate ones and then associate the new cgroups with the +new resource classes. + + + +1.3 How are cgroups implemented ? +--------------------------------- + +Control Groups extends the kernel as follows: + + - Each task in the system has a reference-counted pointer to a + css_set. + + - A css_set contains a set of reference-counted pointers to + cgroup_subsys_state objects, one for each cgroup subsystem + registered in the system. There is no direct link from a task to + the cgroup of which it's a member in each hierarchy, but this + can be determined by following pointers through the + cgroup_subsys_state objects. This is because accessing the + subsystem state is something that's expected to happen frequently + and in performance-critical code, whereas operations that require a + task's actual cgroup assignments (in particular, moving between + cgroups) are less common. A linked list runs through the cg_list + field of each task_struct using the css_set, anchored at + css_set->tasks. + + - A cgroup hierarchy filesystem can be mounted for browsing and + manipulation from user space. + + - You can list all the tasks (by pid) attached to any cgroup. + +The implementation of cgroups requires a few, simple hooks +into the rest of the kernel, none in performance critical paths: + + - in init/main.c, to initialize the root cgroups and initial + css_set at system boot. + + - in fork and exit, to attach and detach a task from its css_set. + +In addition a new file system, of type "cgroup" may be mounted, to +enable browsing and modifying the cgroups presently known to the +kernel. When mounting a cgroup hierarchy, you may specify a +comma-separated list of subsystems to mount as the filesystem mount +options. By default, mounting the cgroup filesystem attempts to +mount a hierarchy containing all registered subsystems. + +If an active hierarchy with exactly the same set of subsystems already +exists, it will be reused for the new mount. If no existing hierarchy +matches, and any of the requested subsystems are in use in an existing +hierarchy, the mount will fail with -EBUSY. Otherwise, a new hierarchy +is activated, associated with the requested subsystems. + +It's not currently possible to bind a new subsystem to an active +cgroup hierarchy, or to unbind a subsystem from an active cgroup +hierarchy. This may be possible in future, but is fraught with nasty +error-recovery issues. + +When a cgroup filesystem is unmounted, if there are any +child cgroups created below the top-level cgroup, that hierarchy +will remain active even though unmounted; if there are no +child cgroups then the hierarchy will be deactivated. + +No new system calls are added for cgroups - all support for +querying and modifying cgroups is via this cgroup file system. + +Each task under /proc has an added file named 'cgroup' displaying, +for each active hierarchy, the subsystem names and the cgroup name +as the path relative to the root of the cgroup file system. + +Each cgroup is represented by a directory in the cgroup file system +containing the following files describing that cgroup: + + - tasks: list of tasks (by pid) attached to that cgroup + - releasable flag: cgroup currently removeable? + - notify_on_release flag: run the release agent on exit? + - release_agent: the path to use for release notifications (this file + exists in the top cgroup only) + +Other subsystems such as cpusets may add additional files in each +cgroup dir. + +New cgroups are created using the mkdir system call or shell +command. The properties of a cgroup, such as its flags, are +modified by writing to the appropriate file in that cgroups +directory, as listed above. + +The named hierarchical structure of nested cgroups allows partitioning +a large system into nested, dynamically changeable, "soft-partitions". + +The attachment of each task, automatically inherited at fork by any +children of that task, to a cgroup allows organizing the work load +on a system into related sets of tasks. A task may be re-attached to +any other cgroup, if allowed by the permissions on the necessary +cgroup file system directories. + +When a task is moved from one cgroup to another, it gets a new +css_set pointer - if there's an already existing css_set with the +desired collection of cgroups then that group is reused, else a new +css_set is allocated. Note that the current implementation uses a +linear search to locate an appropriate existing css_set, so isn't +very efficient. A future version will use a hash table for better +performance. + +To allow access from a cgroup to the css_sets (and hence tasks) +that comprise it, a set of cg_cgroup_link objects form a lattice; +each cg_cgroup_link is linked into a list of cg_cgroup_links for +a single cgroup on its cgrp_link_list field, and a list of +cg_cgroup_links for a single css_set on its cg_link_list. + +Thus the set of tasks in a cgroup can be listed by iterating over +each css_set that references the cgroup, and sub-iterating over +each css_set's task set. + +The use of a Linux virtual file system (vfs) to represent the +cgroup hierarchy provides for a familiar permission and name space +for cgroups, with a minimum of additional kernel code. + +1.4 What does notify_on_release do ? +------------------------------------ + +If the notify_on_release flag is enabled (1) in a cgroup, then +whenever the last task in the cgroup leaves (exits or attaches to +some other cgroup) and the last child cgroup of that cgroup +is removed, then the kernel runs the command specified by the contents +of the "release_agent" file in that hierarchy's root directory, +supplying the pathname (relative to the mount point of the cgroup +file system) of the abandoned cgroup. This enables automatic +removal of abandoned cgroups. The default value of +notify_on_release in the root cgroup at system boot is disabled +(0). The default value of other cgroups at creation is the current +value of their parents notify_on_release setting. The default value of +a cgroup hierarchy's release_agent path is empty. + +1.5 How do I use cgroups ? +-------------------------- + +To start a new job that is to be contained within a cgroup, using +the "cpuset" cgroup subsystem, the steps are something like: + + 1) mkdir /dev/cgroup + 2) mount -t cgroup -ocpuset cpuset /dev/cgroup + 3) Create the new cgroup by doing mkdir's and write's (or echo's) in + the /dev/cgroup virtual file system. + 4) Start a task that will be the "founding father" of the new job. + 5) Attach that task to the new cgroup by writing its pid to the + /dev/cgroup tasks file for that cgroup. + 6) fork, exec or clone the job tasks from this founding father task. + +For example, the following sequence of commands will setup a cgroup +named "Charlie", containing just CPUs 2 and 3, and Memory Node 1, +and then start a subshell 'sh' in that cgroup: + + mount -t cgroup cpuset -ocpuset /dev/cgroup + cd /dev/cgroup + mkdir Charlie + cd Charlie + /bin/echo 2-3 > cpuset.cpus + /bin/echo 1 > cpuset.mems + /bin/echo $$ > tasks + sh + # The subshell 'sh' is now running in cgroup Charlie + # The next line should display '/Charlie' + cat /proc/self/cgroup + +2. Usage Examples and Syntax +============================ + +2.1 Basic Usage +--------------- + +Creating, modifying, using the cgroups can be done through the cgroup +virtual filesystem. + +To mount a cgroup hierarchy will all available subsystems, type: +# mount -t cgroup xxx /dev/cgroup + +The "xxx" is not interpreted by the cgroup code, but will appear in +/proc/mounts so may be any useful identifying string that you like. + +To mount a cgroup hierarchy with just the cpuset and numtasks +subsystems, type: +# mount -t cgroup -o cpuset,numtasks hier1 /dev/cgroup + +To change the set of subsystems bound to a mounted hierarchy, just +remount with different options: + +# mount -o remount,cpuset,ns /dev/cgroup + +Note that changing the set of subsystems is currently only supported +when the hierarchy consists of a single (root) cgroup. Supporting +the ability to arbitrarily bind/unbind subsystems from an existing +cgroup hierarchy is intended to be implemented in the future. + +Then under /dev/cgroup you can find a tree that corresponds to the +tree of the cgroups in the system. For instance, /dev/cgroup +is the cgroup that holds the whole system. + +If you want to create a new cgroup under /dev/cgroup: +# cd /dev/cgroup +# mkdir my_cgroup + +Now you want to do something with this cgroup. +# cd my_cgroup + +In this directory you can find several files: +# ls +notify_on_release releasable tasks +(plus whatever files added by the attached subsystems) + +Now attach your shell to this cgroup: +# /bin/echo $$ > tasks + +You can also create cgroups inside your cgroup by using mkdir in this +directory. +# mkdir my_sub_cs + +To remove a cgroup, just use rmdir: +# rmdir my_sub_cs + +This will fail if the cgroup is in use (has cgroups inside, or +has processes attached, or is held alive by other subsystem-specific +reference). + +2.2 Attaching processes +----------------------- + +# /bin/echo PID > tasks + +Note that it is PID, not PIDs. You can only attach ONE task at a time. +If you have several tasks to attach, you have to do it one after another: + +# /bin/echo PID1 > tasks +# /bin/echo PID2 > tasks + ... +# /bin/echo PIDn > tasks + +You can attach the current shell task by echoing 0: + +# echo 0 > tasks + +3. Kernel API +============= + +3.1 Overview +------------ + +Each kernel subsystem that wants to hook into the generic cgroup +system needs to create a cgroup_subsys object. This contains +various methods, which are callbacks from the cgroup system, along +with a subsystem id which will be assigned by the cgroup system. + +Other fields in the cgroup_subsys object include: + +- subsys_id: a unique array index for the subsystem, indicating which + entry in cgroup->subsys[] this subsystem should be managing. + +- name: should be initialized to a unique subsystem name. Should be + no longer than MAX_CGROUP_TYPE_NAMELEN. + +- early_init: indicate if the subsystem needs early initialization + at system boot. + +Each cgroup object created by the system has an array of pointers, +indexed by subsystem id; this pointer is entirely managed by the +subsystem; the generic cgroup code will never touch this pointer. + +3.2 Synchronization +------------------- + +There is a global mutex, cgroup_mutex, used by the cgroup +system. This should be taken by anything that wants to modify a +cgroup. It may also be taken to prevent cgroups from being +modified, but more specific locks may be more appropriate in that +situation. + +See kernel/cgroup.c for more details. + +Subsystems can take/release the cgroup_mutex via the functions +cgroup_lock()/cgroup_unlock(). + +Accessing a task's cgroup pointer may be done in the following ways: +- while holding cgroup_mutex +- while holding the task's alloc_lock (via task_lock()) +- inside an rcu_read_lock() section via rcu_dereference() + +3.3 Subsystem API +----------------- + +Each subsystem should: + +- add an entry in linux/cgroup_subsys.h +- define a cgroup_subsys object called _subsys + +Each subsystem may export the following methods. The only mandatory +methods are create/destroy. Any others that are null are presumed to +be successful no-ops. + +struct cgroup_subsys_state *create(struct cgroup_subsys *ss, + struct cgroup *cgrp) +(cgroup_mutex held by caller) + +Called to create a subsystem state object for a cgroup. The +subsystem should allocate its subsystem state object for the passed +cgroup, returning a pointer to the new object on success or a +negative error code. On success, the subsystem pointer should point to +a structure of type cgroup_subsys_state (typically embedded in a +larger subsystem-specific object), which will be initialized by the +cgroup system. Note that this will be called at initialization to +create the root subsystem state for this subsystem; this case can be +identified by the passed cgroup object having a NULL parent (since +it's the root of the hierarchy) and may be an appropriate place for +initialization code. + +void destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +(cgroup_mutex held by caller) + +The cgroup system is about to destroy the passed cgroup; the subsystem +should do any necessary cleanup and free its subsystem state +object. By the time this method is called, the cgroup has already been +unlinked from the file system and from the child list of its parent; +cgroup->parent is still valid. (Note - can also be called for a +newly-created cgroup if an error occurs after this subsystem's +create() method has been called for the new cgroup). + +void pre_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp); +(cgroup_mutex held by caller) + +Called before checking the reference count on each subsystem. This may +be useful for subsystems which have some extra references even if +there are not tasks in the cgroup. + +int can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct task_struct *task) +(cgroup_mutex held by caller) + +Called prior to moving a task into a cgroup; if the subsystem +returns an error, this will abort the attach operation. If a NULL +task is passed, then a successful result indicates that *any* +unspecified task can be moved into the cgroup. Note that this isn't +called on a fork. If this method returns 0 (success) then this should +remain valid while the caller holds cgroup_mutex. + +void attach(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct cgroup *old_cgrp, struct task_struct *task) + +Called after the task has been attached to the cgroup, to allow any +post-attachment activity that requires memory allocations or blocking. + +void fork(struct cgroup_subsy *ss, struct task_struct *task) + +Called when a task is forked into a cgroup. + +void exit(struct cgroup_subsys *ss, struct task_struct *task) + +Called during task exit. + +int populate(struct cgroup_subsys *ss, struct cgroup *cgrp) + +Called after creation of a cgroup to allow a subsystem to populate +the cgroup directory with file entries. The subsystem should make +calls to cgroup_add_file() with objects of type cftype (see +include/linux/cgroup.h for details). Note that although this +method can return an error code, the error code is currently not +always handled well. + +void post_clone(struct cgroup_subsys *ss, struct cgroup *cgrp) + +Called at the end of cgroup_clone() to do any paramater +initialization which might be required before a task could attach. For +example in cpusets, no task may attach before 'cpus' and 'mems' are set +up. + +void bind(struct cgroup_subsys *ss, struct cgroup *root) +(cgroup_mutex held by caller) + +Called when a cgroup subsystem is rebound to a different hierarchy +and root cgroup. Currently this will only involve movement between +the default hierarchy (which never has sub-cgroups) and a hierarchy +that is being created/destroyed (and hence has no sub-cgroups). + +4. Questions +============ + +Q: what's up with this '/bin/echo' ? +A: bash's builtin 'echo' command does not check calls to write() against + errors. If you use it in the cgroup file system, you won't be + able to tell whether a command succeeded or failed. + +Q: When I attach processes, only the first of the line gets really attached ! +A: We can only return one error code per call to write(). So you should also + put only ONE pid. + diff --git a/Documentation/cgroups/freezer-subsystem.txt b/Documentation/cgroups/freezer-subsystem.txt new file mode 100644 index 00000000000..c50ab58b72e --- /dev/null +++ b/Documentation/cgroups/freezer-subsystem.txt @@ -0,0 +1,99 @@ + The cgroup freezer is useful to batch job management system which start +and stop sets of tasks in order to schedule the resources of a machine +according to the desires of a system administrator. This sort of program +is often used on HPC clusters to schedule access to the cluster as a +whole. The cgroup freezer uses cgroups to describe the set of tasks to +be started/stopped by the batch job management system. It also provides +a means to start and stop the tasks composing the job. + + The cgroup freezer will also be useful for checkpointing running groups +of tasks. The freezer allows the checkpoint code to obtain a consistent +image of the tasks by attempting to force the tasks in a cgroup into a +quiescent state. Once the tasks are quiescent another task can +walk /proc or invoke a kernel interface to gather information about the +quiesced tasks. Checkpointed tasks can be restarted later should a +recoverable error occur. This also allows the checkpointed tasks to be +migrated between nodes in a cluster by copying the gathered information +to another node and restarting the tasks there. + + Sequences of SIGSTOP and SIGCONT are not always sufficient for stopping +and resuming tasks in userspace. Both of these signals are observable +from within the tasks we wish to freeze. While SIGSTOP cannot be caught, +blocked, or ignored it can be seen by waiting or ptracing parent tasks. +SIGCONT is especially unsuitable since it can be caught by the task. Any +programs designed to watch for SIGSTOP and SIGCONT could be broken by +attempting to use SIGSTOP and SIGCONT to stop and resume tasks. We can +demonstrate this problem using nested bash shells: + + $ echo $$ + 16644 + $ bash + $ echo $$ + 16690 + + From a second, unrelated bash shell: + $ kill -SIGSTOP 16690 + $ kill -SIGCONT 16990 + + + + This happens because bash can observe both signals and choose how it +responds to them. + + Another example of a program which catches and responds to these +signals is gdb. In fact any program designed to use ptrace is likely to +have a problem with this method of stopping and resuming tasks. + + In contrast, the cgroup freezer uses the kernel freezer code to +prevent the freeze/unfreeze cycle from becoming visible to the tasks +being frozen. This allows the bash example above and gdb to run as +expected. + + The freezer subsystem in the container filesystem defines a file named +freezer.state. Writing "FROZEN" to the state file will freeze all tasks in the +cgroup. Subsequently writing "THAWED" will unfreeze the tasks in the cgroup. +Reading will return the current state. + +* Examples of usage : + + # mkdir /containers/freezer + # mount -t cgroup -ofreezer freezer /containers + # mkdir /containers/0 + # echo $some_pid > /containers/0/tasks + +to get status of the freezer subsystem : + + # cat /containers/0/freezer.state + THAWED + +to freeze all tasks in the container : + + # echo FROZEN > /containers/0/freezer.state + # cat /containers/0/freezer.state + FREEZING + # cat /containers/0/freezer.state + FROZEN + +to unfreeze all tasks in the container : + + # echo THAWED > /containers/0/freezer.state + # cat /containers/0/freezer.state + THAWED + +This is the basic mechanism which should do the right thing for user space task +in a simple scenario. + +It's important to note that freezing can be incomplete. In that case we return +EBUSY. This means that some tasks in the cgroup are busy doing something that +prevents us from completely freezing the cgroup at this time. After EBUSY, +the cgroup will remain partially frozen -- reflected by freezer.state reporting +"FREEZING" when read. The state will remain "FREEZING" until one of these +things happens: + + 1) Userspace cancels the freezing operation by writing "THAWED" to + the freezer.state file + 2) Userspace retries the freezing operation by writing "FROZEN" to + the freezer.state file (writing "FREEZING" is not legal + and returns EIO) + 3) The tasks that blocked the cgroup from entering the "FROZEN" + state disappear from the cgroup's set of tasks. diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt index 47e568a9370..5c86c258c79 100644 --- a/Documentation/cpusets.txt +++ b/Documentation/cpusets.txt @@ -48,7 +48,7 @@ hooks, beyond what is already present, required to manage dynamic job placement on large systems. Cpusets use the generic cgroup subsystem described in -Documentation/cgroup.txt. +Documentation/cgroups/cgroups.txt. Requests by a task, using the sched_setaffinity(2) system call to include CPUs in its CPU affinity mask, and using the mbind(2) and -- cgit v1.2.3-70-g09d2 From 2a80a3783d975dadea9740b0ac84c2e8796ee5bb Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 18 Oct 2008 20:27:27 -0700 Subject: Fix documentation of sysrq-q I fell into the trap recently that it only dumps hrtimers instead of all timers. Fix the documentation. Signed-off-by: Andi Kleen Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysrq.txt | 3 ++- drivers/char/sysrq.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 5ce0952aa06..49378a9f2b5 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -95,7 +95,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'p' - Will dump the current registers and flags to your console. -'q' - Will dump a list of all running timers. +'q' - Will dump a list of all running hrtimers. + WARNING: Does not cover any other timers 'r' - Turns off keyboard raw mode and sets it to XLATE. diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index dce4cc0e695..d0c0d64ed36 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -168,7 +168,7 @@ static void sysrq_handle_show_timers(int key, struct tty_struct *tty) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, .help_msg = "show-all-timers(Q)", - .action_msg = "Show Pending Timers", + .action_msg = "Show pending hrtimers (no others)", }; static void sysrq_handle_mountro(int key, struct tty_struct *tty) -- cgit v1.2.3-70-g09d2 From 0e4fb5e283870757024294bc4567a7c59d936f0b Mon Sep 17 00:00:00 2001 From: Hidehiro Kawai Date: Sat, 18 Oct 2008 20:27:57 -0700 Subject: ext3: add an option to control error handling on file data If the journal doesn't abort when it gets an IO error in file data blocks, the file data corruption will spread silently. Because most of applications and commands do buffered writes without fsync(), they don't notice the IO error. It's scary for mission critical systems. On the other hand, if the journal aborts whenever it gets an IO error in file data blocks, the system will easily become inoperable. So this patch introduces a filesystem option to determine whether it aborts the journal or just call printk() when it gets an IO error in file data. If you mount a ext3 fs with data_err=abort option, it aborts on file data write error. If you mount it with data_err=ignore, it doesn't abort, just call printk(). data_err=ignore is the default. Signed-off-by: Hidehiro Kawai Cc: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/ext3.txt | 5 +++++ fs/ext3/super.c | 16 ++++++++++++++++ fs/jbd/commit.c | 2 ++ include/linux/ext3_fs.h | 2 ++ include/linux/jbd.h | 3 +++ 5 files changed, 28 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt index 295f26cd895..9dd2a3bb2ac 100644 --- a/Documentation/filesystems/ext3.txt +++ b/Documentation/filesystems/ext3.txt @@ -96,6 +96,11 @@ errors=remount-ro(*) Remount the filesystem read-only on an error. errors=continue Keep going on a filesystem error. errors=panic Panic and halt the machine if an error occurs. +data_err=ignore(*) Just print an error message if an error occurs + in a file data buffer in ordered mode. +data_err=abort Abort the journal if an error occurs in a file + data buffer in ordered mode. + grpid Give objects the same group ID as their creator. bsdgroups diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 399a96a6c55..3a260af5544 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -625,6 +625,9 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs) else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA) seq_puts(seq, ",data=writeback"); + if (test_opt(sb, DATA_ERR_ABORT)) + seq_puts(seq, ",data_err=abort"); + ext3_show_quota_options(seq, sb); return 0; @@ -754,6 +757,7 @@ enum { Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, Opt_bh, Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, + Opt_data_err_abort, Opt_data_err_ignore, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, @@ -796,6 +800,8 @@ static const match_table_t tokens = { {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, + {Opt_data_err_abort, "data_err=abort"}, + {Opt_data_err_ignore, "data_err=ignore"}, {Opt_offusrjquota, "usrjquota="}, {Opt_usrjquota, "usrjquota=%s"}, {Opt_offgrpjquota, "grpjquota="}, @@ -1011,6 +1017,12 @@ static int parse_options (char *options, struct super_block *sb, sbi->s_mount_opt |= data_opt; } break; + case Opt_data_err_abort: + set_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; + case Opt_data_err_ignore: + clear_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; #ifdef CONFIG_QUOTA case Opt_usrjquota: qtype = USRQUOTA; @@ -1986,6 +1998,10 @@ static void ext3_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_flags |= JFS_BARRIER; else journal->j_flags &= ~JFS_BARRIER; + if (test_opt(sb, DATA_ERR_ABORT)) + journal->j_flags |= JFS_ABORT_ON_SYNCDATA_ERR; + else + journal->j_flags &= ~JFS_ABORT_ON_SYNCDATA_ERR; spin_unlock(&journal->j_state_lock); } diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index d6a6659f3e4..25719d902c5 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -482,6 +482,8 @@ void journal_commit_transaction(journal_t *journal) printk(KERN_WARNING "JBD: Detected IO errors while flushing file data " "on %s\n", bdevname(journal->j_fs_dev, b)); + if (journal->j_flags & JFS_ABORT_ON_SYNCDATA_ERR) + journal_abort(journal, err); err = 0; } diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 159d9b476cd..d14f0291848 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -380,6 +380,8 @@ struct ext3_inode { #define EXT3_MOUNT_QUOTA 0x80000 /* Some quota option set */ #define EXT3_MOUNT_USRQUOTA 0x100000 /* "old" user quota */ #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ +#define EXT3_MOUNT_DATA_ERR_ABORT 0x400000 /* Abort on file data write + * error in ordered mode */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 7ebbcb1c9ba..35d4f6342fa 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -816,6 +816,9 @@ struct journal_s #define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ #define JFS_LOADED 0x010 /* The journal superblock has been loaded */ #define JFS_BARRIER 0x020 /* Use IDE barriers */ +#define JFS_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file + * data write error in ordered + * mode */ /* * Function declarations for the journaling transaction and buffer -- cgit v1.2.3-70-g09d2 From 5b4e655e948d8b6e9b0d001616d4c9d7e7ffe924 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Sat, 18 Oct 2008 20:28:10 -0700 Subject: memcg: avoid accounting special pages There are not-on-LRU pages which can be mapped and they are not worth to be accounted. (becasue we can't shrink them and need dirty codes to handle specical case) We'd like to make use of usual objrmap/radix-tree's protcol and don't want to account out-of-vm's control pages. When special_mapping_fault() is called, page->mapping is tend to be NULL and it's charged as Anonymous page. insert_page() also handles some special pages from drivers. This patch is for avoiding to account special pages. Signed-off-by: KAMEZAWA Hiroyuki Cc: Daisuke Nishimura Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/controllers/memory.txt | 24 ++++++++++++++++-------- mm/memory.c | 25 +++++++++++-------------- mm/rmap.c | 4 ++-- 3 files changed, 29 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/controllers/memory.txt b/Documentation/controllers/memory.txt index 9b53d582736..1c07547d3f8 100644 --- a/Documentation/controllers/memory.txt +++ b/Documentation/controllers/memory.txt @@ -112,14 +112,22 @@ the per cgroup LRU. 2.2.1 Accounting details -All mapped pages (RSS) and unmapped user pages (Page Cache) are accounted. -RSS pages are accounted at the time of page_add_*_rmap() unless they've already -been accounted for earlier. A file page will be accounted for as Page Cache; -it's mapped into the page tables of a process, duplicate accounting is carefully -avoided. Page Cache pages are accounted at the time of add_to_page_cache(). -The corresponding routines that remove a page from the page tables or removes -a page from Page Cache is used to decrement the accounting counters of the -cgroup. +All mapped anon pages (RSS) and cache pages (Page Cache) are accounted. +(some pages which never be reclaimable and will not be on global LRU + are not accounted. we just accounts pages under usual vm management.) + +RSS pages are accounted at page_fault unless they've already been accounted +for earlier. A file page will be accounted for as Page Cache when it's +inserted into inode (radix-tree). While it's mapped into the page tables of +processes, duplicate accounting is carefully avoided. + +A RSS page is unaccounted when it's fully unmapped. A PageCache page is +unaccounted when it's removed from radix-tree. + +At page migration, accounting information is kept. + +Note: we just account pages-on-lru because our purpose is to control amount +of used pages. not-on-lru pages are tend to be out-of-control from vm view. 2.3 Shared Page Accounting diff --git a/mm/memory.c b/mm/memory.c index 54cf20ee0a8..3a6c4a65832 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1323,18 +1323,14 @@ static int insert_page(struct vm_area_struct *vma, unsigned long addr, pte_t *pte; spinlock_t *ptl; - retval = mem_cgroup_charge(page, mm, GFP_KERNEL); - if (retval) - goto out; - retval = -EINVAL; if (PageAnon(page)) - goto out_uncharge; + goto out; retval = -ENOMEM; flush_dcache_page(page); pte = get_locked_pte(mm, addr, &ptl); if (!pte) - goto out_uncharge; + goto out; retval = -EBUSY; if (!pte_none(*pte)) goto out_unlock; @@ -1350,8 +1346,6 @@ static int insert_page(struct vm_area_struct *vma, unsigned long addr, return retval; out_unlock: pte_unmap_unlock(pte, ptl); -out_uncharge: - mem_cgroup_uncharge_page(page); out: return retval; } @@ -2463,6 +2457,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page; pte_t entry; int anon = 0; + int charged = 0; struct page *dirty_page = NULL; struct vm_fault vmf; int ret; @@ -2503,6 +2498,12 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, ret = VM_FAULT_OOM; goto out; } + if (mem_cgroup_charge(page, mm, GFP_KERNEL)) { + ret = VM_FAULT_OOM; + page_cache_release(page); + goto out; + } + charged = 1; /* * Don't let another task, with possibly unlocked vma, * keep the mlocked page. @@ -2543,11 +2544,6 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, } - if (mem_cgroup_charge(page, mm, GFP_KERNEL)) { - ret = VM_FAULT_OOM; - goto out; - } - page_table = pte_offset_map_lock(mm, pmd, address, &ptl); /* @@ -2585,7 +2581,8 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, /* no need to invalidate: a not-present page won't be cached */ update_mmu_cache(vma, address, entry); } else { - mem_cgroup_uncharge_page(page); + if (charged) + mem_cgroup_uncharge_page(page); if (anon) page_cache_release(page); else diff --git a/mm/rmap.c b/mm/rmap.c index 7e90bebbeb6..8701d5fce73 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -727,8 +727,8 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma) page_clear_dirty(page); set_page_dirty(page); } - - mem_cgroup_uncharge_page(page); + if (PageAnon(page)) + mem_cgroup_uncharge_page(page); __dec_zone_page_state(page, PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED); /* -- cgit v1.2.3-70-g09d2 From e515a0d60066c802cc605a5d9c446948f7691519 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Sat, 18 Oct 2008 20:28:27 -0700 Subject: kdump: update elfcorehdr documentation to reflect supported architectures IA64, PPC and SH also support the elfcorehdr command line. Signed-off-by: Simon Horman Acked-by: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d4f4875fc7c..b49fcee5e9c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -690,7 +690,7 @@ and is between 256 and 4096 characters. It is defined in the file See Documentation/block/as-iosched.txt and Documentation/block/deadline-iosched.txt for details. - elfcorehdr= [X86-32, X86_64] + elfcorehdr= [IA64,PPC,SH,X86-32,X86_64] Specifies physical address of start of kernel core image elf header. Generally kexec loader will pass this option to capture kernel. -- cgit v1.2.3-70-g09d2 From b41d6cf38e27a940d998d989526a9748de1bf028 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 17 Aug 2008 21:06:59 +0200 Subject: PCI: Check dynids driver_data value for validity Only accept dynids whose driver_data value matches one of the driver's pci_driver_id entries. This prevents the user from accidentally passing values the drivers do not expect. Cc: Milton Miller Acked-by: Greg Kroah-Hartman Signed-off-by: Jean Delvare Signed-off-by: Jesse Barnes --- Documentation/PCI/pci.txt | 4 ++++ drivers/i2c/busses/i2c-amd756.c | 4 ---- drivers/i2c/busses/i2c-viapro.c | 4 ---- drivers/pci/pci-driver.c | 18 ++++++++++++++++-- 4 files changed, 20 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/PCI/pci.txt b/Documentation/PCI/pci.txt index 8d4dc6250c5..fd4907a2968 100644 --- a/Documentation/PCI/pci.txt +++ b/Documentation/PCI/pci.txt @@ -163,6 +163,10 @@ need pass only as many optional fields as necessary: o class and classmask fields default to 0 o driver_data defaults to 0UL. +Note that driver_data must match the value used by any of the pci_device_id +entries defined in the driver. This makes the driver_data field mandatory +if all the pci_device_id entries have a non-zero driver_data value. + Once added, the driver probe routine will be invoked for any unclaimed PCI devices listed in its (newly updated) pci_ids list. diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index a3542b053c8..424dad6f18d 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -332,10 +332,6 @@ static int __devinit amd756_probe(struct pci_dev *pdev, int error; u8 temp; - /* driver_data might come from user-space, so check it */ - if (id->driver_data >= ARRAY_SIZE(chipname)) - return -EINVAL; - if (amd756_ioport) { dev_err(&pdev->dev, "Only one device supported " "(you have a strange motherboard, btw)\n"); diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 2324780484c..9f194d9efd9 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -332,10 +332,6 @@ static int __devinit vt596_probe(struct pci_dev *pdev, unsigned char temp; int error = -ENODEV; - /* driver_data might come from user-space, so check it */ - if (id->driver_data & 1 || id->driver_data > 0xff) - return -EINVAL; - /* Determine the address of the SMBus areas */ if (force_addr) { vt596_smba = force_addr & 0xfff0; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 4940a53c56a..b4cdd690ae7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -43,18 +43,32 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) { struct pci_dynid *dynid; struct pci_driver *pdrv = to_pci_driver(driver); + const struct pci_device_id *ids = pdrv->id_table; __u32 vendor, device, subvendor=PCI_ANY_ID, subdevice=PCI_ANY_ID, class=0, class_mask=0; unsigned long driver_data=0; int fields=0; - int retval = 0; + int retval; - fields = sscanf(buf, "%x %x %x %x %x %x %lux", + fields = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, &subvendor, &subdevice, &class, &class_mask, &driver_data); if (fields < 2) return -EINVAL; + /* Only accept driver_data values that match an existing id_table + entry */ + retval = -EINVAL; + while (ids->vendor || ids->subvendor || ids->class_mask) { + if (driver_data == ids->driver_data) { + retval = 0; + break; + } + ids++; + } + if (retval) /* No match */ + return retval; + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); if (!dynid) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From c01156061bdd5976397dfb173f8c70ae351a6cb6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 22 Aug 2008 09:53:39 +0200 Subject: PCI: Document that most pci options are shared between i386 and x86-64 Since the code is shared pretty much most of the pci= options are shared, but kernel-parameters.txt marked most of them as i386 only. Signed-off-by: Andi Kleen Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0f1544f6740..08fbc8372ba 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -101,6 +101,7 @@ parameter is applicable: X86-64 X86-64 architecture is enabled. More X86-64 boot options can be found in Documentation/x86_64/boot-options.txt . + X86 Either 32bit or 64bit x86 (same as X86-32+X86-64) In addition, the following text indicates that the option: @@ -1588,7 +1589,7 @@ and is between 256 and 4096 characters. It is defined in the file See also Documentation/paride.txt. pci=option[,option...] [PCI] various PCI subsystem options: - off [X86-32] don't probe for the PCI bus + off [X86] don't probe for the PCI bus bios [X86-32] force use of PCI BIOS, don't access the hardware directly. Use this if your machine has a non-standard PCI host bridge. @@ -1596,9 +1597,9 @@ and is between 256 and 4096 characters. It is defined in the file hardware access methods are allowed. Use this if you experience crashes upon bootup and you suspect they are caused by the BIOS. - conf1 [X86-32] Force use of PCI Configuration + conf1 [X86] Force use of PCI Configuration Mechanism 1. - conf2 [X86-32] Force use of PCI Configuration + conf2 [X86] Force use of PCI Configuration Mechanism 2. noaer [PCIE] If the PCIEAER kernel config parameter is enabled, this kernel boot option can be used to @@ -1618,37 +1619,37 @@ and is between 256 and 4096 characters. It is defined in the file this option if the kernel is unable to allocate IRQs or discover secondary PCI buses on your motherboard. - rom [X86-32] Assign address space to expansion ROMs. + rom [X86] Assign address space to expansion ROMs. Use with caution as certain devices share address decoders between ROMs and other resources. - norom [X86-32,X86_64] Do not assign address space to + norom [X86] Do not assign address space to expansion ROMs that do not already have BIOS assigned address ranges. - irqmask=0xMMMM [X86-32] Set a bit mask of IRQs allowed to be + irqmask=0xMMMM [X86] Set a bit mask of IRQs allowed to be assigned automatically to PCI devices. You can make the kernel exclude IRQs of your ISA cards this way. - pirqaddr=0xAAAAA [X86-32] Specify the physical address + pirqaddr=0xAAAAA [X86] Specify the physical address of the PIRQ table (normally generated by the BIOS) if it is outside the F0000h-100000h range. - lastbus=N [X86-32] Scan all buses thru bus #N. Can be + lastbus=N [X86] Scan all buses thru bus #N. Can be useful if the kernel is unable to find your secondary buses and you want to tell it explicitly which ones they are. - assign-busses [X86-32] Always assign all PCI bus + assign-busses [X86] Always assign all PCI bus numbers ourselves, overriding whatever the firmware may have done. - usepirqmask [X86-32] Honor the possible IRQ mask stored + usepirqmask [X86] Honor the possible IRQ mask stored in the BIOS $PIR table. This is needed on some systems with broken BIOSes, notably some HP Pavilion N5400 and Omnibook XE3 notebooks. This will have no effect if ACPI IRQ routing is enabled. - noacpi [X86-32] Do not use ACPI for IRQ routing + noacpi [X86] Do not use ACPI for IRQ routing or for PCI scanning. - use_crs [X86-32] Use _CRS for PCI resource + use_crs [X86] Use _CRS for PCI resource allocation. routeirq Do IRQ routing for all PCI devices. This is normally done in pci_enable_device(), -- cgit v1.2.3-70-g09d2 From 50cbfa511a21cac1909b6b4c955fa39d1da81457 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Mon, 22 Sep 2008 14:55:24 -0700 Subject: PCI: fix MSI-HOWTO.txt info about MSI-X MMIO space The current MSI-HOWTO.txt says that device drivers should not request the memory space that contains MSI-X tables. This is because the original MSI-X implementation did a request_mem_region() on this space, but that code was removed long ago (in the pre-git era, in fact). Years after the code was changed, we might as well clean up the documention to avoid a confusing mention of requesting regions: drivers using MSI-X can just use pci_request_regions() just like any other driver, and so there's no need for MSI-HOWTO.txt to talk about this at all. Signed-off-by: Roland Dreier Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- Documentation/MSI-HOWTO.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/MSI-HOWTO.txt b/Documentation/MSI-HOWTO.txt index a51f693c154..256defd7e17 100644 --- a/Documentation/MSI-HOWTO.txt +++ b/Documentation/MSI-HOWTO.txt @@ -236,10 +236,8 @@ software system can set different pages for controlling accesses to the MSI-X structure. The implementation of MSI support requires the PCI subsystem, not a device driver, to maintain full control of the MSI-X table/MSI-X PBA (Pending Bit Array) and MMIO address space of the MSI-X -table/MSI-X PBA. A device driver is prohibited from requesting the MMIO -address space of the MSI-X table/MSI-X PBA. Otherwise, the PCI subsystem -will fail enabling MSI-X on its hardware device when it calls the function -pci_enable_msix(). +table/MSI-X PBA. A device driver should not access the MMIO address +space of the MSI-X table/MSI-X PBA. 5.3.2 API pci_enable_msix -- cgit v1.2.3-70-g09d2 From e5665a45fa28d0114f61b5d534a3b2678592219d Mon Sep 17 00:00:00 2001 From: Chuck Ebbert Date: Wed, 24 Sep 2008 20:40:34 -0400 Subject: PCI: document the pcie_aspm kernel parameter It can be handy so make sure people know about it. Signed-off-by: Chuck Ebbert Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 08fbc8372ba..53ba7c7d82b 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1678,6 +1678,12 @@ and is between 256 and 4096 characters. It is defined in the file reserved for the CardBus bridge's memory window. The default value is 64 megabytes. + pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power + Management. + off Disable ASPM. + force Enable ASPM even on devices that claim not to support it. + WARNING: Forcing ASPM on may cause system lockups. + pcmv= [HW,PCMCIA] BadgePAD 4 pd. [PARIDE] -- cgit v1.2.3-70-g09d2 From 270c66be9b4a6f2be53ef3aec5dc8e7b07782ec9 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 19 Oct 2008 20:35:20 +0800 Subject: PCI: fix AER capability check The 'use pci_find_ext_capability everywhere' cleanup brought a new bug, which makes the AER stop working. Fix it by actually using find_ext_cap instead of just find_cap. Drop the unused config space size define while we're at it. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- Documentation/PCI/pcieaer-howto.txt | 11 +++-------- drivers/pci/pcie/aer/aerdrv_core.c | 4 ++-- drivers/pci/pcie/portdrv.h | 1 - include/linux/aer.h | 1 - 4 files changed, 5 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/PCI/pcieaer-howto.txt b/Documentation/PCI/pcieaer-howto.txt index 16c251230c8..ddeb14beacc 100644 --- a/Documentation/PCI/pcieaer-howto.txt +++ b/Documentation/PCI/pcieaer-howto.txt @@ -203,22 +203,17 @@ to mmio_enabled. 3.3 helper functions -3.3.1 int pci_find_aer_capability(struct pci_dev *dev); -pci_find_aer_capability locates the PCI Express AER capability -in the device configuration space. If the device doesn't support -PCI-Express AER, the function returns 0. - -3.3.2 int pci_enable_pcie_error_reporting(struct pci_dev *dev); +3.3.1 int pci_enable_pcie_error_reporting(struct pci_dev *dev); pci_enable_pcie_error_reporting enables the device to send error messages to root port when an error is detected. Note that devices don't enable the error reporting by default, so device drivers need call this function to enable it. -3.3.3 int pci_disable_pcie_error_reporting(struct pci_dev *dev); +3.3.2 int pci_disable_pcie_error_reporting(struct pci_dev *dev); pci_disable_pcie_error_reporting disables the device to send error messages to root port when an error is detected. -3.3.4 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); +3.3.3 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); pci_cleanup_aer_uncorrect_error_status cleanups the uncorrectable error status register. diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 1ff21f6045d..dfc63d01f20 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -33,11 +33,11 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) u16 reg16 = 0; int pos; - pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos) return -EIO; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (!pos) return -EIO; diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 3656e0349dd..2529f3f2ea5 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -25,7 +25,6 @@ #define PCIE_CAPABILITIES_REG 0x2 #define PCIE_SLOT_CAPABILITIES_REG 0x14 #define PCIE_PORT_DEVICE_MAXSERVICES 4 -#define PCI_CFG_SPACE_SIZE 256 #define get_descriptor_id(type, service) (((type - 4) << 4) | service) diff --git a/include/linux/aer.h b/include/linux/aer.h index a2383a72356..f7df1eefc10 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -10,7 +10,6 @@ #if defined(CONFIG_PCIEAER) /* pci-e port driver needs this function to enable aer */ extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); -extern int pci_find_aer_capability(struct pci_dev *dev); extern int pci_disable_pcie_error_reporting(struct pci_dev *dev); extern int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); #else -- cgit v1.2.3-70-g09d2 From 653c03168348ac7aebb969931f87ba281749d7dd Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Mon, 20 Oct 2008 16:00:08 -0700 Subject: misc: replace remaining __FUNCTION__ with __func__ __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Acked-by: Randy Dunlap Signed-off-by: Linus Torvalds --- Documentation/DocBook/kernel-hacking.tmpl | 2 +- arch/arm/mach-iop13xx/include/mach/time.h | 4 ++-- arch/arm/mach-pxa/include/mach/zylonite.h | 4 ++-- arch/powerpc/include/asm/ptrace.h | 2 +- drivers/net/usb/pegasus.c | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl index 4c63e586416..ae15d55350e 100644 --- a/Documentation/DocBook/kernel-hacking.tmpl +++ b/Documentation/DocBook/kernel-hacking.tmpl @@ -1105,7 +1105,7 @@ static struct block_device_operations opt_fops = { - Function names as strings (__FUNCTION__). + Function names as strings (__func__). diff --git a/arch/arm/mach-iop13xx/include/mach/time.h b/arch/arm/mach-iop13xx/include/mach/time.h index 49213d9d7ca..d6d52527589 100644 --- a/arch/arm/mach-iop13xx/include/mach/time.h +++ b/arch/arm/mach-iop13xx/include/mach/time.h @@ -41,7 +41,7 @@ static inline unsigned long iop13xx_core_freq(void) return 1200000000; default: printk("%s: warning unknown frequency, defaulting to 800Mhz\n", - __FUNCTION__); + __func__); } return 800000000; @@ -60,7 +60,7 @@ static inline unsigned long iop13xx_xsi_bus_ratio(void) return 4; default: printk("%s: warning unknown ratio, defaulting to 2\n", - __FUNCTION__); + __func__); } return 2; diff --git a/arch/arm/mach-pxa/include/mach/zylonite.h b/arch/arm/mach-pxa/include/mach/zylonite.h index 0d35ca04731..bf6785adccf 100644 --- a/arch/arm/mach-pxa/include/mach/zylonite.h +++ b/arch/arm/mach-pxa/include/mach/zylonite.h @@ -30,7 +30,7 @@ extern void zylonite_pxa300_init(void); static inline void zylonite_pxa300_init(void) { if (cpu_is_pxa300() || cpu_is_pxa310()) - panic("%s: PXA300/PXA310 not supported\n", __FUNCTION__); + panic("%s: PXA300/PXA310 not supported\n", __func__); } #endif @@ -40,7 +40,7 @@ extern void zylonite_pxa320_init(void); static inline void zylonite_pxa320_init(void) { if (cpu_is_pxa320()) - panic("%s: PXA320 not supported\n", __FUNCTION__); + panic("%s: PXA320 not supported\n", __func__); } #endif diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 734e0754fb9..280a90cc989 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -129,7 +129,7 @@ extern int ptrace_put_reg(struct task_struct *task, int regno, #define CHECK_FULL_REGS(regs) \ do { \ if ((regs)->trap & 1) \ - printk(KERN_CRIT "%s: partial register set\n", __FUNCTION__); \ + printk(KERN_CRIT "%s: partial register set\n", __func__); \ } while (0) #endif /* __powerpc64__ */ diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 38b90e7a7ed..7914867110e 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -168,7 +168,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, netif_device_detach(pegasus->net); if (netif_msg_drv(pegasus) && printk_ratelimit()) dev_err(&pegasus->intf->dev, "%s, status %d\n", - __FUNCTION__, ret); + __func__, ret); goto out; } @@ -192,7 +192,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, if (!buffer) { if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "out of memory in %s\n", - __FUNCTION__); + __func__); return -ENOMEM; } memcpy(buffer, data, size); -- cgit v1.2.3-70-g09d2